2011-07-28 14:26:00 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2006 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.
|
|
|
|
*/
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
#include "SkImageDecoder.h"
|
|
|
|
#include "SkBitmap.h"
|
2013-02-22 21:38:35 +00:00
|
|
|
#include "SkImagePriv.h"
|
2008-12-17 15:59:43 +00:00
|
|
|
#include "SkPixelRef.h"
|
|
|
|
#include "SkStream.h"
|
|
|
|
#include "SkTemplates.h"
|
2013-03-14 14:42:18 +00:00
|
|
|
#include "SkCanvas.h"
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
|
|
|
|
|
|
|
|
SkBitmap::Config SkImageDecoder::GetDeviceConfig()
|
|
|
|
{
|
|
|
|
return gDeviceConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
|
|
|
|
{
|
|
|
|
gDeviceConfig = config;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
SkImageDecoder::SkImageDecoder()
|
2013-05-03 20:14:28 +00:00
|
|
|
: fPeeker(NULL)
|
|
|
|
, fChooser(NULL)
|
|
|
|
, fAllocator(NULL)
|
|
|
|
, fSampleSize(1)
|
|
|
|
, fDefaultPref(SkBitmap::kNo_Config)
|
|
|
|
, fDitherImage(true)
|
|
|
|
, fUsePrefTable(false)
|
Add an option on SkImageDecoder to skip writing 0s.
Only implemented for PNG.
Add a getter and setter, and sets the default to false in the
constructor. Also copies the setting in copyFieldsToOther.
Fix an indpendent bug where fDitherImage was not being copied in
copyFieldsToOther.
In SkScaledBitmapSampler::begin, consolidate the settings passed in
by passing a const reference to the decoder. The decoder can be
referenced for its settings of dither, unpremultiplied, and now
skipping writing zeroes. Update callers to use the new API. In png
decoder, rather than passing around a pointer to an initial
read of getDitherImage, and potentially changing it, look at the
field on the decoder itself, and modify it directly. This is a
change in behavior - now if that same decoder is used to decode
a different image, the dither setting has changed. I think this is
okay because A) the typical use case is to use a new decoder for
each decode, B) we do not make any promises that a decode does not
change the decoder and C) it makes the code in SkScaledBitmapSampler
much cleaner.
In SkScaledBitmapScampler, add new row procs for skipping zeroes. Now
that choosing the row proc has five dimensions (src config, dst config,
dither, skip writing zeroes, unpremultiplied), use a new method: each
src/dst combination has a function for choosing the right proc depending
on the decoder.
SkScaledBitmapScampler::RowProc is now public for convenience.
Remove Sample_Gray_D8888_Unpremul, which is effectively no different
from Sample_Gray_D8888.
In cases where unpremultiplied was trivial, such as 565 and when
sampling from gray, decoding may now succeed.
Add a benchmark (currently disabled) for comparing the speed of skipping
writing zeroes versus not skipping. For this particular image, which is
mostly transparent pixels, normal decoding took about 3.6 milliseconds,
while skipping zeroes in the decode took only about 2.5 milliseconds
(this is on a Nexus 4). Presumably it would be slower on an image
with a small amount of transparency, but there will be no slowdown
for an image which reports that it has no transparency.
In SkImageRef_ashmem, always skip writing zeroes, since ashmem
memory is guaranteed to be initialized to 0.
Add a flag to skip writing zeroes in skimage.
Add a regression test for choosing the rowproc to ensure I did not
change any behavior accidentally.
BUG=skia:1661
R=reed@google.com
Review URL: https://codereview.chromium.org/24269006
git-svn-id: http://skia.googlecode.com/svn/trunk@11558 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-10-01 17:27:15 +00:00
|
|
|
, fSkipWritingZeroes(false)
|
2013-06-14 15:33:20 +00:00
|
|
|
, fPreferQualityOverSpeed(false)
|
|
|
|
, fRequireUnpremultipliedColors(false) {
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkImageDecoder::~SkImageDecoder() {
|
2011-02-07 15:30:46 +00:00
|
|
|
SkSafeUnref(fPeeker);
|
|
|
|
SkSafeUnref(fChooser);
|
|
|
|
SkSafeUnref(fAllocator);
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
2013-07-09 15:48:24 +00:00
|
|
|
void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
|
|
|
|
if (NULL == other) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
other->setPeeker(fPeeker);
|
|
|
|
other->setChooser(fChooser);
|
|
|
|
other->setAllocator(fAllocator);
|
|
|
|
other->setSampleSize(fSampleSize);
|
|
|
|
if (fUsePrefTable) {
|
|
|
|
other->setPrefConfigTable(fPrefTable);
|
|
|
|
} else {
|
|
|
|
other->fDefaultPref = fDefaultPref;
|
|
|
|
}
|
Add an option on SkImageDecoder to skip writing 0s.
Only implemented for PNG.
Add a getter and setter, and sets the default to false in the
constructor. Also copies the setting in copyFieldsToOther.
Fix an indpendent bug where fDitherImage was not being copied in
copyFieldsToOther.
In SkScaledBitmapSampler::begin, consolidate the settings passed in
by passing a const reference to the decoder. The decoder can be
referenced for its settings of dither, unpremultiplied, and now
skipping writing zeroes. Update callers to use the new API. In png
decoder, rather than passing around a pointer to an initial
read of getDitherImage, and potentially changing it, look at the
field on the decoder itself, and modify it directly. This is a
change in behavior - now if that same decoder is used to decode
a different image, the dither setting has changed. I think this is
okay because A) the typical use case is to use a new decoder for
each decode, B) we do not make any promises that a decode does not
change the decoder and C) it makes the code in SkScaledBitmapSampler
much cleaner.
In SkScaledBitmapScampler, add new row procs for skipping zeroes. Now
that choosing the row proc has five dimensions (src config, dst config,
dither, skip writing zeroes, unpremultiplied), use a new method: each
src/dst combination has a function for choosing the right proc depending
on the decoder.
SkScaledBitmapScampler::RowProc is now public for convenience.
Remove Sample_Gray_D8888_Unpremul, which is effectively no different
from Sample_Gray_D8888.
In cases where unpremultiplied was trivial, such as 565 and when
sampling from gray, decoding may now succeed.
Add a benchmark (currently disabled) for comparing the speed of skipping
writing zeroes versus not skipping. For this particular image, which is
mostly transparent pixels, normal decoding took about 3.6 milliseconds,
while skipping zeroes in the decode took only about 2.5 milliseconds
(this is on a Nexus 4). Presumably it would be slower on an image
with a small amount of transparency, but there will be no slowdown
for an image which reports that it has no transparency.
In SkImageRef_ashmem, always skip writing zeroes, since ashmem
memory is guaranteed to be initialized to 0.
Add a flag to skip writing zeroes in skimage.
Add a regression test for choosing the rowproc to ensure I did not
change any behavior accidentally.
BUG=skia:1661
R=reed@google.com
Review URL: https://codereview.chromium.org/24269006
git-svn-id: http://skia.googlecode.com/svn/trunk@11558 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-10-01 17:27:15 +00:00
|
|
|
other->setDitherImage(fDitherImage);
|
|
|
|
other->setSkipWritingZeroes(fSkipWritingZeroes);
|
2013-07-09 15:48:24 +00:00
|
|
|
other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
|
|
|
|
other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
|
|
|
|
}
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
SkImageDecoder::Format SkImageDecoder::getFormat() const {
|
|
|
|
return kUnknown_Format;
|
|
|
|
}
|
|
|
|
|
2013-03-14 14:42:18 +00:00
|
|
|
const char* SkImageDecoder::getFormatName() const {
|
2013-05-15 14:53:49 +00:00
|
|
|
return GetFormatName(this->getFormat());
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* SkImageDecoder::GetFormatName(Format format) {
|
|
|
|
switch (format) {
|
2013-04-25 17:33:51 +00:00
|
|
|
case kUnknown_Format:
|
|
|
|
return "Unknown Format";
|
|
|
|
case kBMP_Format:
|
|
|
|
return "BMP";
|
|
|
|
case kGIF_Format:
|
|
|
|
return "GIF";
|
|
|
|
case kICO_Format:
|
|
|
|
return "ICO";
|
|
|
|
case kJPEG_Format:
|
|
|
|
return "JPEG";
|
|
|
|
case kPNG_Format:
|
|
|
|
return "PNG";
|
|
|
|
case kWBMP_Format:
|
|
|
|
return "WBMP";
|
|
|
|
case kWEBP_Format:
|
|
|
|
return "WEBP";
|
|
|
|
default:
|
2013-08-22 15:37:26 +00:00
|
|
|
SkDEBUGFAIL("Invalid format type!");
|
2013-04-25 17:33:51 +00:00
|
|
|
}
|
|
|
|
return "Unknown Format";
|
2013-03-14 14:42:18 +00:00
|
|
|
}
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
|
|
|
|
SkRefCnt_SafeAssign(fPeeker, peeker);
|
|
|
|
return peeker;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
|
|
|
|
SkRefCnt_SafeAssign(fChooser, chooser);
|
|
|
|
return chooser;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
|
|
|
|
SkRefCnt_SafeAssign(fAllocator, alloc);
|
|
|
|
return alloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkImageDecoder::setSampleSize(int size) {
|
|
|
|
if (size < 1) {
|
|
|
|
size = 1;
|
|
|
|
}
|
|
|
|
fSampleSize = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
|
|
|
|
int height) const {
|
|
|
|
Chooser* chooser = fChooser;
|
|
|
|
|
|
|
|
if (NULL == chooser) { // no chooser, we just say YES to decoding :)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
chooser->begin(1);
|
|
|
|
chooser->inspect(0, config, width, height);
|
|
|
|
return chooser->choose() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
|
|
|
|
SkColorTable* ctable) const {
|
|
|
|
return bitmap->allocPixels(fAllocator, ctable);
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2009-07-29 20:56:52 +00:00
|
|
|
|
2013-07-18 19:34:49 +00:00
|
|
|
void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
|
|
|
|
fUsePrefTable = true;
|
|
|
|
fPrefTable = prefTable;
|
|
|
|
}
|
|
|
|
|
2010-03-03 21:04:12 +00:00
|
|
|
SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
|
|
|
|
bool srcHasAlpha) const {
|
2013-07-18 19:42:35 +00:00
|
|
|
SkBitmap::Config config = SkBitmap::kNo_Config;
|
2010-03-03 21:04:12 +00:00
|
|
|
|
|
|
|
if (fUsePrefTable) {
|
|
|
|
switch (srcDepth) {
|
|
|
|
case kIndex_SrcDepth:
|
2013-07-18 19:34:49 +00:00
|
|
|
config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
|
|
|
|
: fPrefTable.fPrefFor_8Index_NoAlpha_src;
|
2010-03-03 21:04:12 +00:00
|
|
|
break;
|
2013-07-18 19:34:49 +00:00
|
|
|
case k8BitGray_SrcDepth:
|
|
|
|
config = fPrefTable.fPrefFor_8Gray_src;
|
2010-03-03 21:04:12 +00:00
|
|
|
break;
|
|
|
|
case k32Bit_SrcDepth:
|
2013-07-18 19:34:49 +00:00
|
|
|
config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
|
|
|
|
: fPrefTable.fPrefFor_8bpc_NoAlpha_src;
|
2010-03-03 21:04:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
config = fDefaultPref;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SkBitmap::kNo_Config == config) {
|
|
|
|
config = SkImageDecoder::GetDeviceConfig();
|
|
|
|
}
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
|
2013-07-09 15:45:14 +00:00
|
|
|
SkBitmap::Config pref, Mode mode) {
|
2008-12-17 15:59:43 +00:00
|
|
|
// we reset this to false before calling onDecode
|
|
|
|
fShouldCancelDecode = false;
|
2010-03-03 21:04:12 +00:00
|
|
|
// assign this, for use by getPrefConfig(), in case fUsePrefTable is false
|
|
|
|
fDefaultPref = pref;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2013-03-14 14:42:18 +00:00
|
|
|
// pass a temporary bitmap, so that if we return false, we are assured of
|
|
|
|
// leaving the caller's bitmap untouched.
|
|
|
|
SkBitmap tmp;
|
2010-03-03 21:04:12 +00:00
|
|
|
if (!this->onDecode(stream, &tmp, mode)) {
|
2009-02-11 15:07:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bm->swap(tmp);
|
|
|
|
return true;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
2013-05-03 20:14:28 +00:00
|
|
|
bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
|
2013-03-14 14:42:18 +00:00
|
|
|
SkBitmap::Config pref) {
|
2013-05-03 20:14:28 +00:00
|
|
|
// we reset this to false before calling onDecodeSubset
|
2013-03-14 14:42:18 +00:00
|
|
|
fShouldCancelDecode = false;
|
|
|
|
// assign this, for use by getPrefConfig(), in case fUsePrefTable is false
|
|
|
|
fDefaultPref = pref;
|
|
|
|
|
2013-05-03 20:14:28 +00:00
|
|
|
return this->onDecodeSubset(bm, rect);
|
2013-03-14 14:42:18 +00:00
|
|
|
}
|
|
|
|
|
2013-09-25 21:34:24 +00:00
|
|
|
bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream,
|
|
|
|
int *width, int *height) {
|
2013-03-14 14:42:18 +00:00
|
|
|
// we reset this to false before calling onBuildTileIndex
|
|
|
|
fShouldCancelDecode = false;
|
|
|
|
|
|
|
|
return this->onBuildTileIndex(stream, width, height);
|
|
|
|
}
|
|
|
|
|
2013-05-03 20:14:28 +00:00
|
|
|
bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
|
|
|
|
int dstX, int dstY, int width, int height,
|
|
|
|
int srcX, int srcY) {
|
2013-03-14 14:42:18 +00:00
|
|
|
int w = width / sampleSize;
|
|
|
|
int h = height / sampleSize;
|
2013-10-31 17:28:30 +00:00
|
|
|
if (src->config() == SkBitmap::kIndex8_Config) {
|
2013-05-03 20:14:28 +00:00
|
|
|
// kIndex8 does not allow drawing via an SkCanvas, as is done below.
|
|
|
|
// Instead, use extractSubset. Note that this shares the SkPixelRef and
|
|
|
|
// SkColorTable.
|
|
|
|
// FIXME: Since src is discarded in practice, this holds on to more
|
|
|
|
// pixels than is strictly necessary. Switch to a copy if memory
|
|
|
|
// savings are more important than speed here. This also means
|
|
|
|
// that the pixels in dst can not be reused (though there is no
|
|
|
|
// allocation, which was already done on src).
|
|
|
|
int x = (dstX - srcX) / sampleSize;
|
|
|
|
int y = (dstY - srcY) / sampleSize;
|
|
|
|
SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
|
|
|
|
return src->extractSubset(dst, subset);
|
|
|
|
}
|
2013-03-14 14:42:18 +00:00
|
|
|
// if the destination has no pixels then we must allocate them.
|
|
|
|
if (dst->isNull()) {
|
2013-10-31 17:28:30 +00:00
|
|
|
dst->setConfig(src->config(), w, h, 0, src->alphaType());
|
2013-03-14 14:42:18 +00:00
|
|
|
|
|
|
|
if (!this->allocPixelRef(dst, NULL)) {
|
|
|
|
SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
|
2013-05-03 20:14:28 +00:00
|
|
|
return false;
|
2013-03-14 14:42:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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.
|
2013-05-03 20:14:28 +00:00
|
|
|
if (dst->width() < w || dst->height() < h) {
|
|
|
|
SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
|
|
|
|
}
|
2013-03-14 14:42:18 +00:00
|
|
|
|
|
|
|
// 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);
|
2013-05-03 20:14:28 +00:00
|
|
|
return true;
|
2013-03-14 14:42:18 +00:00
|
|
|
}
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
|
2009-06-15 13:04:45 +00:00
|
|
|
SkBitmap::Config pref, Mode mode, Format* format) {
|
2008-12-17 15:59:43 +00:00
|
|
|
SkASSERT(file);
|
|
|
|
SkASSERT(bm);
|
|
|
|
|
2013-09-25 21:34:24 +00:00
|
|
|
SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
|
2013-03-19 02:18:33 +00:00
|
|
|
if (stream.get()) {
|
|
|
|
if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
|
2008-12-17 15:59:43 +00:00
|
|
|
bm->pixelRef()->setURI(file);
|
2012-01-03 14:42:08 +00:00
|
|
|
return true;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
|
2009-06-15 13:04:45 +00:00
|
|
|
SkBitmap::Config pref, Mode mode, Format* format) {
|
2008-12-17 15:59:43 +00:00
|
|
|
if (0 == size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SkASSERT(buffer);
|
|
|
|
|
|
|
|
SkMemoryStream stream(buffer, size);
|
2009-06-15 13:04:45 +00:00
|
|
|
return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
2013-09-25 21:34:24 +00:00
|
|
|
bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm,
|
|
|
|
SkBitmap::Config pref, Mode mode,
|
|
|
|
Format* format) {
|
2008-12-17 15:59:43 +00:00
|
|
|
SkASSERT(stream);
|
|
|
|
SkASSERT(bm);
|
|
|
|
|
|
|
|
bool success = false;
|
|
|
|
SkImageDecoder* codec = SkImageDecoder::Factory(stream);
|
|
|
|
|
|
|
|
if (NULL != codec) {
|
|
|
|
success = codec->decode(stream, bm, pref, mode);
|
2009-06-15 13:04:45 +00:00
|
|
|
if (success && format) {
|
|
|
|
*format = codec->getFormat();
|
2013-04-25 17:33:51 +00:00
|
|
|
if (kUnknown_Format == *format) {
|
|
|
|
if (stream->rewind()) {
|
|
|
|
*format = GetStreamFormat(stream);
|
|
|
|
}
|
|
|
|
}
|
2009-06-15 13:04:45 +00:00
|
|
|
}
|
2008-12-17 15:59:43 +00:00
|
|
|
delete codec;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|