зеркало из https://github.com/mozilla/moz-skia.git
Add an option to create unpremultiplied bitmaps.
Currently they cannot be used directly by Skia, but the pixels can be used elsewhere. SkImageDecoder: Add functions to require unpremultiplied output and query the presence of the requirement SkImageDecoder_libpng: SkImageDecoder_libwebp: SkImageDecoder_WIC: Respect the requirement for unpremultiplied output. TODO: Fix SkImageDecoder_CG. SkScaledBitmapSampler: Add procs to skip premultiplication and a boolean parameter to use those procs. ImageDecodingTest: Test unpremultiplied bitmap decoding. SampleUnpremul: Add a sample which allows visually comparing between the unpremultiplied version (copied into a premultiplied bitmap, since drawing unpremultiplied is not currently supported) and a premultiplied version of image files. gm.h: Add a getter for the resource path, so Samples can use it. As of patch set 13, https://codereview.chromium.org/16816016/ and https://codereview.chromium.org/16983004/, which were approved separately. R=reed@google.com Review URL: https://codereview.chromium.org/16410009 git-svn-id: http://skia.googlecode.com/svn/trunk@9612 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
99e0d08113
Коммит
2bbc2c945b
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBenchmark.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkData.h"
|
||||
#include "SkForceLinking.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
|
||||
__SK_FORCE_IMAGE_DECODER_LINKING;
|
||||
|
||||
class SkCanvas;
|
||||
|
||||
class ImageDecodeBench : public SkBenchmark {
|
||||
public:
|
||||
ImageDecodeBench(void* p, const char* filename)
|
||||
: INHERITED(p)
|
||||
, fName("image_decode_")
|
||||
, fFilename(filename)
|
||||
, fStream()
|
||||
, fValid(false) {
|
||||
fName.append(SkOSPath::SkBasename(filename));
|
||||
fIsRendering = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual const char* onGetName() SK_OVERRIDE {
|
||||
return fName.c_str();
|
||||
}
|
||||
|
||||
virtual void onPreDraw() SK_OVERRIDE {
|
||||
SkFILEStream fileStream(fFilename.c_str());
|
||||
fValid = fileStream.isValid() && fileStream.getLength() > 0;
|
||||
if (fValid) {
|
||||
const size_t size = fileStream.getLength();
|
||||
void* data = sk_malloc_throw(size);
|
||||
if (fileStream.read(data, size) < size) {
|
||||
fValid = false;
|
||||
} else {
|
||||
SkAutoTUnref<SkData> skdata(SkData::NewFromMalloc(data, size));
|
||||
fStream.setData(skdata.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void onDraw(SkCanvas*) SK_OVERRIDE {
|
||||
#ifdef SK_DEBUG
|
||||
if (!fValid) {
|
||||
SkDebugf("stream was invalid: %s\n", fName.c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Decode a bunch of times
|
||||
SkBitmap bm;
|
||||
for (int i = 0; i < SkBENCHLOOP(1000); ++i) {
|
||||
SkDEBUGCODE(bool success =) SkImageDecoder::DecodeStream(&fStream, &bm);
|
||||
#ifdef SK_DEBUG
|
||||
if (!success) {
|
||||
SkDebugf("failed to decode %s\n", fName.c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
SkDEBUGCODE(success =) fStream.rewind();
|
||||
#ifdef SK_DEBUG
|
||||
if (!success) {
|
||||
SkDebugf("failed to rewind %s\n", fName.c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fName;
|
||||
const SkString fFilename;
|
||||
SkMemoryStream fStream;
|
||||
bool fValid;
|
||||
|
||||
typedef SkBenchmark INHERITED;
|
||||
};
|
||||
|
||||
// These are files which call decodePalette
|
||||
//DEF_BENCH( return SkNEW_ARGS(ImageDecodeBench, (p, "/usr/local/google/home/scroggo/Downloads/images/hal_163x90.png")); )
|
||||
//DEF_BENCH( return SkNEW_ARGS(ImageDecodeBench, (p, "/usr/local/google/home/scroggo/Downloads/images/box_19_top-left.png")); )
|
4
gm/gm.h
4
gm/gm.h
|
@ -80,6 +80,10 @@ namespace skiagm {
|
|||
gResourcePath = resourcePath;
|
||||
}
|
||||
|
||||
static SkString& GetResourcePath() {
|
||||
return gResourcePath;
|
||||
}
|
||||
|
||||
bool isCanvasDeferred() const { return fCanvasIsDeferred; }
|
||||
void setCanvasIsDeferred(bool isDeferred) {
|
||||
fCanvasIsDeferred = isDeferred;
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
'../samplecode/SampleTiling.cpp',
|
||||
'../samplecode/SampleTinyBitmap.cpp',
|
||||
'../samplecode/SampleUnitMapper.cpp',
|
||||
'../samplecode/SampleUnpremul.cpp',
|
||||
'../samplecode/SampleVertices.cpp',
|
||||
'../samplecode/SampleXfermodesBlur.cpp',
|
||||
'../samplecode/TransitionView.cpp',
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
'../bench/GameBench.cpp',
|
||||
'../bench/GradientBench.cpp',
|
||||
'../bench/GrMemoryPoolBench.cpp',
|
||||
'../bench/ImageDecodeBench.cpp',
|
||||
'../bench/InterpBench.cpp',
|
||||
'../bench/LineBench.cpp',
|
||||
'../bench/LightingBench.cpp',
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
'../tests/GrMemoryPoolTest.cpp',
|
||||
'../tests/GrSurfaceTest.cpp',
|
||||
'../tests/HashCacheTest.cpp',
|
||||
'../tests/ImageDecodingTest.cpp',
|
||||
'../tests/InfRectTest.cpp',
|
||||
'../tests/LListTest.cpp',
|
||||
'../tests/LayerDrawLooperTest.cpp',
|
||||
|
|
|
@ -82,6 +82,21 @@ public:
|
|||
fPreferQualityOverSpeed = qualityOverSpeed;
|
||||
}
|
||||
|
||||
/** Set to true to require the decoder to return a bitmap with unpremultiplied
|
||||
colors. The default is false, meaning the resulting bitmap will have its
|
||||
colors premultiplied.
|
||||
NOTE: Passing true to this function may result in a bitmap which cannot
|
||||
be properly used by Skia.
|
||||
*/
|
||||
void setRequireUnpremultipliedColors(bool request) {
|
||||
fRequireUnpremultipliedColors = request;
|
||||
}
|
||||
|
||||
/** Returns true if the decoder will only return bitmaps with unpremultiplied
|
||||
colors.
|
||||
*/
|
||||
bool getRequireUnpremultipliedColors() const { return fRequireUnpremultipliedColors; }
|
||||
|
||||
/** \class Peeker
|
||||
|
||||
Base class for optional callbacks to retrieve meta/chunk data out of
|
||||
|
@ -433,6 +448,7 @@ private:
|
|||
bool fUsePrefTable;
|
||||
mutable bool fShouldCancelDecode;
|
||||
bool fPreferQualityOverSpeed;
|
||||
bool fRequireUnpremultipliedColors;
|
||||
};
|
||||
|
||||
/** Calling newDecoder with a stream returns a new matching imagedecoder
|
||||
|
|
|
@ -84,7 +84,8 @@ static void test_5bits() {
|
|||
SkDebugf("--- trunc: %d %d round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2);
|
||||
}
|
||||
|
||||
static SkShader* createChecker() {
|
||||
// No longer marked static, since it is externed in SampleUnpremul.
|
||||
SkShader* createChecker() {
|
||||
SkBitmap bm;
|
||||
bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
|
||||
bm.allocPixels();
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "gm.h"
|
||||
#include "SampleCode.h"
|
||||
#include "SkBlurDrawLooper.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkForceLinking.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
#include "SkSystemEventTypes.h"
|
||||
#include "SkTypes.h"
|
||||
#include "SkUtils.h"
|
||||
#include "SkView.h"
|
||||
|
||||
__SK_FORCE_IMAGE_DECODER_LINKING;
|
||||
|
||||
// Defined in SampleColorFilter.cpp
|
||||
extern SkShader* createChecker();
|
||||
|
||||
/**
|
||||
* Interprets c as an unpremultiplied color, and returns the
|
||||
* premultiplied equivalent.
|
||||
*/
|
||||
static SkPMColor premultiply_unpmcolor(SkPMColor c) {
|
||||
U8CPU a = SkGetPackedA32(c);
|
||||
U8CPU r = SkGetPackedR32(c);
|
||||
U8CPU g = SkGetPackedG32(c);
|
||||
U8CPU b = SkGetPackedB32(c);
|
||||
return SkPreMultiplyARGB(a, r, g, b);
|
||||
}
|
||||
|
||||
class UnpremulView : public SampleView {
|
||||
public:
|
||||
UnpremulView(SkString res)
|
||||
: fResPath(res)
|
||||
, fPremul(true)
|
||||
, fDecodeSucceeded(false) {
|
||||
this->nextImage();
|
||||
}
|
||||
|
||||
protected:
|
||||
// overrides from SkEventSink
|
||||
virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
|
||||
if (SampleCode::TitleQ(*evt)) {
|
||||
SampleCode::TitleR(evt, "unpremul");
|
||||
return true;
|
||||
}
|
||||
SkUnichar uni;
|
||||
if (SampleCode::CharQ(*evt, &uni)) {
|
||||
char utf8[kMaxBytesInUTF8Sequence];
|
||||
size_t size = SkUTF8_FromUnichar(uni, utf8);
|
||||
// Only consider events for single char keys
|
||||
if (1 == size) {
|
||||
switch (utf8[0]) {
|
||||
case fNextImageChar:
|
||||
this->nextImage();
|
||||
return true;
|
||||
case fTogglePremulChar:
|
||||
this->togglePremul();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this->INHERITED::onQuery(evt);
|
||||
}
|
||||
|
||||
virtual void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE {
|
||||
SkPaint paint;
|
||||
SkAutoTUnref<SkShader> shader(createChecker());
|
||||
paint.setShader(shader.get());
|
||||
canvas->drawPaint(paint);
|
||||
}
|
||||
|
||||
virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextSize(SkIntToScalar(24));
|
||||
SkAutoTUnref<SkBlurDrawLooper> looper(SkNEW_ARGS(SkBlurDrawLooper,
|
||||
(SkIntToScalar(2), 0, 0, SK_ColorBLUE)));
|
||||
paint.setLooper(looper);
|
||||
SkScalar height = paint.getFontMetrics(NULL);
|
||||
if (!fDecodeSucceeded) {
|
||||
SkString failure;
|
||||
if (fResPath.size() == 0) {
|
||||
failure.printf("resource path is required!");
|
||||
} else {
|
||||
failure.printf("Failed to decode %s", fCurrFile.c_str());
|
||||
}
|
||||
canvas->drawText(failure.c_str(), failure.size(), 0, height, paint);
|
||||
return;
|
||||
}
|
||||
|
||||
// Name, size of the file, and whether or not it is premultiplied.
|
||||
SkString header(SkOSPath::SkBasename(fCurrFile.c_str()));
|
||||
header.appendf(" [%dx%d] %s", fBitmap.width(), fBitmap.height(),
|
||||
(fPremul ? "premultiplied" : "unpremultiplied"));
|
||||
canvas->drawText(header.c_str(), header.size(), 0, height, paint);
|
||||
canvas->translate(0, height);
|
||||
|
||||
// Help messages
|
||||
header.printf("Press '%c' to move to the next image.'", fNextImageChar);
|
||||
canvas->drawText(header.c_str(), header.size(), 0, height, paint);
|
||||
canvas->translate(0, height);
|
||||
|
||||
header.printf("Press '%c' to toggle premultiplied decode.", fTogglePremulChar);
|
||||
canvas->drawText(header.c_str(), header.size(), 0, height, paint);
|
||||
|
||||
// Now draw the image itself.
|
||||
canvas->translate(height * 2, height * 2);
|
||||
if (!fPremul) {
|
||||
// A premultiplied bitmap cannot currently be drawn.
|
||||
SkAutoLockPixels alp(fBitmap);
|
||||
// Copy it to a bitmap which can be drawn, converting
|
||||
// to premultiplied:
|
||||
SkBitmap bm;
|
||||
bm.setConfig(SkBitmap::kARGB_8888_Config, fBitmap.width(),
|
||||
fBitmap.height());
|
||||
SkASSERT(fBitmap.config() == SkBitmap::kARGB_8888_Config);
|
||||
if (!bm.allocPixels()) {
|
||||
SkString errMsg("allocPixels failed");
|
||||
canvas->drawText(errMsg.c_str(), errMsg.size(), 0, height, paint);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < fBitmap.width(); ++i) {
|
||||
for (int j = 0; j < fBitmap.height(); ++j) {
|
||||
*bm.getAddr32(i, j) = premultiply_unpmcolor(*fBitmap.getAddr32(i, j));
|
||||
}
|
||||
}
|
||||
canvas->drawBitmap(bm, 0, 0);
|
||||
} else {
|
||||
canvas->drawBitmap(fBitmap, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const SkString fResPath;
|
||||
SkString fCurrFile;
|
||||
bool fPremul;
|
||||
bool fDecodeSucceeded;
|
||||
SkBitmap fBitmap;
|
||||
SkOSFile::Iter fFileIter;
|
||||
|
||||
static const char fNextImageChar = 'j';
|
||||
static const char fTogglePremulChar = 'h';
|
||||
|
||||
void nextImage() {
|
||||
if (fResPath.size() == 0) {
|
||||
return;
|
||||
}
|
||||
SkString basename;
|
||||
if (!fFileIter.next(&basename)) {
|
||||
fFileIter.reset(fResPath.c_str());
|
||||
if (!fFileIter.next(&basename)) {
|
||||
// Perhaps this should draw some error message?
|
||||
return;
|
||||
}
|
||||
}
|
||||
fCurrFile = SkOSPath::SkPathJoin(fResPath.c_str(), basename.c_str());
|
||||
this->decodeCurrFile();
|
||||
}
|
||||
|
||||
void decodeCurrFile() {
|
||||
if (fCurrFile.size() == 0) {
|
||||
fDecodeSucceeded = false;
|
||||
return;
|
||||
}
|
||||
SkFILEStream stream(fCurrFile.c_str());
|
||||
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
|
||||
if (NULL == decoder.get()) {
|
||||
fDecodeSucceeded = false;
|
||||
return;
|
||||
}
|
||||
if (!fPremul) {
|
||||
decoder->setRequireUnpremultipliedColors(true);
|
||||
}
|
||||
fDecodeSucceeded = decoder->decode(&stream, &fBitmap,
|
||||
SkBitmap::kARGB_8888_Config,
|
||||
SkImageDecoder::kDecodePixels_Mode);
|
||||
this->inval(NULL);
|
||||
}
|
||||
|
||||
void togglePremul() {
|
||||
fPremul = !fPremul;
|
||||
this->decodeCurrFile();
|
||||
}
|
||||
|
||||
typedef SampleView INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static SkView* MyFactory() {
|
||||
return new UnpremulView(skiagm::GM::GetResourcePath());
|
||||
}
|
||||
static SkViewRegister reg(MyFactory);
|
|
@ -40,7 +40,8 @@ SkImageDecoder::SkImageDecoder()
|
|||
, fDefaultPref(SkBitmap::kNo_Config)
|
||||
, fDitherImage(true)
|
||||
, fUsePrefTable(false)
|
||||
, fPreferQualityOverSpeed(false) {
|
||||
, fPreferQualityOverSpeed(false)
|
||||
, fRequireUnpremultipliedColors(false) {
|
||||
}
|
||||
|
||||
SkImageDecoder::~SkImageDecoder() {
|
||||
|
|
|
@ -80,8 +80,9 @@ 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 decodePalette(png_structp png_ptr, png_infop info_ptr,
|
||||
bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
|
||||
SkColorTable **colorTablep);
|
||||
bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
|
||||
SkBitmap::Config *config, bool *hasAlpha,
|
||||
bool *doDither, SkPMColor *theTranspColor);
|
||||
|
@ -311,7 +312,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
|||
|
||||
if (!reuseBitmap) {
|
||||
decodedBitmap->setConfig(config, sampler.scaledWidth(),
|
||||
sampler.scaledHeight(), 0);
|
||||
sampler.scaledHeight());
|
||||
}
|
||||
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
||||
return true;
|
||||
|
@ -383,7 +384,8 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
|||
upscale png's palette to a direct model
|
||||
*/
|
||||
SkAutoLockColors ctLock(colorTable);
|
||||
if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
|
||||
if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors(),
|
||||
this->getRequireUnpremultipliedColors())) {
|
||||
return false;
|
||||
}
|
||||
const int height = decodedBitmap->height();
|
||||
|
@ -435,6 +437,13 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
|||
if (0 != theTranspColor) {
|
||||
reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
|
||||
}
|
||||
if (reallyHasAlpha && this->getRequireUnpremultipliedColors() &&
|
||||
SkBitmap::kARGB_8888_Config != decodedBitmap->config()) {
|
||||
// If the caller wants an unpremultiplied bitmap, and we let them get
|
||||
// away with a config other than 8888, and it has alpha after all,
|
||||
// return false, since the result will have premultiplied colors.
|
||||
return false;
|
||||
}
|
||||
decodedBitmap->setIsOpaque(!reallyHasAlpha);
|
||||
if (reuseBitmap) {
|
||||
decodedBitmap->notifyPixelsChanged();
|
||||
|
@ -445,7 +454,7 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
|||
|
||||
|
||||
bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
|
||||
SkBitmap::Config *configp, bool *hasAlphap,
|
||||
SkBitmap::Config *configp, bool * SK_RESTRICT hasAlphap,
|
||||
bool *doDitherp, SkPMColor *theTranspColorp) {
|
||||
png_uint_32 origWidth, origHeight;
|
||||
int bitDepth, colorType;
|
||||
|
@ -546,9 +555,20 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
|
|||
}
|
||||
}
|
||||
|
||||
return this->chooseFromOneChoice(*configp, origWidth, origHeight);
|
||||
if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the image has alpha and the decoder wants unpremultiplied
|
||||
// colors, the only supported config is 8888.
|
||||
if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
|
||||
*configp = SkBitmap::kARGB_8888_Config;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
|
||||
|
||||
bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
|
||||
bool *hasAlphap, bool *reallyHasAlphap,
|
||||
SkColorTable **colorTablep) {
|
||||
|
@ -587,9 +607,17 @@ bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
|
|||
int index = 0;
|
||||
int transLessThanFF = 0;
|
||||
|
||||
// Choose which function to use to create the color table. If the final destination's
|
||||
// config is unpremultiplied, the color table will store unpremultiplied colors.
|
||||
PackColorProc proc;
|
||||
if (this->getRequireUnpremultipliedColors()) {
|
||||
proc = &SkPackARGB32NoCheck;
|
||||
} else {
|
||||
proc = &SkPreMultiplyARGB;
|
||||
}
|
||||
for (; index < numTrans; index++) {
|
||||
transLessThanFF |= (int)*trans - 0xFF;
|
||||
*colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
|
||||
*colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
|
||||
palette++;
|
||||
}
|
||||
reallyHasAlpha |= (transLessThanFF < 0);
|
||||
|
@ -679,7 +707,7 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
|
|||
SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
|
||||
|
||||
SkBitmap decodedBitmap;
|
||||
decodedBitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0);
|
||||
decodedBitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
|
||||
|
||||
// from here down we are concerned with colortables and pixels
|
||||
|
||||
|
@ -773,7 +801,8 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
|
|||
upscale png's palette to a direct model
|
||||
*/
|
||||
SkAutoLockColors ctLock(colorTable);
|
||||
if (!sampler.begin(&decodedBitmap, sc, doDither, ctLock.colors())) {
|
||||
if (!sampler.begin(&decodedBitmap, sc, doDither, ctLock.colors(),
|
||||
this->getRequireUnpremultipliedColors())) {
|
||||
return false;
|
||||
}
|
||||
const int height = decodedBitmap.height();
|
||||
|
|
|
@ -114,7 +114,17 @@ protected:
|
|||
virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called when determining the output config to request to webp.
|
||||
* If the image does not have alpha, there is no need to premultiply.
|
||||
* If the caller wants unpremultiplied colors, that is respected.
|
||||
*/
|
||||
bool shouldPremultiply() const {
|
||||
return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors();
|
||||
}
|
||||
|
||||
bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
|
||||
|
||||
SkStream* fInputStream;
|
||||
int fOrigWidth;
|
||||
int fOrigHeight;
|
||||
|
@ -157,18 +167,16 @@ static bool return_false(const SkBitmap& bm, const char msg[]) {
|
|||
return false; // must always return false
|
||||
}
|
||||
|
||||
static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, int hasAlpha) {
|
||||
static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) {
|
||||
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;
|
||||
mode = premultiply ? MODE_rgbA : MODE_RGBA;
|
||||
} else if (config == SkBitmap::kARGB_4444_Config) {
|
||||
mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444;
|
||||
mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444;
|
||||
} else if (config == SkBitmap::kRGB_565_Config) {
|
||||
mode = MODE_RGB_565;
|
||||
mode = MODE_RGB_565;
|
||||
}
|
||||
SkASSERT(MODE_LAST != mode);
|
||||
return mode;
|
||||
|
@ -224,8 +232,8 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
|
|||
|
||||
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);
|
||||
int width, int height, bool premultiply) {
|
||||
WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply);
|
||||
if (MODE_LAST == mode) {
|
||||
return false;
|
||||
}
|
||||
|
@ -251,10 +259,10 @@ static bool webp_get_config_resize(WebPDecoderConfig* config,
|
|||
|
||||
static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
|
||||
SkBitmap* decodedBitmap,
|
||||
const SkIRect& region, int hasAlpha) {
|
||||
const SkIRect& region, bool premultiply) {
|
||||
|
||||
if (!webp_get_config_resize(config, decodedBitmap, region.width(),
|
||||
region.height(), hasAlpha)) {
|
||||
region.height(), premultiply)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -372,7 +380,8 @@ bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap,
|
|||
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
WebPDecoderConfig config;
|
||||
if (!webp_get_config_resize_crop(&config, bitmap, rect, fHasAlpha)) {
|
||||
if (!webp_get_config_resize_crop(&config, bitmap, rect,
|
||||
this->shouldPremultiply())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -430,7 +439,7 @@ bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
|
|||
|
||||
WebPDecoderConfig config;
|
||||
if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
|
||||
hasAlpha)) {
|
||||
this->shouldPremultiply())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* Copyright 2007 The Android Open Source Project
|
||||
*
|
||||
|
@ -11,6 +10,7 @@
|
|||
#include "SkBitmap.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkDither.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
// 8888
|
||||
|
||||
|
@ -289,6 +289,41 @@ static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
|
|||
return false;
|
||||
}
|
||||
|
||||
// 8888 Unpremul
|
||||
|
||||
static bool Sample_Gray_D8888_Unpremul(void* SK_RESTRICT dstRow,
|
||||
const uint8_t* SK_RESTRICT src,
|
||||
int width, int deltaSrc, int,
|
||||
const SkPMColor[]) {
|
||||
uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[x] = SkPackARGB32NoCheck(0xFF, src[0], src[0], src[0]);
|
||||
src += deltaSrc;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sample_RGBx_D8888_Unpremul is no different from Sample_RGBx_D8888, since alpha
|
||||
// is 0xFF
|
||||
|
||||
static bool Sample_RGBA_D8888_Unpremul(void* SK_RESTRICT dstRow,
|
||||
const uint8_t* SK_RESTRICT src,
|
||||
int width, int deltaSrc, int,
|
||||
const SkPMColor[]) {
|
||||
uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
|
||||
unsigned alphaMask = 0xFF;
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned alpha = src[3];
|
||||
dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
|
||||
src += deltaSrc;
|
||||
alphaMask &= alpha;
|
||||
}
|
||||
return alphaMask != 0xFF;
|
||||
}
|
||||
|
||||
// Sample_Index_D8888_Unpremul is the same as Sample_Index_D8888, since the
|
||||
// color table has its colors inserted unpremultiplied.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SkScaledBitmapSampler.h"
|
||||
|
@ -334,33 +369,44 @@ SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
|
|||
}
|
||||
|
||||
bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
|
||||
const SkPMColor ctable[]) {
|
||||
const SkPMColor ctable[],
|
||||
bool requireUnpremul) {
|
||||
static const RowProc gProcs[] = {
|
||||
// 8888 (no dither distinction)
|
||||
Sample_Gray_D8888, Sample_Gray_D8888,
|
||||
Sample_RGBx_D8888, Sample_RGBx_D8888,
|
||||
Sample_RGBA_D8888, Sample_RGBA_D8888,
|
||||
Sample_Index_D8888, Sample_Index_D8888,
|
||||
NULL, NULL,
|
||||
Sample_Gray_D8888, Sample_Gray_D8888,
|
||||
Sample_RGBx_D8888, Sample_RGBx_D8888,
|
||||
Sample_RGBA_D8888, Sample_RGBA_D8888,
|
||||
Sample_Index_D8888, Sample_Index_D8888,
|
||||
NULL, NULL,
|
||||
// 565 (no alpha distinction)
|
||||
Sample_Gray_D565, Sample_Gray_D565_D,
|
||||
Sample_RGBx_D565, Sample_RGBx_D565_D,
|
||||
Sample_RGBx_D565, Sample_RGBx_D565_D,
|
||||
Sample_Index_D565, Sample_Index_D565_D,
|
||||
Sample_D565_D565, Sample_D565_D565,
|
||||
Sample_Gray_D565, Sample_Gray_D565_D,
|
||||
Sample_RGBx_D565, Sample_RGBx_D565_D,
|
||||
Sample_RGBx_D565, Sample_RGBx_D565_D,
|
||||
Sample_Index_D565, Sample_Index_D565_D,
|
||||
Sample_D565_D565, Sample_D565_D565,
|
||||
// 4444
|
||||
Sample_Gray_D4444, Sample_Gray_D4444_D,
|
||||
Sample_RGBx_D4444, Sample_RGBx_D4444_D,
|
||||
Sample_RGBA_D4444, Sample_RGBA_D4444_D,
|
||||
Sample_Index_D4444, Sample_Index_D4444_D,
|
||||
NULL, NULL,
|
||||
Sample_Gray_D4444, Sample_Gray_D4444_D,
|
||||
Sample_RGBx_D4444, Sample_RGBx_D4444_D,
|
||||
Sample_RGBA_D4444, Sample_RGBA_D4444_D,
|
||||
Sample_Index_D4444, Sample_Index_D4444_D,
|
||||
NULL, NULL,
|
||||
// Index8
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
Sample_Index_DI, Sample_Index_DI,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
Sample_Index_DI, Sample_Index_DI,
|
||||
NULL, NULL,
|
||||
// 8888 Unpremul (no dither distinction)
|
||||
Sample_Gray_D8888_Unpremul, Sample_Gray_D8888_Unpremul,
|
||||
Sample_RGBx_D8888, Sample_RGBx_D8888,
|
||||
Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul,
|
||||
Sample_Index_D8888, Sample_Index_D8888,
|
||||
NULL, NULL,
|
||||
};
|
||||
// The jump between dst configs in the table
|
||||
static const int gProcDstConfigSpan = 10;
|
||||
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 5 * gProcDstConfigSpan,
|
||||
gProcs_has_the_wrong_number_of_entries);
|
||||
|
||||
fCTable = ctable;
|
||||
|
||||
|
@ -399,21 +445,28 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
|
|||
|
||||
switch (dst->config()) {
|
||||
case SkBitmap::kARGB_8888_Config:
|
||||
index += 0;
|
||||
index += 0 * gProcDstConfigSpan;
|
||||
break;
|
||||
case SkBitmap::kRGB_565_Config:
|
||||
index += 10;
|
||||
index += 1 * gProcDstConfigSpan;
|
||||
break;
|
||||
case SkBitmap::kARGB_4444_Config:
|
||||
index += 20;
|
||||
index += 2 * gProcDstConfigSpan;
|
||||
break;
|
||||
case SkBitmap::kIndex8_Config:
|
||||
index += 30;
|
||||
index += 3 * gProcDstConfigSpan;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requireUnpremul) {
|
||||
if (dst->config() != SkBitmap::kARGB_8888_Config) {
|
||||
return false;
|
||||
}
|
||||
index += 4 * gProcDstConfigSpan;
|
||||
}
|
||||
|
||||
fRowProc = gProcs[index];
|
||||
fDstRow = (char*)dst->getPixels();
|
||||
fDstRowBytes = dst->rowBytes();
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
// prepares iterator to process the src colors and write them into dst.
|
||||
// Returns false if the request cannot be fulfulled.
|
||||
bool begin(SkBitmap* dst, SrcConfig sc, bool doDither,
|
||||
const SkPMColor* = NULL);
|
||||
const SkPMColor* = NULL, bool requireUnPremul = false);
|
||||
// call with row of src pixels, for y = 0...scaledHeight-1.
|
||||
// returns true if the row had non-opaque alpha in it
|
||||
bool next(const uint8_t* SK_RESTRICT src);
|
||||
|
|
|
@ -183,10 +183,17 @@ bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes w
|
|||
hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
|
||||
}
|
||||
|
||||
GUID destinationPixelFormat;
|
||||
if (this->getRequireUnpremultipliedColors()) {
|
||||
destinationPixelFormat = GUID_WICPixelFormat32bppBGRA;
|
||||
} else {
|
||||
destinationPixelFormat = GUID_WICPixelFormat32bppPBGRA;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = piFormatConverter->Initialize(
|
||||
piBitmapSourceOriginal.get() //Input bitmap to convert
|
||||
, GUID_WICPixelFormat32bppPBGRA //Destination pixel format
|
||||
, destinationPixelFormat //Destination pixel format
|
||||
, WICBitmapDitherTypeNone //Specified dither patterm
|
||||
, NULL //Specify a particular palette
|
||||
, 0.f //Alpha threshold
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkForceLinking.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
#include "Test.h"
|
||||
|
||||
__SK_FORCE_IMAGE_DECODER_LINKING;
|
||||
|
||||
/**
|
||||
* Interprets c as an unpremultiplied color, and returns the
|
||||
* premultiplied equivalent.
|
||||
*/
|
||||
static SkPMColor premultiply_unpmcolor(SkPMColor c) {
|
||||
U8CPU a = SkGetPackedA32(c);
|
||||
U8CPU r = SkGetPackedR32(c);
|
||||
U8CPU g = SkGetPackedG32(c);
|
||||
U8CPU b = SkGetPackedB32(c);
|
||||
return SkPreMultiplyARGB(a, r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this stream format should be skipped, due
|
||||
* to do being an opaque format or not a valid format.
|
||||
*/
|
||||
static bool skip_image_format(SkImageDecoder::Format format) {
|
||||
switch (format) {
|
||||
case SkImageDecoder::kPNG_Format:
|
||||
case SkImageDecoder::kWEBP_Format:
|
||||
return false;
|
||||
// Skip unknown since it will not be decoded anyway.
|
||||
case SkImageDecoder::kUnknown_Format:
|
||||
// Technically ICO and BMP supports alpha channels, but our image
|
||||
// decoders do not, so skip them as well.
|
||||
case SkImageDecoder::kICO_Format:
|
||||
case SkImageDecoder::kBMP_Format:
|
||||
// The rest of these are opaque.
|
||||
case SkImageDecoder::kWBMP_Format:
|
||||
case SkImageDecoder::kGIF_Format:
|
||||
case SkImageDecoder::kJPEG_Format:
|
||||
return true;
|
||||
}
|
||||
SkASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decoding an image in premultiplied mode and unpremultiplied mode and compare
|
||||
* them.
|
||||
*/
|
||||
static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
|
||||
// Decode a resource:
|
||||
SkBitmap bm8888;
|
||||
SkBitmap bm8888Unpremul;
|
||||
|
||||
SkFILEStream stream(filename.c_str());
|
||||
|
||||
SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
|
||||
if (skip_image_format(format)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
|
||||
if (NULL == decoder.get()) {
|
||||
SkDebugf("couldn't decode %s\n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config,
|
||||
SkImageDecoder::kDecodePixels_Mode);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
success = stream.rewind();
|
||||
REPORTER_ASSERT(reporter, success);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
decoder->setRequireUnpremultipliedColors(true);
|
||||
success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config,
|
||||
SkImageDecoder::kDecodePixels_Mode);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
|
||||
&& bm8888.height() == bm8888Unpremul.height();
|
||||
REPORTER_ASSERT(reporter, dimensionsMatch);
|
||||
if (!dimensionsMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only do the comparison if the two bitmaps are both 8888.
|
||||
if (bm8888.config() != SkBitmap::kARGB_8888_Config
|
||||
|| bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now compare the two bitmaps.
|
||||
for (int i = 0; i < bm8888.width(); ++i) {
|
||||
for (int j = 0; j < bm8888.height(); ++j) {
|
||||
// "c0" is the color of the premultiplied bitmap at (i, j).
|
||||
const SkPMColor c0 = *bm8888.getAddr32(i, j);
|
||||
// "c1" is the result of premultiplying the color of the unpremultiplied
|
||||
// bitmap at (i, j).
|
||||
const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
|
||||
// Compute the difference for each component.
|
||||
int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
|
||||
int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
|
||||
int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
|
||||
int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
|
||||
|
||||
// Alpha component must be exactly the same.
|
||||
REPORTER_ASSERT(reporter, 0 == da);
|
||||
// Other components may differ if rounding is done differently,
|
||||
// but currently that is not the case. If an image fails here
|
||||
// in the future, we can change these to account for differences.
|
||||
REPORTER_ASSERT(reporter, 0 == dr);
|
||||
REPORTER_ASSERT(reporter, 0 == dg);
|
||||
REPORTER_ASSERT(reporter, 0 == db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_imageDecodingTests(skiatest::Reporter* reporter) {
|
||||
// This test cannot run if there is no resource path.
|
||||
SkString resourcePath = skiatest::Test::GetResourcePath();
|
||||
if (resourcePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
SkOSFile::Iter iter(resourcePath.c_str());
|
||||
SkString basename;
|
||||
if (iter.next(&basename)) {
|
||||
do {
|
||||
SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
|
||||
//SkDebugf("about to decode \"%s\"\n", filename.c_str());
|
||||
compare_unpremul(reporter, filename);
|
||||
} while (iter.next(&basename));
|
||||
} else {
|
||||
SkDebugf("Failed to find any files :(\n");
|
||||
}
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
DEFINE_TESTCLASS("ImageDecoding", ImageDecodingTestClass,
|
||||
test_imageDecodingTests)
|
Загрузка…
Ссылка в новой задаче