Upstream Android modifications to the image encoders/decoders.
This CL does not update the libjpeg as that change is large enough to warrant its own CL. Author: djsollen@google.com Reviewed By: reed@google.com,robertphillips@google.com,scroggo@google.com Review URL: https://chromiumcodereview.appspot.com/12604006 git-svn-id: http://skia.googlecode.com/svn/trunk@8155 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
5439ea2e19
commit
a936e37cc7
1
DEPS
1
DEPS
@ -15,6 +15,7 @@ deps = {
|
||||
"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 = [
|
||||
|
@ -7,6 +7,7 @@
|
||||
'standalone_static_library': 1,
|
||||
'dependencies': [
|
||||
'libjpeg.gyp:*',
|
||||
'libwebp.gyp:libwebp',
|
||||
'utils.gyp:utils',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
@ -31,13 +32,17 @@
|
||||
|
||||
'../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',
|
||||
|
174
gyp/libwebp.gyp
Normal file
174
gyp/libwebp.gyp
Normal file
@ -0,0 +1,174 @@
|
||||
# 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',
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
54
include/images/SkBitmapRegionDecoder.h
Normal file
54
include/images/SkBitmapRegionDecoder.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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
|
@ -13,6 +13,7 @@
|
||||
#include "SkBitmap.h"
|
||||
#include "SkBitmapFactory.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkStream;
|
||||
@ -25,6 +26,7 @@ class SkImageDecoder {
|
||||
public:
|
||||
virtual ~SkImageDecoder();
|
||||
|
||||
// Should be consistent with kFormatName
|
||||
enum Format {
|
||||
kUnknown_Format,
|
||||
kBMP_Format,
|
||||
@ -33,14 +35,19 @@ public:
|
||||
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.
|
||||
*/
|
||||
@ -51,6 +58,20 @@ public:
|
||||
*/
|
||||
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
|
||||
@ -175,11 +196,29 @@ public:
|
||||
|
||||
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.
|
||||
*/
|
||||
@ -296,6 +335,38 @@ protected:
|
||||
// must be overridden in subclasses. This guy is called by decode(...)
|
||||
virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
|
||||
|
||||
// If the decoder wants to support tiled based decoding,
|
||||
// this method must be overridden. This guy is called by buildTileIndex(...)
|
||||
virtual bool onBuildTileIndex(SkStream*, int *width, int *height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the decoder wants to support tiled based decoding,
|
||||
// this method must be overridden. This guy is called by decodeRegion(...)
|
||||
virtual bool onDecodeRegion(SkBitmap* bitmap, 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.
|
||||
@ -346,6 +417,14 @@ private:
|
||||
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&);
|
||||
@ -396,5 +475,6 @@ DECLARE_DECODER_CREATOR(ICOImageDecoder);
|
||||
DECLARE_DECODER_CREATOR(JPEGImageDecoder);
|
||||
DECLARE_DECODER_CREATOR(PNGImageDecoder);
|
||||
DECLARE_DECODER_CREATOR(WBMPImageDecoder);
|
||||
DECLARE_DECODER_CREATOR(WEBPImageDecoder);
|
||||
|
||||
#endif
|
||||
|
@ -17,7 +17,8 @@ class SkImageEncoder {
|
||||
public:
|
||||
enum Type {
|
||||
kJPEG_Type,
|
||||
kPNG_Type
|
||||
kPNG_Type,
|
||||
kWEBP_Type
|
||||
};
|
||||
static SkImageEncoder* Create(Type);
|
||||
|
||||
@ -80,5 +81,6 @@ protected:
|
||||
// not all of these will be available
|
||||
DECLARE_ENCODER_CREATOR(JPEGImageEncoder);
|
||||
DECLARE_ENCODER_CREATOR(PNGImageEncoder);
|
||||
DECLARE_ENCODER_CREATOR(WEBPImageEncoder);
|
||||
|
||||
#endif
|
||||
|
14
src/images/SkBitmapRegionDecoder.cpp
Normal file
14
src/images/SkBitmapRegionDecoder.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -12,11 +12,23 @@
|
||||
#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()
|
||||
@ -34,7 +46,7 @@ void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
|
||||
SkImageDecoder::SkImageDecoder()
|
||||
: fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
|
||||
fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
|
||||
fUsePrefTable(false) {
|
||||
fUsePrefTable(false),fPreferQualityOverSpeed(false) {
|
||||
}
|
||||
|
||||
SkImageDecoder::~SkImageDecoder() {
|
||||
@ -47,6 +59,11 @@ SkImageDecoder::Format SkImageDecoder::getFormat() const {
|
||||
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;
|
||||
@ -129,16 +146,22 @@ SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -146,6 +169,55 @@ bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
|
||||
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,
|
||||
|
@ -19,12 +19,15 @@ class SkBMPImageDecoder : public SkImageDecoder {
|
||||
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;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -115,11 +118,18 @@ bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||
|
||||
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;
|
||||
|
@ -18,12 +18,15 @@
|
||||
|
||||
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[] = {
|
||||
@ -201,11 +204,18 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
|
||||
width, height)) {
|
||||
return error_return(gif, *bm, "chooseFromOneChoice");
|
||||
}
|
||||
|
||||
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);
|
||||
if (SkImageDecoder::kDecodeBounds_Mode == mode)
|
||||
return true;
|
||||
|
||||
SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
|
||||
const GifImageDesc& desc = image->ImageDesc;
|
||||
|
||||
|
@ -16,19 +16,16 @@ class SkICOImageDecoder : public SkImageDecoder {
|
||||
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;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -235,12 +232,16 @@ bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode)
|
||||
//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))
|
||||
{
|
||||
|
@ -23,14 +23,52 @@ extern "C" {
|
||||
#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
|
||||
@ -43,7 +81,7 @@ protected:
|
||||
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;
|
||||
@ -51,13 +89,21 @@ private:
|
||||
};
|
||||
|
||||
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);
|
||||
@ -67,16 +113,14 @@ static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,10 +172,8 @@ static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
|
||||
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
|
||||
@ -143,15 +185,15 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
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
|
||||
@ -165,6 +207,9 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
* 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 */ );
|
||||
@ -179,138 +224,77 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
* 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
|
||||
|
||||
// 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 0
|
||||
if (has_sbit) {
|
||||
SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
|
||||
sig_bit->blue, 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 (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
|
||||
config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
|
||||
// now see if we can upscale to their requested config
|
||||
if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) {
|
||||
config = SkBitmap::kIndex8_Config;
|
||||
}
|
||||
} 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 = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
|
||||
// now match the request against our capabilities
|
||||
if (hasAlpha) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check for size
|
||||
{
|
||||
Sk64 size;
|
||||
size.setMul(origWidth, origHeight);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
|
||||
if (!getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &doDither, &theTranspColor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int sampleSize = this->getSampleSize();
|
||||
SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
|
||||
|
||||
decodedBitmap->setConfig(config, sampler.scaledWidth(),
|
||||
sampler.scaledHeight(), 0);
|
||||
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;
|
||||
}
|
||||
@ -323,91 +307,36 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
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);
|
||||
if (colorType == PNG_COLOR_TYPE_PALETTE) {
|
||||
decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
|
||||
}
|
||||
|
||||
SkAutoUnref aur(colorTable);
|
||||
|
||||
if (!this->allocPixelRef(decodedBitmap,
|
||||
SkBitmap::kIndex8_Config == config ?
|
||||
colorTable : NULL)) {
|
||||
return false;
|
||||
if (!reuseBitmap) {
|
||||
if (!this->allocPixelRef(decodedBitmap,
|
||||
SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
|
||||
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) {
|
||||
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:
|
||||
* 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).
|
||||
* 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);
|
||||
|
||||
@ -415,7 +344,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
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, NULL, 1);
|
||||
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -444,21 +373,21 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
if (number_passes > 1) {
|
||||
SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
|
||||
uint8_t* base = (uint8_t*)storage.get();
|
||||
size_t rb = origWidth * srcBytesPerPixel;
|
||||
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, NULL, 1);
|
||||
row += rb;
|
||||
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
||||
row += rowBytes;
|
||||
}
|
||||
}
|
||||
// now sample it
|
||||
base += sampler.srcY0() * rb;
|
||||
base += sampler.srcY0() * rowBytes;
|
||||
for (int y = 0; y < height; y++) {
|
||||
reallyHasAlpha |= sampler.next(base);
|
||||
base += sampler.srcDY() * rb;
|
||||
base += sampler.srcDY() * rowBytes;
|
||||
}
|
||||
} else {
|
||||
SkAutoMalloc storage(origWidth * srcBytesPerPixel);
|
||||
@ -467,7 +396,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
|
||||
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);
|
||||
@ -489,16 +418,405 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
||||
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 (*doDitherp) {
|
||||
#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)) {
|
||||
*doDitherp = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (colorType == PNG_COLOR_TYPE_PALETTE) {
|
||||
bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
|
||||
*configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
|
||||
// now see if we can upscale to their requested config
|
||||
if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
|
||||
*configp = SkBitmap::kIndex8_Config;
|
||||
}
|
||||
} 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 (colorType & PNG_COLOR_MASK_COLOR) {
|
||||
if (16 == bitDepth) {
|
||||
*theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
|
||||
transpColor->green >> 8,
|
||||
transpColor->blue >> 8);
|
||||
} else {
|
||||
*theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
|
||||
transpColor->green,
|
||||
transpColor->blue);
|
||||
}
|
||||
} else { // gray
|
||||
if (16 == bitDepth) {
|
||||
*theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
|
||||
transpColor->gray >> 8,
|
||||
transpColor->gray >> 8);
|
||||
} else {
|
||||
*theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
|
||||
transpColor->gray,
|
||||
transpColor->gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid ||
|
||||
PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
|
||||
PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
|
||||
*hasAlphap = true;
|
||||
}
|
||||
*configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
|
||||
// now match the request against our capabilities
|
||||
if (*hasAlphap) {
|
||||
if (*configp != SkBitmap::kARGB_4444_Config) {
|
||||
*configp = SkBitmap::kARGB_8888_Config;
|
||||
}
|
||||
} else {
|
||||
if (*configp != SkBitmap::kRGB_565_Config &&
|
||||
*configp != SkBitmap::kARGB_4444_Config) {
|
||||
*configp = SkBitmap::kARGB_8888_Config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check for size
|
||||
{
|
||||
Sk64 size;
|
||||
size.setMul(origWidth, origHeight);
|
||||
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 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;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
int transLessThanFF = 0;
|
||||
|
||||
for (; index < numTrans; index++) {
|
||||
transLessThanFF |= (int)*trans - 0xFF;
|
||||
*colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
|
||||
palette++;
|
||||
}
|
||||
reallyHasAlpha |= (transLessThanFF < 0);
|
||||
|
||||
for (; index < numPalette; index++) {
|
||||
*colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
|
||||
palette++;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *height) {
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)) != 0) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
*width = origWidth;
|
||||
*height = origHeight;
|
||||
|
||||
png_build_index(png_ptr);
|
||||
|
||||
if (fImageIndex) {
|
||||
SkDELETE(fImageIndex);
|
||||
}
|
||||
fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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, 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);
|
||||
}
|
||||
|
||||
SkAutoUnref aur(colorTable);
|
||||
|
||||
// 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 (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_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, 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 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 (int32_t y = 0; y < rect.height(); y++) {
|
||||
uint8_t* bmRow = row;
|
||||
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
||||
row += rb;
|
||||
}
|
||||
}
|
||||
// now sample it
|
||||
base += sampler.srcY0() * rb;
|
||||
for (int y = 0; y < height; y++) {
|
||||
reallyHasAlpha |= sampler.next(base);
|
||||
base += sampler.srcDY() * 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, png_bytepp_NULL, 1);
|
||||
reallyHasAlpha |= sampler.next(srcRow);
|
||||
if (y < height - 1) {
|
||||
skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != theTranspColor) {
|
||||
reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
|
||||
}
|
||||
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 "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_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!");
|
||||
}
|
||||
@ -609,12 +927,14 @@ static inline int pack_palette(SkColorTable* ctable,
|
||||
|
||||
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,
|
||||
@ -697,7 +1017,7 @@ bool SkPNGImageEncoder::doEncode(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;
|
||||
}
|
||||
|
||||
@ -709,7 +1029,7 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
|
||||
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
|
||||
|
595
src/images/SkImageDecoder_libwebp.cpp
Normal file
595
src/images/SkImageDecoder_libwebp.cpp
Normal file
@ -0,0 +1,595 @@
|
||||
/*
|
||||
* 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);
|
@ -17,12 +17,15 @@
|
||||
|
||||
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)
|
||||
@ -107,14 +110,21 @@ bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
|
||||
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user