今天在调试一个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位)
图片的显示效果会好很多。