diff options
Diffstat (limited to 'src/images')
-rw-r--r-- | src/images/SkImageDecoder_libwebp.cpp | 333 |
1 files changed, 25 insertions, 308 deletions
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp index 6f8dfcc..51f8a46 100644 --- a/src/images/SkImageDecoder_libwebp.cpp +++ b/src/images/SkImageDecoder_libwebp.cpp @@ -38,9 +38,6 @@ extern "C" { #include "webp/encode.h" } -/* If defined, work around missing padding byte in content generated by webpconv */ -#define WEBPCONV_MISSING_PADDING 1 - #ifdef ANDROID #include <cutils/properties.h> @@ -59,24 +56,6 @@ static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -// An helper to extract a integer (little endian) from byte array. This is -// called only once per decoding, so no real need to optimize it in any way -static uint32_t getint32l(unsigned char *in) { - int result; - unsigned char *buffer = (unsigned char*) in; - - if (buffer == NULL) { - return 0; - } - - result = buffer[3]; - result = (result << 8) + buffer[2]; - result = (result << 8) + buffer[1]; - result = (result << 8) + buffer[0]; - - return result; -} - static const size_t WEBP_VP8_HEADER_SIZE = 30; static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); @@ -152,288 +131,6 @@ static bool return_false(const SkBitmap& bm, const char msg[]) { return false; // must always return false } -typedef struct { - SkBitmap* image; - SkStream* stream; -} WEBPImage; - -// WebP library embeds its own YUV to RGB converter. However, High-level API doesn't take benefit -// of (U,v) clipped values being valid for up to 4 pixels, and so there is a significant improvement -// in performance in handling this on our own. -// TODO: use architecture-optimized (eventually hardware-accelerated) YUV converters -#define YUV_HALF (1 << (YUV_FIX - 1)) -#define YUV_FIX 16 // fixed-point precision -#define YUV_RANGE_MIN (-227) // min value of r/g/b output -#define YUV_RANGE_MAX (256 + 226) // max value of r/g/b output -static int16_t VP8kVToR[256], VP8kUToB[256]; -static int32_t VP8kVToG[256], VP8kUToG[256]; -static uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; - -static void yuv_init_tables() { - int i; - - for (i = 0; i < 256; ++i) { - VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX; - VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF; - VP8kVToG[i] = -45773 * (i - 128); - VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX; - } - for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) { - const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX; - VP8kClip[i - YUV_RANGE_MIN] = (k < 0) ? 0 : (k > 255) ? 255 : k; - } -} - -// Static global mutex to protect Webp initialization -static SkMutex gYUVMutex; -static bool gYUVReady = false; - -static bool yuv_init() { - if (!gYUVReady) { - gYUVMutex.acquire(); - if (!gYUVReady) { - yuv_init_tables(); - gYUVReady = true; - } - gYUVMutex.release(); - } - - return gYUVReady; -} - -#define PutRGBA(p,r,g,b) (((SkPMColor*) (p))[0] = SkPackARGB32(0xff,(r),(g),(b))) -#define PutRGB565(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackRGB16((r)>>3,(g)>>2,(b)>>3)) -#define PutRGBA4444(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackARGB4444(0xf,(r)>>4,(g)>>4,(b)>>4)) - -#define CRGBA(p,y,roff,goff,boff) PutRGBA(p, \ - VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) -#define CRGB565(p,y,roff,goff,boff) PutRGB565(p, \ - VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) -#define CRGBA4444(p,y,roff,goff,boff) PutRGBA4444(p, \ - VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ - VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) - -static int block_put(const VP8Io* io) { - WEBPImage *p = (WEBPImage*) io->opaque; - SkBitmap* decodedBitmap = p->image; - - const int w = io->width; - const int mb_h = io->mb_h; - - const uint8_t *y, *y2, *u, *v; - const uint8_t *py, *py2, *pu, *pv; - - uint8_t* pout; - uint8_t* pout2; - - int i, j; - const int ystride2 = io->y_stride * 2; - int bpp; - SkBitmap::Config config = decodedBitmap->config(); - - //SkASSERT(!(io->mb_y & 1)); - - y = io->y; - u = io->u; - v = io->v; - - switch (config) { - case SkBitmap::kARGB_8888_Config: - bpp = 4; - break; - case SkBitmap::kRGB_565_Config: - bpp = 2; - break; - case SkBitmap::kARGB_4444_Config: - bpp = 2; - break; - default: - // Unsupported config - return 0; - } - - for (j = 0; j < mb_h;) { - pout = decodedBitmap->getAddr8(0, io->mb_y + j); - if (j + 1 < mb_h) { - y2 = y + io->y_stride; - pout2 = decodedBitmap->getAddr8(0, io->mb_y + j + 1); - } else { - y2 = NULL; - pout2 = NULL; - } - - // Copy YUV into target buffer - py = y; - pu = u; - pv = v; - - py2 = y2; - - // Leave test for config out of inner loop. This implies some redundancy in code, - // but help in supporting several configs without degrading performance. - // As a reminder, one must *NOT* put py increment into parameters (i.e. *py++) in the hope to - // improve performance or code readability. Since it is used as argument of a macro which uses it - // several times in its expression, so this would end up in having it too much incremented - switch (config) { - case SkBitmap::kARGB_8888_Config: - for (i = 0; i < w; i += 2) { - // U and V are common for up to 4 pixels - const int r_off = VP8kVToR[*pv]; - const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; - const int b_off = VP8kUToB[*pu]; - - CRGBA(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGBA(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - } - - if (pout2) { - CRGBA(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGBA(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - } - } - - pu++; - pv++; - } - break; - case SkBitmap::kRGB_565_Config: - for (i = 0; i < w; i += 2) { - // U and V are common for up to 4 pixels - const int r_off = VP8kVToR[*pv]; - const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; - const int b_off = VP8kUToB[*pu]; - - CRGB565(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGB565(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - } - - if (pout2) { - CRGB565(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGB565(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - } - } - - pu++; - pv++; - } - break; - case SkBitmap::kARGB_4444_Config: - for (i = 0; i < w; i += 2) { - // U and V are common for up to 4 pixels - const int r_off = VP8kVToR[*pv]; - const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; - const int b_off = VP8kUToB[*pu]; - - CRGBA4444(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGBA4444(pout, *py, r_off, g_off, b_off); - pout += bpp; - py++; - } - - if (pout2) { - CRGBA4444(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - - // Width shouldn't be odd, so this should always be true - if (i + 1 < w) { - CRGBA4444(pout2, *py2, r_off, g_off, b_off); - pout2 += bpp; - py2++; - } - } - - pu++; - pv++; - } - break; - default: - // Unsupported config (can't happen, but prevents compiler warning) - SkASSERT(0); - break; - } - - if (y2) { - // Scanned and populated two rows - y += ystride2; - y2 += ystride2; - j += 2; - } else { - // Skip to next row - y += io->y_stride; - j++; - } - - u += io->uv_stride; - v += io->uv_stride; - } - - return 1; -} - -static int block_setup(VP8Io* io) { - yuv_init(); - return 1; -} - -static void block_teardown(const VP8Io* io) { -} - -static bool webp_init_custom_io(WebPIDecoder* idec, SkBitmap* decodedBitmap) { - if (idec == NULL) { - return false; - } - - WEBPImage pSrc; - // Custom Put callback need reference to target image. - pSrc.image = decodedBitmap; - - if (!WebPISetIOHooks(idec, block_put, block_setup, block_teardown, - (void*)&pSrc)) { - return false; - } - - return true; -} - // Incremental WebP image decoding. Reads input buffer of 64K size iteratively // and decodes this block to appropriate color-space as per config object. static bool webp_idecode(SkStream* stream, SkBitmap* decodedBitmap) { @@ -442,13 +139,32 @@ static bool webp_idecode(SkStream* stream, SkBitmap* decodedBitmap) { stream->rewind(); const uint32_t contentSize = stream->getLength(); - WebPIDecoder* idec = WebPINew(MODE_YUV); - if (idec == NULL) { + WEBP_CSP_MODE mode = MODE_LAST; + SkBitmap::Config config = decodedBitmap->config(); + if (config == SkBitmap::kARGB_8888_Config) { + mode = MODE_RGBA; + } else if (config == SkBitmap::kARGB_4444_Config) { + mode = MODE_RGBA_4444; + } else if (config == SkBitmap::kRGB_565_Config) { + mode = MODE_RGB_565; + } else { + return false; + } + + WebPDecoderConfig decode_config; + if (WebPInitDecoderConfig(&decode_config) == 0) { return false; } - if (!webp_init_custom_io(idec, decodedBitmap)) { - WebPIDelete(idec); + decode_config.output.colorspace = mode; + decode_config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); + decode_config.output.u.RGBA.stride = decodedBitmap->rowBytes(); + decode_config.output.u.RGBA.size = decodedBitmap->getSize(); + decode_config.output.is_external_memory = 1; + + WebPIDecoder* idec = WebPIDecode(NULL, NULL, &decode_config); + if (idec == NULL) { + WebPFreeDecBuffer(&decode_config.output); return false; } @@ -460,6 +176,7 @@ static bool webp_idecode(SkStream* stream, SkBitmap* decodedBitmap) { unsigned char* input = (uint8_t*)srcStorage.get(); if (input == NULL) { WebPIDelete(idec); + WebPFreeDecBuffer(&decode_config.output); return false; } @@ -483,6 +200,7 @@ static bool webp_idecode(SkStream* stream, SkBitmap* decodedBitmap) { } srcStorage.free(); WebPIDelete(idec); + WebPFreeDecBuffer(&decode_config.output); if (bytes_remaining > 0) { return false; @@ -509,7 +227,6 @@ bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, } } - if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { return false; } |