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