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
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位)
圖片的顯示效果會好很多。