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:
scroggo@google.com 2013-06-14 15:33:20 +00:00
Родитель 99e0d08113
Коммит 2bbc2c945b
15 изменённых файлов: 627 добавлений и 52 удалений

Просмотреть файл

@ -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")); )

Просмотреть файл

@ -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

157
tests/ImageDecodingTest.cpp Normal file
Просмотреть файл

@ -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)