Skia往SkBitmap上绘图时画不出来的问题
2016-02-23 09:26
435 查看
使用SkBitmap作为SkCanvas后端绘图时画不出来的问题
用默认条件在采用了Intel Pentium CPU的PC上编译Skia(参见Windows下从源码编译Skia)后,采用SkBitmap作为SkCanvas的后端来绘图时,遇到了奇怪问题:“无论画什么,跟没画一个样”。
代码如下:
经过试验,发现采用下面的代码可以绘制成功:
所以我觉得可能是颜色类型的问题。就一路跟下去,搞了很久也没搞明白……SkCanvas的绘图源码,一层套一层,看起来比较艰辛……没看进去,中间绕道通过将SkCanvas绘制好的SkBitmap的像素手动转换为RGBA达到了目的。不过如果图片大,逐像素转换就很慢……容易的路总是有后患……
SkBitmapDevice在创建绘图设备时校验了拿到的SkBitmap对象的SkImageInfo属性,对其中的Alpha Type和Color Type做了检查。
SkBitmapDevice(SkBitmapDevice.cpp)的构造函数内部会调用valid_for_bitmap_device(),而valid_for_bitmap_device()方法,根据传入的SkImageInfo(就是创建SkBitmap时设定的那个SkImageInfo)做了过滤,只针对kAlpha_8_SkColorType、kRGB_565_SkColorType、kN32_SkColorType三种颜色类别创建SkBitmapDevice,而kN32_SkColorType==kBGRA_8888_SkColorType所以,我设定颜色为kRGBA_8888_SkColorType时,创建出来的SkBitmapDevice是无效的,所以怎么绘制都无效。
valid_for_bitmap_device()的代码如下:
另外根据上面的代码,只处理了kPremul_SkAlphaType、kOpaque_SkAlphaType两种Alpha类型,所以我们给SkBitmap指定Alpha类型时,如果是其他的,也不能成功创建SkBitmapDevice。
现在来看为什么默认编译出来的kN32_SkColorType==kBGRA_8888_SkColorType。
kN32_SkColorType在SkImageInfo.h中定义:
kN32_SkColorType实际上是根据字节序决定的一个值,用到的宏SK_PMCOLOR_BYTE_ORDER在SkPostConfig.h中定义:
因为我的主机是小端字节序,所以SK_PMCOLOR_BYTE_ORDER是:
这个宏又用到了SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT这三个宏。分析SkPostConfig.h可知,默认编译时,这三个宏的值在这里定义:
SK_A32_SHIFT=24、SK_R32_SHIFT=16、SK_G32_SHIFT=8、SK_B32_SHIFT=0,所以,SkImageInfo.h中,SK_PMCOLOR_BYTE_ORDER(B,G,R,A)展开后如下:
这个表达式的值是 true ,所以kN32_SkColorType==kBGRA_8888_SkColorType。
我想用kRGBA_8888_SkColorType,需要在编译时修改SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT这三个宏的值为下面的样子:
这样编译出来的库,kN32_SkColorType==kRGBA_8888_SkColorType。
有两种方法。
编译前修改CFLAGS等变量
生成ninja编译脚本前,在cmd.exe里执行下面的命令即可:
这样编译后,SkBitmapDevice在校验Color Type时,kN32_SkColorType==kRGBA_8888_SkColorType,我们传递的SkBitmap的SkImageInfo的颜色类型为kRGBA_8888_SkColorType,就能成功创建绘图设备。不过,要使用kBGRA_8888_SkColorType的颜色类型的SkBitmap作为SkCanvas的后端绘图设备就不行了。
还有一点要注意:因为编译时通过CFLAGS传递的SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT三个宏的值,并没有被记录到头文件里,所以使用Skia的头文件时,检测出来的SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT还是错的,kN32_SkColorType也是错的哈。要想避免有隐患,请在VS工程里设置这三个宏的值和编译时一致。
修改SkUserConfig.h
如果不想在编译时通过环境变量修改SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT,也可以修改SkUserConfig.h(在include\config目录内),这样的话,改动也被记录下来了,因为SkTypes.h顺序包含了SkPreConfig.h、SkUserConfig.h、SkPostConfig.h,改动在哪里都是有效的,还省去了设置VS工程。SkUserConfig.h中有对这个文件的说明。
(⊙o⊙)…还得继续战斗啊。
硬着头皮读了半天源码,以png为例,楞没发现怎么回事儿。后来都想用下面的方法绕过去了:把解码出来的图片数据逐像素R、B交换。
总是想走容易的路。
再后来使劲实验,去看SkImageDecoder.cpp、SkImageDecoder_libpng.cpp,漫天添加日志信息查看图片解码器的创建流程,花了一天多时间,终于明白了问题在那里(参见Skia图片编解码模块分析):
原来虽然我定义了SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT,把kN32_SkColorType调整过来了,让SkBitmapDevice能创建kRGBA_8888_SkColorType格式的绘图设备了,但上面三个宏,并不能影响图片解码,因为解码器用的是Windows平台的COM组件,不受这三个宏影响,默认解码出来的就是BGRA(参见SkImageDecoder_WIC::decodeStream方法)!
所以,只好分析解码器选择流程,才有了Skia图片编解码模块分析,发现png、gif、jpeg等根本没编译。那么把它们编译进去就好了。
有两个办法:
直接修改out\Release\obj\gyp\images.ninja文件,添加相关解码器的cpp文件
修改skia\gyp\images.gyp,把win平台下的条件编译改一下
我用的第一种,硬把png等加上了,遇到各种错,再改,好歹过来了……skia\gyp\images.gyp中说编译顺序会影响解码器选择,所以,加进去也不行,最后只好直接调用CreatePNGImageDecoder之类的方法,没用SkImageDecoder::DecodeXXX了。
现在看第二种应该更好些,Skia构建系统会自己来处理各种依赖,还能使用统一的SkImageDecoder接口,不过会因为个人的特殊需求污染Skia的编译脚本……
就这样吧。
其他参考文章详见我的专栏:【CEF与PPAPI开发】。
用默认条件在采用了Intel Pentium CPU的PC上编译Skia(参见Windows下从源码编译Skia)后,采用SkBitmap作为SkCanvas的后端来绘图时,遇到了奇怪问题:“无论画什么,跟没画一个样”。
代码如下:
[code]SkImageInfo ii = SkImageInfo::Make(480, 320, kRGBA_8888_SkColorType, kPremul_SkAlphaType); bitmap.allocPixels(ii, ii.minRowBytes()); SkCanvas canvas(bitmap);
经过试验,发现采用下面的代码可以绘制成功:
[code]SkImageInfo ii = SkImageInfo::Make(480, 320, kBGRA_8888_SkColorType, kPremul_SkAlphaType); bitmap.allocPixels(ii, ii.minRowBytes()); SkCanvas canvas(bitmap);
所以我觉得可能是颜色类型的问题。就一路跟下去,搞了很久也没搞明白……SkCanvas的绘图源码,一层套一层,看起来比较艰辛……没看进去,中间绕道通过将SkCanvas绘制好的SkBitmap的像素手动转换为RGBA达到了目的。不过如果图片大,逐像素转换就很慢……容易的路总是有后患……
SkBitmapDevice与kN32_SkColorType
骨头还是得啃。我确认了使用SkBitmap作为SkCanvas的后端时,比如上面的代码,实际上SkCanvas自己分配了一个SkBitmapDevice来作为绘图设备,绘图操作会递交给SkBitmapDevice来完成。SkBitmapDevice在创建绘图设备时校验了拿到的SkBitmap对象的SkImageInfo属性,对其中的Alpha Type和Color Type做了检查。
SkBitmapDevice(SkBitmapDevice.cpp)的构造函数内部会调用valid_for_bitmap_device(),而valid_for_bitmap_device()方法,根据传入的SkImageInfo(就是创建SkBitmap时设定的那个SkImageInfo)做了过滤,只针对kAlpha_8_SkColorType、kRGB_565_SkColorType、kN32_SkColorType三种颜色类别创建SkBitmapDevice,而kN32_SkColorType==kBGRA_8888_SkColorType所以,我设定颜色为kRGBA_8888_SkColorType时,创建出来的SkBitmapDevice是无效的,所以怎么绘制都无效。
valid_for_bitmap_device()的代码如下:
[code]static bool valid_for_bitmap_device(const SkImageInfo& info, SkAlphaType* newAlphaType) { if (info.width() < 0 || info.height() < 0) { return false; } // TODO: can we stop supporting kUnknown in SkBitmkapDevice? if (kUnknown_SkColorType == info.colorType()) { if (newAlphaType) { *newAlphaType = kUnknown_SkAlphaType; } return true; } switch (info.alphaType()) { case kPremul_SkAlphaType: case kOpaque_SkAlphaType: break; default: return false; } SkAlphaType canonicalAlphaType = info.alphaType(); switch (info.colorType()) { case kAlpha_8_SkColorType: break; case kRGB_565_SkColorType: canonicalAlphaType = kOpaque_SkAlphaType; break; case kN32_SkColorType: break; default: return false; } if (newAlphaType) { *newAlphaType = canonicalAlphaType; } return true; }
另外根据上面的代码,只处理了kPremul_SkAlphaType、kOpaque_SkAlphaType两种Alpha类型,所以我们给SkBitmap指定Alpha类型时,如果是其他的,也不能成功创建SkBitmapDevice。
现在来看为什么默认编译出来的kN32_SkColorType==kBGRA_8888_SkColorType。
kN32_SkColorType在SkImageInfo.h中定义:
[code]enum SkColorType { kUnknown_SkColorType, kAlpha_8_SkColorType, kRGB_565_SkColorType, kARGB_4444_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType, kIndex_8_SkColorType, kGray_8_SkColorType, kLastEnum_SkColorType = kGray_8_SkColorType, #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) kN32_SkColorType = kBGRA_8888_SkColorType, #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) kN32_SkColorType = kRGBA_8888_SkColorType, #else #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order" #endif };
kN32_SkColorType实际上是根据字节序决定的一个值,用到的宏SK_PMCOLOR_BYTE_ORDER在SkPostConfig.h中定义:
[code]#ifdef SK_CPU_BENDIAN # define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \ (SK_ ## C3 ## 32_SHIFT == 0 && \ SK_ ## C2 ## 32_SHIFT == 8 && \ SK_ ## C1 ## 32_SHIFT == 16 && \ SK_ ## C0 ## 32_SHIFT == 24) #else # define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \ (SK_ ## C0 ## 32_SHIFT == 0 && \ SK_ ## C1 ## 32_SHIFT == 8 && \ SK_ ## C2 ## 32_SHIFT == 16 && \ SK_ ## C3 ## 32_SHIFT == 24) #endif
因为我的主机是小端字节序,所以SK_PMCOLOR_BYTE_ORDER是:
[code] # define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \ (SK_ ## C0 ## 32_SHIFT == 0 && \ SK_ ## C1 ## 32_SHIFT == 8 && \ SK_ ## C2 ## 32_SHIFT == 16 && \ SK_ ## C3 ## 32_SHIFT == 24)
这个宏又用到了SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT这三个宏。分析SkPostConfig.h可知,默认编译时,这三个宏的值在这里定义:
[code]#ifdef SK_BUILD_FOR_WIN # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # define WIN32_IS_MEAN_WAS_LOCALLY_DEFINED # endif # ifndef NOMINMAX # define NOMINMAX # define NOMINMAX_WAS_LOCALLY_DEFINED # endif # # include <windows.h> # # ifdef WIN32_IS_MEAN_WAS_LOCALLY_DEFINED # undef WIN32_IS_MEAN_WAS_LOCALLY_DEFINED # undef WIN32_LEAN_AND_MEAN # endif # ifdef NOMINMAX_WAS_LOCALLY_DEFINED # undef NOMINMAX_WAS_LOCALLY_DEFINED # undef NOMINMAX # endif # # ifndef SK_A32_SHIFT # define SK_A32_SHIFT 24 # define SK_R32_SHIFT 16 # define SK_G32_SHIFT 8 # define SK_B32_SHIFT 0 # endif # #endif
SK_A32_SHIFT=24、SK_R32_SHIFT=16、SK_G32_SHIFT=8、SK_B32_SHIFT=0,所以,SkImageInfo.h中,SK_PMCOLOR_BYTE_ORDER(B,G,R,A)展开后如下:
[code]SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && SK_A32_SHIFT == 24
这个表达式的值是 true ,所以kN32_SkColorType==kBGRA_8888_SkColorType。
我想用kRGBA_8888_SkColorType,需要在编译时修改SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT这三个宏的值为下面的样子:
[code]SK_A32_SHIFT=24 SK_B32_SHIFT=16 SK_G32_SHIFT=8 SK_R32_SHIFT=0
这样编译出来的库,kN32_SkColorType==kRGBA_8888_SkColorType。
有两种方法。
编译前修改CFLAGS等变量
生成ninja编译脚本前,在cmd.exe里执行下面的命令即可:
[code] set "CFLAGS=-DSK_A32_SHIFT=24 -DSK_B32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_R32_SHIFT=0" set "CPPFLAGS=-DSK_A32_SHIFT=24 -DSK_B32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_R32_SHIFT=0" set "CXXFLAGS=-DSK_A32_SHIFT=24 -DSK_B32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_R32_SHIFT=0"
这样编译后,SkBitmapDevice在校验Color Type时,kN32_SkColorType==kRGBA_8888_SkColorType,我们传递的SkBitmap的SkImageInfo的颜色类型为kRGBA_8888_SkColorType,就能成功创建绘图设备。不过,要使用kBGRA_8888_SkColorType的颜色类型的SkBitmap作为SkCanvas的后端绘图设备就不行了。
还有一点要注意:因为编译时通过CFLAGS传递的SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT三个宏的值,并没有被记录到头文件里,所以使用Skia的头文件时,检测出来的SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT还是错的,kN32_SkColorType也是错的哈。要想避免有隐患,请在VS工程里设置这三个宏的值和编译时一致。
修改SkUserConfig.h
如果不想在编译时通过环境变量修改SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT,也可以修改SkUserConfig.h(在include\config目录内),这样的话,改动也被记录下来了,因为SkTypes.h顺序包含了SkPreConfig.h、SkUserConfig.h、SkPostConfig.h,改动在哪里都是有效的,还省去了设置VS工程。SkUserConfig.h中有对这个文件的说明。
解码图片时Red与Blue通道反了
解决了问题之后,新的问题又来:解码图片时Red与Blue通道反了。(⊙o⊙)…还得继续战斗啊。
硬着头皮读了半天源码,以png为例,楞没发现怎么回事儿。后来都想用下面的方法绕过去了:把解码出来的图片数据逐像素R、B交换。
总是想走容易的路。
再后来使劲实验,去看SkImageDecoder.cpp、SkImageDecoder_libpng.cpp,漫天添加日志信息查看图片解码器的创建流程,花了一天多时间,终于明白了问题在那里(参见Skia图片编解码模块分析):
原来虽然我定义了SK_A32_SHIFT、SK_R32_SHIFT、SK_G32_SHIFT、SK_B32_SHIFT,把kN32_SkColorType调整过来了,让SkBitmapDevice能创建kRGBA_8888_SkColorType格式的绘图设备了,但上面三个宏,并不能影响图片解码,因为解码器用的是Windows平台的COM组件,不受这三个宏影响,默认解码出来的就是BGRA(参见SkImageDecoder_WIC::decodeStream方法)!
所以,只好分析解码器选择流程,才有了Skia图片编解码模块分析,发现png、gif、jpeg等根本没编译。那么把它们编译进去就好了。
有两个办法:
直接修改out\Release\obj\gyp\images.ninja文件,添加相关解码器的cpp文件
修改skia\gyp\images.gyp,把win平台下的条件编译改一下
我用的第一种,硬把png等加上了,遇到各种错,再改,好歹过来了……skia\gyp\images.gyp中说编译顺序会影响解码器选择,所以,加进去也不行,最后只好直接调用CreatePNGImageDecoder之类的方法,没用SkImageDecoder::DecodeXXX了。
现在看第二种应该更好些,Skia构建系统会自己来处理各种依赖,还能使用统一的SkImageDecoder接口,不过会因为个人的特殊需求污染Skia的编译脚本……
就这样吧。
其他参考文章详见我的专栏:【CEF与PPAPI开发】。
相关文章推荐
- ros pub sub ("~")
- codeforces 417D. Cunning Gena 状压dp
- easyui-prompt弹出框操作
- adb client, adb server, adbd原理浅析
- 在没有盗版的世界:Linux 桌面装机量占比可达40%
- C实例--推断一个字符串是否是回文数
- zoj 1003 Crashing Balloon
- 云舒网络:容器系列二:容器的视角-设计交付和架构
- Python中调用父类的同名方法
- VOC-release4.01 DPM训练的model(mat)转为OpenCV latentsvm可以加载的model(xml)
- ajax级联菜单实例代码下载
- JDK1.8的一些改动
- 在Eclipse中打开文件夹
- 使用xcode storyboard设置按钮圆角方法
- EMC FAST Cache和FAST VP简单比较
- 桥接 NAT HOST-ONLY
- jQuery拖拽排序插件制作拖拽排序效果(附源码下载)
- iOS simulator Code = 4
- redis
- C++语言-01-简介