本文主要是介绍2311skia,07编解码图像下,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
5
,典型解码器的onDecode
方法
可做为编解码图像
库用法的参考
.
(1)Jpeg
bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
#ifdef TIME_DECODESkAutoTime atm("JPEG Decode");
#endifJPEGAutoClean autoClean;jpeg_decompress_struct cinfo;skjpeg_source_mgr srcManager(stream, this);skjpeg_error_mgr errorManager;set_error_mgr(&cinfo, &errorManager);//在调用`setjmp`前,要`实例化`所有对象,以便在错误时正确清理它们.if (setjmp(errorManager.fJmpBuf)) {return return_false(cinfo, *bm, "setjmp");}initialize_info(&cinfo, &srcManager);autoClean.set(&cinfo);int status = jpeg_read_header(&cinfo, true);if (status != JPEG_HEADER_OK) {return return_false(cinfo, *bm, "read_header");}/*试满足请求的`sampleSize`.因为`jpeg`(尽量)可更快,因此只需使用他们的`num/denomapi`来估算大小即可.*/int sampleSize = this->getSampleSize();set_dct_method(*this, &cinfo);SkASSERT(1 == cinfo.scale_num);cinfo.scale_denom = sampleSize;turn_off_visual_optimizations(&cinfo);const SkColorType colorType = this->getBitmapColorType(&cinfo);const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ? kPremul_SkAlphaType : kOpaque_SkAlphaType;adjust_out_color_space_and_dither(&cinfo, colorType, *this);if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {//假设`A8`位图不透明,以避免`检查`每个单独的像素.它不太可能是不透明的,因为不透明的`A8`位图不会很有趣.否则,`jpeg`图像是不透明的.return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height, colorType, alphaType));}/*`image_width`和`image_height`是,在`jpeg_read_header()`之后可用的`原始尺寸`.要查看缩放的维度,必须调用`jpeg_start_decompress()`,然后读取`output_width`和`output_height.`*/if (!jpeg_start_decompress(&cinfo)) {/*如果在此失败了,如果他们只是想(子采样边界),可能仍有足够的信息来返回给调用者.如果`sampleSize`为`1`,则已可返回了.因此,只需检查是否为`kDecodeBounds_Mode`状态,及是否有有效的输出大小.这里失败的一个原因是没有足够的`流数据`来完成设置.然而,似乎很早就计算出来了`输出维度`,因此该特殊的检查有作用.*/if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height, recompute_sampleSize(sampleSize, cinfo));//假设`A8`位图不透明,以避免检查每个单独的像素.它不太可能是不透明的,因为不透明的`A8`位图不会很有趣.否则,`jpeg`图像是不透明的.return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(), colorType, alphaType));} else {return return_false(cinfo, *bm, "start_decompress");}}sampleSize = recompute_sampleSize(sampleSize, cinfo);
#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER//(如果有)是否应该允许选择器`选择`颜色类型if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {return return_false(cinfo, *bm, "chooseFromOneChoice");}
#endifSkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);//假设`A8`位图不透明,以避免检查每个单独的像素.它不太可能是不透明的,因为不透明的`A8`位图不会很有趣.否则`,jpeg`图像是不透明的.bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType, alphaType));if (SkImageDecoder::kDecodeBounds_Mode == mode) {return true;}if (!this->allocPixelRef(bm, NULL)) {return return_false(cinfo, *bm, "allocPixelRef");}SkAutoLockPixels alp(*bm);
#ifdef ANDROID_RGB/*如果可能,请短路`SkScaledBitmapSampler`,因为这会显著提高性能.*/if (sampleSize == 1 &&((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||(kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565))){JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();INT32 const bpr = bm->rowBytes();while (cinfo.output_scanline < cinfo.output_height) {int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);if (0 == row_count) {//如果`row_count==0`,则没有得到扫描行,所以`提前返回`.返回部分图像.fill_below_level(cinfo.output_scanline, bm);cinfo.output_scanline = cinfo.output_height;break; //跳到`jpeg_finish_decompress()`}if (this->shouldCancelDecode()) {return return_false(cinfo, *bm, "shouldCancelDecode");}rowptr += bpr;}jpeg_finish_decompress(&cinfo);return true;}
#endif//检查支持的格式SkScaledBitmapSampler::SrcConfig sc;int srcBytesPerPixel;if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {return return_false(cinfo, *bm, "jpeg colorspace");}if (!sampler.begin(bm, sc, *this)) {return return_false(cinfo, *bm, "sampler.begin");}SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);uint8_t* srcRow = (uint8_t*)srcStorage.get();//可能跳过初始行`[sampler.srcY0]`if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {return return_false(cinfo, *bm, "skip rows");}//现在遍历扫描行,直到`y==bm->height()-1`for (int y = 0;; y++) {JSAMPLE* rowptr = (JSAMPLE*)srcRow;int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);if (0 == row_count) {//如果`row_count==0`,则没有得到扫描行,所以提前返回.返回部分图像.fill_below_level(y, bm);cinfo.output_scanline = cinfo.output_height;break; //跳到`jpeg_finish_decompress()`}if (this->shouldCancelDecode()) {return return_false(cinfo, *bm, "shouldCancelDecode");}if (JCS_CMYK == cinfo.out_color_space) {convert_CMYK_to_RGB(srcRow, cinfo.output_width);}sampler.next(srcRow);if (bm->height() - 1 == y) {//大功告成break;}if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {return return_false(cinfo, *bm, "skip rows");}}//正式跳过了其余部分,因此`libjpeg`不会抱怨if (!skip_src_rows(&cinfo, srcRow, cinfo.output_height - cinfo.output_scanline)) {return return_false(cinfo, *bm, "skip rows");}jpeg_finish_decompress(&cinfo);return true;
}
a
,配置Jpeg
解码选项(输出格式,采样率
,idct
类型,下采样率
等等,在SkImageDecoder.h
的注释中解释过一些)
b
,在不设置下采样
且目标格式
为RGBA
时,直接解码到目标Bitmap
上(Jpeg
是按YUV
三分量分别压缩的,设置为Jpeg
库解码时作YUV-RGBA
的颜色转换).
c
,需要下采样
时,因为可简化jpeg
库,因此重新计算
一个下采样率
,在目标位图
上,下采样SkScaledBitmapSampler
解码出来的图像
.
(2)Png
bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp, png_infop *info_ptrp) {/*使用期望错误处理器函数创建并初化`png_struct`.如果要用默认的`stderr`和`longjump`方法,可为最后三个参数提供`NULL`.还提供了编译器头文件版本,以便知道应用是否使用`兼容版本`的库编译.*/png_error_ptr user_warning_fn =(c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;/*`NULL`表示保留为默认库行为.*//*`c_suppressPNGImageDecoderWarnings`默认值根据`SK_DEBUG`.*//*要使用`SK_DEBUG`二进制文件,禁止显示警告,请设置*环境变量`"skia_images_png_suppressDecoderWarnings"*`为`"true"`.在链接到`skia`的程序中:`*SK_CONF_SET("images.png.suppressDecoderWarnings",true);`*/png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, user_warning_fn);//`png_voidp user_error_ptr,user_error_fn,user_warning_fn);`if (png_ptr == NULL) {return false;}*png_ptrp = png_ptr;/*`分配/初化`图像信息的内存.*/png_infop info_ptr = png_create_info_struct(png_ptr);if (info_ptr == NULL) {png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);return false;}*info_ptrp = info_ptr;/*如果使用`setjmp/longjmp`方法,请设置错误处理(这是使用`libpng`执行操作的普通方法).除非在前面`png_create_read_struct()`中设置了自己的错误处理器,否则为必需.*/if (setjmp(png_jmpbuf(png_ptr))) {png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);return false;}/*如果使用替换读函数,而不是在此处调用`*png_init_io()`,你可调用:*/png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
#ifdef SK_BUILD_FOR_ANDROIDpng_set_seek_fn(png_ptr, sk_seek_fn);
#endif/*其中`user_io_ptr`是想可用来回调的结构*//*如果已阅读了一些签名*///`png_set_sig_bytes(png_ptr,0/*sig_read*/);`连接的偷看器,以便可见调用者可能感兴趣的用户块png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);if (this->getPeeker()) {png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);}/*调用`png_read_info()`,提供了第一个`IDAT`(图像数据块)之前的`PNG`文件中的所有信息.*/png_read_info(png_ptr, info_ptr);png_uint_32 origWidth, origHeight;int bitDepth, colorType;png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &colorType, int_p_NULL, int_p_NULL, int_p_NULL);/*告诉`libpng`去掉`16`位/颜色文件为8位/颜色*/if (bitDepth == 16) {png_set_strip_16(png_ptr);}/*将位深度为`1,2`和4的多个像素从单个字节提取进单独字节(适合调色板和灰度图像).*/if (bitDepth < 8) {png_set_packing(png_ptr);}/*将灰度图像从`1,2`或4位/像素扩展到完整的8位*/if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {png_set_expand_gray_1_2_4_to_8(png_ptr);}return true;
}
bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, Mode mode) {png_structp png_ptr;png_infop info_ptr;if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {return false;}PNGAutoClean autoClean(png_ptr, info_ptr);if (setjmp(png_jmpbuf(png_ptr))) {return false;}png_uint_32 origWidth, origHeight;int bitDepth, pngColorType, interlaceType;png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);SkColorType colorType;bool hasAlpha = false;SkPMColor theTranspColor = 0; //0告诉不要试匹配if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {return false;}SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;const int sampleSize = this->getSampleSize();SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);decodedBitmap->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType, alphaType));if (SkImageDecoder::kDecodeBounds_Mode == mode) {return true;}//从此,关注的是`颜色表`和`跟踪`的像素,如果真看到`不透明`的像素,因为有时`PNG`设置其颜色类型为`|=PNG_COLOR_MASK_ALPHA`,但它的`所有像素`都是不透明的.小心,因为如果可标记位图不透明,绘画的速度会更快bool reallyHasAlpha = false;SkColorTable* colorTable = NULL;if (pngColorType == PNG_COLOR_TYPE_PALETTE) {decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);}SkAutoUnref aur(colorTable);if (!this->allocPixelRef(decodedBitmap, kIndex_8_SkColorType == colorType ? colorTable : NULL)) {return false;}SkAutoLockPixels alp(*decodedBitmap);/*打开隔行扫描处理.如果不使用`png_read_image()`,则为必须.要了解如何处理隔行扫描,见下面的`png_read_row()`方法:*/const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ? png_set_interlace_handling(png_ptr) : 1;/*可选调用`gamma`校正,并添加`背景`到调色板,并更新`信息结构`.如果想`libpng`为你更新调色板(即你在`上面`选择了`此转换`),则为必需.*/png_read_update_info(png_ptr, info_ptr);if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) &&1 == sampleSize) {if (kAlpha_8_SkColorType == colorType) {//对`A8`位图,假设速度有一个`alpha`.位图可能是不透明的,但这是一个不太可能的用例,因为它不会很有趣. reallyHasAlpha = true;//仅当原图为灰色时,才允许使用`A8`.SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);}for (int i = 0; i < number_passes; i++) {for (png_uint_32 y = 0; y < origHeight; y++) {uint8_t* bmRow = decodedBitmap->getAddr8(0, y);png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);}}} else {SkScaledBitmapSampler::SrcConfig sc;int srcBytesPerPixel = 4;if (colorTable != NULL) {sc = SkScaledBitmapSampler::kIndex;srcBytesPerPixel = 1;} else if (kAlpha_8_SkColorType == colorType) {//仅当原图为灰色时,才允许使用`A8`.SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);sc = SkScaledBitmapSampler::kGray;srcBytesPerPixel = 1;} else if (hasAlpha) {sc = SkScaledBitmapSampler::kRGBA;} else {sc = SkScaledBitmapSampler::kRGBX;}/*必须显式传递颜色表,因为要求升级`png`的调色板到直接模型,即使`decodedBitmap`没有,也可能有一个*/SkAutoLockColors ctLock(colorTable);if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) {return false;}const int height = decodedBitmap->height();if (number_passes > 1) {SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);uint8_t* base = (uint8_t*)storage.get();size_t rowBytes = origWidth * srcBytesPerPixel;for (int i = 0; i < number_passes; i++) {uint8_t* row = base;for (png_uint_32 y = 0; y < origHeight; y++) {uint8_t* bmRow = row;png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);row += rowBytes;}}//现在采样它.base += sampler.srcY0() * rowBytes;for (int y = 0; y < height; y++) {reallyHasAlpha |= sampler.next(base);base += sampler.srcDY() * rowBytes;}} else {SkAutoMalloc storage(origWidth * srcBytesPerPixel);uint8_t* srcRow = (uint8_t*)storage.get();skip_src_rows(png_ptr, srcRow, sampler.srcY0());for (int y = 0; y < height; y++) {uint8_t* tmp = srcRow;png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);reallyHasAlpha |= sampler.next(srcRow);if (y < height - 1) {skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);}}//(如果有)跳过其余行png_uint_32 read = (height - 1) * sampler.srcDY() +sampler.srcY0() + 1;SkASSERT(read <= origHeight);skip_src_rows(png_ptr, srcRow, origHeight - read);}}/*读取文件的其余部分,并在`info_ptr`中取其他块,必需*/png_read_end(png_ptr, info_ptr);if (0 != theTranspColor) {reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);}if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {switch (decodedBitmap->colorType()) {case kIndex_8_SkColorType://直降.case kARGB_4444_SkColorType://对这些颜色类型,选择不支持`unpremul`.return false;default: {//未完成解码.该颜色类型或支持`unpremul`,或不重要,因为它没有`alpha`(或只有`alpha`).这些括号可避免警告.//}}}if (!reallyHasAlpha) {decodedBitmap->setAlphaType(kOpaque_SkAlphaType);}return true;
}
Png
格式图像
可能包含透明度
.Skia
有个额外检查
透明度是否全为255
的,是则置不透明
标志,毕竟在后续渲染不透明的图像
时,消耗较少.
(3)Gif
bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
#if GIFLIB_MAJOR < 5GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
#elseGifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
#endifif (NULL == gif) {return error_return(*bm, "DGifOpen");}SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);SavedImage temp_save;temp_save.ExtensionBlocks=NULL;temp_save.ExtensionBlockCount=0;SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);int width, height;GifRecordType recType;GifByteType *extData;
#if GIFLIB_MAJOR >= 5int extFunction;
#endifint transpIndex = -1; //`-1`表示还没有它int fillIndex = gif->SBackGroundColor;do {if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {return error_return(*bm, "DGifGetRecordType");}switch (recType) {case IMAGE_DESC_RECORD_TYPE: {if (DGifGetImageDesc(gif) == GIF_ERROR) {return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");}if (gif->ImageCount < 1) { //健全性检查return error_return(*bm, "ImageCount < 1");}width = gif->SWidth;height = gif->SHeight;SavedImage* image = &gif->SavedImages[gif->ImageCount-1];const GifImageDesc& desc = image->ImageDesc;int imageLeft = desc.Left;int imageTop = desc.Top;const int innerWidth = desc.Width;const int innerHeight = desc.Height;if (innerWidth <= 0 || innerHeight <= 0) {return error_return(*bm, "invalid dimensions");}//检查有效的描述符if (innerWidth > width) {gif_warning(*bm, "image too wide, expanding output to size");width = innerWidth;imageLeft = 0;} else if (imageLeft + innerWidth > width) {gif_warning(*bm, "shifting image left to fit");imageLeft = width - innerWidth;} else if (imageLeft < 0) {gif_warning(*bm, "shifting image right to fit");imageLeft = 0;}if (innerHeight > height) {gif_warning(*bm, "image too tall, expanding output to size");height = innerHeight;imageTop = 0;} else if (imageTop + innerHeight > height) {gif_warning(*bm, "shifting image up to fit");imageTop = height - innerHeight;} else if (imageTop < 0) {gif_warning(*bm, "shifting image down to fit");imageTop = 0;}
#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER//`FIXME`:可让调用者选择图像或配置.if (!this->chooseFromOneChoice(kIndex_8_SkColorType, width, height)) {return error_return(*bm, "chooseFromOneChoice");}
#endifSkScaledBitmapSampler sampler(width, height, this->getSampleSize());bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), kIndex_8_SkColorType, kPremul_SkAlphaType));if (SkImageDecoder::kDecodeBounds_Mode == mode) {return true;}//现在解码`颜色表colortable`int colorCount = 0;{//在此处按域声明`colorPtr`.SkPMColor colorPtr[256]; //最坏时候的存储const ColorMapObject* cmap = find_colormap(gif);SkAlphaType alphaType = kOpaque_SkAlphaType;if (cmap != NULL) {SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel)));colorCount = cmap->ColorCount;if (colorCount > 256) {colorCount = 256; //`kIndex8`不能支持更多}for (int index = 0; index < colorCount; index++) {colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red, cmap->Colors[index].Green, cmap->Colors[index].Blue); }} else {//`find_colormap()`返回`NULL`.一些(罕见的,破碎的)`GIF`没有颜色表,所以强制使用颜色表. gif_warning(*bm, "missing colormap");colorCount = 256;sk_memset32(colorPtr, SK_ColorWHITE, colorCount);}transpIndex = find_transpIndex(temp_save, colorCount);if (transpIndex >= 0) {colorPtr[transpIndex] = SK_ColorTRANSPARENT; //在透明的`SkPMColor`中`ram`alphaType = kPremul_SkAlphaType;fillIndex = transpIndex;} else if (fillIndex >= colorCount) {//`gif->SBackGroundColor`应小于`colorCount`.如果没有,请修复它.fillIndex = 0; //}SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, (colorPtr, colorCount, alphaType)));if (!this->allocPixelRef(bm, ctable)) {return error_return(*bm, "allocPixelRef");}}//如果任一内部尺寸为`<=0`,则中止if (innerWidth <= 0 || innerHeight <= 0) {return error_return(*bm, "non-pos inner width/height");}SkAutoLockPixels alp(*bm);SkAutoMalloc storage(innerWidth);uint8_t* scanline = (uint8_t*) storage.get();//`GIF`有一个存储图像及由填充颜色填充的较大背景扫描行的`选项`.本例中,使用`较大位图`的子集采样. SkBitmap subset;SkBitmap* workingBitmap;//只是`总边界`的一个子集吗if ((imageTop | imageLeft) > 0 ||innerWidth < width || innerHeight < height) {//填充背景.memset(bm->getPixels(), fillIndex, bm->getSize());//创建位图的子集.SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(), imageTop / sampler.srcDY(), innerWidth / sampler.srcDX(), innerHeight / sampler.srcDY()));if (!bm->extractSubset(&subset, subsetRect)) {return error_return(*bm, "Extract failed.");}//更新采样器.现在,将仅采样子集.sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());workingBitmap = } else {workingBitmap = bm;}//已锁定`bm`了,但如果必须取一个子集,也必须锁定它,这样`getPixels()`才会指向它的像素. SkAutoLockPixels alpWorking(*workingBitmap);if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {return error_return(*bm, "Sampler failed to begin.");}//现在解码每条扫描行if (gif->Image.Interlace) {//遍历源数据的高度.采样器跳过不需要的行.GifInterlaceIter iter(innerHeight);for (int y = 0; y < innerHeight; y++) {if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {gif_warning(*bm, "interlace DGifGetLine");memset(scanline, fillIndex, innerWidth);for (; y < innerHeight; y++) {sampler.sampleInterlaced(scanline, iter.currY());iter.next();}return true;}sampler.sampleInterlaced(scanline, iter.currY());iter.next();}} else {//简单,无隔行例const int outHeight = workingBitmap->height();skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());for (int y = 0; y < outHeight; y++) {if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {gif_warning(*bm, "DGifGetLine");memset(scanline, fillIndex, innerWidth);for (; y < outHeight; y++) {sampler.next(scanline);}return true;}//`Scanline`现在包含原始数据.试一试.sampler.next(scanline);if (y < outHeight - 1) {skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);}}//(如果有)跳过其余行int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1;SkASSERT(read <= innerHeight);skip_src_rows(gif, scanline, innerWidth, innerHeight - read);}sanitize_indexed_bitmap(bm);return true;} break;case EXTENSION_RECORD_TYPE:
#if GIFLIB_MAJOR < 5if (DGifGetExtension(gif, &temp_save.Function, &extData) == GIF_ERROR) {
#elseif (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
#endifreturn error_return(*bm, "DGifGetExtension");}while (extData != NULL) {/*使用数据创建扩展块*/
#if GIFLIB_MAJOR < 5if (AddExtensionBlock(&temp_save, extData[0], &extData[1]) == GIF_ERROR) {
#elseif (GifAddExtensionBlock(&gif->ExtensionBlockCount, &gif->ExtensionBlocks, extFunction, extData[0], &extData[1]) == GIF_ERROR) {
#endifreturn error_return(*bm, "AddExtensionBlock");}if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {return error_return(*bm, "DGifGetExtensionNext");}
#if GIFLIB_MAJOR < 5temp_save.Function = 0;
#endif}break;case TERMINATE_RECORD_TYPE:break;default: /*`DGifGetRecordType`应抓它*/break;}} while (recType != TERMINATE_RECORD_TYPE);sanitize_indexed_bitmap(bm);return true;
}
因为适配
不同版本的gif
库的原因,这段代码写得略显杂乱,也存在一些八哥
.
注意Skia
对Gif
动态图,只解最后一帧:
SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
由SkGIFMovie
类处理Gif
动画,走不同的流程.应用需要调另外的类
(详细见:frameworks/base/graphics/java/android/graphics/Movie.java
).
(4)bmp
bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {//首先读取整个流,以便传递`所有数据`给`BmpDecoderHelper`.分配保存数据的空间.SkAutoMalloc storage;//所有数据的字节长度.const size_t length = CopyStreamToStorage(&storage, stream);if (0 == length) {return 0;}const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;SkBmpDecoderCallback callback(justBounds);//现在按回调的`rgb()`数组`[r,g,b,r,g,b,...]`解码`BMP`{image_codec::BmpDecoderHelper helper;const int max_pixels = 16383*16383; //最大宽度*高度if (!helper.DecodeImage((const char*)storage.get(), length, max_pixels, &callback)) {return false;}}//不再需要它了,(在试分配位图的像素之前)而不是等待它的析构器,所以现在`释放`它storage.free();int width = callback.width();int height = callback.height();SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, false);//只有在有`意义`时才接受`prefConfig`if (kARGB_4444_SkColorType != colorType && kRGB_565_SkColorType != colorType) {colorType = kN32_SkColorType;}SkScaledBitmapSampler sampler(width, height, getSampleSize());bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType, kOpaque_SkAlphaType));if (justBounds) {return true;}if (!this->allocPixelRef(bm, NULL)) {return false;}SkAutoLockPixels alp(*bm);if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {return false;}const int srcRowBytes = width * 3;const int dstHeight = sampler.scaledHeight();const uint8_t* srcRow = callback.rgb();srcRow += sampler.srcY0() * srcRowBytes;for (int y = 0; y < dstHeight; y++) {sampler.next(srcRow);srcRow += sampler.srcDY() * srcRowBytes;}return true;
}
这是最简单
的一个格式,但用得较少,Skia
里面的处理也比较随意.
a
,把流中内容
全部读到内存(CopyStreamToStorage)
b
,解析
流中的内容,用(image_codec::BmpDecoderHelper::DecodeImage)
转化它为RGB
格式
c
,使用SkScaledBitmapSampler
,来转换RGB
为目标格式
,同时下采样
.
6
,典型编码器的onEncode
方法
(1)Jpeg
class SkJPEGImageEncoder : public SkImageEncoder {
protected:virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
#ifdef TIME_ENCODESkAutoTime atm("JPEG Encode");
#endifSkAutoLockPixels alp(bm);if (NULL == bm.getPixels()) {return false;}jpeg_compress_struct cinfo;skjpeg_error_mgr sk_err;skjpeg_destination_mgr sk_wstream(stream);//在设置调用`setjmp`前分配这些SkAutoMalloc oneRow;SkAutoLockColors ctLocker;cinfo.err = jpeg_std_error(&sk_err);sk_err.error_exit = skjpeg_error_exit;if (setjmp(sk_err.fJmpBuf)) {return false;}//在`setjmp`或`markvolatile`之后保留.const WriteScanline writer = ChooseWriter(bm);if (NULL == writer) {return false;}jpeg_create_compress(&cinfo);cinfo.dest = &sk_wstream;cinfo.image_width = bm.width();cinfo.image_height = bm.height();cinfo.input_components = 3;
#ifdef WE_CONVERT_TO_YUVcinfo.in_color_space = JCS_YCbCr;
#elsecinfo.in_color_space = JCS_RGB;
#endifcinfo.input_gamma = 1;jpeg_set_defaults(&cinfo);jpeg_set_quality(&cinfo, quality, TRUE /*对基线`JPEG`值的限制*/);
#ifdef DCT_IFAST_SUPPORTEDcinfo.dct_method = JDCT_IFAST;
#endifjpeg_start_compress(&cinfo, TRUE);const int width = bm.width();uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);const SkPMColor* colors = ctLocker.lockColors(bm);const void* srcRow = bm.getPixels();while (cinfo.next_scanline < cinfo.image_height) {JSAMPROW row_pointer[1]; /*`JSAMPLE`行指针*/writer(oneRowP, srcRow, width, colors);row_pointer[0] = oneRowP;(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);srcRow = (const void*)((const char*)srcRow + bm.rowBytes());}jpeg_finish_compress(&cinfo);jpeg_destroy_compress(&cinfo);return true;}
};
Jpeg
是有损压缩,传入的quality
决定其量化参数表.
a
,Skia
默认先转换图片
为YUV444
格式,再编码(默认打开WE_CONVERT_TO_YUV
宏,否则就是先转为RGB888
格式,再传入Jpeg
编码时转YUV
).
b
,默认使用JDCT_IFAST
方法来傅立叶变换
,很明显会造成一定的图片
质量损失(即使quality
设成100
也存在,是计算精度
的问题).
(2)Png
static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) {//如果是`kIndex8`,并不关心`alpha`上的搜索,因为只有打包`颜色表`关心该区别,而不是像素if (kIndex_8_SkColorType == ct) {hasAlpha = false; //在`kIndex8`的表项中存储`false`}static const struct {SkColorType fColorType;bool fHasAlpha;transform_scanline_proc fProc;} gMap[] = {{ kRGB_565_SkColorType, false, transform_scanline_565 },{ kN32_SkColorType, false, transform_scanline_888 },{ kN32_SkColorType, true, transform_scanline_8888 },{ kARGB_4444_SkColorType, false, transform_scanline_444 },{ kARGB_4444_SkColorType, true, transform_scanline_4444 },{ kIndex_8_SkColorType, false, transform_scanline_memcpy },};for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) {return gMap[i].fProc;}}sk_throw();return NULL;
}
bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int /*质量*/) {SkColorType ct = bitmap.colorType();const bool hasAlpha = !bitmap.isOpaque();int colorType = PNG_COLOR_MASK_COLOR;int bitDepth = 8; //颜色的默认值png_color_8 sig_bit;switch (ct) {case kIndex_8_SkColorType:colorType |= PNG_COLOR_MASK_PALETTE;//直降至`ARGB_8888`例case kN32_SkColorType:sig_bit.red = 8;sig_bit.green = 8;sig_bit.blue = 8;sig_bit.alpha = 8;break;case kARGB_4444_SkColorType:sig_bit.red = 4;sig_bit.green = 4;sig_bit.blue = 4;sig_bit.alpha = 4;break;case kRGB_565_SkColorType:sig_bit.red = 5;sig_bit.green = 6;sig_bit.blue = 5;sig_bit.alpha = 0;break;default:return false;}if (hasAlpha) {//如果是调色板,则即使`ctable`有`alpha`,也不要指定`alpha`if (!(colorType & PNG_COLOR_MASK_PALETTE)) {colorType |= PNG_COLOR_MASK_ALPHA;}} else {sig_bit.alpha = 0;}SkAutoLockPixels alp(bitmap);//`readyToDraw`检查像素(如果需要,颜色表)if (!bitmap.readyToDraw()) {return false;}//必须在锁定像素后才这样SkColorTable* ctable = bitmap.getColorTable();if (NULL != ctable) {if (ctable->count() == 0) {return false;}//检查是否可按小于8位存储bitDepth = computeBitDepth(ctable->count());}return doEncode(stream, bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit);
}
bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, const bool& hasAlpha, int colorType, int bitDepth, SkColorType ct, png_color_8& sig_bit) {png_structp png_ptr;png_infop info_ptr;png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL);if (NULL == png_ptr) {return false;}info_ptr = png_create_info_struct(png_ptr);if (NULL == info_ptr) {png_destroy_write_struct(&png_ptr, png_infopp_NULL);return false;}/*设置错误处理.如果未在`png_create_write_struct()`调用中提供自己的错误处理函数,则为必填项.*/if (setjmp(png_jmpbuf(png_ptr))) {png_destroy_write_struct(&png_ptr, &info_ptr);return false;}png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);/*在此处设置图像信息.宽度和高度最大为`2^31`,`bit_depth`是`1,2,4,8`或`16`之一,但有效值还根据所选的`color_type`.`color_type`是`PNG_COLOR_TYPE_GRAY,PNG_COLOR_TYPE_GRAY_ALPHA,PNG_COLOR_TYPE_PALETTE,PNG_COLOR_TYPE_RGB`,或`PNG_COLOR_TYPE_RGB_ALPHA`之一.隔行扫描是`PNG_INTERLACE_NONE`或`PNG_INTERLACE_ADAM7,compression_type`和`filter_type`当前必须为`PNG_COMPRESSION_TYPE_BASE`和`PNG_FILTER_TYPE_BASE`.必填*/png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), bitDepth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);//如果需要,设置`colortable/trans`数组png_color paletteColors[256];png_byte trans[256];if (kIndex_8_SkColorType == ct) {SkColorTable* ct = bitmap.getColorTable();int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());if (numTrans > 0) {png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);}}png_set_sBIT(png_ptr, info_ptr, &sig_bit);png_write_info(png_ptr, info_ptr);const char* srcImage = (const char*)bitmap.getPixels();SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);char* storage = (char*)rowStorage.get();transform_scanline_proc proc = choose_proc(ct, hasAlpha);for (int y = 0; y < bitmap.height(); y++) {png_bytep row_ptr = (png_bytep)storage;proc(srcImage, bitmap.width(), storage);png_write_rows(png_ptr, &row_ptr, 1);srcImage += bitmap.rowBytes();}png_write_end(png_ptr, info_ptr);/*写入后清理,并释放分配的所有内存*/png_destroy_write_struct(&png_ptr, &info_ptr);return true;
}
a
,统一把图像格式
转为RGBA8888
,再按png
编码,另外注明
是否包含透明度信息
.
b
,对原先带透明
的图像格式(RGBA8888,RGBA4444
),做反alpha
预乘,也即每个像素值
除以其alpha
值,自然,会由乘法
替换除法
.
这篇关于2311skia,07编解码图像下的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!