Skia 的图像编解码部分
jopen
13年前
<span style="font-size:18px;">今天在调试一个png图片时,发现解码出来的效果很差,显示出一个个模糊块,后来查看代码发现原来使用的解码是RGB565,所以就查证一下代码,修改成ARGB8888解码及输出,希望对需要的朋友有所帮助。</span> <p><span style="font-size:18px;"><span style="white-space:pre;"><span style="line-height:26px;font-family:宋体;color:#333333;"><span lang="EN-US">Skia </span>是<span lang="EN-US"> Google</span>一个底层的图形、图像、动画、<span lang="EN-US">SVG</span>、文本等多方面的图形库,是<span lang="EN-US"> Android </span>中图形系统的引擎。<span lang="EN-US">Skia </span>作为第三方软件放在<span lang="EN-US"> external </span>目录下:<span lang="EN-US"> external/skia/</span></span></span></span></p> <p><span style="font-size:18px;"><span style="white-space:pre;"><span style="line-height:26px;font-family:宋体;color:#333333;"><span lang="EN-US">这次只关注我的重点问题,分析\external\skia\src\images下面的代码:</span></span></span></span></p> <p><span style="font-size:18px;"><span style="white-space:pre;"><span style="line-height:26px;font-family:宋体;color:#333333;"><span lang="EN-US">1、先贴一个框架图:(不是我画的,借来用的,呵呵)</span></span></span></span></p> <p></p> <p style="text-align:center;"><span style="font-size:18px;"><span style="white-space:pre;"><span style="line-height:26px;font-family:宋体;color:#333333;"><span lang="EN-US"><img alt="Skia 的图像编解码部分 " src="https://simg.open-open.com/show/34649044e9cd201f13c4abce5851777a.png" width="476" height="418" /><br /> </span></span></span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">从上图可以看到,具体的图片解码以codec plugin或者库方式给SkiaImageDecoder提供具体的功能。</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">对于<span style="line-height:26px;font-family:Arial;color:#333333;font-size:14px;"><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">Skia</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">的图像编解码部分:</span></span></span></span></span></span></span></p> <p></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">external/include/image/SKImageDecoder.h //</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">把图像文件或者流解码到</span> </span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">skia</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">的内部内存</span></span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">SKBitmap</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">中</span> </span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">;</span></span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">external/include/image/SKImageEncoder.h //</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">把</span> </span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">skia</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">内部内存</span> </span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">SKBitmap</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">编码成文件或流的形式;</span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">这些接口需要具体的类实现,主要代码在</span> </span></span></span><span style="font-family:'AR PL UMing CN',serif;"><span style="font-family:Arab;"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">src/image</span> </span></span></span></span><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">文件中。</span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">2、下面以代码进行简单的说明一下:</span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;"><span style="white-space:pre;">/** \class SkImageDecoder<br /> </span> <span style="white-space:pre;">Base class for decoding compressed images into a SkBitmap*/<br /> class SkImageDecoder { </span></span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;"><span style="white-space:pre;"> // Should be consistent with kFormatName<br /> enum Format {<br /> kUnknown_Format,<br /> kBMP_Format,<br /> kGIF_Format,<br /> kICO_Format,<br /> kJPEG_Format,<br /> kPNG_Format,<br /> kWBMP_Format,<br /> <br /> <br /> kLastKnownFormat = kWBMP_Format<br /> }; </span>//【这里可以指定使用哪种方式进行解码】</span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">【子类需要复写的方法】</span></span></span></span></p> <p style="margin-bottom:0cm;" align="left"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">protected:<br /> // must be overridden in subclasses. This guy is called by decode(...)<br /> virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;<br /> <br /> <br /> // If the decoder wants to support tiled based decoding,<br /> // this method must be overridden. This guy is called by buildTileIndex(...)<br /> virtual bool onBuildTileIndex(SkStream*,<br /> int *width, int *height) {<br /> return false;<br /> }<br /> <br /> <br /> // If the decoder wants to support tiled based decoding,<br /> // this method must be overridden. This guy is called by decodeRegion(...)<br /> virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect) {<br /> return false;<br /> } </span></span></span></span></p> <br /> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;"><span style="line-height:26px;font-family:Arial;color:#333333;font-size:14px;"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">3、子类实现的代码格式,以jpeg为例:</span></span></span></span></span></span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;"><span style="line-height:26px;font-family:Arial;color:#333333;font-size:14px;"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;"><span style="white-space:pre;">class SkJPEGImageDecoder : public SkImageDecoder {</span> 【首先继承<span style="line-height:26px;font-family:'AR PL UKai CN';white-space:pre;color:#333333;font-size:16px;"><span style="white-space:pre;">SkImageDecoder</span>类</span>】</span></span></span></span></span></span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;"><span style="line-height:26px;font-family:Arial;color:#333333;font-size:14px;"><span style="font-family:'AR PL UKai CN';"><span style="font-size:16px;"><span style="font-style:normal;"><span style="font-weight:normal;">protected:<br /> virtual bool onBuildTileIndex(SkStream *stream,<br /> int *width, int *height);<br /> virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);<br /> virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); </span></span></span></span></span></span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">}; 【需要实现的重要三个方法】</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;"> </span></span><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;">如此的话,调用逻辑如下:</span></p> <p><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;">【从父类调用过用,选择具体的解码库,调用子类的onDecode方法进行具体解码工作】</span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,<br /> SkBitmap::Config pref, Mode mode) {<br /> // pass a temporary bitmap, so that if we return false, we are assured of<br /> // leaving the caller's bitmap untouched.<br /> SkBitmap tmp;<br /> <br /> <br /> // we reset this to false before calling onDecode<br /> fShouldCancelDecode = false;<br /> // assign this, for use by getPrefConfig(), in case fUsePrefTable is false<br /> fDefaultPref = pref;<br /> <br /> if (!this->onDecode(stream, &tmp, mode)) {<br /> return false;<br /> }<br /> bm->swap(tmp);<br /> return true;<br /> }</span></span></p> <p></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">4、举例说明一下用法:</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">SkBitmap bp;<br /> SkImageDecoder::Format fmt; <br /> char propBuf[PROPERTY_VALUE_MAX]; <br /> LOGI("property_get: %s.", "<span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;">/data/test.jpeg"</span>);<br /> Bool result = SkImageDecoder::DecodeFile(propBuf,<br /> <span style="white-space:pre;"> </span>&bp,SkBitmap::kARGB_8888_Config, <br /> <span style="white-space:pre;"> </span>SkImageDecoder::kDecodePixels_Mode, <br /> <span style="white-space:pre;"> </span>&fmt);<br /> if(!result){<br /> LOGI("decoder file fail!");<br /> }else{<br /> if(fmt!= SkImageDecoder::kJPEG_Format){<br /> LOGI("decoder file not jpeg!");<br /> }else{<br /> LOGI("width %d,height %d,rowBytesAsPixels %d,config %d,<br /> bytesPerPixel %d",bp.width(),bp.height(),bp.rowBytesAsPixels(),bp.config(),bp.bytesPerPixel());<br /> FILE *f_rgb=fopen("/data/test_argb8888.raw","wb");<br /> short *pixl = (short *) bp.getPixels();<br /> for(int j=0;j <bp.height();j++){<br /> fwrite(pixl,1,bp.width()*bp.bytesPerPixel(),f_rgb);<br /> pixl += bp.rowBytesAsPixels();<br /> }<br /> fclose(f_rgb);<br /> }<br /> } </span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">利用DecodeFile解析出来并保存到文件中</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;"><br /> </span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">5、对于文件格式的修改:</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">A、解码的格式,这里首先确认解码出来的图片是argb8888格式的</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">1)、DecodeFile、DecodeMemory,DecodeStream中通过SkBitmap::Config prefConfig进行指定</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">2)、利用函数setPrefConfigTable 进行指定</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">B、初始化skia库指定默认颜色格式</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">AndroidRuntime::AndroidRuntime()<br /> {<br /> SkGraphics::Init();<br /> // this sets our preference for 16bit images during decode<br /> // in case the src is opaque and 24bit<br /> SkImageDecoder::SetDeviceConfig(SkBitmap::kARGB_8888_Config); </span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">...</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">}</span></span></p> <p><span style="font-family:宋体;color:#333333;font-size:18px;"><span style="line-height:26px;white-space:pre;">C、显示时设定颜色格式</span></span><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;"><span style="white-space:pre;"> </span></span></p> <p><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;"><span style="white-space:pre;">设定默认图片解码格式:<br /> frameworks\base\core\java\android\view\SurfaceView.java<br /> //int mRequestedFormat = PixelFormat.RGB_565;<br /> /**解决图片解码遇到的像素问题,把默认的565改为8888*/<br /> int mRequestedFormat = PixelFormat.RGBX_8888;<br /> <br /> <br /> frameworks\base\core\java\android\app\WallpaperManager.java<br /> // This is the final bitmap we want to return.<br /> // XXX We should get the pixel depth from the system (to match the<br /> // physical display depth), when there is a way.<br /> Bitmap newbm = Bitmap.createBitmap(width, height,<br /> Bitmap.Config.ARGB_8888); </span></span></p> <p><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;"><span style="white-space:pre;"> <br /> </span>基本上修改以上几个点后,解码图片默认是argb8888格式(32位)而不是默认的rgb565(16位)</span></p> <p><span style="line-height:26px;font-family:宋体;white-space:pre;color:#333333;font-size:18px;">图片的显示效果会好很多。</span></p>