1/* 2 * Copyright 2010, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "SkImageDecoder.h" 18#include "SkImageEncoder.h" 19#include "SkColorPriv.h" 20#include "SkScaledBitmapSampler.h" 21#include "SkStream.h" 22#include "SkTemplates.h" 23#include "SkUtils.h" 24#include "SkTScopedPtr.h" 25 26// A WebP decoder only, on top of (subset of) libwebp 27// For more information on WebP image format, and libwebp library, see: 28// http://code.google.com/speed/webp/ 29// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library 30// http://review.webmproject.org/gitweb?p=libwebp.git 31 32#include <stdio.h> 33extern "C" { 34// If moving libwebp out of skia source tree, path for webp headers must be 35// updated accordingly. Here, we enforce using local copy in webp sub-directory. 36#include "webp/decode.h" 37#include "webp/encode.h" 38} 39 40#ifdef ANDROID 41#include <cutils/properties.h> 42 43// Key to lookup the size of memory buffer set in system property 44static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; 45#endif 46 47// this enables timing code to report milliseconds for a decode 48//#define TIME_DECODE 49 50////////////////////////////////////////////////////////////////////////// 51////////////////////////////////////////////////////////////////////////// 52 53// Define VP8 I/O on top of Skia stream 54 55////////////////////////////////////////////////////////////////////////// 56////////////////////////////////////////////////////////////////////////// 57 58static const size_t WEBP_VP8_HEADER_SIZE = 64; 59static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); 60 61// Parse headers of RIFF container, and check for valid Webp (VP8) content. 62static bool webp_parse_header(SkStream* stream, int* width, int* height, 63 int* alpha) { 64 unsigned char buffer[WEBP_VP8_HEADER_SIZE]; 65 const uint32_t contentSize = stream->getLength(); 66 const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE); 67 const uint32_t read_bytes = (contentSize < WEBP_VP8_HEADER_SIZE) ? 68 contentSize : WEBP_VP8_HEADER_SIZE; 69 if (len != read_bytes) { 70 return false; // can't read enough 71 } 72 73 WebPBitstreamFeatures features; 74 VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features); 75 if (status != VP8_STATUS_OK) { 76 return false; // Invalid WebP file. 77 } 78 *width = features.width; 79 *height = features.height; 80 *alpha = features.has_alpha; 81 82 // sanity check for image size that's about to be decoded. 83 { 84 Sk64 size; 85 size.setMul(*width, *height); 86 if (size.isNeg() || !size.is32()) { 87 return false; 88 } 89 // now check that if we are 4-bytes per pixel, we also don't overflow 90 if (size.get32() > (0x7FFFFFFF >> 2)) { 91 return false; 92 } 93 } 94 return true; 95} 96 97class SkWEBPImageDecoder: public SkImageDecoder { 98public: 99 virtual Format getFormat() const { 100 return kWEBP_Format; 101 } 102 103protected: 104 virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height); 105 virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect); 106 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 107 108private: 109 bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); 110 SkStream *inputStream; 111 int origWidth; 112 int origHeight; 113 int hasAlpha; 114}; 115 116////////////////////////////////////////////////////////////////////////// 117 118#include "SkTime.h" 119 120class AutoTimeMillis { 121public: 122 AutoTimeMillis(const char label[]) : 123 fLabel(label) { 124 if (!fLabel) { 125 fLabel = ""; 126 } 127 fNow = SkTime::GetMSecs(); 128 } 129 ~AutoTimeMillis() { 130 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 131 } 132private: 133 const char* fLabel; 134 SkMSec fNow; 135}; 136 137/////////////////////////////////////////////////////////////////////////////// 138 139// This guy exists just to aid in debugging, as it allows debuggers to just 140// set a break-point in one place to see all error exists. 141static bool return_false(const SkBitmap& bm, const char msg[]) { 142#if 0 143 SkDebugf("libwebp error %s [%d %d]", msg, bm.width(), bm.height()); 144#endif 145 return false; // must always return false 146} 147 148static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap, int hasAlpha) { 149 WEBP_CSP_MODE mode = MODE_LAST; 150 SkBitmap::Config config = decodedBitmap->config(); 151 // For images that have alpha, choose appropriate color mode (MODE_rgbA, 152 // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency 153 // factor (alpha). 154 if (config == SkBitmap::kARGB_8888_Config) { 155 mode = hasAlpha ? MODE_rgbA : MODE_RGBA; 156 } else if (config == SkBitmap::kARGB_4444_Config) { 157 mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444; 158 } else if (config == SkBitmap::kRGB_565_Config) { 159 mode = MODE_RGB_565; 160 } 161 SkASSERT(mode != MODE_LAST); 162 return mode; 163} 164 165// Incremental WebP image decoding. Reads input buffer of 64K size iteratively 166// and decodes this block to appropriate color-space as per config object. 167static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { 168 WebPIDecoder* idec = WebPIDecode(NULL, 0, &config); 169 if (idec == NULL) { 170 WebPFreeDecBuffer(&config.output); 171 return false; 172 } 173 174 stream->rewind(); 175 const uint32_t contentSize = stream->getLength(); 176 const uint32_t read_buffer_size = (contentSize < WEBP_IDECODE_BUFFER_SZ) ? 177 contentSize : WEBP_IDECODE_BUFFER_SZ; 178 SkAutoMalloc srcStorage(read_buffer_size); 179 unsigned char* input = (uint8_t*)srcStorage.get(); 180 if (input == NULL) { 181 WebPIDelete(idec); 182 WebPFreeDecBuffer(&config.output); 183 return false; 184 } 185 186 uint32_t bytes_remaining = contentSize; 187 while (bytes_remaining > 0) { 188 const uint32_t bytes_to_read = 189 (bytes_remaining < WEBP_IDECODE_BUFFER_SZ) ? 190 bytes_remaining : WEBP_IDECODE_BUFFER_SZ; 191 192 const size_t bytes_read = stream->read(input, bytes_to_read); 193 if (bytes_read == 0) { 194 break; 195 } 196 197 VP8StatusCode status = WebPIAppend(idec, input, bytes_read); 198 if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) { 199 bytes_remaining -= bytes_read; 200 } else { 201 break; 202 } 203 } 204 srcStorage.free(); 205 WebPIDelete(idec); 206 WebPFreeDecBuffer(&config.output); 207 208 if (bytes_remaining > 0) { 209 return false; 210 } else { 211 return true; 212 } 213} 214 215static bool webp_get_config_resize(WebPDecoderConfig& config, 216 SkBitmap* decodedBitmap, 217 int width, int height, int hasAlpha) { 218 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha); 219 if (mode == MODE_LAST) { 220 return false; 221 } 222 223 if (WebPInitDecoderConfig(&config) == 0) { 224 return false; 225 } 226 227 config.output.colorspace = mode; 228 config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); 229 config.output.u.RGBA.stride = decodedBitmap->rowBytes(); 230 config.output.u.RGBA.size = decodedBitmap->getSize(); 231 config.output.is_external_memory = 1; 232 233 if (width != decodedBitmap->width() || 234 height != decodedBitmap->height()) { 235 config.options.use_scaling = 1; 236 config.options.scaled_width = decodedBitmap->width(); 237 config.options.scaled_height = decodedBitmap->height(); 238 } 239 240 return true; 241} 242 243static bool webp_get_config_resize_crop(WebPDecoderConfig& config, 244 SkBitmap* decodedBitmap, 245 SkIRect region, int hasAlpha) { 246 247 if (!webp_get_config_resize( 248 config, decodedBitmap, region.width(), region.height(), hasAlpha)) { 249 return false; 250 } 251 252 config.options.use_cropping = 1; 253 config.options.crop_left = region.fLeft; 254 config.options.crop_top = region.fTop; 255 config.options.crop_width = region.width(); 256 config.options.crop_height = region.height(); 257 258 return true; 259} 260 261bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, 262 int width, int height) { 263 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha); 264 265 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. 266 if (hasAlpha) { 267 if (config != SkBitmap::kARGB_4444_Config) { 268 config = SkBitmap::kARGB_8888_Config; 269 } 270 } else { 271 if (config != SkBitmap::kRGB_565_Config && 272 config != SkBitmap::kARGB_4444_Config) { 273 config = SkBitmap::kARGB_8888_Config; 274 } 275 } 276 277 if (!this->chooseFromOneChoice(config, width, height)) { 278 return false; 279 } 280 281 decodedBitmap->setConfig(config, width, height, 0); 282 283 decodedBitmap->setIsOpaque(!hasAlpha); 284 285 return true; 286} 287 288bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, 289 int *width, int *height) { 290 int origWidth, origHeight, hasAlpha; 291 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 292 return false; 293 } 294 295 stream->rewind(); 296 *width = origWidth; 297 *height = origHeight; 298 299 this->inputStream = stream; 300 this->origWidth = origWidth; 301 this->origHeight = origHeight; 302 this->hasAlpha = hasAlpha; 303 304 return true; 305} 306 307static bool isConfigCompatible(SkBitmap* bitmap) { 308 SkBitmap::Config config = bitmap->config(); 309 return config == SkBitmap::kARGB_4444_Config || 310 config == SkBitmap::kRGB_565_Config || 311 config == SkBitmap::kARGB_8888_Config; 312} 313 314bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, 315 SkIRect region) { 316 SkIRect rect = SkIRect::MakeWH(origWidth, origHeight); 317 318 if (!rect.intersect(region)) { 319 // If the requested region is entirely outsides the image, just 320 // returns false 321 return false; 322 } 323 324 const int sampleSize = this->getSampleSize(); 325 SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); 326 const int width = sampler.scaledWidth(); 327 const int height = sampler.scaledHeight(); 328 329 // The image can be decoded directly to decodedBitmap if 330 // 1. the region is within the image range 331 // 2. bitmap's config is compatible 332 // 3. bitmap's size is same as the required region (after sampled) 333 bool directDecode = (rect == region) && 334 (decodedBitmap->isNull() || 335 (isConfigCompatible(decodedBitmap) && 336 (decodedBitmap->width() == width) && 337 (decodedBitmap->height() == height))); 338 SkTScopedPtr<SkBitmap> adb; 339 SkBitmap *bitmap = decodedBitmap; 340 341 if (!directDecode) { 342 // allocates a temp bitmap 343 bitmap = new SkBitmap; 344 adb.reset(bitmap); 345 } 346 347 if (bitmap->isNull()) { 348 if (!setDecodeConfig(bitmap, width, height)) { 349 return false; 350 } 351 // alloc from native heap if it is a temp bitmap. (prevent GC) 352 bool allocResult = (bitmap == decodedBitmap) 353 ? allocPixelRef(bitmap, NULL) 354 : bitmap->allocPixels(); 355 if (!allocResult) { 356 return return_false(*decodedBitmap, "allocPixelRef"); 357 } 358 } else { 359 // This is also called in setDecodeConfig in above block. 360 // i.e., when bitmap->isNull() is true. 361 if (!chooseFromOneChoice(bitmap->config(), width, height)) { 362 return false; 363 } 364 } 365 366 SkAutoLockPixels alp(*bitmap); 367 WebPDecoderConfig config; 368 if (!webp_get_config_resize_crop(config, bitmap, rect, hasAlpha)) { 369 return false; 370 } 371 372 // Decode the WebP image data stream using WebP incremental decoding for 373 // the specified cropped image-region. 374 if (!webp_idecode(this->inputStream, config)) { 375 return false; 376 } 377 378 if (!directDecode) { 379 cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), 380 region.width(), region.height(), rect.x(), rect.y()); 381 } 382 return true; 383} 384 385bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, 386 Mode mode) { 387#ifdef TIME_DECODE 388 AutoTimeMillis atm("WEBP Decode"); 389#endif 390 391 int origWidth, origHeight, hasAlpha; 392 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 393 return false; 394 } 395 this->hasAlpha = hasAlpha; 396 397 const int sampleSize = this->getSampleSize(); 398 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); 399 400 // If only bounds are requested, done 401 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 402 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 403 sampler.scaledHeight())) { 404 return false; 405 } 406 return true; 407 } 408#ifdef SK_BUILD_FOR_ANDROID 409 // No Bitmap reuse supported for this format 410 if (!decodedBitmap->isNull()) { 411 return false; 412 } 413#endif 414 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 415 sampler.scaledHeight())) { 416 return false; 417 } 418 419 if (!this->allocPixelRef(decodedBitmap, NULL)) { 420 return return_false(*decodedBitmap, "allocPixelRef"); 421 } 422 423 SkAutoLockPixels alp(*decodedBitmap); 424 425 WebPDecoderConfig config; 426 if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight, 427 hasAlpha)) { 428 return false; 429 } 430 431 // Decode the WebP image data stream using WebP incremental decoding. 432 return webp_idecode(stream, config); 433} 434 435/////////////////////////////////////////////////////////////////////////////// 436 437typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, 438 const SkPMColor* SK_RESTRICT ctable); 439 440static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 441 const SkPMColor*) { 442 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 443 for (int i = 0; i < width; ++i) { 444 const uint32_t c = *src++; 445 rgb[0] = SkGetPackedR32(c); 446 rgb[1] = SkGetPackedG32(c); 447 rgb[2] = SkGetPackedB32(c); 448 rgb += 3; 449 } 450} 451 452static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 453 const SkPMColor*) { 454 const uint16_t* SK_RESTRICT src = (const uint16_t*)in; 455 for (int i = 0; i < width; ++i) { 456 const uint16_t c = *src++; 457 rgb[0] = SkPacked16ToR32(c); 458 rgb[1] = SkPacked16ToG32(c); 459 rgb[2] = SkPacked16ToB32(c); 460 rgb += 3; 461 } 462} 463 464static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 465 const SkPMColor*) { 466 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 467 for (int i = 0; i < width; ++i) { 468 const SkPMColor16 c = *src++; 469 rgb[0] = SkPacked4444ToR32(c); 470 rgb[1] = SkPacked4444ToG32(c); 471 rgb[2] = SkPacked4444ToB32(c); 472 rgb += 3; 473 } 474} 475 476static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 477 const SkPMColor* SK_RESTRICT ctable) { 478 const uint8_t* SK_RESTRICT src = (const uint8_t*)in; 479 for (int i = 0; i < width; ++i) { 480 const uint32_t c = ctable[*src++]; 481 rgb[0] = SkGetPackedR32(c); 482 rgb[1] = SkGetPackedG32(c); 483 rgb[2] = SkGetPackedB32(c); 484 rgb += 3; 485 } 486} 487 488static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) { 489 switch (config) { 490 case SkBitmap::kARGB_8888_Config: 491 return ARGB_8888_To_RGB; 492 case SkBitmap::kRGB_565_Config: 493 return RGB_565_To_RGB; 494 case SkBitmap::kARGB_4444_Config: 495 return ARGB_4444_To_RGB; 496 case SkBitmap::kIndex8_Config: 497 return Index8_To_RGB; 498 default: 499 return NULL; 500 } 501} 502 503static int StreamWriter(const uint8_t* data, size_t data_size, 504 const WebPPicture* const picture) { 505 SkWStream* const stream = (SkWStream*)picture->custom_ptr; 506 return stream->write(data, data_size) ? 1 : 0; 507} 508 509class SkWEBPImageEncoder : public SkImageEncoder { 510protected: 511 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); 512}; 513 514bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, 515 int quality) { 516 const SkBitmap::Config config = bm.getConfig(); 517 const ScanlineImporter scanline_import = ChooseImporter(config); 518 if (NULL == scanline_import) { 519 return false; 520 } 521 522 SkAutoLockPixels alp(bm); 523 SkAutoLockColors ctLocker; 524 if (NULL == bm.getPixels()) { 525 return false; 526 } 527 528 WebPConfig webp_config; 529 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) { 530 return false; 531 } 532 533 WebPPicture pic; 534 WebPPictureInit(&pic); 535 pic.width = bm.width(); 536 pic.height = bm.height(); 537 pic.writer = StreamWriter; 538 pic.custom_ptr = (void*)stream; 539 540 const SkPMColor* colors = ctLocker.lockColors(bm); 541 const uint8_t* src = (uint8_t*)bm.getPixels(); 542 const int rgb_stride = pic.width * 3; 543 544 // Import (for each scanline) the bit-map image (in appropriate color-space) 545 // to RGB color space. 546 uint8_t* rgb = new uint8_t[rgb_stride * pic.height]; 547 for (int y = 0; y < pic.height; ++y) { 548 scanline_import(src + y * bm.rowBytes(), rgb + y * rgb_stride, 549 pic.width, colors); 550 } 551 552 bool ok = WebPPictureImportRGB(&pic, rgb, rgb_stride); 553 delete[] rgb; 554 555 ok = ok && WebPEncode(&webp_config, &pic); 556 WebPPictureFree(&pic); 557 558 return ok; 559} 560 561 562/////////////////////////////////////////////////////////////////////////////// 563 564#include "SkTRegistry.h" 565 566static SkImageDecoder* DFactory(SkStream* stream) { 567 int width, height, hasAlpha; 568 if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { 569 return NULL; 570 } 571 572 // Magic matches, call decoder 573 return SkNEW(SkWEBPImageDecoder); 574} 575 576SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) { 577 return DFactory(stream); 578} 579 580static SkImageEncoder* EFactory(SkImageEncoder::Type t) { 581 return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL; 582} 583 584SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { 585 return EFactory(t); 586} 587 588static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory); 589static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory); 590