Skia 的图像编解码部分

今天在调试一个png图片时,发现解码出来的效果很差,显示出一个个模糊块,后来查看代码发现原来使用的解码是RGB565,所以就查证一下代码,修改成ARGB8888解码及输出,希望对需要的朋友有所帮助。

Skia  Google一个底层的图形、图像、动画、SVG、文本等多方面的图形库,是 Android 中图形系统的引擎。Skia 作为第三方软件放在 external 目录下: external/skia/

这次只关注我的重点问题,分析\external\skia\src\images下面的代码:

1、先贴一个框架图:(不是我画的,借来用的,呵呵)


从上图可以看到,具体的图片解码以codec plugin或者库方式给SkiaImageDecoder提供具体的功能。

对于Skia 的图像编解码部分:

external/include/image/SKImageDecoder.h // 把图像文件或者流解码到 skia 的内部内存SKBitmap  ;

external/include/image/SKImageEncoder.h //  skia 内部内存 SKBitmap 编码成文件或流的形式;

这些接口需要具体的类实现,主要代码在 src/image 文件中。

2、下面以代码进行简单的说明一下:

/** \class SkImageDecoder
    
Base class for decoding compressed images into a SkBitmap*/
class SkImageDecoder {

    // Should be consistent with kFormatName
    enum Format {
        kUnknown_Format,
        kBMP_Format,
        kGIF_Format,
        kICO_Format,
        kJPEG_Format,
        kPNG_Format,
        kWBMP_Format,


        kLastKnownFormat = kWBMP_Format
    };
//【这里可以指定使用哪种方式进行解码】

【子类需要复写的方法】

protected:
    // must be overridden in subclasses. This guy is called by decode(...)
    virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;


    // If the decoder wants to support tiled based decoding,
    // this method must be overridden. This guy is called by buildTileIndex(...)
    virtual bool onBuildTileIndex(SkStream*,
                int *width, int *height) {
        return false;
    }


    // If the decoder wants to support tiled based decoding,
    // this method must be overridden. This guy is called by decodeRegion(...)
    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect) {
        return false;
    }


3、子类实现的代码格式,以jpeg为例:

class SkJPEGImageDecoder : public SkImageDecoder { 【首先继承SkImageDecoder

protected:
    virtual bool onBuildTileIndex(SkStream *stream,
                                int *width, int *height);
    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);

}; 【需要实现的重要三个方法】

 如此的话,调用逻辑如下:

【从父类调用过用,选择具体的解码库,调用子类的onDecode方法进行具体解码工作】

bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
                           SkBitmap::Config pref, Mode mode) {
   // pass a temporary bitmap, so that if we return false, we are assured of
   // leaving the caller's bitmap untouched.
   SkBitmap    tmp;


   // we reset this to false before calling onDecode
   fShouldCancelDecode = false;
   // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
   fDefaultPref = pref;

   if (!this->onDecode(stream, &tmp, mode)) {
       return false;
   }
   bm->swap(tmp);
   return true;
}

4、举例说明一下用法:

SkBitmap bp;
SkImageDecoder::Format fmt;
   char propBuf[PROPERTY_VALUE_MAX]; 
    LOGI("property_get: %s.", "/data/test.jpeg");
Bool result = SkImageDecoder::DecodeFile(propBuf,
  &bp,SkBitmap::kARGB_8888_Config, 
  SkImageDecoder::kDecodePixels_Mode, 
  &fmt);
    if(!result){
        LOGI("decoder file fail!");
    }else{
        if(fmt!= SkImageDecoder::kJPEG_Format){
            LOGI("decoder file not jpeg!");
        }else{
LOGI("width %d,height %d,rowBytesAsPixels %d,config %d,
bytesPerPixel %d",bp.width(),bp.height(),bp.rowBytesAsPixels(),bp.config(),bp.bytesPerPixel());
        FILE *f_rgb=fopen("/data/test_argb8888.raw","wb");
        short *pixl = (short *) bp.getPixels();
        for(int j=0;j<bp.height();j++){
            fwrite(pixl,1,bp.width()*bp.bytesPerPixel(),f_rgb);
            pixl += bp.rowBytesAsPixels();
        }
        fclose(f_rgb);
        }
}

利用DecodeFile解析出来并保存到文件中


5、对于文件格式的修改:

A、解码的格式,这里首先确认解码出来的图片是argb8888格式的

1)、DecodeFile、DecodeMemory,DecodeStream中通过SkBitmap::Config prefConfig进行指定

2)、利用函数setPrefConfigTable 进行指定

B、初始化skia库指定默认颜色格式

AndroidRuntime::AndroidRuntime()
{
     SkGraphics::Init();
     // this sets our preference for 16bit images during decode
    // in case the src is opaque and 24bit
    SkImageDecoder::SetDeviceConfig(SkBitmap::kARGB_8888_Config);

...

}

C、显示时设定颜色格式

设定默认图片解码格式:
frameworks\base\core\java\android\view\SurfaceView.java
//int mRequestedFormat = PixelFormat.RGB_565;
/**解决图片解码遇到的像素问题,把默认的565改为8888*/
int mRequestedFormat = PixelFormat.RGBX_8888;


frameworks\base\core\java\android\app\WallpaperManager.java
// This is the final bitmap we want to return.
// XXX We should get the pixel depth from the system (to match the
// physical display depth), when there is a way.
Bitmap newbm = Bitmap.createBitmap(width, height,
   Bitmap.Config.ARGB_8888); 

         
基本上修改以上几个点后,解码图片默认是argb8888格式(32位)而不是默认的rgb565(16位)

图片的显示效果会好很多。


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值