"third_party/externals/libjpeg" : "http://src.chromium.org/svn/trunk/src/third_party/libjpeg@125399",
"third_party/externals/jsoncpp" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248",
"third_party/externals/jsoncpp-chromium" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
+ "third_party/externals/libwebp" : "http://src.chromium.org/svn/trunk/src/third_party/libwebp@186718",
}
#hooks = [
'standalone_static_library': 1,
'dependencies': [
'libjpeg.gyp:*',
+ 'libwebp.gyp:libwebp',
'utils.gyp:utils',
],
'export_dependent_settings': [
'../src/images/bmpdecoderhelper.cpp',
'../src/images/bmpdecoderhelper.h',
+
+ '../src/images/SkBitmapRegionDecoder.cpp',
+
'../src/images/SkImageDecoder.cpp',
'../src/images/SkImageDecoder_Factory.cpp',
- '../src/images/SkImageDecoder_libjpeg.cpp',
'../src/images/SkImageDecoder_libbmp.cpp',
'../src/images/SkImageDecoder_libgif.cpp',
'../src/images/SkImageDecoder_libico.cpp',
+ '../src/images/SkImageDecoder_libjpeg.cpp',
'../src/images/SkImageDecoder_libpng.cpp',
+ '../src/images/SkImageDecoder_libwebp.cpp',
'../src/images/SkImageDecoder_wbmp.cpp',
'../src/images/SkImageEncoder.cpp',
'../src/images/SkImageEncoder_Factory.cpp',
--- /dev/null
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'use_system_libwebp%': 0,
+ },
+ 'conditions': [
+ ['use_system_libwebp==0', {
+ 'targets': [
+ {
+ 'target_name': 'libwebp_dec',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ 'sources': [
+ '../third_party/externals/libwebp/dec/alpha.c',
+ '../third_party/externals/libwebp/dec/buffer.c',
+ '../third_party/externals/libwebp/dec/frame.c',
+ '../third_party/externals/libwebp/dec/idec.c',
+ '../third_party/externals/libwebp/dec/io.c',
+ '../third_party/externals/libwebp/dec/layer.c',
+ '../third_party/externals/libwebp/dec/quant.c',
+ '../third_party/externals/libwebp/dec/tree.c',
+ '../third_party/externals/libwebp/dec/vp8.c',
+ '../third_party/externals/libwebp/dec/vp8l.c',
+ '../third_party/externals/libwebp/dec/webp.c',
+ ],
+ 'cflags!': [
+ '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ 'sources': [
+ '../third_party/externals/libwebp/dsp/cpu.c',
+ '../third_party/externals/libwebp/dsp/dec.c',
+ '../third_party/externals/libwebp/dsp/dec_sse2.c',
+ '../third_party/externals/libwebp/dsp/enc.c',
+ '../third_party/externals/libwebp/dsp/enc_sse2.c',
+ '../third_party/externals/libwebp/dsp/lossless.c',
+ '../third_party/externals/libwebp/dsp/upsampling.c',
+ '../third_party/externals/libwebp/dsp/upsampling_sse2.c',
+ '../third_party/externals/libwebp/dsp/yuv.c',
+ ],
+ 'cflags!': [
+ '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+ ],
+ 'conditions': [
+ ['skia_os == "android"', {
+ 'dependencies' : [
+ 'android_deps.gyp:cpu_features',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_neon',
+ 'conditions': [
+ ['armv7 == 1', {
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ 'sources': [
+ '../third_party/externals/libwebp/dsp/dec_neon.c',
+ ],
+ # behavior similar dsp_neon.c.neon in an Android.mk
+ 'cflags!': [
+ '-mfpu=vfpv3-d16',
+ '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+ ],
+ 'cflags': [ '-mfpu=neon' ],
+ },{ # "armv7 != 1"
+ 'type': 'none',
+ }],
+ ],
+ },
+ {
+ 'target_name': 'libwebp_enc',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ 'sources': [
+ '../third_party/externals/libwebp/enc/alpha.c',
+ '../third_party/externals/libwebp/enc/analysis.c',
+ '../third_party/externals/libwebp/enc/backward_references.c',
+ '../third_party/externals/libwebp/enc/config.c',
+ '../third_party/externals/libwebp/enc/cost.c',
+ '../third_party/externals/libwebp/enc/filter.c',
+ '../third_party/externals/libwebp/enc/frame.c',
+ '../third_party/externals/libwebp/enc/histogram.c',
+ '../third_party/externals/libwebp/enc/iterator.c',
+ '../third_party/externals/libwebp/enc/layer.c',
+ '../third_party/externals/libwebp/enc/picture.c',
+ '../third_party/externals/libwebp/enc/quant.c',
+ '../third_party/externals/libwebp/enc/syntax.c',
+ '../third_party/externals/libwebp/enc/tree.c',
+ '../third_party/externals/libwebp/enc/vp8l.c',
+ '../third_party/externals/libwebp/enc/webpenc.c',
+ ],
+ 'cflags!': [
+ '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+ ],
+ },
+ {
+ 'target_name': 'libwebp_utils',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ 'sources': [
+ '../third_party/externals/libwebp/utils/bit_reader.c',
+ '../third_party/externals/libwebp/utils/bit_writer.c',
+ '../third_party/externals/libwebp/utils/color_cache.c',
+ '../third_party/externals/libwebp/utils/filters.c',
+ '../third_party/externals/libwebp/utils/huffman.c',
+ '../third_party/externals/libwebp/utils/huffman_encode.c',
+ '../third_party/externals/libwebp/utils/quant_levels.c',
+ '../third_party/externals/libwebp/utils/rescaler.c',
+ '../third_party/externals/libwebp/utils/thread.c',
+ '../third_party/externals/libwebp/utils/utils.c',
+ ],
+ 'cflags!': [
+ '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+ ],
+ },
+ {
+ 'target_name': 'libwebp',
+ 'type': 'none',
+ 'dependencies' : [
+ 'libwebp_dec',
+ 'libwebp_dsp',
+ 'libwebp_dsp_neon',
+ 'libwebp_enc',
+ 'libwebp_utils',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../third_party/externals/libwebp',
+ ],
+ },
+ 'conditions': [
+ ['OS!="win"', {'product_name': 'webp'}],
+ ],
+ },
+ ],
+ }, {
+ 'targets': [
+ {
+ 'target_name': 'libwebp',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'ENABLE_WEBP',
+ ],
+ },
+ 'link_settings': {
+ 'libraries': [
+ '-lwebp',
+ ],
+ },
+ }
+ ],
+ }],
+ ],
+}
--- /dev/null
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBitmapRegionDecoder_DEFINED
+#define SkBitmapRegionDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+class SkIRect;
+
+/**
+ * SkBitmapRegionDecoder can be used to decode a specified rect from an image.
+ * This is particularly useful when the original image is large and you only
+ * need parts of the image.
+ *
+ * However, not all image codecs on all platforms support this feature so be
+ * prepared to fallback to standard decoding if decodeRegion(...) returns false.
+ */
+class SkBitmapRegionDecoder {
+public:
+ SkBitmapRegionDecoder(SkImageDecoder* decoder, SkStream* stream,
+ int width, int height) {
+ fDecoder = decoder;
+ fStream = stream;
+ fWidth = width;
+ fHeight = height;
+ }
+ ~SkBitmapRegionDecoder() {
+ SkDELETE(fDecoder);
+ SkSafeUnref(fStream);
+ }
+
+ bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
+ SkBitmap::Config pref, int sampleSize);
+
+ SkImageDecoder* getDecoder() const { return fDecoder; }
+ int getWidth() const { return fWidth; }
+ int getHeight() const { return fHeight; }
+
+private:
+ SkImageDecoder* fDecoder;
+ SkStream* fStream;
+ int fWidth;
+ int fHeight;
+};
+
+#endif
#include "SkBitmap.h"
#include "SkBitmapFactory.h"
#include "SkImage.h"
+#include "SkRect.h"
#include "SkRefCnt.h"
class SkStream;
public:
virtual ~SkImageDecoder();
+ // Should be consistent with kFormatName
enum Format {
kUnknown_Format,
kBMP_Format,
kJPEG_Format,
kPNG_Format,
kWBMP_Format,
+ kWEBP_Format,
- kLastKnownFormat = kWBMP_Format
+ kLastKnownFormat = kWEBP_Format
};
/** Return the compressed data's format (see Format enum)
*/
virtual Format getFormat() const;
+ /** Return the compressed data's format name.
+ */
+ const char* getFormatName() const;
+
/** Returns true if the decoder should try to dither the resulting image.
The default setting is true.
*/
*/
void setDitherImage(bool dither) { fDitherImage = dither; }
+ /** Returns true if the decoder should try to decode the
+ resulting image to a higher quality even at the expense of
+ the decoding speed.
+ */
+ bool getPreferQualityOverSpeed() const { return fPreferQualityOverSpeed; }
+
+ /** Set to true if the the decoder should try to decode the
+ resulting image to a higher quality even at the expense of
+ the decoding speed.
+ */
+ void setPreferQualityOverSpeed(bool qualityOverSpeed) {
+ fPreferQualityOverSpeed = qualityOverSpeed;
+ }
+
/** \class Peeker
Base class for optional callbacks to retrieve meta/chunk data out of
note: document use of Allocator, Peeker and Chooser
*/
- bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode);
- bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
- return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode);
+ bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode, bool reuseBitmap = false);
+ bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode, bool reuseBitmap = false) {
+ return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode, reuseBitmap);
}
+ /**
+ * Given a stream, build an index for doing tile-based decode.
+ * The built index will be saved in the decoder, and the image size will
+ * be returned in width and height.
+ *
+ * Return true for success or false on failure.
+ */
+ bool buildTileIndex(SkStream*, int *width, int *height);
+
+ /**
+ * Decode a rectangle region in the image specified by rect.
+ * The method can only be called after buildTileIndex().
+ *
+ * Return true for success.
+ * Return false if the index is never built or failing in decoding.
+ */
+ bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect, SkBitmap::Config pref);
+
/** Given a stream, this will try to find an appropriate decoder object.
If none is found, the method returns NULL.
*/
// must be overridden in subclasses. This guy is called by decode(...)
virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
+ // If the decoder wants to support tiled based decoding,
+ // this method must be overridden. This guy is called by buildTileIndex(...)
+ virtual bool onBuildTileIndex(SkStream*, int *width, int *height) {
+ return false;
+ }
+
+ // If the decoder wants to support tiled based decoding,
+ // this method must be overridden. This guy is called by decodeRegion(...)
+ virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) {
+ return false;
+ }
+
+ /*
+ * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dst are
+ * both sampled by sampleSize from an original Bitmap.
+ *
+ * @param dst the destination bitmap.
+ * @param src the source bitmap that is sampled by sampleSize from the
+ * original bitmap.
+ * @param sampleSize the sample size that src is sampled from the original bitmap.
+ * @param (dstX, dstY) the upper-left point of the dest bitmap in terms of
+ * the coordinate in the original bitmap.
+ * @param (width, height) the width and height of the unsampled dst.
+ * @param (srcX, srcY) the upper-left point of the src bitimap in terms of
+ * the coordinate in the original bitmap.
+ */
+ void cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
+ int dstX, int dstY, int width, int height,
+ int srcX, int srcY);
+
+
+
/** Can be queried from within onDecode, to see if the user (possibly in
a different thread) has requested the decode to cancel. If this returns
true, your onDecode() should stop and return false.
bool fDitherImage;
bool fUsePrefTable;
mutable bool fShouldCancelDecode;
+ bool fPreferQualityOverSpeed;
+
+ /** Contains the image format name.
+ * This should be consistent with Format.
+ *
+ * The format name gives a more meaningful error message than enum.
+ */
+ static const char* sFormatName[];
// illegal
SkImageDecoder(const SkImageDecoder&);
DECLARE_DECODER_CREATOR(JPEGImageDecoder);
DECLARE_DECODER_CREATOR(PNGImageDecoder);
DECLARE_DECODER_CREATOR(WBMPImageDecoder);
+DECLARE_DECODER_CREATOR(WEBPImageDecoder);
#endif
public:
enum Type {
kJPEG_Type,
- kPNG_Type
+ kPNG_Type,
+ kWEBP_Type
};
static SkImageEncoder* Create(Type);
// not all of these will be available
DECLARE_ENCODER_CREATOR(JPEGImageEncoder);
DECLARE_ENCODER_CREATOR(PNGImageEncoder);
+DECLARE_ENCODER_CREATOR(WEBPImageEncoder);
#endif
--- /dev/null
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapRegionDecoder.h"
+
+bool SkBitmapRegionDecoder::decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
+ SkBitmap::Config pref, int sampleSize) {
+ fDecoder->setSampleSize(sampleSize);
+ return fDecoder->decodeRegion(bitmap, rect, pref);
+}
#include "SkPixelRef.h"
#include "SkStream.h"
#include "SkTemplates.h"
+#include "SkCanvas.h"
SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
+const char *SkImageDecoder::sFormatName[] = {
+ "Unknown Format",
+ "BMP",
+ "GIF",
+ "ICO",
+ "JPEG",
+ "PNG",
+ "WBMP",
+ "WEBP",
+};
+
static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
SkBitmap::Config SkImageDecoder::GetDeviceConfig()
SkImageDecoder::SkImageDecoder()
: fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
- fUsePrefTable(false) {
+ fUsePrefTable(false),fPreferQualityOverSpeed(false) {
}
SkImageDecoder::~SkImageDecoder() {
return kUnknown_Format;
}
+const char* SkImageDecoder::getFormatName() const {
+ SkASSERT(SK_ARRAY_COUNT(sFormatName) == kLastKnownFormat);
+ return sFormatName[this->getFormat()];
+}
+
SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
SkRefCnt_SafeAssign(fPeeker, peeker);
return peeker;
}
bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
- SkBitmap::Config pref, Mode mode) {
- // pass a temporary bitmap, so that if we return false, we are assured of
- // leaving the caller's bitmap untouched.
- SkBitmap tmp;
-
+ SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
// we reset this to false before calling onDecode
fShouldCancelDecode = false;
// assign this, for use by getPrefConfig(), in case fUsePrefTable is false
fDefaultPref = pref;
+ if (reuseBitmap) {
+ SkAutoLockPixels alp(*bm);
+ if (NULL != bm->getPixels()) {
+ return this->onDecode(stream, bm, mode);
+ }
+ }
+
+ // 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;
}
return true;
}
+bool SkImageDecoder::decodeRegion(SkBitmap* bm, const SkIRect& rect,
+ SkBitmap::Config pref) {
+ // we reset this to false before calling onDecodeRegion
+ fShouldCancelDecode = false;
+ // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
+ fDefaultPref = pref;
+
+ return this->onDecodeRegion(bm, rect);
+}
+
+bool SkImageDecoder::buildTileIndex(SkStream* stream,
+ int *width, int *height) {
+ // we reset this to false before calling onBuildTileIndex
+ fShouldCancelDecode = false;
+
+ return this->onBuildTileIndex(stream, width, height);
+}
+
+void SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
+ int dstX, int dstY, int width, int height,
+ int srcX, int srcY) {
+ int w = width / sampleSize;
+ int h = height / sampleSize;
+ // if the destination has no pixels then we must allocate them.
+ if (dst->isNull()) {
+ dst->setConfig(src->getConfig(), w, h);
+ dst->setIsOpaque(src->isOpaque());
+
+ if (!this->allocPixelRef(dst, NULL)) {
+ SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
+ return;
+ }
+ }
+ // check to see if the destination is large enough to decode the desired
+ // region. If this assert fails we will just draw as much of the source
+ // into the destination that we can.
+ SkASSERT(dst->width() >= w && dst->height() >= h);
+
+ // Set the Src_Mode for the paint to prevent transparency issue in the
+ // dest in the event that the dest was being re-used.
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+ SkCanvas canvas(*dst);
+ canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
+ (srcY - dstY) / sampleSize,
+ &paint);
+}
+
///////////////////////////////////////////////////////////////////////////////
bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
public:
SkBMPImageDecoder() {}
- virtual Format getFormat() const {
+ virtual Format getFormat() const SK_OVERRIDE {
return kBMP_Format;
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+
+private:
+ typedef SkImageDecoder INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
SkScaledBitmapSampler sampler(width, height, getSampleSize());
- bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
- bm->setIsOpaque(true);
if (justBounds) {
+ bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+ bm->setIsOpaque(true);
return true;
}
+ // No Bitmap reuse supported for this format
+ if (!bm->isNull()) {
+ return false;
+ }
+
+ bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+ bm->setIsOpaque(true);
if (!this->allocPixelRef(bm, NULL)) {
return false;
class SkGIFImageDecoder : public SkImageDecoder {
public:
- virtual Format getFormat() const {
+ virtual Format getFormat() const SK_OVERRIDE {
return kGIF_Format;
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+
+private:
+ typedef SkImageDecoder INHERITED;
};
static const uint8_t gStartingIterlaceYValue[] = {
width, height)) {
return error_return(gif, *bm, "chooseFromOneChoice");
}
-
- bm->setConfig(SkBitmap::kIndex8_Config, width, height);
- if (SkImageDecoder::kDecodeBounds_Mode == mode)
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ bm->setConfig(SkBitmap::kIndex8_Config, width, height);
return true;
+ }
+
+ // No Bitmap reuse supported for this format
+ if (!bm->isNull()) {
+ return false;
+ }
+ bm->setConfig(SkBitmap::kIndex8_Config, width, height);
SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
const GifImageDesc& desc = image->ImageDesc;
public:
SkICOImageDecoder();
- virtual Format getFormat() const {
+ virtual Format getFormat() const SK_OVERRIDE {
return kICO_Format;
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
-};
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
-#if 0 // UNUSED
-SkImageDecoder* SkCreateICOImageDecoder() {
- return new SkICOImageDecoder;
-}
-#endif
+private:
+ typedef SkImageDecoder INHERITED;
+};
/////////////////////////////////////////////////////////////////////////////////////////
//if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap
//however, with small images with large colortables, maybe it's better to still do argb_8888
- bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
-
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
delete[] colors;
return true;
}
+ // No Bitmap reuse supported for this format
+ if (!bm->isNull()) {
+ return false;
+ }
+ bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
if (!this->allocPixelRef(bm, NULL))
{
#include "png.h"
}
+class SkPNGImageIndex {
+public:
+ SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) {
+ this->png_ptr = png_ptr;
+ this->info_ptr = info_ptr;
+ }
+ ~SkPNGImageIndex() {
+ if (NULL != png_ptr) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+ }
+ }
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+};
+
class SkPNGImageDecoder : public SkImageDecoder {
public:
- virtual Format getFormat() const {
+ SkPNGImageDecoder() {
+ fImageIndex = NULL;
+ }
+ virtual Format getFormat() const SK_OVERRIDE {
return kPNG_Format;
}
+ virtual ~SkPNGImageDecoder() {
+ SkDELETE(fImageIndex);
+ }
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+#ifdef SK_BUILD_FOR_ANDROID
+ virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
+ virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
+#endif
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+ SkPNGImageIndex* fImageIndex;
+
+ bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
+ bool decodePalette(png_structp png_ptr, png_infop info_ptr, bool *hasAlphap,
+ bool *reallyHasAlphap, SkColorTable **colorTablep);
+ bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+ SkBitmap::Config *config, bool *hasAlpha,
+ bool *doDither, SkPMColor *theTranspColor);
+
+ typedef SkImageDecoder INHERITED;
};
#ifndef png_jmpbuf
struct PNGAutoClean {
PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
~PNGAutoClean() {
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
}
private:
png_structp png_ptr;
};
static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
- SkStream* sk_stream = (SkStream*)png_get_io_ptr(png_ptr);
+ 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!");
}
}
+#ifdef SK_BUILD_FOR_ANDROID
+static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
+ SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+ sk_stream->rewind();
+ (void)sk_stream->skip(offset);
+}
+#endif
+
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);
}
static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
-#if 0
- SkDebugf("------ png error %s\n", msg);
-#endif
+ SkDEBUGF(("------ png error %s\n", msg));
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, NULL, 1);
+ png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
}
}
return false;
}
-bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
- Mode mode) {
-// SkAutoTrace apr("SkPNGImageDecoder::onDecode");
-
+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
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, NULL, NULL);
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return false;
}
-
- PNGAutoClean autoClean(png_ptr, info_ptr);
+ *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
* 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 */ );
* 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, NULL, NULL);
+ 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 (bit_depth == 16) {
+ 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 (bit_depth < 8) {
+ if (bitDepth < 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_expand_gray_1_2_4_to_8(png_ptr);
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 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) {
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(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;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ return false;
+ }
+
+ PNGAutoClean autoClean(png_ptr, info_ptr);
+
+ png_uint_32 origWidth, origHeight;
+ int bitDepth, colorType, interlaceType;
+ png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+ &colorType, &interlaceType, int_p_NULL, int_p_NULL);
SkBitmap::Config config;
bool hasAlpha = false;
bool doDither = this->getDitherImage();
SkPMColor theTranspColor = 0; // 0 tells us not to try to match
+ if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
+ return false;
+ }
+
+ const int sampleSize = this->getSampleSize();
+ SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+ decodedBitmap->lockPixels();
+ void* rowptr = (void*) decodedBitmap->getPixels();
+ bool reuseBitmap = (rowptr != NULL);
+ decodedBitmap->unlockPixels();
+ if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
+ sampler.scaledHeight() != decodedBitmap->height())) {
+ // Dimensions must match
+ return false;
+ }
+
+ if (!reuseBitmap) {
+ 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 (colorType == PNG_COLOR_TYPE_PALETTE) {
+ decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+ }
+
+ SkAutoUnref aur(colorTable);
+
+ if (!reuseBitmap) {
+ if (!this->allocPixelRef(decodedBitmap,
+ SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
+ return false;
+ }
+ }
+
+ SkAutoLockPixels alp(*decodedBitmap);
+
+ /* Add filler (or alpha) byte (before/after each RGB triplet) */
+ if (colorType == PNG_COLOR_TYPE_RGB || colorType == 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 = (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 (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 (colorTable != NULL) {
+ sc = SkScaledBitmapSampler::kIndex;
+ 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, doDither, 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);
+ }
+ decodedBitmap->setIsOpaque(!reallyHasAlpha);
+ if (reuseBitmap) {
+ decodedBitmap->notifyPixelsChanged();
+ }
+ return true;
+}
+
+
+
+bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+ SkBitmap::Config *configp, bool *hasAlphap,
+ bool *doDitherp, SkPMColor *theTranspColorp) {
+ 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);
+
// check for sBIT chunk data, in case we should disable dithering because
// our data is not truely 8bits per component
- if (doDither) {
- png_color_8p sig_bit = NULL;
- bool has_sbit = PNG_INFO_sBIT == png_get_sBIT(png_ptr, info_ptr,
- &sig_bit);
+ if (*doDitherp) {
#if 0
- if (has_sbit) {
- SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
- sig_bit->blue, sig_bit->alpha);
- }
+ 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 (has_sbit && pos_le(sig_bit->red, SK_R16_BITS) &&
- pos_le(sig_bit->green, SK_G16_BITS) &&
- pos_le(sig_bit->blue, SK_B16_BITS)) {
- doDither = false;
+ 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)) {
+ *doDitherp = false;
}
}
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
- config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
+ *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
// now see if we can upscale to their requested config
- if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) {
- config = SkBitmap::kIndex8_Config;
+ if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
+ *configp = SkBitmap::kIndex8_Config;
}
} else {
- png_color_16p transpColor = NULL;
- int numTransp = 0;
+ png_color_16p transpColor = NULL;
+ int numTransp = 0;
png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
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);
+ if (colorType & PNG_COLOR_MASK_COLOR) {
+ if (16 == bitDepth) {
+ *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
+ transpColor->green >> 8,
+ transpColor->blue >> 8);
} else {
- theTranspColor = SkPackARGB32(0xFF, transpColor->red,
- transpColor->green, transpColor->blue);
+ *theTranspColorp = 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);
+ if (16 == bitDepth) {
+ *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
+ transpColor->gray >> 8,
+ transpColor->gray >> 8);
} else {
- theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
- transpColor->gray, transpColor->gray);
+ *theTranspColorp = 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;
+ PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
+ PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
+ *hasAlphap = true;
}
- config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
+ *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
// now match the request against our capabilities
- if (hasAlpha) {
- if (config != SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
+ if (*hasAlphap) {
+ if (*configp != SkBitmap::kARGB_4444_Config) {
+ *configp = SkBitmap::kARGB_8888_Config;
}
} else {
- if (config != SkBitmap::kRGB_565_Config &&
- config != SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
+ if (*configp != SkBitmap::kRGB_565_Config &&
+ *configp != SkBitmap::kARGB_4444_Config) {
+ *configp = SkBitmap::kARGB_8888_Config;
}
}
}
}
}
- if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
- return false;
+ return this->chooseFromOneChoice(*configp, origWidth, origHeight);
+}
+
+bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
+ bool *hasAlphap, bool *reallyHasAlphap,
+ SkColorTable **colorTablep) {
+ int numPalette;
+ png_colorp palette;
+ png_bytep trans;
+ int numTrans;
+ bool reallyHasAlpha = false;
+ SkColorTable* colorTable = NULL;
+
+ png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
+
+ /* 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 = numPalette + (numPalette < 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, &numTrans, NULL);
+ *hasAlphap = (numTrans > 0);
+ } else {
+ numTrans = 0;
+ colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+ }
+ // check for bad images that might make us crash
+ if (numTrans > numPalette) {
+ numTrans = numPalette;
}
- const int sampleSize = this->getSampleSize();
- SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+ int index = 0;
+ int transLessThanFF = 0;
- decodedBitmap->setConfig(config, sampler.scaledWidth(),
- sampler.scaledHeight(), 0);
- if (SkImageDecoder::kDecodeBounds_Mode == mode) {
- return true;
+ for (; index < numTrans; index++) {
+ transLessThanFF |= (int)*trans - 0xFF;
+ *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+ palette++;
}
+ reallyHasAlpha |= (transLessThanFF < 0);
- // from here down we are concerned with colortables and pixels
+ for (; index < numPalette; index++) {
+ *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+ palette++;
+ }
- // 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;
+ // see BUGGY IMAGE WORKAROUND comment above
+ if (numPalette < 256) {
+ *colorPtr = colorPtr[-1];
+ }
+ colorTable->unlockColors(true);
+ *colorTablep = colorTable;
+ *reallyHasAlphap = reallyHasAlpha;
+ return true;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
- int num_palette;
- png_colorp palette;
- png_bytep trans;
- int num_trans;
+bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *height) {
+ png_structp png_ptr;
+ png_infop info_ptr;
- png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+ if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
+ return false;
+ }
- /* BUGGY IMAGE WORKAROUND
+ if (setjmp(png_jmpbuf(png_ptr)) != 0) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+ return false;
+ }
- 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);
+ 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);
- colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+ *width = origWidth;
+ *height = origHeight;
- 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;
- }
+ png_build_index(png_ptr);
- int index = 0;
- int transLessThanFF = 0;
+ if (fImageIndex) {
+ SkDELETE(fImageIndex);
+ }
+ fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr));
- for (; index < num_trans; index++) {
- transLessThanFF |= (int)*trans - 0xFF;
- *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
- palette++;
- }
- reallyHasAlpha |= (transLessThanFF < 0);
+ return true;
+}
- for (; index < num_palette; index++) {
- *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
- palette++;
- }
+bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
+ png_structp png_ptr = fImageIndex->png_ptr;
+ png_infop info_ptr = fImageIndex->info_ptr;
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ return false;
+ }
- // see BUGGY IMAGE WORKAROUND comment above
- if (num_palette < 256) {
- *colorPtr = colorPtr[-1];
- }
- colorTable->unlockColors(true);
+ png_uint_32 origWidth, origHeight;
+ int bitDepth, colorType, interlaceType;
+ png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
+ &colorType, &interlaceType, int_p_NULL, int_p_NULL);
+
+ SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
+
+ if (!rect.intersect(region)) {
+ // If the requested region is entirely outsides the image, just
+ // returns false
+ return false;
}
- SkAutoUnref aur(colorTable);
+ SkBitmap::Config config;
+ bool hasAlpha = false;
+ bool doDither = this->getDitherImage();
+ SkPMColor theTranspColor = 0; // 0 tells us not to try to match
- if (!this->allocPixelRef(decodedBitmap,
- SkBitmap::kIndex8_Config == config ?
- colorTable : NULL)) {
+ if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
return false;
}
- SkAutoLockPixels alp(*decodedBitmap);
+ const int sampleSize = this->getSampleSize();
+ SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
+
+ SkBitmap decodedBitmap;
+ decodedBitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0);
+
+ // 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 (colorType == PNG_COLOR_TYPE_PALETTE) {
+ decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+ }
- /* 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);
+ SkAutoUnref aur(colorTable);
- /* swap bytes of 16 bit files to least significant byte first */
- // png_set_swap(png_ptr);
+ // Check ahead of time if the swap(dest, src) is possible.
+ // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
+ // If no, then we will use alloc to allocate pixels to prevent garbage collection.
+ int w = rect.width() / sampleSize;
+ int h = rect.height() / sampleSize;
+ const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
+ (h == decodedBitmap.height()) && bm->isNull();
+ const bool needColorTable = SkBitmap::kIndex8_Config == config;
+ if (swapOnly) {
+ if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
+ return false;
+ }
+ } else {
+ if (!decodedBitmap.allocPixels(NULL, needColorTable ? colorTable : NULL)) {
+ return false;
+ }
+ }
+ SkAutoLockPixels alp(decodedBitmap);
/* Add filler (or alpha) byte (before/after each RGB triplet) */
- if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+ if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) {
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
}
* 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;
+ 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_ptr->pass = 0;
png_read_update_info(png_ptr, info_ptr);
+ int actualTop = rect.fTop;
+
if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
for (int i = 0; i < number_passes; i++) {
+ png_configure_decoder(png_ptr, &actualTop, i);
+ for (int j = 0; j < rect.fTop - actualTop; j++) {
+ uint8_t* bmRow = decodedBitmap.getAddr8(0, 0);
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+ }
for (png_uint_32 y = 0; y < origHeight; y++) {
- uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
- png_read_rows(png_ptr, &bmRow, NULL, 1);
+ uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
}
}
} else {
upscale png's palette to a direct model
*/
SkAutoLockColors ctLock(colorTable);
- if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
+ if (!sampler.begin(&decodedBitmap, sc, doDither, ctLock.colors())) {
return false;
}
- const int height = decodedBitmap->height();
+ const int height = decodedBitmap.height();
if (number_passes > 1) {
SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
size_t rb = origWidth * srcBytesPerPixel;
for (int i = 0; i < number_passes; i++) {
+ png_configure_decoder(png_ptr, &actualTop, i);
+ for (int j = 0; j < rect.fTop - actualTop; j++) {
+ uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+ }
uint8_t* row = base;
- for (png_uint_32 y = 0; y < origHeight; y++) {
+ for (int32_t y = 0; y < rect.height(); y++) {
uint8_t* bmRow = row;
- png_read_rows(png_ptr, &bmRow, NULL, 1);
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
row += rb;
}
}
} else {
SkAutoMalloc storage(origWidth * srcBytesPerPixel);
uint8_t* srcRow = (uint8_t*)storage.get();
+
+ png_configure_decoder(png_ptr, &actualTop, 0);
skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+ for (int i = 0; i < rect.fTop - actualTop; i++) {
+ uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+ }
for (int y = 0; y < height; y++) {
uint8_t* tmp = srcRow;
- png_read_rows(png_ptr, &tmp, NULL, 1);
+ 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);
+ reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
}
- decodedBitmap->setIsOpaque(!reallyHasAlpha);
+ decodedBitmap.setIsOpaque(!reallyHasAlpha);
+
+ if (swapOnly) {
+ bm->swap(decodedBitmap);
+ } else {
+ cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
+ region.width(), region.height(), 0, rect.y());
+ }
+
return true;
}
+#endif
///////////////////////////////////////////////////////////////////////////////
#include "SkUnPreMultiply.h"
static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
- SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
+ SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
if (!sk_stream->write(data, len)) {
png_error(png_ptr, "sk_write_fn Error!");
}
class SkPNGImageEncoder : public SkImageEncoder {
protected:
- virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+ virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
private:
bool doEncode(SkWStream* stream, const SkBitmap& bm,
const bool& hasAlpha, int colorType,
int bitDepth, SkBitmap::Config config,
png_color_8& sig_bit);
+
+ typedef SkImageEncoder INHERITED;
};
bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
info_ptr = png_create_info_struct(png_ptr);
if (NULL == info_ptr) {
- png_destroy_write_struct(&png_ptr, NULL);
+ png_destroy_write_struct(&png_ptr, png_infopp_NULL);
return false;
}
return false;
}
- png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, NULL);
+ 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
--- /dev/null
+/*
+ * Copyright 2010, 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 "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkTScopedPtr.h"
+
+// A WebP decoder only, on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+// http://code.google.com/speed/webp/
+// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
+// http://review.webmproject.org/gitweb?p=libwebp.git
+
+#include <stdio.h>
+extern "C" {
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
+#include "webp/decode.h"
+#include "webp/encode.h"
+}
+
+#ifdef ANDROID
+#include <cutils/properties.h>
+
+// Key to lookup the size of memory buffer set in system property
+static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap";
+#endif
+
+// this enables timing code to report milliseconds for a decode
+//#define TIME_DECODE
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+// Define VP8 I/O on top of Skia stream
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+static const size_t WEBP_VP8_HEADER_SIZE = 64;
+static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
+
+// Parse headers of RIFF container, and check for valid Webp (VP8) content.
+static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) {
+ unsigned char buffer[WEBP_VP8_HEADER_SIZE];
+ const uint32_t contentSize = stream->getLength();
+ const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE);
+ const uint32_t read_bytes =
+ (contentSize < WEBP_VP8_HEADER_SIZE) ? contentSize : WEBP_VP8_HEADER_SIZE;
+ if (len != read_bytes) {
+ return false; // can't read enough
+ }
+
+ WebPBitstreamFeatures features;
+ VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features);
+ if (VP8_STATUS_OK != status) {
+ return false; // Invalid WebP file.
+ }
+ *width = features.width;
+ *height = features.height;
+ *alpha = features.has_alpha;
+
+ // sanity check for image size that's about to be decoded.
+ {
+ Sk64 size;
+ size.setMul(*width, *height);
+ if (size.isNeg() || !size.is32()) {
+ return false;
+ }
+ // now check that if we are 4-bytes per pixel, we also don't overflow
+ if (size.get32() > (0x7FFFFFFF >> 2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+class SkWEBPImageDecoder: public SkImageDecoder {
+public:
+ SkWEBPImageDecoder() {
+ fInputStream = NULL;
+ fOrigWidth = 0;
+ fOrigHeight = 0;
+ fHasAlpha = 0;
+ }
+ virtual ~SkWEBPImageDecoder() {
+ SkSafeUnref(fInputStream);
+ }
+
+ virtual Format getFormat() const SK_OVERRIDE {
+ return kWEBP_Format;
+ }
+
+protected:
+ virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
+ virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+ bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
+ SkStream* fInputStream;
+ int fOrigWidth;
+ int fOrigHeight;
+ int fHasAlpha;
+
+ typedef SkImageDecoder INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef TIME_DECODE
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+ AutoTimeMillis(const char label[]) :
+ fLabel(label) {
+ if (NULL == fLabel) {
+ fLabel = "";
+ }
+ fNow = SkTime::GetMSecs();
+ }
+ ~AutoTimeMillis() {
+ SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+ }
+private:
+ const char* fLabel;
+ SkMSec fNow;
+};
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const SkBitmap& bm, const char msg[]) {
+ SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
+ return false; // must always return false
+}
+
+static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, int hasAlpha) {
+ WEBP_CSP_MODE mode = MODE_LAST;
+ SkBitmap::Config config = decodedBitmap->config();
+ // For images that have alpha, choose appropriate color mode (MODE_rgbA,
+ // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency
+ // factor (alpha).
+ if (config == SkBitmap::kARGB_8888_Config) {
+ mode = hasAlpha ? MODE_rgbA : MODE_RGBA;
+ } else if (config == SkBitmap::kARGB_4444_Config) {
+ mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444;
+ } else if (config == SkBitmap::kRGB_565_Config) {
+ mode = MODE_RGB_565;
+ }
+ SkASSERT(MODE_LAST != mode);
+ return mode;
+}
+
+// Incremental WebP image decoding. Reads input buffer of 64K size iteratively
+// and decodes this block to appropriate color-space as per config object.
+static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
+ WebPIDecoder* idec = WebPIDecode(NULL, 0, config);
+ if (NULL == idec) {
+ WebPFreeDecBuffer(&config->output);
+ return false;
+ }
+
+ stream->rewind();
+ const uint32_t contentSize = stream->getLength();
+ const uint32_t readBufferSize = (contentSize < WEBP_IDECODE_BUFFER_SZ) ?
+ contentSize : WEBP_IDECODE_BUFFER_SZ;
+ SkAutoMalloc srcStorage(readBufferSize);
+ unsigned char* input = (uint8_t*)srcStorage.get();
+ if (NULL == input) {
+ WebPIDelete(idec);
+ WebPFreeDecBuffer(&config->output);
+ return false;
+ }
+
+ uint32_t bytesRemaining = contentSize;
+ while (bytesRemaining > 0) {
+ const uint32_t bytesToRead = (bytesRemaining < WEBP_IDECODE_BUFFER_SZ) ?
+ bytesRemaining : WEBP_IDECODE_BUFFER_SZ;
+ const size_t bytesRead = stream->read(input, bytesToRead);
+ if (0 == bytesRead) {
+ break;
+ }
+
+ VP8StatusCode status = WebPIAppend(idec, input, bytesRead);
+ if (VP8_STATUS_OK == status || VP8_STATUS_SUSPENDED == status) {
+ bytesRemaining -= bytesRead;
+ } else {
+ break;
+ }
+ }
+ srcStorage.free();
+ WebPIDelete(idec);
+ WebPFreeDecBuffer(&config->output);
+
+ if (bytesRemaining > 0) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static bool webp_get_config_resize(WebPDecoderConfig* config,
+ SkBitmap* decodedBitmap,
+ int width, int height, int hasAlpha) {
+ WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
+ if (MODE_LAST == mode) {
+ return false;
+ }
+
+ if (0 == WebPInitDecoderConfig(config)) {
+ return false;
+ }
+
+ config->output.colorspace = mode;
+ config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+ config->output.u.RGBA.stride = decodedBitmap->rowBytes();
+ config->output.u.RGBA.size = decodedBitmap->getSize();
+ config->output.is_external_memory = 1;
+
+ if (width != decodedBitmap->width() || height != decodedBitmap->height()) {
+ config->options.use_scaling = 1;
+ config->options.scaled_width = decodedBitmap->width();
+ config->options.scaled_height = decodedBitmap->height();
+ }
+
+ return true;
+}
+
+static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
+ SkBitmap* decodedBitmap,
+ const SkIRect& region, int hasAlpha) {
+
+ if (!webp_get_config_resize(config, decodedBitmap, region.width(),
+ region.height(), hasAlpha)) {
+ return false;
+ }
+
+ config->options.use_cropping = 1;
+ config->options.crop_left = region.fLeft;
+ config->options.crop_top = region.fTop;
+ config->options.crop_width = region.width();
+ config->options.crop_height = region.height();
+
+ return true;
+}
+
+bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
+ int width, int height) {
+ SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, fHasAlpha);
+
+ // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
+ if (fHasAlpha) {
+ if (config != SkBitmap::kARGB_4444_Config) {
+ config = SkBitmap::kARGB_8888_Config;
+ }
+ } else {
+ if (config != SkBitmap::kRGB_565_Config &&
+ config != SkBitmap::kARGB_4444_Config) {
+ config = SkBitmap::kARGB_8888_Config;
+ }
+ }
+
+ if (!this->chooseFromOneChoice(config, width, height)) {
+ return false;
+ }
+
+ decodedBitmap->setConfig(config, width, height, 0);
+
+ decodedBitmap->setIsOpaque(!fHasAlpha);
+
+ return true;
+}
+
+bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
+ int *width, int *height) {
+ int origWidth, origHeight, hasAlpha;
+ if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+ return false;
+ }
+
+ stream->rewind();
+ *width = origWidth;
+ *height = origHeight;
+
+ SkRefCnt_SafeAssign(this->fInputStream, stream);
+ this->fOrigWidth = origWidth;
+ this->fOrigHeight = origHeight;
+ this->fHasAlpha = hasAlpha;
+
+ return true;
+}
+
+static bool is_config_compatible(const SkBitmap& bitmap) {
+ SkBitmap::Config config = bitmap.config();
+ return config == SkBitmap::kARGB_4444_Config ||
+ config == SkBitmap::kRGB_565_Config ||
+ config == SkBitmap::kARGB_8888_Config;
+}
+
+bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
+ const SkIRect& region) {
+ SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight);
+
+ if (!rect.intersect(region)) {
+ // If the requested region is entirely outsides the image, return false
+ return false;
+ }
+
+ const int sampleSize = this->getSampleSize();
+ SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
+ const int width = sampler.scaledWidth();
+ const int height = sampler.scaledHeight();
+
+ // The image can be decoded directly to decodedBitmap if
+ // 1. the region is within the image range
+ // 2. bitmap's config is compatible
+ // 3. bitmap's size is same as the required region (after sampled)
+ bool directDecode = (rect == region) &&
+ (decodedBitmap->isNull() ||
+ (is_config_compatible(*decodedBitmap) &&
+ (decodedBitmap->width() == width) &&
+ (decodedBitmap->height() == height)));
+ SkTScopedPtr<SkBitmap> adb;
+ SkBitmap *bitmap = decodedBitmap;
+
+ if (!directDecode) {
+ // allocates a temp bitmap
+ bitmap = new SkBitmap;
+ adb.reset(bitmap);
+ }
+
+ if (bitmap->isNull()) {
+ if (!setDecodeConfig(bitmap, width, height)) {
+ return false;
+ }
+ // alloc from native heap if it is a temp bitmap. (prevent GC)
+ bool allocResult = (bitmap == decodedBitmap)
+ ? allocPixelRef(bitmap, NULL)
+ : bitmap->allocPixels();
+ if (!allocResult) {
+ return return_false(*decodedBitmap, "allocPixelRef");
+ }
+ } else {
+ // This is also called in setDecodeConfig in above block.
+ // i.e., when bitmap->isNull() is true.
+ if (!chooseFromOneChoice(bitmap->config(), width, height)) {
+ return false;
+ }
+ }
+
+ SkAutoLockPixels alp(*bitmap);
+ WebPDecoderConfig config;
+ if (!webp_get_config_resize_crop(&config, bitmap, rect, fHasAlpha)) {
+ return false;
+ }
+
+ // Decode the WebP image data stream using WebP incremental decoding for
+ // the specified cropped image-region.
+ if (!webp_idecode(this->fInputStream, &config)) {
+ return false;
+ }
+
+ if (!directDecode) {
+ cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
+ region.width(), region.height(), rect.x(), rect.y());
+ }
+ return true;
+}
+
+bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+ Mode mode) {
+#ifdef TIME_DECODE
+ AutoTimeMillis atm("WEBP Decode");
+#endif
+
+ int origWidth, origHeight, hasAlpha;
+ if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+ return false;
+ }
+ this->fHasAlpha = hasAlpha;
+
+ const int sampleSize = this->getSampleSize();
+ SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+ // If only bounds are requested, done
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+ sampler.scaledHeight())) {
+ return false;
+ }
+ return true;
+ }
+
+ // No Bitmap reuse supported for this format
+ if (!decodedBitmap->isNull()) {
+ return false;
+ }
+ if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+ sampler.scaledHeight())) {
+ return false;
+ }
+
+ if (!this->allocPixelRef(decodedBitmap, NULL)) {
+ return return_false(*decodedBitmap, "allocPixelRef");
+ }
+
+ SkAutoLockPixels alp(*decodedBitmap);
+
+ WebPDecoderConfig config;
+ if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
+ hasAlpha)) {
+ return false;
+ }
+
+ // Decode the WebP image data stream using WebP incremental decoding.
+ return webp_idecode(stream, &config);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
+ const SkPMColor* SK_RESTRICT ctable);
+
+static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+ const SkPMColor*) {
+ const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
+ for (int i = 0; i < width; ++i) {
+ const uint32_t c = *src++;
+ rgb[0] = SkGetPackedR32(c);
+ rgb[1] = SkGetPackedG32(c);
+ rgb[2] = SkGetPackedB32(c);
+ rgb += 3;
+ }
+}
+
+static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+ const SkPMColor*) {
+ const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
+ for (int i = 0; i < width; ++i) {
+ const uint16_t c = *src++;
+ rgb[0] = SkPacked16ToR32(c);
+ rgb[1] = SkPacked16ToG32(c);
+ rgb[2] = SkPacked16ToB32(c);
+ rgb += 3;
+ }
+}
+
+static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+ const SkPMColor*) {
+ const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
+ for (int i = 0; i < width; ++i) {
+ const SkPMColor16 c = *src++;
+ rgb[0] = SkPacked4444ToR32(c);
+ rgb[1] = SkPacked4444ToG32(c);
+ rgb[2] = SkPacked4444ToB32(c);
+ rgb += 3;
+ }
+}
+
+static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+ const SkPMColor* SK_RESTRICT ctable) {
+ const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
+ for (int i = 0; i < width; ++i) {
+ const uint32_t c = ctable[*src++];
+ rgb[0] = SkGetPackedR32(c);
+ rgb[1] = SkGetPackedG32(c);
+ rgb[2] = SkGetPackedB32(c);
+ rgb += 3;
+ }
+}
+
+static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return ARGB_8888_To_RGB;
+ case SkBitmap::kRGB_565_Config:
+ return RGB_565_To_RGB;
+ case SkBitmap::kARGB_4444_Config:
+ return ARGB_4444_To_RGB;
+ case SkBitmap::kIndex8_Config:
+ return Index8_To_RGB;
+ default:
+ return NULL;
+ }
+}
+
+static int stream_writer(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ SkWStream* const stream = (SkWStream*)picture->custom_ptr;
+ return stream->write(data, data_size) ? 1 : 0;
+}
+
+class SkWEBPImageEncoder : public SkImageEncoder {
+protected:
+ virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+
+private:
+ typedef SkImageEncoder INHERITED;
+};
+
+bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
+ int quality) {
+ const SkBitmap::Config config = bm.getConfig();
+ const ScanlineImporter scanline_import = ChooseImporter(config);
+ if (NULL == scanline_import) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(bm);
+ SkAutoLockColors ctLocker;
+ if (NULL == bm.getPixels()) {
+ return false;
+ }
+
+ WebPConfig webp_config;
+ if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) {
+ return false;
+ }
+
+ WebPPicture pic;
+ WebPPictureInit(&pic);
+ pic.width = bm.width();
+ pic.height = bm.height();
+ pic.writer = stream_writer;
+ pic.custom_ptr = (void*)stream;
+
+ const SkPMColor* colors = ctLocker.lockColors(bm);
+ const uint8_t* src = (uint8_t*)bm.getPixels();
+ const int rgbStride = pic.width * 3;
+
+ // Import (for each scanline) the bit-map image (in appropriate color-space)
+ // to RGB color space.
+ uint8_t* rgb = new uint8_t[rgbStride * pic.height];
+ for (int y = 0; y < pic.height; ++y) {
+ scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride,
+ pic.width, colors);
+ }
+
+ bool ok = WebPPictureImportRGB(&pic, rgb, rgbStride);
+ delete[] rgb;
+
+ ok = ok && WebPEncode(&webp_config, &pic);
+ WebPPictureFree(&pic);
+
+ return ok;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(WEBPImageDecoder);
+DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
+ int width, height, hasAlpha;
+ if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
+ return NULL;
+ }
+
+ // Magic matches, call decoder
+ return SkNEW(SkWEBPImageDecoder);
+}
+
+static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
+ return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
class SkWBMPImageDecoder : public SkImageDecoder {
public:
- virtual Format getFormat() const {
+ virtual Format getFormat() const SK_OVERRIDE {
return kWBMP_Format;
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+
+private:
+ typedef SkImageDecoder INHERITED;
};
static bool read_byte(SkStream* stream, uint8_t* data)
int width = head.fWidth;
int height = head.fHeight;
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+ decodedBitmap->setIsOpaque(true);
+ return true;
+ }
+
+ // No Bitmap reuse supported for this format
+ if (!decodedBitmap->isNull()) {
+ return false;
+ }
- // assign these directly, in case we return kDimensions_Result
decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
decodedBitmap->setIsOpaque(true);
- if (SkImageDecoder::kDecodeBounds_Mode == mode)
- return true;
-
const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
SkAutoUnref aur(ct);