| Index: skia/images/SkImageDecoder_libpng.cpp
|
| ===================================================================
|
| --- skia/images/SkImageDecoder_libpng.cpp (revision 16859)
|
| +++ skia/images/SkImageDecoder_libpng.cpp (working copy)
|
| @@ -1,795 +0,0 @@
|
| -/* libs/graphics/images/SkImageDecoder_libpng.cpp
|
| -**
|
| -** Copyright 2006, The Android Open Source Project
|
| -**
|
| -** Licensed under the Apache License, Version 2.0 (the "License");
|
| -** you may not use this file except in compliance with the License.
|
| -** You may obtain a copy of the License at
|
| -**
|
| -** http://www.apache.org/licenses/LICENSE-2.0
|
| -**
|
| -** Unless required by applicable law or agreed to in writing, software
|
| -** distributed under the License is distributed on an "AS IS" BASIS,
|
| -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -** See the License for the specific language governing permissions and
|
| -** limitations under the License.
|
| -*/
|
| -
|
| -#include "SkImageDecoder.h"
|
| -#include "SkColor.h"
|
| -#include "SkColorPriv.h"
|
| -#include "SkDither.h"
|
| -#include "SkMath.h"
|
| -#include "SkScaledBitmapSampler.h"
|
| -#include "SkStream.h"
|
| -#include "SkTemplates.h"
|
| -#include "SkUtils.h"
|
| -
|
| -extern "C" {
|
| -#include "png.h"
|
| -}
|
| -
|
| -class SkPNGImageDecoder : public SkImageDecoder {
|
| -public:
|
| - virtual Format getFormat() const {
|
| - return kPNG_Format;
|
| - }
|
| -
|
| -protected:
|
| - virtual bool onDecode(SkStream* stream, SkBitmap* bm,
|
| - SkBitmap::Config pref, Mode);
|
| -};
|
| -
|
| -#ifndef png_jmpbuf
|
| -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
|
| -#endif
|
| -
|
| -#define PNG_BYTES_TO_CHECK 4
|
| -
|
| -/* Automatically clean up after throwing an exception */
|
| -struct PNGAutoClean {
|
| - PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
|
| - ~PNGAutoClean() {
|
| - png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
| - }
|
| -private:
|
| - png_structp png_ptr;
|
| - png_infop info_ptr;
|
| -};
|
| -
|
| -SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) {
|
| - char buf[PNG_BYTES_TO_CHECK];
|
| - if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
|
| - !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
|
| - return SkNEW(SkPNGImageDecoder);
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
|
| - SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
|
| - size_t bytes = sk_stream->read(data, length);
|
| - if (bytes != length) {
|
| - png_error(png_ptr, "Read Error!");
|
| - }
|
| -}
|
| -
|
| -static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
|
| - SkImageDecoder::Peeker* peeker =
|
| - (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
|
| - // peek() returning true means continue decoding
|
| - return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
|
| - 1 : -1;
|
| -}
|
| -
|
| -static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
|
| -#if 0
|
| - SkDebugf("------ png error %s\n", msg);
|
| -#endif
|
| - longjmp(png_jmpbuf(png_ptr), 1);
|
| -}
|
| -
|
| -static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
|
| - for (int i = 0; i < count; i++) {
|
| - uint8_t* tmp = storage;
|
| - png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
|
| - }
|
| -}
|
| -
|
| -static bool pos_le(int value, int max) {
|
| - return value > 0 && value <= max;
|
| -}
|
| -
|
| -static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
|
| - SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
|
| -
|
| - bool reallyHasAlpha = false;
|
| -
|
| - for (int y = bm->height() - 1; y >= 0; --y) {
|
| - SkPMColor* p = bm->getAddr32(0, y);
|
| - for (int x = bm->width() - 1; x >= 0; --x) {
|
| - if (match == *p) {
|
| - *p = 0;
|
| - reallyHasAlpha = true;
|
| - }
|
| - p += 1;
|
| - }
|
| - }
|
| - return reallyHasAlpha;
|
| -}
|
| -
|
| -bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| - SkBitmap::Config prefConfig, Mode mode) {
|
| -// SkAutoTrace apr("SkPNGImageDecoder::onDecode");
|
| -
|
| - /* 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_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
| - NULL, sk_error_fn, NULL);
|
| - // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
|
| - if (png_ptr == NULL) {
|
| - return false;
|
| - }
|
| -
|
| - /* 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;
|
| - }
|
| -
|
| - PNGAutoClean autoClean(png_ptr, 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))) {
|
| - 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);
|
| - /* 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 bit_depth, color_type, interlace_type;
|
| - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type,
|
| - &interlace_type, int_p_NULL, int_p_NULL);
|
| -
|
| - /* tell libpng to strip 16 bit/color files down to 8 bits/color */
|
| - if (bit_depth == 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 (bit_depth < 8) {
|
| - png_set_packing(png_ptr);
|
| - }
|
| - /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
|
| - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
| - png_set_gray_1_2_4_to_8(png_ptr);
|
| - }
|
| -
|
| - /* Make a grayscale image into RGB. */
|
| - if (color_type == PNG_COLOR_TYPE_GRAY ||
|
| - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
| - png_set_gray_to_rgb(png_ptr);
|
| - }
|
| -
|
| - SkBitmap::Config config;
|
| - bool hasAlpha = false;
|
| - bool doDither = this->getDitherImage();
|
| - SkPMColor theTranspColor = 0; // 0 tells us not to try to match
|
| -
|
| - // check for sBIT chunk data, in case we should disable dithering because
|
| - // our data is not truely 8bits per component
|
| - if (doDither) {
|
| -#if 0
|
| - SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
|
| - info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
|
| - info_ptr->sig_bit.alpha);
|
| -#endif
|
| - // 0 seems to indicate no information available
|
| - if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
|
| - pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
|
| - pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
|
| - doDither = false;
|
| - }
|
| - }
|
| -
|
| - if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
| - config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha
|
| - } else {
|
| - png_color_16p transpColor = NULL;
|
| - int numTransp = 0;
|
| -
|
| - png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
|
| -
|
| - bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
|
| -
|
| - if (valid && numTransp == 1 && transpColor != NULL) {
|
| - /* Compute our transparent color, which we'll match against later.
|
| - We don't really handle 16bit components properly here, since we
|
| - do our compare *after* the values have been knocked down to 8bit
|
| - which means we will find more matches than we should. The real
|
| - fix seems to be to see the actual 16bit components, do the
|
| - compare, and then knock it down to 8bits ourselves.
|
| - */
|
| - if (color_type & PNG_COLOR_MASK_COLOR) {
|
| - if (16 == bit_depth) {
|
| - theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
|
| - transpColor->green >> 8, transpColor->blue >> 8);
|
| - } else {
|
| - theTranspColor = SkPackARGB32(0xFF, transpColor->red,
|
| - transpColor->green, transpColor->blue);
|
| - }
|
| - } else { // gray
|
| - if (16 == bit_depth) {
|
| - theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
|
| - transpColor->gray >> 8, transpColor->gray >> 8);
|
| - } else {
|
| - theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
|
| - transpColor->gray, transpColor->gray);
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (valid ||
|
| - PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
|
| - PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
|
| - hasAlpha = true;
|
| - config = SkBitmap::kARGB_8888_Config;
|
| - } else { // we get to choose the config
|
| - config = prefConfig;
|
| - if (config == SkBitmap::kNo_Config) {
|
| - config = SkImageDecoder::GetDeviceConfig();
|
| - }
|
| - if (config != SkBitmap::kRGB_565_Config &&
|
| - config != SkBitmap::kARGB_4444_Config) {
|
| - config = SkBitmap::kARGB_8888_Config;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
|
| - return false;
|
| - }
|
| -
|
| - const int sampleSize = this->getSampleSize();
|
| - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
|
| -
|
| - decodedBitmap->setConfig(config, sampler.scaledWidth(),
|
| - sampler.scaledHeight(), 0);
|
| - 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 (color_type == PNG_COLOR_TYPE_PALETTE) {
|
| - int num_palette;
|
| - png_colorp palette;
|
| - png_bytep trans;
|
| - int num_trans;
|
| -
|
| - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
|
| -
|
| - /* BUGGY IMAGE WORKAROUND
|
| -
|
| - We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
|
| - which is a problem since we use the byte as an index. To work around this we grow
|
| - the colortable by 1 (if its < 256) and duplicate the last color into that slot.
|
| - */
|
| - int colorCount = num_palette + (num_palette < 256);
|
| -
|
| - colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
|
| -
|
| - SkPMColor* colorPtr = colorTable->lockColors();
|
| - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
| - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
|
| - hasAlpha = (num_trans > 0);
|
| - } else {
|
| - num_trans = 0;
|
| - colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
|
| - }
|
| - // check for bad images that might make us crash
|
| - if (num_trans > num_palette) {
|
| - num_trans = num_palette;
|
| - }
|
| -
|
| - int index = 0;
|
| - int transLessThanFF = 0;
|
| -
|
| - for (; index < num_trans; index++) {
|
| - transLessThanFF |= (int)*trans - 0xFF;
|
| - *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| - reallyHasAlpha |= (transLessThanFF < 0);
|
| -
|
| - for (; index < num_palette; index++) {
|
| - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| -
|
| - // see BUGGY IMAGE WORKAROUND comment above
|
| - if (num_palette < 256) {
|
| - *colorPtr = colorPtr[-1];
|
| - }
|
| - colorTable->unlockColors(true);
|
| - }
|
| -
|
| - SkAutoUnref aur(colorTable);
|
| -
|
| - if (!this->allocPixelRef(decodedBitmap, colorTable)) {
|
| - return false;
|
| - }
|
| -
|
| - SkAutoLockPixels alp(*decodedBitmap);
|
| -
|
| - /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
|
| -// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
| -// ; // png_set_swap_alpha(png_ptr);
|
| -
|
| - /* swap bytes of 16 bit files to least significant byte first */
|
| - // png_set_swap(png_ptr);
|
| -
|
| - /* Add filler (or alpha) byte (before/after each RGB triplet) */
|
| - if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
|
| - png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
| - }
|
| -
|
| - /* 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 = interlace_type != 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 (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
|
| - 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 (SkBitmap::kIndex8_Config == config) {
|
| - sc = SkScaledBitmapSampler::kIndex;
|
| - srcBytesPerPixel = 1;
|
| - } else if (hasAlpha) {
|
| - sc = SkScaledBitmapSampler::kRGBA;
|
| - } else {
|
| - sc = SkScaledBitmapSampler::kRGBX;
|
| - }
|
| -
|
| - SkAutoMalloc storage(origWidth * srcBytesPerPixel);
|
| - const int height = decodedBitmap->height();
|
| -
|
| - for (int i = 0; i < number_passes; i++) {
|
| - if (!sampler.begin(decodedBitmap, sc, doDither)) {
|
| - return false;
|
| - }
|
| -
|
| - 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);
|
| - }
|
| -
|
| - if (hasAlpha && !reallyHasAlpha) {
|
| -#if 0
|
| - SkDEBUGF(("Image doesn't really have alpha [%d %d]\n",
|
| - origWidth, origHeight));
|
| -#endif
|
| - }
|
| - }
|
| -
|
| - /* 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);
|
| - }
|
| - decodedBitmap->setIsOpaque(!reallyHasAlpha);
|
| - return true;
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -#ifdef SK_SUPPORT_IMAGE_ENCODE
|
| -
|
| -#include "SkColorPriv.h"
|
| -#include "SkUnPreMultiply.h"
|
| -
|
| -static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
|
| - SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
|
| - if (!sk_stream->write(data, len)) {
|
| - png_error(png_ptr, "sk_write_fn Error!");
|
| - }
|
| -}
|
| -
|
| -typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
|
| - int width, char* SK_RESTRICT dst);
|
| -
|
| -static void transform_scanline_565(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
|
| - for (int i = 0; i < width; i++) {
|
| - unsigned c = *srcP++;
|
| - *dst++ = SkPacked16ToR32(c);
|
| - *dst++ = SkPacked16ToG32(c);
|
| - *dst++ = SkPacked16ToB32(c);
|
| - }
|
| -}
|
| -
|
| -static void transform_scanline_888(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
|
| - for (int i = 0; i < width; i++) {
|
| - SkPMColor c = *srcP++;
|
| - *dst++ = SkGetPackedR32(c);
|
| - *dst++ = SkGetPackedG32(c);
|
| - *dst++ = SkGetPackedB32(c);
|
| - }
|
| -}
|
| -
|
| -static void transform_scanline_444(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
|
| - for (int i = 0; i < width; i++) {
|
| - SkPMColor16 c = *srcP++;
|
| - *dst++ = SkPacked4444ToR32(c);
|
| - *dst++ = SkPacked4444ToG32(c);
|
| - *dst++ = SkPacked4444ToB32(c);
|
| - }
|
| -}
|
| -
|
| -static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
|
| - const SkUnPreMultiply::Scale* SK_RESTRICT table =
|
| - SkUnPreMultiply::GetScaleTable();
|
| -
|
| - for (int i = 0; i < width; i++) {
|
| - SkPMColor c = *srcP++;
|
| - unsigned a = SkGetPackedA32(c);
|
| - unsigned r = SkGetPackedR32(c);
|
| - unsigned g = SkGetPackedG32(c);
|
| - unsigned b = SkGetPackedB32(c);
|
| -
|
| - if (0 != a && 255 != a) {
|
| - SkUnPreMultiply::Scale scale = table[a];
|
| - r = SkUnPreMultiply::ApplyScale(scale, r);
|
| - g = SkUnPreMultiply::ApplyScale(scale, g);
|
| - b = SkUnPreMultiply::ApplyScale(scale, b);
|
| - }
|
| - *dst++ = r;
|
| - *dst++ = g;
|
| - *dst++ = b;
|
| - *dst++ = a;
|
| - }
|
| -}
|
| -
|
| -static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
|
| - const SkUnPreMultiply::Scale* SK_RESTRICT table =
|
| - SkUnPreMultiply::GetScaleTable();
|
| -
|
| - for (int i = 0; i < width; i++) {
|
| - SkPMColor16 c = *srcP++;
|
| - unsigned a = SkPacked4444ToA32(c);
|
| - unsigned r = SkPacked4444ToR32(c);
|
| - unsigned g = SkPacked4444ToG32(c);
|
| - unsigned b = SkPacked4444ToB32(c);
|
| -
|
| - if (0 != a && 255 != a) {
|
| - SkUnPreMultiply::Scale scale = table[a];
|
| - r = SkUnPreMultiply::ApplyScale(scale, r);
|
| - g = SkUnPreMultiply::ApplyScale(scale, g);
|
| - b = SkUnPreMultiply::ApplyScale(scale, b);
|
| - }
|
| - *dst++ = r;
|
| - *dst++ = g;
|
| - *dst++ = b;
|
| - *dst++ = a;
|
| - }
|
| -}
|
| -
|
| -static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
|
| - char* SK_RESTRICT dst) {
|
| - memcpy(dst, src, width);
|
| -}
|
| -
|
| -static transform_scanline_proc choose_proc(SkBitmap::Config config,
|
| - 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 (SkBitmap::kIndex8_Config == config) {
|
| - hasAlpha = false; // we store false in the table entries for kIndex8
|
| - }
|
| -
|
| - static const struct {
|
| - SkBitmap::Config fConfig;
|
| - bool fHasAlpha;
|
| - transform_scanline_proc fProc;
|
| - } gMap[] = {
|
| - { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
|
| - { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
|
| - { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
|
| - { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
|
| - { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
|
| - { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
|
| - };
|
| -
|
| - for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
|
| - if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
|
| - return gMap[i].fProc;
|
| - }
|
| - }
|
| - sk_throw();
|
| - return NULL;
|
| -}
|
| -
|
| -// return the minimum legal bitdepth (by png standards) for this many colortable
|
| -// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
|
| -// we can use fewer bits per in png
|
| -static int computeBitDepth(int colorCount) {
|
| -#if 0
|
| - int bits = SkNextLog2(colorCount);
|
| - SkASSERT(bits >= 1 && bits <= 8);
|
| - // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
|
| - return SkNextPow2(bits);
|
| -#else
|
| - // for the moment, we don't know how to pack bitdepth < 8
|
| - return 8;
|
| -#endif
|
| -}
|
| -
|
| -/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
|
| - pack trans[] and return the number of trans[] entries written. If hasAlpha
|
| - is false, the return value will always be 0.
|
| -
|
| - Note: this routine takes care of unpremultiplying the RGB values when we
|
| - have alpha in the colortable, since png doesn't support premul colors
|
| -*/
|
| -static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
|
| - png_byte* SK_RESTRICT trans, bool hasAlpha) {
|
| - SkAutoLockColors alc(ctable);
|
| - const SkPMColor* SK_RESTRICT colors = alc.colors();
|
| - const int ctCount = ctable->count();
|
| - int i, num_trans = 0;
|
| -
|
| - if (hasAlpha) {
|
| - /* first see if we have some number of fully opaque at the end of the
|
| - ctable. PNG allows num_trans < num_palette, but all of the trans
|
| - entries must come first in the palette. If I was smarter, I'd
|
| - reorder the indices and ctable so that all non-opaque colors came
|
| - first in the palette. But, since that would slow down the encode,
|
| - I'm leaving the indices and ctable order as is, and just looking
|
| - at the tail of the ctable for opaqueness.
|
| - */
|
| - num_trans = ctCount;
|
| - for (i = ctCount - 1; i >= 0; --i) {
|
| - if (SkGetPackedA32(colors[i]) != 0xFF) {
|
| - break;
|
| - }
|
| - num_trans -= 1;
|
| - }
|
| -
|
| - const SkUnPreMultiply::Scale* SK_RESTRICT table =
|
| - SkUnPreMultiply::GetScaleTable();
|
| -
|
| - for (i = 0; i < num_trans; i++) {
|
| - const SkPMColor c = *colors++;
|
| - const unsigned a = SkGetPackedA32(c);
|
| - const SkUnPreMultiply::Scale s = table[a];
|
| - trans[i] = a;
|
| - palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
|
| - palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
|
| - palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
|
| - }
|
| - // now fall out of this if-block to use common code for the trailing
|
| - // opaque entries
|
| - }
|
| -
|
| - // these (remaining) entries are opaque
|
| - for (i = num_trans; i < ctCount; i++) {
|
| - SkPMColor c = *colors++;
|
| - palette[i].red = SkGetPackedR32(c);
|
| - palette[i].green = SkGetPackedG32(c);
|
| - palette[i].blue = SkGetPackedB32(c);
|
| - }
|
| - return num_trans;
|
| -}
|
| -
|
| -class SkPNGImageEncoder : public SkImageEncoder {
|
| -protected:
|
| - virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
|
| -};
|
| -
|
| -bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
|
| - int /*quality*/) {
|
| - SkBitmap::Config config = bitmap.getConfig();
|
| -
|
| - const bool hasAlpha = !bitmap.isOpaque();
|
| - int colorType = PNG_COLOR_MASK_COLOR;
|
| - int bitDepth = 8; // default for color
|
| - png_color_8 sig_bit;
|
| -
|
| - switch (config) {
|
| - case SkBitmap::kIndex8_Config:
|
| - colorType |= PNG_COLOR_MASK_PALETTE;
|
| - // fall through to the ARGB_8888 case
|
| - case SkBitmap::kARGB_8888_Config:
|
| - sig_bit.red = 8;
|
| - sig_bit.green = 8;
|
| - sig_bit.blue = 8;
|
| - sig_bit.alpha = 8;
|
| - break;
|
| - case SkBitmap::kARGB_4444_Config:
|
| - sig_bit.red = 4;
|
| - sig_bit.green = 4;
|
| - sig_bit.blue = 4;
|
| - sig_bit.alpha = 4;
|
| - break;
|
| - case SkBitmap::kRGB_565_Config:
|
| - 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());
|
| - }
|
| -
|
| - 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);
|
| -
|
| -#if 0 // need to support this some day
|
| - /* set the palette if there is one. REQUIRED for indexed-color images */
|
| - palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
|
| - * png_sizeof (png_color));
|
| - /* ... set palette colors ... */
|
| - png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
|
| - /* You must not free palette here, because png_set_PLTE only makes a link to
|
| - the palette that you malloced. Wait until you are about to destroy
|
| - the png structure. */
|
| -#endif
|
| -
|
| - 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(config, 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;
|
| -}
|
| -
|
| -SkImageEncoder* SkImageEncoder_PNG_Factory();
|
| -SkImageEncoder* SkImageEncoder_PNG_Factory() {
|
| - return SkNEW(SkPNGImageEncoder);
|
| -}
|
| -
|
| -#endif /* SK_SUPPORT_IMAGE_ENCODE */
|
|
|