1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/images/SkImageDecoder_libwebp.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,683 @@ 1.4 +/* 1.5 + * Copyright 2010, The Android Open Source Project 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * Unless required by applicable law or agreed to in writing, software 1.14 + * distributed under the License is distributed on an "AS IS" BASIS, 1.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.16 + * See the License for the specific language governing permissions and 1.17 + * limitations under the License. 1.18 + */ 1.19 + 1.20 +#include "SkImageDecoder.h" 1.21 +#include "SkImageEncoder.h" 1.22 +#include "SkColorPriv.h" 1.23 +#include "SkScaledBitmapSampler.h" 1.24 +#include "SkStream.h" 1.25 +#include "SkTemplates.h" 1.26 +#include "SkUtils.h" 1.27 + 1.28 +// A WebP decoder only, on top of (subset of) libwebp 1.29 +// For more information on WebP image format, and libwebp library, see: 1.30 +// http://code.google.com/speed/webp/ 1.31 +// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library 1.32 +// http://review.webmproject.org/gitweb?p=libwebp.git 1.33 + 1.34 +#include <stdio.h> 1.35 +extern "C" { 1.36 +// If moving libwebp out of skia source tree, path for webp headers must be 1.37 +// updated accordingly. Here, we enforce using local copy in webp sub-directory. 1.38 +#include "webp/decode.h" 1.39 +#include "webp/encode.h" 1.40 +} 1.41 + 1.42 +// this enables timing code to report milliseconds for a decode 1.43 +//#define TIME_DECODE 1.44 + 1.45 +////////////////////////////////////////////////////////////////////////// 1.46 +////////////////////////////////////////////////////////////////////////// 1.47 + 1.48 +// Define VP8 I/O on top of Skia stream 1.49 + 1.50 +////////////////////////////////////////////////////////////////////////// 1.51 +////////////////////////////////////////////////////////////////////////// 1.52 + 1.53 +static const size_t WEBP_VP8_HEADER_SIZE = 64; 1.54 +static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); 1.55 + 1.56 +// Parse headers of RIFF container, and check for valid Webp (VP8) content. 1.57 +static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) { 1.58 + unsigned char buffer[WEBP_VP8_HEADER_SIZE]; 1.59 + size_t bytesToRead = WEBP_VP8_HEADER_SIZE; 1.60 + size_t totalBytesRead = 0; 1.61 + do { 1.62 + unsigned char* dst = buffer + totalBytesRead; 1.63 + const size_t bytesRead = stream->read(dst, bytesToRead); 1.64 + if (0 == bytesRead) { 1.65 + // Could not read any bytes. Check to see if we are at the end (exit 1.66 + // condition), and continue reading if not. Important for streams 1.67 + // that do not have all the data ready. 1.68 + continue; 1.69 + } 1.70 + bytesToRead -= bytesRead; 1.71 + totalBytesRead += bytesRead; 1.72 + SkASSERT(bytesToRead + totalBytesRead == WEBP_VP8_HEADER_SIZE); 1.73 + } while (!stream->isAtEnd() && bytesToRead > 0); 1.74 + 1.75 + WebPBitstreamFeatures features; 1.76 + VP8StatusCode status = WebPGetFeatures(buffer, totalBytesRead, &features); 1.77 + if (VP8_STATUS_OK != status) { 1.78 + return false; // Invalid WebP file. 1.79 + } 1.80 + *width = features.width; 1.81 + *height = features.height; 1.82 + *alpha = features.has_alpha; 1.83 + 1.84 + // sanity check for image size that's about to be decoded. 1.85 + { 1.86 + int64_t size = sk_64_mul(*width, *height); 1.87 + if (!sk_64_isS32(size)) { 1.88 + return false; 1.89 + } 1.90 + // now check that if we are 4-bytes per pixel, we also don't overflow 1.91 + if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { 1.92 + return false; 1.93 + } 1.94 + } 1.95 + return true; 1.96 +} 1.97 + 1.98 +class SkWEBPImageDecoder: public SkImageDecoder { 1.99 +public: 1.100 + SkWEBPImageDecoder() { 1.101 + fInputStream = NULL; 1.102 + fOrigWidth = 0; 1.103 + fOrigHeight = 0; 1.104 + fHasAlpha = 0; 1.105 + } 1.106 + virtual ~SkWEBPImageDecoder() { 1.107 + SkSafeUnref(fInputStream); 1.108 + } 1.109 + 1.110 + virtual Format getFormat() const SK_OVERRIDE { 1.111 + return kWEBP_Format; 1.112 + } 1.113 + 1.114 +protected: 1.115 + virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE; 1.116 + virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE; 1.117 + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; 1.118 + 1.119 +private: 1.120 + /** 1.121 + * Called when determining the output config to request to webp. 1.122 + * If the image does not have alpha, there is no need to premultiply. 1.123 + * If the caller wants unpremultiplied colors, that is respected. 1.124 + */ 1.125 + bool shouldPremultiply() const { 1.126 + return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors(); 1.127 + } 1.128 + 1.129 + bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); 1.130 + 1.131 + SkStream* fInputStream; 1.132 + int fOrigWidth; 1.133 + int fOrigHeight; 1.134 + int fHasAlpha; 1.135 + 1.136 + typedef SkImageDecoder INHERITED; 1.137 +}; 1.138 + 1.139 +////////////////////////////////////////////////////////////////////////// 1.140 + 1.141 +#ifdef TIME_DECODE 1.142 + 1.143 +#include "SkTime.h" 1.144 + 1.145 +class AutoTimeMillis { 1.146 +public: 1.147 + AutoTimeMillis(const char label[]) : 1.148 + fLabel(label) { 1.149 + if (NULL == fLabel) { 1.150 + fLabel = ""; 1.151 + } 1.152 + fNow = SkTime::GetMSecs(); 1.153 + } 1.154 + ~AutoTimeMillis() { 1.155 + SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 1.156 + } 1.157 +private: 1.158 + const char* fLabel; 1.159 + SkMSec fNow; 1.160 +}; 1.161 + 1.162 +#endif 1.163 + 1.164 +/////////////////////////////////////////////////////////////////////////////// 1.165 + 1.166 +// This guy exists just to aid in debugging, as it allows debuggers to just 1.167 +// set a break-point in one place to see all error exists. 1.168 +static bool return_false(const SkBitmap& bm, const char msg[]) { 1.169 + SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); 1.170 + return false; // must always return false 1.171 +} 1.172 + 1.173 +static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) { 1.174 + WEBP_CSP_MODE mode = MODE_LAST; 1.175 + SkBitmap::Config config = decodedBitmap->config(); 1.176 + 1.177 + if (config == SkBitmap::kARGB_8888_Config) { 1.178 + #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 1.179 + mode = premultiply ? MODE_bgrA : MODE_BGRA; 1.180 + #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 1.181 + mode = premultiply ? MODE_rgbA : MODE_RGBA; 1.182 + #else 1.183 + #error "Skia uses BGRA or RGBA byte order" 1.184 + #endif 1.185 + } else if (config == SkBitmap::kARGB_4444_Config) { 1.186 + mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444; 1.187 + } else if (config == SkBitmap::kRGB_565_Config) { 1.188 + mode = MODE_RGB_565; 1.189 + } 1.190 + SkASSERT(MODE_LAST != mode); 1.191 + return mode; 1.192 +} 1.193 + 1.194 +// Incremental WebP image decoding. Reads input buffer of 64K size iteratively 1.195 +// and decodes this block to appropriate color-space as per config object. 1.196 +static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { 1.197 + WebPIDecoder* idec = WebPIDecode(NULL, 0, config); 1.198 + if (NULL == idec) { 1.199 + WebPFreeDecBuffer(&config->output); 1.200 + return false; 1.201 + } 1.202 + 1.203 + if (!stream->rewind()) { 1.204 + SkDebugf("Failed to rewind webp stream!"); 1.205 + return false; 1.206 + } 1.207 + const size_t readBufferSize = stream->hasLength() ? 1.208 + SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; 1.209 + SkAutoMalloc srcStorage(readBufferSize); 1.210 + unsigned char* input = (uint8_t*)srcStorage.get(); 1.211 + if (NULL == input) { 1.212 + WebPIDelete(idec); 1.213 + WebPFreeDecBuffer(&config->output); 1.214 + return false; 1.215 + } 1.216 + 1.217 + bool success = true; 1.218 + VP8StatusCode status = VP8_STATUS_SUSPENDED; 1.219 + do { 1.220 + const size_t bytesRead = stream->read(input, readBufferSize); 1.221 + if (0 == bytesRead) { 1.222 + success = false; 1.223 + break; 1.224 + } 1.225 + 1.226 + status = WebPIAppend(idec, input, bytesRead); 1.227 + if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) { 1.228 + success = false; 1.229 + break; 1.230 + } 1.231 + } while (VP8_STATUS_OK != status); 1.232 + srcStorage.free(); 1.233 + WebPIDelete(idec); 1.234 + WebPFreeDecBuffer(&config->output); 1.235 + 1.236 + return success; 1.237 +} 1.238 + 1.239 +static bool webp_get_config_resize(WebPDecoderConfig* config, 1.240 + SkBitmap* decodedBitmap, 1.241 + int width, int height, bool premultiply) { 1.242 + WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply); 1.243 + if (MODE_LAST == mode) { 1.244 + return false; 1.245 + } 1.246 + 1.247 + if (0 == WebPInitDecoderConfig(config)) { 1.248 + return false; 1.249 + } 1.250 + 1.251 + config->output.colorspace = mode; 1.252 + config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); 1.253 + config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes(); 1.254 + config->output.u.RGBA.size = decodedBitmap->getSize(); 1.255 + config->output.is_external_memory = 1; 1.256 + 1.257 + if (width != decodedBitmap->width() || height != decodedBitmap->height()) { 1.258 + config->options.use_scaling = 1; 1.259 + config->options.scaled_width = decodedBitmap->width(); 1.260 + config->options.scaled_height = decodedBitmap->height(); 1.261 + } 1.262 + 1.263 + return true; 1.264 +} 1.265 + 1.266 +static bool webp_get_config_resize_crop(WebPDecoderConfig* config, 1.267 + SkBitmap* decodedBitmap, 1.268 + const SkIRect& region, bool premultiply) { 1.269 + 1.270 + if (!webp_get_config_resize(config, decodedBitmap, region.width(), 1.271 + region.height(), premultiply)) { 1.272 + return false; 1.273 + } 1.274 + 1.275 + config->options.use_cropping = 1; 1.276 + config->options.crop_left = region.fLeft; 1.277 + config->options.crop_top = region.fTop; 1.278 + config->options.crop_width = region.width(); 1.279 + config->options.crop_height = region.height(); 1.280 + 1.281 + return true; 1.282 +} 1.283 + 1.284 +bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, 1.285 + int width, int height) { 1.286 + SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, SkToBool(fHasAlpha)); 1.287 + 1.288 + // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. 1.289 + if (fHasAlpha) { 1.290 + if (config != SkBitmap::kARGB_4444_Config) { 1.291 + config = SkBitmap::kARGB_8888_Config; 1.292 + } 1.293 + } else { 1.294 + if (config != SkBitmap::kRGB_565_Config && 1.295 + config != SkBitmap::kARGB_4444_Config) { 1.296 + config = SkBitmap::kARGB_8888_Config; 1.297 + } 1.298 + } 1.299 + 1.300 + if (!this->chooseFromOneChoice(config, width, height)) { 1.301 + return false; 1.302 + } 1.303 + 1.304 + return decodedBitmap->setConfig(config, width, height, 0, 1.305 + fHasAlpha ? kPremul_SkAlphaType : kOpaque_SkAlphaType); 1.306 +} 1.307 + 1.308 +bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, 1.309 + int *width, int *height) { 1.310 + int origWidth, origHeight, hasAlpha; 1.311 + if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 1.312 + return false; 1.313 + } 1.314 + 1.315 + if (!stream->rewind()) { 1.316 + SkDebugf("Failed to rewind webp stream!"); 1.317 + return false; 1.318 + } 1.319 + 1.320 + *width = origWidth; 1.321 + *height = origHeight; 1.322 + 1.323 + SkRefCnt_SafeAssign(this->fInputStream, stream); 1.324 + this->fOrigWidth = origWidth; 1.325 + this->fOrigHeight = origHeight; 1.326 + this->fHasAlpha = hasAlpha; 1.327 + 1.328 + return true; 1.329 +} 1.330 + 1.331 +static bool is_config_compatible(const SkBitmap& bitmap) { 1.332 + SkBitmap::Config config = bitmap.config(); 1.333 + return config == SkBitmap::kARGB_4444_Config || 1.334 + config == SkBitmap::kRGB_565_Config || 1.335 + config == SkBitmap::kARGB_8888_Config; 1.336 +} 1.337 + 1.338 +bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap, 1.339 + const SkIRect& region) { 1.340 + SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight); 1.341 + 1.342 + if (!rect.intersect(region)) { 1.343 + // If the requested region is entirely outsides the image, return false 1.344 + return false; 1.345 + } 1.346 + 1.347 + const int sampleSize = this->getSampleSize(); 1.348 + SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); 1.349 + const int width = sampler.scaledWidth(); 1.350 + const int height = sampler.scaledHeight(); 1.351 + 1.352 + // The image can be decoded directly to decodedBitmap if 1.353 + // 1. the region is within the image range 1.354 + // 2. bitmap's config is compatible 1.355 + // 3. bitmap's size is same as the required region (after sampled) 1.356 + bool directDecode = (rect == region) && 1.357 + (decodedBitmap->isNull() || 1.358 + (is_config_compatible(*decodedBitmap) && 1.359 + (decodedBitmap->width() == width) && 1.360 + (decodedBitmap->height() == height))); 1.361 + 1.362 + SkBitmap tmpBitmap; 1.363 + SkBitmap *bitmap = decodedBitmap; 1.364 + 1.365 + if (!directDecode) { 1.366 + bitmap = &tmpBitmap; 1.367 + } 1.368 + 1.369 + if (bitmap->isNull()) { 1.370 + if (!setDecodeConfig(bitmap, width, height)) { 1.371 + return false; 1.372 + } 1.373 + // alloc from native heap if it is a temp bitmap. (prevent GC) 1.374 + bool allocResult = (bitmap == decodedBitmap) 1.375 + ? allocPixelRef(bitmap, NULL) 1.376 + : bitmap->allocPixels(); 1.377 + if (!allocResult) { 1.378 + return return_false(*decodedBitmap, "allocPixelRef"); 1.379 + } 1.380 + } else { 1.381 + // This is also called in setDecodeConfig in above block. 1.382 + // i.e., when bitmap->isNull() is true. 1.383 + if (!chooseFromOneChoice(bitmap->config(), width, height)) { 1.384 + return false; 1.385 + } 1.386 + } 1.387 + 1.388 + SkAutoLockPixels alp(*bitmap); 1.389 + WebPDecoderConfig config; 1.390 + if (!webp_get_config_resize_crop(&config, bitmap, rect, 1.391 + this->shouldPremultiply())) { 1.392 + return false; 1.393 + } 1.394 + 1.395 + // Decode the WebP image data stream using WebP incremental decoding for 1.396 + // the specified cropped image-region. 1.397 + if (!webp_idecode(this->fInputStream, &config)) { 1.398 + return false; 1.399 + } 1.400 + 1.401 + if (!directDecode) { 1.402 + cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), 1.403 + region.width(), region.height(), rect.x(), rect.y()); 1.404 + } 1.405 + return true; 1.406 +} 1.407 + 1.408 +bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, 1.409 + Mode mode) { 1.410 +#ifdef TIME_DECODE 1.411 + AutoTimeMillis atm("WEBP Decode"); 1.412 +#endif 1.413 + 1.414 + int origWidth, origHeight, hasAlpha; 1.415 + if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 1.416 + return false; 1.417 + } 1.418 + this->fHasAlpha = hasAlpha; 1.419 + 1.420 + const int sampleSize = this->getSampleSize(); 1.421 + SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); 1.422 + if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 1.423 + sampler.scaledHeight())) { 1.424 + return false; 1.425 + } 1.426 + 1.427 + // If only bounds are requested, done 1.428 + if (SkImageDecoder::kDecodeBounds_Mode == mode) { 1.429 + return true; 1.430 + } 1.431 + 1.432 + if (!this->allocPixelRef(decodedBitmap, NULL)) { 1.433 + return return_false(*decodedBitmap, "allocPixelRef"); 1.434 + } 1.435 + 1.436 + SkAutoLockPixels alp(*decodedBitmap); 1.437 + 1.438 + WebPDecoderConfig config; 1.439 + if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, 1.440 + this->shouldPremultiply())) { 1.441 + return false; 1.442 + } 1.443 + 1.444 + // Decode the WebP image data stream using WebP incremental decoding. 1.445 + return webp_idecode(stream, &config); 1.446 +} 1.447 + 1.448 +/////////////////////////////////////////////////////////////////////////////// 1.449 + 1.450 +#include "SkUnPreMultiply.h" 1.451 + 1.452 +typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, 1.453 + const SkPMColor* SK_RESTRICT ctable); 1.454 + 1.455 +static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 1.456 + const SkPMColor*) { 1.457 + const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 1.458 + for (int i = 0; i < width; ++i) { 1.459 + const uint32_t c = *src++; 1.460 + rgb[0] = SkGetPackedR32(c); 1.461 + rgb[1] = SkGetPackedG32(c); 1.462 + rgb[2] = SkGetPackedB32(c); 1.463 + rgb += 3; 1.464 + } 1.465 +} 1.466 + 1.467 +static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 1.468 + const SkPMColor*) { 1.469 + const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 1.470 + const SkUnPreMultiply::Scale* SK_RESTRICT table = 1.471 + SkUnPreMultiply::GetScaleTable(); 1.472 + for (int i = 0; i < width; ++i) { 1.473 + const uint32_t c = *src++; 1.474 + uint8_t a = SkGetPackedA32(c); 1.475 + uint8_t r = SkGetPackedR32(c); 1.476 + uint8_t g = SkGetPackedG32(c); 1.477 + uint8_t b = SkGetPackedB32(c); 1.478 + if (0 != a && 255 != a) { 1.479 + SkUnPreMultiply::Scale scale = table[a]; 1.480 + r = SkUnPreMultiply::ApplyScale(scale, r); 1.481 + g = SkUnPreMultiply::ApplyScale(scale, g); 1.482 + b = SkUnPreMultiply::ApplyScale(scale, b); 1.483 + } 1.484 + rgb[0] = r; 1.485 + rgb[1] = g; 1.486 + rgb[2] = b; 1.487 + rgb[3] = a; 1.488 + rgb += 4; 1.489 + } 1.490 +} 1.491 + 1.492 +static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 1.493 + const SkPMColor*) { 1.494 + const uint16_t* SK_RESTRICT src = (const uint16_t*)in; 1.495 + for (int i = 0; i < width; ++i) { 1.496 + const uint16_t c = *src++; 1.497 + rgb[0] = SkPacked16ToR32(c); 1.498 + rgb[1] = SkPacked16ToG32(c); 1.499 + rgb[2] = SkPacked16ToB32(c); 1.500 + rgb += 3; 1.501 + } 1.502 +} 1.503 + 1.504 +static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 1.505 + const SkPMColor*) { 1.506 + const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 1.507 + for (int i = 0; i < width; ++i) { 1.508 + const SkPMColor16 c = *src++; 1.509 + rgb[0] = SkPacked4444ToR32(c); 1.510 + rgb[1] = SkPacked4444ToG32(c); 1.511 + rgb[2] = SkPacked4444ToB32(c); 1.512 + rgb += 3; 1.513 + } 1.514 +} 1.515 + 1.516 +static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 1.517 + const SkPMColor*) { 1.518 + const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 1.519 + const SkUnPreMultiply::Scale* SK_RESTRICT table = 1.520 + SkUnPreMultiply::GetScaleTable(); 1.521 + for (int i = 0; i < width; ++i) { 1.522 + const SkPMColor16 c = *src++; 1.523 + uint8_t a = SkPacked4444ToA32(c); 1.524 + uint8_t r = SkPacked4444ToR32(c); 1.525 + uint8_t g = SkPacked4444ToG32(c); 1.526 + uint8_t b = SkPacked4444ToB32(c); 1.527 + if (0 != a && 255 != a) { 1.528 + SkUnPreMultiply::Scale scale = table[a]; 1.529 + r = SkUnPreMultiply::ApplyScale(scale, r); 1.530 + g = SkUnPreMultiply::ApplyScale(scale, g); 1.531 + b = SkUnPreMultiply::ApplyScale(scale, b); 1.532 + } 1.533 + rgb[0] = r; 1.534 + rgb[1] = g; 1.535 + rgb[2] = b; 1.536 + rgb[3] = a; 1.537 + rgb += 4; 1.538 + } 1.539 +} 1.540 + 1.541 +static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 1.542 + const SkPMColor* SK_RESTRICT ctable) { 1.543 + const uint8_t* SK_RESTRICT src = (const uint8_t*)in; 1.544 + for (int i = 0; i < width; ++i) { 1.545 + const uint32_t c = ctable[*src++]; 1.546 + rgb[0] = SkGetPackedR32(c); 1.547 + rgb[1] = SkGetPackedG32(c); 1.548 + rgb[2] = SkGetPackedB32(c); 1.549 + rgb += 3; 1.550 + } 1.551 +} 1.552 + 1.553 +static ScanlineImporter ChooseImporter(const SkBitmap::Config& config, 1.554 + bool hasAlpha, 1.555 + int* bpp) { 1.556 + switch (config) { 1.557 + case SkBitmap::kARGB_8888_Config: 1.558 + if (hasAlpha) { 1.559 + *bpp = 4; 1.560 + return ARGB_8888_To_RGBA; 1.561 + } else { 1.562 + *bpp = 3; 1.563 + return ARGB_8888_To_RGB; 1.564 + } 1.565 + case SkBitmap::kARGB_4444_Config: 1.566 + if (hasAlpha) { 1.567 + *bpp = 4; 1.568 + return ARGB_4444_To_RGBA; 1.569 + } else { 1.570 + *bpp = 3; 1.571 + return ARGB_4444_To_RGB; 1.572 + } 1.573 + case SkBitmap::kRGB_565_Config: 1.574 + *bpp = 3; 1.575 + return RGB_565_To_RGB; 1.576 + case SkBitmap::kIndex8_Config: 1.577 + *bpp = 3; 1.578 + return Index8_To_RGB; 1.579 + default: 1.580 + return NULL; 1.581 + } 1.582 +} 1.583 + 1.584 +static int stream_writer(const uint8_t* data, size_t data_size, 1.585 + const WebPPicture* const picture) { 1.586 + SkWStream* const stream = (SkWStream*)picture->custom_ptr; 1.587 + return stream->write(data, data_size) ? 1 : 0; 1.588 +} 1.589 + 1.590 +class SkWEBPImageEncoder : public SkImageEncoder { 1.591 +protected: 1.592 + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE; 1.593 + 1.594 +private: 1.595 + typedef SkImageEncoder INHERITED; 1.596 +}; 1.597 + 1.598 +bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, 1.599 + int quality) { 1.600 + const SkBitmap::Config config = bm.config(); 1.601 + const bool hasAlpha = !bm.isOpaque(); 1.602 + int bpp = -1; 1.603 + const ScanlineImporter scanline_import = ChooseImporter(config, hasAlpha, 1.604 + &bpp); 1.605 + if (NULL == scanline_import) { 1.606 + return false; 1.607 + } 1.608 + if (-1 == bpp) { 1.609 + return false; 1.610 + } 1.611 + 1.612 + SkAutoLockPixels alp(bm); 1.613 + SkAutoLockColors ctLocker; 1.614 + if (NULL == bm.getPixels()) { 1.615 + return false; 1.616 + } 1.617 + 1.618 + WebPConfig webp_config; 1.619 + if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) { 1.620 + return false; 1.621 + } 1.622 + 1.623 + WebPPicture pic; 1.624 + WebPPictureInit(&pic); 1.625 + pic.width = bm.width(); 1.626 + pic.height = bm.height(); 1.627 + pic.writer = stream_writer; 1.628 + pic.custom_ptr = (void*)stream; 1.629 + 1.630 + const SkPMColor* colors = ctLocker.lockColors(bm); 1.631 + const uint8_t* src = (uint8_t*)bm.getPixels(); 1.632 + const int rgbStride = pic.width * bpp; 1.633 + 1.634 + // Import (for each scanline) the bit-map image (in appropriate color-space) 1.635 + // to RGB color space. 1.636 + uint8_t* rgb = new uint8_t[rgbStride * pic.height]; 1.637 + for (int y = 0; y < pic.height; ++y) { 1.638 + scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, 1.639 + pic.width, colors); 1.640 + } 1.641 + 1.642 + bool ok; 1.643 + if (bpp == 3) { 1.644 + ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride)); 1.645 + } else { 1.646 + ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride)); 1.647 + } 1.648 + delete[] rgb; 1.649 + 1.650 + ok = ok && WebPEncode(&webp_config, &pic); 1.651 + WebPPictureFree(&pic); 1.652 + 1.653 + return ok; 1.654 +} 1.655 + 1.656 + 1.657 +/////////////////////////////////////////////////////////////////////////////// 1.658 +DEFINE_DECODER_CREATOR(WEBPImageDecoder); 1.659 +DEFINE_ENCODER_CREATOR(WEBPImageEncoder); 1.660 +/////////////////////////////////////////////////////////////////////////////// 1.661 + 1.662 +static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) { 1.663 + int width, height, hasAlpha; 1.664 + if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { 1.665 + return NULL; 1.666 + } 1.667 + 1.668 + // Magic matches, call decoder 1.669 + return SkNEW(SkWEBPImageDecoder); 1.670 +} 1.671 + 1.672 +static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) { 1.673 + int width, height, hasAlpha; 1.674 + if (webp_parse_header(stream, &width, &height, &hasAlpha)) { 1.675 + return SkImageDecoder::kWEBP_Format; 1.676 + } 1.677 + return SkImageDecoder::kUnknown_Format; 1.678 +} 1.679 + 1.680 +static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { 1.681 + return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL; 1.682 +} 1.683 + 1.684 +static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory); 1.685 +static SkImageDecoder_FormatReg gFormatReg(get_format_webp); 1.686 +static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory);