aboutsummaryrefslogtreecommitdiffstats
path: root/src/images
diff options
context:
space:
mode:
Diffstat (limited to 'src/images')
-rw-r--r--src/images/SkImageDecoder_libwebp.cpp333
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;
}