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