版权声明:本文为博主原创文章,转载请注明出处:http://blog.csdn.net/jxt1234and2010
1、API和自注册机制
Skia中编码解码图片都只需要一行代码:
- SkBitmap bitmap;
- SkImageDecoder::DecodeFile("test.xxx", &bitmap);//由文件名解码,自动推断图片类型
- //或者由流解码
- SkFILEStream stream("test.xxx");
- SkImageDecoder::DecodeStream(stream, &bitmap);//由输入流解码,自动推断图片类型
- //编码
- SkImageEncoder::EncodeFile("test.jpg", bitmap, SkImageEncoder::kJPEG_Type, 90/*编码图片质量,对jpeg格式和webp格式有用*/);
- </pre><p></p><p>其设计是用抽象工厂模式产生编码器、解码器实例,将产生函数用自注册方式录入,使之在加载库时即初始化完成。</p><p>通用注册模板见:include/core/SkTRegistry.h</p><p></p><pre code_snippet_id="1582962" snippet_file_name="blog_20160220_3_9002512" name="code" class="cpp">template <typename T> class SkTRegistry : SkNoncopyable {
- public:
- typedef T Factory;
- explicit SkTRegistry(T fact) : fFact(fact) {
- #ifdef SK_BUILD_FOR_ANDROID
- // work-around for double-initialization bug
- {
- SkTRegistry* reg = gHead;
- while (reg) {
- if (reg == this) {
- return;
- }
- reg = reg->fChain;
- }
- }
- #endif
- fChain = gHead;
- gHead = this;
- }
- static const SkTRegistry* Head() { return gHead; }
- const SkTRegistry* next() const { return fChain; }
- const Factory& factory() const { return fFact; }
- private:
- Factory fFact;
- SkTRegistry* fChain;
- static SkTRegistry* gHead;
- };
- // The caller still needs to declare an instance of this somewhere
- template <typename T> SkTRegistry<T>* SkTRegistry<T>::gHead;
SkImageDecoder中规定的解码器工厂注册函数:
- typedef SkTRegistry<SkImageDecoder*(*)(SkStreamRewindable*)> SkImageDecoder_DecodeReg;
gif的解码器工厂便按如下方法注册:
- static bool is_gif(SkStreamRewindable* stream) {
- char buf[GIF_STAMP_LEN];
- if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
- if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
- memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
- memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
- return true;
- }
- }
- return false;
- }
- static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) {
- if (is_gif(stream)) {
- return SkNEW(SkGIFImageDecoder);
- }
- return NULL;
- }
- static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory);
2、解码的流程:
- bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref,
- Mode mode, Format* format) {
- SkASSERT(stream);
- SkASSERT(bm);
- bool success = false;
- SkImageDecoder* codec = SkImageDecoder::Factory(stream);
- if (NULL != codec) {
- success = codec->decode(stream, bm, pref, mode);
- if (success && format) {
- *format = codec->getFormat();
- if (kUnknown_Format == *format) {
- if (stream->rewind()) {
- *format = GetStreamFormat(stream);
- }
- }
- }
- delete codec;
- }
- return success;
- }
- bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref, Mode mode) {
- // we reset this to false before calling onDecode
- fShouldCancelDecode = false;
- // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
- fDefaultPref = pref;
- // pass a temporary bitmap, so that if we return false, we are assured of
- // leaving the caller's bitmap untouched.
- SkBitmap tmp;
- if (!this->onDecode(stream, &tmp, mode)) {
- return false;
- }
- bm->swap(tmp);
- return true;
- }
(1)遍历所有解码器,找到支持该文件的解码器:
遍历的代码见:
external/skia/src/images/SkImageDecoder_FactoryRegistrar.cpp
- SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) {
- return image_decoder_from_stream(stream);
- }
- SkImageDecoder* image_decoder_from_stream(SkStreamRewindable* stream) {
- SkImageDecoder* codec = NULL;
- const SkImageDecoder_DecodeReg* curr = SkImageDecoder_DecodeReg::Head();
- while (curr) {
- codec = curr->factory()(stream);
- // we rewind here, because we promise later when we call "decode", that
- // the stream will be at its beginning.
- bool rewindSuceeded = stream->rewind();
- // our image decoder's require that rewind is supported so we fail early
- // if we are given a stream that does not support rewinding.
- if (!rewindSuceeded) {
- SkDEBUGF(("Unable to rewind the image stream."));
- SkDELETE(codec);
- return NULL;
- }
- if (codec) {
- return codec;
- }
- curr = curr->next();
- }
- return NULL;
- }
(2)解码器调用相应的解码库函数(简单的图片如bmp自行处理),解出原始图片,见各个解码器的onDecode方法
(3)在onDecode方法中,一般需要用SkScaledBitmapSampler类作图片后续的缩放和透明度预乘编码的流程相对简单,就是根据类型取相应的编码器做文件编码,不详述
3、解码中流的设置:
从framework层调Skia的解码,主要是以下几个函数:
nativeDecodeStream:以Java的InputStream类作为输入解码,创建JavaInputStreamAdaptor流(其实现是先回调Java中InputStream的读取方法,将其读到缓存区,再从缓存区中读到目标内存。)
nativeDecodeFileDescriptor:以文件描述符作为输入解码,创建SkFILEStream,直接读取文件内容
nativeDecodeAsset:以Asset作为输入解码,创建AssetStreamAdaptor(frameworks/base/core/jni/android/graphics/Utils.cpp)。
nativeDecodeByteArray:以ByteArray作为输入解码,创建SkMemoryStream,直接从内存中读。
- #define BYTES_TO_BUFFER 64
- static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
- jobject padding, jobject options) {
- jobject bitmap = NULL;
- SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
- if (stream.get()) {
- SkAutoTUnref<SkStreamRewindable> bufferedStream(
- SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
- SkASSERT(bufferedStream.get() != NULL);
- bitmap = doDecode(env, bufferedStream, padding, options);
- }
- return bitmap;
- }
- static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
- jobject padding, jobject bitmapFactoryOptions) {
- NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
- int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
- struct stat fdStat;
- if (fstat(descriptor, &fdStat) == -1) {
- doThrowIOE(env, "broken file descriptor");
- return nullObjectReturn("fstat return -1");
- }
- // Restore the descriptor's offset on exiting this function. Even though
- // we dup the descriptor, both the original and dup refer to the same open
- // file description and changes to the file offset in one impact the other.
- AutoFDSeek autoRestore(descriptor);
- // Duplicate the descriptor here to prevent leaking memory. A leak occurs
- // if we only close the file descriptor and not the file object it is used to
- // create. If we don't explicitly clean up the file (which in turn closes the
- // descriptor) the buffers allocated internally by fseek will be leaked.
- int dupDescriptor = dup(descriptor);
- FILE* file = fdopen(dupDescriptor, "r");
- if (file == NULL) {
- // cleanup the duplicated descriptor since it will not be closed when the
- // file is cleaned up (fclose).
- close(dupDescriptor);
- return nullObjectReturn("Could not open file");
- }
- SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
- SkFILEStream::kCallerPasses_Ownership));
- // Use a buffered stream. Although an SkFILEStream can be rewound, this
- // ensures that SkImageDecoder::Factory never rewinds beyond the
- // current position of the file descriptor.
- SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream,
- BYTES_TO_BUFFER));
- return doDecode(env, stream, padding, bitmapFactoryOptions);
- }
由于以文件描述符和InputStream为输入时,不能保证该流可以rewind,因此加了一层 SkFrontBufferedStream 的包装,这个主要作用是缓存输入流中最前面的一段区域(#define BYTES_TO_BUFFER 64),以便在读这段区域时可以回溯(rewind),这个主要作用让解码器读完文件头判断类型后可以回溯。
详细见:
external/skia/src/utils/SkFrontBufferedStream.cpp
4、解码选项
external/skia/include/core/SkImageDecoder.h
- class SkImageDecoder : SkNoncopyable {
- private:
- Peeker* fPeeker;//仅在解ninepatch图片中使用,用于提取子图片断,仅对png格式有效
- SkBitmap::Allocator* fAllocator;//解码需要产生SkBitmap,这里是其像素的内存分配器
- int fSampleSize;//图片下采样率,为2的幂次,这个设计主要针对的是jpeg格式的图像。Jpeg格式编码时以8*8像素单位为一个block作傅立叶变换,设成2、4、8时可以针对性简化反傅立叶变换(idct)的运算。
- SkColorType fDefaultPref;//优先选取的解码出来的图片格式
- bool fDitherImage;//是否做抖动处理,主要针对16位色,在png和jpeg解码时生效
- bool fSkipWritingZeroes;//是否默认结果图已经清零并不写入像素值零的点。这个选项仅在 SkScaledBitmapSampler 中有判断,如(get_RGBA_to_8888_proc函数),试图起到后续缩放时一些计算节省,意义不大。(真要优化不如用simd改写,如neon)
- mutable bool fShouldCancelDecode;//比如用户在A图片未解析完时,已经划过A图片所在区域,此时可设置fShouldCancelDecode使A图片的解码取消。
- bool fPreferQualityOverSpeed;//只对jpeg格式有用,决定反傅立叶变换(idct)的运算精度,速度优先时,以8位精度整数替代浮点运算,质量优先时,以16位精度整数替代浮点运算
- bool fRequireUnpremultipliedColors;//解带透明度的图片,如png时有用,表示是否对图片像素作透明度预乘,默认是作预乘,以便渲染时不用再做乘法
- };
这些私有变量对应于BitmapFactory.Options中的相应的配置项,见
frameworks/base/graphics/java/android/graphics/BitmapFactory.java
设置的代码见doDecode方法:
frameworks/base/core/jni/Android/graphics/BitmapFactory.cpp
- static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
- /*......*/
- SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
- if (decoder == NULL) {
- return nullObjectReturn("SkImageDecoder::Factory returned null");
- }
- decoder->setSampleSize(sampleSize);
- decoder->setDitherImage(doDither);
- decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
- decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
- /*......*/
- }
5、典型解码器的onDecode方法
这一部分的代码可做为这些图像编解码库使用方法的参考
(1)Jpeg
- bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
- #ifdef TIME_DECODE
- SkAutoTime atm("JPEG Decode");
- #endif
- JPEGAutoClean autoClean;
- jpeg_decompress_struct cinfo;
- skjpeg_source_mgr srcManager(stream, this);
- skjpeg_error_mgr errorManager;
- set_error_mgr(&cinfo, &errorManager);
- // All objects need to be instantiated before this setjmp call so that
- // they will be cleaned up properly if an error occurs.
- 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");
- }
- /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
- can) much faster that we, just use their num/denom api to approximate
- the size.
- */
- 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) {
- // Assume an A8 bitmap is not opaque to avoid the check of each
- // individual pixel. It is very unlikely to be opaque, since
- // an opaque A8 bitmap would not be very interesting.
- // Otherwise, a jpeg image is opaque.
- return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
- colorType, alphaType));
- }
- /* image_width and image_height are the original dimensions, available
- after jpeg_read_header(). To see the scaled dimensions, we have to call
- jpeg_start_decompress(), and then read output_width and output_height.
- */
- if (!jpeg_start_decompress(&cinfo)) {
- /* If we failed here, we may still have enough information to return
- to the caller if they just wanted (subsampled bounds). If sampleSize
- was 1, then we would have already returned. Thus we just check if
- we're in kDecodeBounds_Mode, and that we have valid output sizes.
- One reason to fail here is that we have insufficient stream data
- to complete the setup. However, output dimensions seem to get
- computed very early, which is why this special check can pay off.
- */
- if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
- SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
- recompute_sampleSize(sampleSize, cinfo));
- // Assume an A8 bitmap is not opaque to avoid the check of each
- // individual pixel. It is very unlikely to be opaque, since
- // an opaque A8 bitmap would not be very interesting.
- // Otherwise, a jpeg image is opaque.
- 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
- // should we allow the Chooser (if present) to pick a colortype for us???
- if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
- return return_false(cinfo, *bm, "chooseFromOneChoice");
- }
- #endif
- SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
- // Assume an A8 bitmap is not opaque to avoid the check of each
- // individual pixel. It is very unlikely to be opaque, since
- // an opaque A8 bitmap would not be very interesting.
- // Otherwise, a jpeg image is opaque.
- 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
- /* short-circuit the SkScaledBitmapSampler when possible, as this gives
- a significant performance boost.
- */
- 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) {
- // if row_count == 0, then we didn't get a scanline,
- // so return early. We will return a partial image.
- fill_below_level(cinfo.output_scanline, bm);
- cinfo.output_scanline = cinfo.output_height;
- break; // Skip to jpeg_finish_decompress()
- }
- if (this->shouldCancelDecode()) {
- return return_false(cinfo, *bm, "shouldCancelDecode");
- }
- rowptr += bpr;
- }
- jpeg_finish_decompress(&cinfo);
- return true;
- }
- #endif
- // check for supported formats
- 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();
- // Possibly skip initial rows [sampler.srcY0]
- if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
- return return_false(cinfo, *bm, "skip rows");
- }
- // now loop through scanlines until 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) {
- // if row_count == 0, then we didn't get a scanline,
- // so return early. We will return a partial image.
- fill_below_level(y, bm);
- cinfo.output_scanline = cinfo.output_height;
- break; // Skip to 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) {
- // we're done
- break;
- }
- if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
- return return_false(cinfo, *bm, "skip rows");
- }
- }
- // we formally skip the rest, so we don't get a complaint from 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将解码出来的图像下采样到目标bitmap上。
(2)Png
- bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
- png_infop *info_ptrp) {
- /* Create and initialize the png_struct with the desired error handler
- * functions. If you want to use the default stderr and longjump method,
- * you can supply NULL for the last three parameters. We also supply the
- * the compiler header file version, so that we know if the application
- * was compiled with a compatible version of the library. */
- png_error_ptr user_warning_fn =
- (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;
- /* NULL means to leave as default library behavior. */
- /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */
- /* To suppress warnings with a SK_DEBUG binary, set the
- * environment variable "skia_images_png_suppressDecoderWarnings"
- * to "true". Inside a program that links to 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;
- /* Allocate/initialize the memory for image information. */
- 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;
- /* Set error handling if you are using the setjmp/longjmp method (this is
- * the normal method of doing things with libpng). REQUIRED unless you
- * set up your own error handlers in the png_create_read_struct() earlier.
- */
- if (setjmp(png_jmpbuf(png_ptr))) {
- png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
- return false;
- }
- /* If you are using replacement read functions, instead of calling
- * png_init_io() here you would call:
- */
- png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
- #ifdef SK_BUILD_FOR_ANDROID
- png_set_seek_fn(png_ptr, sk_seek_fn);
- #endif
- /* where user_io_ptr is a structure you want available to the callbacks */
- /* If we have already read some of the signature */
- // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
- // hookup our peeker so we can see any user-chunks the caller may be interested in
- 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);
- }
- /* The call to png_read_info() gives us all of the information from the
- * PNG file before the first IDAT (image data chunk). */
- 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);
- /* tell libpng to strip 16 bit/color files down to 8 bits/color */
- if (bitDepth == 16) {
- png_set_strip_16(png_ptr);
- }
- /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
- * byte into separate bytes (useful for paletted and grayscale images). */
- if (bitDepth < 8) {
- png_set_packing(png_ptr);
- }
- /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
- 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 tells us not to try to match
- 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;
- }
- // from here down we are concerned with colortables and pixels
- // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
- // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
- // draw lots faster if we can flag the bitmap has being opaque
- 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);
- /* Turn on interlace handling. REQUIRED if you are not using
- * png_read_image(). To see how to handle interlacing passes,
- * see the png_read_row() method below:
- */
- const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
- png_set_interlace_handling(png_ptr) : 1;
- /* Optional call to gamma correct and add the background to the palette
- * and update info structure. REQUIRED if you are expecting libpng to
- * update the palette for you (ie you selected such a transform above).
- */
- png_read_update_info(png_ptr, info_ptr);
- if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) &&
- 1 == sampleSize) {
- if (kAlpha_8_SkColorType == colorType) {
- // For an A8 bitmap, we assume there is an alpha for speed. It is
- // possible the bitmap is opaque, but that is an unlikely use case
- // since it would not be very interesting.
- reallyHasAlpha = true;
- // A8 is only allowed if the original was GRAY.
- 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 is only allowed if the original was GRAY.
- SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
- sc = SkScaledBitmapSampler::kGray;
- srcBytesPerPixel = 1;
- } else if (hasAlpha) {
- sc = SkScaledBitmapSampler::kRGBA;
- } else {
- sc = SkScaledBitmapSampler::kRGBX;
- }
- /* We have to pass the colortable explicitly, since we may have one
- even if our decodedBitmap doesn't, due to the request that we
- upscale png's palette to a direct model
- */
- 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;
- }
- }
- // now sample it
- 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);
- }
- }
- // skip the rest of the rows (if any)
- png_uint_32 read = (height - 1) * sampler.srcDY() +
- sampler.srcY0() + 1;
- SkASSERT(read <= origHeight);
- skip_src_rows(png_ptr, srcRow, origHeight - read);
- }
- }
- /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
- 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:
- // Fall through.
- case kARGB_4444_SkColorType:
- // We have chosen not to support unpremul for these colortypes.
- return false;
- default: {
- // Fall through to finish the decode. This colortype either
- // supports unpremul or it is irrelevant because it has no
- // alpha (or only alpha).
- // These brackets prevent a warning.
- }
- }
- }
- 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 < 5
- GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
- #else
- GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
- #endif
- if (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 >= 5
- int extFunction;
- #endif
- int transpIndex = -1; // -1 means we don't have it (yet)
- 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) { // sanity check
- 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");
- }
- // check for valid descriptor
- 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: We could give the caller a choice of images or configs.
- if (!this->chooseFromOneChoice(kIndex_8_SkColorType, width, height)) {
- return error_return(*bm, "chooseFromOneChoice");
- }
- #endif
- SkScaledBitmapSampler 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;
- }
- // now we decode the colortable
- int colorCount = 0;
- {
- // Declare colorPtr here for scope.
- SkPMColor colorPtr[256]; // storage for worst-case
- 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; // our kIndex8 can't support more
- }
- 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() returned NULL. Some (rare, broken)
- // GIFs don't have a color table, so we force one.
- 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; // ram in a transparent SkPMColor
- alphaType = kPremul_SkAlphaType;
- fillIndex = transpIndex;
- } else if (fillIndex >= colorCount) {
- // gif->SBackGroundColor should be less than colorCount.
- fillIndex = 0; // If not, fix it.
- }
- SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,
- (colorPtr, colorCount,
- alphaType)));
- if (!this->allocPixelRef(bm, ctable)) {
- return error_return(*bm, "allocPixelRef");
- }
- }
- // abort if either inner dimension is <= 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 has an option to store the scanlines of an image, plus a larger background,
- // filled by a fill color. In this case, we will use a subset of the larger bitmap
- // for sampling.
- SkBitmap subset;
- SkBitmap* workingBitmap;
- // are we only a subset of the total bounds?
- if ((imageTop | imageLeft) > 0 ||
- innerWidth < width || innerHeight < height) {
- // Fill the background.
- memset(bm->getPixels(), fillIndex, bm->getSize());
- // Create a subset of the bitmap.
- 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.");
- }
- // Update the sampler. We'll now be only sampling into the subset.
- sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());
- workingBitmap = ⊂
- } else {
- workingBitmap = bm;
- }
- // bm is already locked, but if we had to take a subset, it must be locked also,
- // so that getPixels() will point to its pixels.
- SkAutoLockPixels alpWorking(*workingBitmap);
- if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {
- return error_return(*bm, "Sampler failed to begin.");
- }
- // now decode each scanline
- if (gif->Image.Interlace) {
- // Iterate over the height of the source data. The sampler will
- // take care of skipping unneeded rows.
- 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 {
- // easy, non-interlace case
- 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 now contains the raw data. Sample it.
- sampler.next(scanline);
- if (y < outHeight - 1) {
- skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);
- }
- }
- // skip the rest of the rows (if any)
- 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 < 5
- if (DGifGetExtension(gif, &temp_save.Function,
- &extData) == GIF_ERROR) {
- #else
- if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
- #endif
- return error_return(*bm, "DGifGetExtension");
- }
- while (extData != NULL) {
- /* Create an extension block with our data */
- #if GIFLIB_MAJOR < 5
- if (AddExtensionBlock(&temp_save, extData[0],
- &extData[1]) == GIF_ERROR) {
- #else
- if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
- &gif->ExtensionBlocks,
- extFunction,
- extData[0],
- &extData[1]) == GIF_ERROR) {
- #endif
- return error_return(*bm, "AddExtensionBlock");
- }
- if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
- return error_return(*bm, "DGifGetExtensionNext");
- }
- #if GIFLIB_MAJOR < 5
- temp_save.Function = 0;
- #endif
- }
- break;
- case TERMINATE_RECORD_TYPE:
- break;
- default: /* Should be trapped by DGifGetRecordType */
- break;
- }
- } while (recType != TERMINATE_RECORD_TYPE);
- sanitize_indexed_bitmap(bm);
- return true;
- }
由于适配不同版本的gif库的原因,这段代码写得略显杂乱,也存在一些Bug。
值得注意的是Skia对于Gif动态图,只解最后一帧(SavedImage* image = &gif->SavedImages[gif->ImageCount-1];)
至于Gif动画,由SkGIFMovie类处理,走不同的流程。应用需要调另外的类(详细见:frameworks/base/graphics/java/android/graphics/Movie.java)。
(4)bmp
- bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
- // First read the entire stream, so that all of the data can be passed to
- // the BmpDecoderHelper.
- // Allocated space used to hold the data.
- SkAutoMalloc storage;
- // Byte length of all of the data.
- const size_t length = CopyStreamToStorage(&storage, stream);
- if (0 == length) {
- return 0;
- }
- const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
- SkBmpDecoderCallback callback(justBounds);
- // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
- {
- image_codec::BmpDecoderHelper helper;
- const int max_pixels = 16383*16383; // max width*height
- if (!helper.DecodeImage((const char*)storage.get(), length,
- max_pixels, &callback)) {
- return false;
- }
- }
- // we don't need this anymore, so free it now (before we try to allocate
- // the bitmap's pixels) rather than waiting for its destructor
- storage.free();
- int width = callback.width();
- int height = callback.height();
- SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, false);
- // only accept prefConfig if it makes sense for us
- 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、解析流中的内容,将其转化为RGB格式(image_codec::BmpDecoderHelper::DecodeImage)
c、使用SkScaledBitmapSampler,将RGB转成目标格式,同时作下采样。
6、典型编码器的onEncode方法
(1)Jpeg
- class SkJPEGImageEncoder : public SkImageEncoder {
- protected:
- virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
- #ifdef TIME_ENCODE
- SkAutoTime atm("JPEG Encode");
- #endif
- SkAutoLockPixels alp(bm);
- if (NULL == bm.getPixels()) {
- return false;
- }
- jpeg_compress_struct cinfo;
- skjpeg_error_mgr sk_err;
- skjpeg_destination_mgr sk_wstream(stream);
- // allocate these before set call 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;
- }
- // Keep after setjmp or mark volatile.
- 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_YUV
- cinfo.in_color_space = JCS_YCbCr;
- #else
- cinfo.in_color_space = JCS_RGB;
- #endif
- cinfo.input_gamma = 1;
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
- #ifdef DCT_IFAST_SUPPORTED
- cinfo.dct_method = JDCT_IFAST;
- #endif
- jpeg_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]; /* pointer to JSAMPLE row[s] */
- 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) {
- // we don't care about search on alpha if we're kIndex8, since only the
- // colortable packing cares about that distinction, not the pixels
- if (kIndex_8_SkColorType == ct) {
- hasAlpha = false; // we store false in the table entries for kIndex8
- }
- 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 /*quality*/) {
- SkColorType ct = bitmap.colorType();
- const bool hasAlpha = !bitmap.isOpaque();
- int colorType = PNG_COLOR_MASK_COLOR;
- int bitDepth = 8; // default for color
- png_color_8 sig_bit;
- switch (ct) {
- case kIndex_8_SkColorType:
- colorType |= PNG_COLOR_MASK_PALETTE;
- // fall through to the ARGB_8888 case
- 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) {
- // don't specify alpha if we're a palette, even if our ctable has alpha
- if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
- colorType |= PNG_COLOR_MASK_ALPHA;
- }
- } else {
- sig_bit.alpha = 0;
- }
- SkAutoLockPixels alp(bitmap);
- // readyToDraw checks for pixels (and colortable if that is required)
- if (!bitmap.readyToDraw()) {
- return false;
- }
- // we must do this after we have locked the pixels
- SkColorTable* ctable = bitmap.getColorTable();
- if (NULL != ctable) {
- if (ctable->count() == 0) {
- return false;
- }
- // check if we can store in fewer than 8 bits
- 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;
- }
- /* Set error handling. REQUIRED if you aren't supplying your own
- * error handling functions in the png_create_write_struct() call.
- */
- 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);
- /* Set the image information here. Width and height are up to 2^31,
- * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
- * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
- * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
- * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
- * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
- * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
- */
- png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
- bitDepth, colorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
- PNG_FILTER_TYPE_BASE);
- // set our colortable/trans arrays if needed
- 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);
- /* clean up after the write, and free any memory allocated */
- png_destroy_write_struct(&png_ptr, &info_ptr);
- return true;
- }
b、对于原先带透明的图像格式(RGBA8888,RGBA4444),做反alpha预乘,也即每个像素值除以其alpha值,自然,除法会做些转化由乘法替代的。