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>