Skia 的圖像編解碼部分

jopen 12年前發布 | 49K 次閱讀 Android Android開發 移動開發

今天在調試一個png圖片時,發現解碼出來的效果很差,顯示出一個個模糊塊,后來查看代碼發現原來使用的解碼是RGB565,所以就查證一下代碼,修改成ARGB8888解碼及輸出,希望對需要的朋友有所幫助。

Skia  Google一個底層的圖形、圖像、動畫、SVG、文本等多方面的圖形庫,是 Android 中圖形系統的引擎。Skia 作為第三方軟件放在 external 目錄下: external/skia/

這次只關注我的重點問題,分析\external\skia\src\images下面的代碼:

1、先貼一個框架圖:(不是我畫的,借來用的,呵呵)

Skia 的圖像編解碼部分

從上圖可以看到,具體的圖片解碼以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             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位)

圖片的顯示效果會好很多。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!