diff options
author | scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-01 17:27:15 +0000 |
---|---|---|
committer | scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-01 17:27:15 +0000 |
commit | 8d2392487cd97e68c0a71da9fd5d2b42ecac5ec8 (patch) | |
tree | e7133b74f371352488ce900f9371bc68977ee1ea /src/images | |
parent | 3702b2587cb18021d8efdd001ab3c9085f0ac51a (diff) |
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
Diffstat (limited to 'src/images')
-rw-r--r-- | src/images/SkImageDecoder.cpp | 3 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libbmp.cpp | 2 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libjpeg.cpp | 4 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libpng.cpp | 19 | ||||
-rw-r--r-- | src/images/SkImageRef_ashmem.cpp | 3 | ||||
-rw-r--r-- | src/images/SkScaledBitmapSampler.cpp | 517 | ||||
-rw-r--r-- | src/images/SkScaledBitmapSampler.h | 20 |
7 files changed, 446 insertions, 122 deletions
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp index 1544edf366..15cd1a60c0 100644 --- a/src/images/SkImageDecoder.cpp +++ b/src/images/SkImageDecoder.cpp @@ -40,6 +40,7 @@ SkImageDecoder::SkImageDecoder() , fDefaultPref(SkBitmap::kNo_Config) , fDitherImage(true) , fUsePrefTable(false) + , fSkipWritingZeroes(false) , fPreferQualityOverSpeed(false) , fRequireUnpremultipliedColors(false) { } @@ -63,6 +64,8 @@ void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) { } else { other->fDefaultPref = fDefaultPref; } + other->setDitherImage(fDitherImage); + other->setSkipWritingZeroes(fSkipWritingZeroes); other->setPreferQualityOverSpeed(fPreferQualityOverSpeed); other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors); } diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp index 3eb181f781..2283dbf51d 100644 --- a/src/images/SkImageDecoder_libbmp.cpp +++ b/src/images/SkImageDecoder_libbmp.cpp @@ -146,7 +146,7 @@ bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { SkAutoLockPixels alp(*bm); - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) { + if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { return false; } diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index 37be2a7091..3b3ea887e3 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -588,7 +588,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { return return_false(cinfo, *bm, "jpeg colorspace"); } - if (!sampler.begin(bm, sc, this->getDitherImage())) { + if (!sampler.begin(bm, sc, *this)) { return return_false(cinfo, *bm, "sampler.begin"); } @@ -827,7 +827,7 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { return return_false(*cinfo, *bm, "jpeg colorspace"); } - if (!sampler.begin(&bitmap, sc, this->getDitherImage())) { + if (!sampler.begin(&bitmap, sc, *this)) { return return_false(*cinfo, bitmap, "sampler.begin"); } diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp index b6aa329104..e942f21c78 100644 --- a/src/images/SkImageDecoder_libpng.cpp +++ b/src/images/SkImageDecoder_libpng.cpp @@ -91,7 +91,7 @@ private: SkColorTable **colorTablep); bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr, SkBitmap::Config *config, bool *hasAlpha, - bool *doDither, SkPMColor *theTranspColor); + SkPMColor *theTranspColor); typedef SkImageDecoder INHERITED; }; @@ -294,10 +294,9 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, 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)) { + if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) { return false; } @@ -377,8 +376,7 @@ 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(), - this->getRequireUnpremultipliedColors())) { + if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) { return false; } const int height = decodedBitmap->height(); @@ -446,7 +444,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, SkBitmap::Config* SK_RESTRICT configp, bool* SK_RESTRICT hasAlphap, - bool* SK_RESTRICT doDitherp, SkPMColor* SK_RESTRICT theTranspColorp) { png_uint_32 origWidth, origHeight; int bitDepth, colorType; @@ -456,7 +453,7 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component png_color_8p sig_bit; - if (*doDitherp && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) { + if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) { #if 0 SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green, sig_bit->blue, sig_bit->alpha); @@ -465,7 +462,7 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr, if (pos_le(sig_bit->red, SK_R16_BITS) && pos_le(sig_bit->green, SK_G16_BITS) && pos_le(sig_bit->blue, SK_B16_BITS)) { - *doDitherp = false; + this->setDitherImage(false); } } @@ -724,10 +721,9 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { 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)) { + if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) { return false; } @@ -834,8 +830,7 @@ 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(), - this->getRequireUnpremultipliedColors())) { + if (!sampler.begin(&decodedBitmap, sc, *this, ctLock.colors())) { return false; } const int height = decodedBitmap.height(); diff --git a/src/images/SkImageRef_ashmem.cpp b/src/images/SkImageRef_ashmem.cpp index 0186bf6a55..0dba1d1191 100644 --- a/src/images/SkImageRef_ashmem.cpp +++ b/src/images/SkImageRef_ashmem.cpp @@ -134,6 +134,9 @@ bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStreamRewindable* stre return this->INHERITED::onDecode(codec, stream, bitmap, config, mode); } + // Ashmem memory is guaranteed to be initialized to 0. + codec->setSkipWritingZeroes(true); + AshmemAllocator alloc(&fRec, this->getURI()); codec->setAllocator(&alloc); diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp index 021f86ba86..3bab8de9fd 100644 --- a/src/images/SkScaledBitmapSampler.cpp +++ b/src/images/SkScaledBitmapSampler.cpp @@ -25,6 +25,11 @@ static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_gray_to_8888_proc(const SkImageDecoder& decoder) { + // Dither, unpremul, and skipZeroes have no effect + return Sample_Gray_D8888; +} + static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -36,6 +41,11 @@ static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_RGBx_to_8888_proc(const SkImageDecoder& decoder) { + // Dither, unpremul, and skipZeroes have no effect + return Sample_RGBx_D8888; +} + static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -50,6 +60,52 @@ static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow, return alphaMask != 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; +} + +static bool Sample_RGBA_D8888_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int, + const SkPMColor[]) { + SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; + unsigned alphaMask = 0xFF; + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + if (0 != alpha) { + dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + } + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + +static SkScaledBitmapSampler::RowProc get_RGBA_to_8888_proc(const SkImageDecoder& decoder) { + // Dither has no effect. + if (decoder.getRequireUnpremultipliedColors()) { + // We could check each component for a zero, at the expense of extra checks. + // For now, just return unpremul. + return Sample_RGBA_D8888_Unpremul; + } + // Supply the versions that premultiply the colors + if (decoder.getSkipWritingZeroes()) { + return Sample_RGBA_D8888_SkipZ; + } + return Sample_RGBA_D8888; +} + // 565 static bool Sample_Gray_D565(void* SK_RESTRICT dstRow, @@ -75,6 +131,14 @@ static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_gray_to_565_proc(const SkImageDecoder& decoder) { + // Unpremul and skip zeroes make no difference + if (decoder.getDitherImage()) { + return Sample_Gray_D565_D; + } + return Sample_Gray_D565; +} + static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -86,6 +150,28 @@ static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow, return false; } +static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y, + const SkPMColor[]) { + uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; + DITHER_565_SCAN(y); + for (int x = 0; x < width; x++) { + dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x)); + src += deltaSrc; + } + return false; +} + +static SkScaledBitmapSampler::RowProc get_RGBx_to_565_proc(const SkImageDecoder& decoder) { + // Unpremul and skip zeroes make no difference + if (decoder.getDitherImage()) { + return Sample_RGBx_D565_D; + } + return Sample_RGBx_D565; +} + + static bool Sample_D565_D565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -98,16 +184,9 @@ static bool Sample_D565_D565(void* SK_RESTRICT dstRow, return false; } -static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - DITHER_565_SCAN(y); - for (int x = 0; x < width; x++) { - dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x)); - src += deltaSrc; - } - return false; +static SkScaledBitmapSampler::RowProc get_565_to_565_proc(const SkImageDecoder& decoder) { + // Unpremul, dither, and skip zeroes have no effect + return Sample_D565_D565; } // 4444 @@ -137,6 +216,14 @@ static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_gray_to_4444_proc(const SkImageDecoder& decoder) { + // Skip zeroes and unpremul make no difference + if (decoder.getDitherImage()) { + return Sample_Gray_D4444_D; + } + return Sample_Gray_D4444; +} + static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -162,6 +249,14 @@ static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_RGBx_to_4444_proc(const SkImageDecoder& decoder) { + // Skip zeroes and unpremul make no difference + if (decoder.getDitherImage()) { + return Sample_RGBx_D4444_D; + } + return Sample_RGBx_D4444; +} + static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -178,9 +273,30 @@ static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow, return alphaMask != 0xFF; } +static bool Sample_RGBA_D4444_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int, + const SkPMColor[]) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + unsigned alphaMask = 0xFF; + + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + if (alpha != 0) { + SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + dst[x] = SkPixel32ToPixel4444(c); + } + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + + static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, const SkPMColor[]) { + int width, int deltaSrc, int y, + const SkPMColor[]) { SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; unsigned alphaMask = 0xFF; DITHER_4444_SCAN(y); @@ -195,6 +311,44 @@ static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow, return alphaMask != 0xFF; } +static bool Sample_RGBA_D4444_D_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y, + const SkPMColor[]) { + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + unsigned alphaMask = 0xFF; + DITHER_4444_SCAN(y); + + for (int x = 0; x < width; x++) { + unsigned alpha = src[3]; + if (alpha != 0) { + SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); + dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); + } + src += deltaSrc; + alphaMask &= alpha; + } + return alphaMask != 0xFF; +} + +static SkScaledBitmapSampler::RowProc get_RGBA_to_4444_proc(const SkImageDecoder& decoder) { + if (decoder.getRequireUnpremultipliedColors()) { + // Unpremultiplied is not supported for 4444 + return NULL; + } + const bool dither = decoder.getDitherImage(); + if (decoder.getSkipWritingZeroes()) { + if (dither) { + return Sample_RGBA_D4444_D_SkipZ; + } + return Sample_RGBA_D4444_SkipZ; + } + if (dither) { + return Sample_RGBA_D4444_D; + } + return Sample_RGBA_D4444; +} + // Index #define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT) @@ -214,6 +368,36 @@ static bool Sample_Index_D8888(void* SK_RESTRICT dstRow, return cc != A32_MASK_IN_PLACE; } +static bool Sample_Index_D8888_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int, + const SkPMColor ctable[]) { + + SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; + SkPMColor cc = A32_MASK_IN_PLACE; + for (int x = 0; x < width; x++) { + SkPMColor c = ctable[*src]; + cc &= c; + if (c != 0) { + dst[x] = c; + } + src += deltaSrc; + } + return cc != A32_MASK_IN_PLACE; +} + +static SkScaledBitmapSampler::RowProc get_index_to_8888_proc(const SkImageDecoder& decoder) { + if (decoder.getRequireUnpremultipliedColors()) { + // Unpremultiplied is not supported for an index source. + return NULL; + } + // Dither makes no difference + if (decoder.getSkipWritingZeroes()) { + return Sample_Index_D8888_SkipZ; + } + return Sample_Index_D8888; +} + static bool Sample_Index_D565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor ctable[]) { @@ -242,6 +426,14 @@ static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_index_to_565_proc(const SkImageDecoder& decoder) { + // Unpremultiplied and skip zeroes make no difference + if (decoder.getDitherImage()) { + return Sample_Index_D565_D; + } + return Sample_Index_D565; +} + static bool Sample_Index_D4444(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int y, const SkPMColor ctable[]) { @@ -274,6 +466,60 @@ static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow, return cc != A32_MASK_IN_PLACE; } +static bool Sample_Index_D4444_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, int width, + int deltaSrc, int y, const SkPMColor ctable[]) { + + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + SkPMColor cc = A32_MASK_IN_PLACE; + for (int x = 0; x < width; x++) { + SkPMColor c = ctable[*src]; + cc &= c; + if (c != 0) { + dst[x] = SkPixel32ToPixel4444(c); + } + src += deltaSrc; + } + return cc != A32_MASK_IN_PLACE; +} + +static bool Sample_Index_D4444_D_SkipZ(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, int width, + int deltaSrc, int y, const SkPMColor ctable[]) { + + SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; + SkPMColor cc = A32_MASK_IN_PLACE; + DITHER_4444_SCAN(y); + + for (int x = 0; x < width; x++) { + SkPMColor c = ctable[*src]; + cc &= c; + if (c != 0) { + dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); + } + src += deltaSrc; + } + return cc != A32_MASK_IN_PLACE; +} + +static SkScaledBitmapSampler::RowProc get_index_to_4444_proc(const SkImageDecoder& decoder) { + // Unpremul not allowed + if (decoder.getRequireUnpremultipliedColors()) { + return NULL; + } + const bool dither = decoder.getDitherImage(); + if (decoder.getSkipWritingZeroes()) { + if (dither) { + return Sample_Index_D4444_D_SkipZ; + } + return Sample_Index_D4444_SkipZ; + } + if (dither) { + return Sample_Index_D4444_D; + } + return Sample_Index_D4444; +} + static bool Sample_Index_DI(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, int deltaSrc, int, const SkPMColor[]) { @@ -289,6 +535,15 @@ static bool Sample_Index_DI(void* SK_RESTRICT dstRow, return false; } +static SkScaledBitmapSampler::RowProc get_index_to_index_proc(const SkImageDecoder& decoder) { + // Unpremul not allowed + if (decoder.getRequireUnpremultipliedColors()) { + return NULL; + } + // Ignore dither and skip zeroes + return Sample_Index_DI; +} + // A8 static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, @@ -298,41 +553,15 @@ static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow, return true; } -// 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; +static SkScaledBitmapSampler::RowProc get_gray_to_A8_proc(const SkImageDecoder& decoder) { + if (decoder.getRequireUnpremultipliedColors()) { + return NULL; } - return alphaMask != 0xFF; + // Ignore skip and dither. + return Sample_Gray_DA8; } -// Sample_Index_D8888_Unpremul is the same as Sample_Index_D8888, since the -// color table has its colors inserted unpremultiplied. - +typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkImageDecoder& decoder); /////////////////////////////////////////////////////////////////////////////// #include "SkScaledBitmapSampler.h" @@ -377,58 +606,49 @@ SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height, SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height); } -bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, - 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, - // 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, - // 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, - // Index8 - NULL, NULL, - NULL, NULL, - NULL, NULL, - Sample_Index_DI, Sample_Index_DI, - NULL, NULL, - // A8 - Sample_Gray_DA8, Sample_Gray_DA8, - NULL, NULL, - NULL, NULL, - NULL, NULL, - 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, +bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, + const SkImageDecoder& decoder, + const SkPMColor ctable[]) { + static const RowProcChooser gProcChoosers[] = { + get_gray_to_8888_proc, + get_RGBx_to_8888_proc, + get_RGBA_to_8888_proc, + get_index_to_8888_proc, + NULL, // 565 to 8888 + + get_gray_to_565_proc, + get_RGBx_to_565_proc, + get_RGBx_to_565_proc, // The source alpha will be ignored. + get_index_to_565_proc, + get_565_to_565_proc, + + get_gray_to_4444_proc, + get_RGBx_to_4444_proc, + get_RGBA_to_4444_proc, + get_index_to_4444_proc, + NULL, // 565 to 4444 + + NULL, // gray to index + NULL, // rgbx to index + NULL, // rgba to index + get_index_to_index_proc, + NULL, // 565 to index + + get_gray_to_A8_proc, + NULL, // rgbx to a8 + NULL, // rgba to a8 + NULL, // index to a8 + NULL, // 565 to a8 }; + // The jump between dst configs in the table - static const int gProcDstConfigSpan = 10; - SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 6 * gProcDstConfigSpan, + static const int gProcDstConfigSpan = 5; + SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan, gProcs_has_the_wrong_number_of_entries); fCTable = ctable; int index = 0; - if (dither) { - index += 1; - } switch (sc) { case SkScaledBitmapSampler::kGray: fSrcPixelSize = 1; @@ -436,23 +656,23 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, break; case SkScaledBitmapSampler::kRGB: fSrcPixelSize = 3; - index += 2; + index += 1; break; case SkScaledBitmapSampler::kRGBX: fSrcPixelSize = 4; - index += 2; + index += 1; break; case SkScaledBitmapSampler::kRGBA: fSrcPixelSize = 4; - index += 4; + index += 2; break; case SkScaledBitmapSampler::kIndex: fSrcPixelSize = 1; - index += 6; + index += 3; break; case SkScaledBitmapSampler::kRGB_565: fSrcPixelSize = 2; - index += 8; + index += 4; break; default: return false; @@ -478,14 +698,12 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither, return false; } - if (requireUnpremul) { - if (dst->config() != SkBitmap::kARGB_8888_Config) { - return false; - } - index += 5 * gProcDstConfigSpan; + RowProcChooser chooser = gProcChoosers[index]; + if (NULL == chooser) { + fRowProc = NULL; + } else { + fRowProc = chooser(decoder); } - - fRowProc = gProcs[index]; fDstRow = (char*)dst->getPixels(); fDstRowBytes = dst->rowBytes(); fCurrY = 0; @@ -501,3 +719,102 @@ bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) { fCurrY += 1; return hadAlpha; } + +#ifdef SK_DEBUG +// The following code is for a test to ensure that changing the method to get the right row proc +// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp + +// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc. +class RowProcTester { +public: + static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) { + return sampler.fRowProc; + } +}; + + +// Table showing the expected RowProc for each combination of inputs. +// Table formated as follows: +// Each group of 5 consecutive rows represents sampling from a single +// SkScaledBitmapSampler::SrcConfig. +// Within each set, each row represents a different destination SkBitmap::Config +// Each column represents a different combination of dither and unpremul. +// D = dither ~D = no dither +// U = unpremul ~U = no unpremul +// ~D~U D~U ~DU DU +SkScaledBitmapSampler::RowProc gTestProcs[] = { + // Gray + Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8 + NULL, NULL, NULL, NULL, // to Index8 + Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565 + Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444 + Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888 + // Index + NULL, NULL, NULL, NULL, // to A8 + Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8 + Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565 + Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444 + Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888 + // RGB + NULL, NULL, NULL, NULL, // to A8 + NULL, NULL, NULL, NULL, // to Index8 + Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 + Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444 + Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888 + // RGBx is the same as RGB + NULL, NULL, NULL, NULL, // to A8 + NULL, NULL, NULL, NULL, // to Index8 + Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 + Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444 + Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888 + // RGBA + NULL, NULL, NULL, NULL, // to A8 + NULL, NULL, NULL, NULL, // to Index8 + Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 + Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444 + Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888 + // RGB_565 + NULL, NULL, NULL, NULL, // to A8 + NULL, NULL, NULL, NULL, // to Index8 + Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565 + NULL, NULL, NULL, NULL, // to 4444 + NULL, NULL, NULL, NULL, // to 8888 +}; + +// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields. +class DummyDecoder : public SkImageDecoder { +public: + DummyDecoder() {} +protected: + virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE { + return false; + } +}; + +void test_row_proc_choice() { + SkBitmap dummyBitmap; + DummyDecoder dummyDecoder; + size_t procCounter = 0; + for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) { + for (int c = SkBitmap::kA8_Config; c <= SkBitmap::kARGB_8888_Config; ++c) { + for (int unpremul = 0; unpremul <= 1; ++unpremul) { + for (int dither = 0; dither <= 1; ++dither) { + // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to + // be considered valid. + SkScaledBitmapSampler sampler(10, 10, 1); + dummyBitmap.setConfig((SkBitmap::Config) c, 10, 10); + dummyDecoder.setDitherImage(SkToBool(dither)); + dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul)); + sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc, + dummyDecoder); + SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter]; + SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler); + SkASSERT(expected == actual); + procCounter++; + } + } + } + } + SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter); +} +#endif // SK_DEBUG diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h index 6477db2178..a293fe7ebb 100644 --- a/src/images/SkScaledBitmapSampler.h +++ b/src/images/SkScaledBitmapSampler.h @@ -10,6 +10,7 @@ #include "SkTypes.h" #include "SkColor.h" +#include "SkImageDecoder.h" class SkBitmap; @@ -35,12 +36,17 @@ public: // Given a dst bitmap (with pixels already allocated) and a src-config, // 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, bool requireUnPremul = false); + bool begin(SkBitmap* dst, SrcConfig sc, const SkImageDecoder& decoder, + const SkPMColor* = NULL); // 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); + typedef bool (*RowProc)(void* SK_RESTRICT dstRow, + const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int y, + const SkPMColor[]); + private: int fScaledWidth; int fScaledHeight; @@ -50,11 +56,6 @@ private: int fDX; // step between X samples int fDY; // step between Y samples - typedef bool (*RowProc)(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, - const SkPMColor[]); - // setup state char* fDstRow; // points into bitmap's pixels size_t fDstRowBytes; @@ -64,6 +65,11 @@ private: // optional reference to the src colors if the src is a palette model const SkPMColor* fCTable; + +#ifdef SK_DEBUG + // Helper class allowing a test to have access to fRowProc. + friend class RowProcTester; +#endif }; #endif |