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