SkImageDecoder_libwebp.cpp revision 8432fc7b32e4de877bb86b38c050b944bed53f14
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 "SkDither.h" 21#include "SkScaledBitmapSampler.h" 22#include "SkStream.h" 23#include "SkTemplates.h" 24#include "SkUtils.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 updated accordingly. 35// Here, we enforce using local copy in webp sub-directory. 36#include "webp/decode.h" 37#include "webp/decode_vp8.h" 38} 39 40/* If defined, work around missing padding byte in content generated by webpconv */ 41#define WEBPCONV_MISSING_PADDING 1 42 43#ifdef ANDROID 44#include <cutils/properties.h> 45 46// Key to lookup the size of memory buffer set in system property 47static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; 48#endif 49 50// this enables timing code to report milliseconds for a decode 51//#define TIME_DECODE 52 53////////////////////////////////////////////////////////////////////////// 54////////////////////////////////////////////////////////////////////////// 55 56// Define VP8 I/O on top of Skia stream 57 58////////////////////////////////////////////////////////////////////////// 59////////////////////////////////////////////////////////////////////////// 60 61// An helper to extract a integer (little endian) from byte array. This is 62// called only once per decoding, so no real need to optimize it in any way 63static uint32_t getint32l(unsigned char *in) { 64 int result; 65 unsigned char *buffer = (unsigned char*) in; 66 67 if (buffer == NULL) { 68 return 0; 69 } 70 71 result = buffer[3]; 72 result = (result << 8) + buffer[2]; 73 result = (result << 8) + buffer[1]; 74 result = (result << 8) + buffer[0]; 75 76 return result; 77} 78 79// Parse headers of RIFF container, and check for valid Webp (VP8) content 80// return VP8 chunk content size on success, 0 on error. 81static const size_t WEBP_HEADER_SIZE = 20; 82static const size_t VP8_HEADER_SIZE = 10; 83 84static uint32_t webp_parse_header(SkStream* stream) { 85 unsigned char buffer[WEBP_HEADER_SIZE]; 86 size_t len; 87 uint32_t totalSize; 88 uint32_t contentSize; 89 90 // RIFF container for WEBP image should always have: 91 // 0 "RIFF" 4-byte tag 92 // 4 size of image data (including metadata) starting at offset 8 93 // 8 "WEBP" the form-type signature 94 // 12 "VP8 " 4-bytes tags, describing the raw video format used 95 // 16 size of the raw VP8 image data, starting at offset 20 96 // 20 the VP8 bytes 97 // First check for RIFF top chunk, consuming only 8 bytes 98 len = stream->read(buffer, 8); 99 if (len != 8) { 100 return 0; // can't read enough 101 } 102 // Inline magic matching instead of memcmp() 103 if (buffer[0] != 'R' || buffer[1] != 'I' || buffer[2] != 'F' || buffer[3] != 'F') { 104 return 0; 105 } 106 107 totalSize = getint32l(buffer + 4); 108 if (totalSize < (int) (WEBP_HEADER_SIZE - 8)) { 109 return 0; 110 } 111 112 // If RIFF header found, check for RIFF content to start with WEBP/VP8 chunk 113 len = stream->read(buffer + 8, WEBP_HEADER_SIZE - 8); 114 if (len != (int) (WEBP_HEADER_SIZE - 8)) { 115 return 0; 116 } 117 if (buffer[8] != 'W' || buffer[9] != 'E' || buffer[10] != 'B' || buffer[11] != 'P') { 118 return 0; 119 } 120 if (buffer[12] != 'V' || buffer[13] != 'P' || buffer[14] != '8' || buffer[15] != ' ') { 121 return 0; 122 } 123 124 // Magic matches, extract content size 125 contentSize = getint32l(buffer + 16); 126 127 // Check consistency of reported sizes 128 if (contentSize <= 0 || contentSize > 0x7fffffff) { 129 return 0; 130 } 131 if (totalSize < 12 + contentSize) { 132 return 0; 133 } 134 if (contentSize & 1) { 135 return 0; 136 } 137 138 return contentSize; 139} 140 141class SkWEBPImageDecoder: public SkImageDecoder { 142public: 143 virtual Format getFormat() const { 144 return kWEBP_Format; 145 } 146 147protected: 148 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 149}; 150 151////////////////////////////////////////////////////////////////////////// 152 153#include "SkTime.h" 154 155class AutoTimeMillis { 156public: 157 AutoTimeMillis(const char label[]) : 158 fLabel(label) { 159 if (!fLabel) { 160 fLabel = ""; 161 } 162 fNow = SkTime::GetMSecs(); 163 } 164 ~AutoTimeMillis() { 165 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 166 } 167private: 168 const char* fLabel; 169 SkMSec fNow; 170}; 171 172/////////////////////////////////////////////////////////////////////////////// 173 174// This guy exists just to aid in debugging, as it allows debuggers to just 175// set a break-point in one place to see all error exists. 176static bool return_false(const SkBitmap& bm, const char msg[]) { 177#if 0 178 SkDebugf("libwebp error %s [%d %d]", msg, bm.width(), bm.height()); 179#endif 180 return false; // must always return false 181} 182 183typedef struct { 184 SkBitmap* image; 185 SkStream* stream; 186} WEBPImage; 187 188// WebP library embeds its own YUV to RGB converter. However, High-level API doesn't take benefit 189// of (U,v) clipped values being valid for up to 4 pixels, and so there is a significant improvement 190// in performance in handling this on our own. 191// TODO: use architecture-optimized (eventually hardware-accelerated) YUV converters 192#define YUV_HALF (1 << (YUV_FIX - 1)) 193#define YUV_FIX 16 // fixed-point precision 194#define YUV_RANGE_MIN (-227) // min value of r/g/b output 195#define YUV_RANGE_MAX (256 + 226) // max value of r/g/b output 196static int16_t VP8kVToR[256], VP8kUToB[256]; 197static int32_t VP8kVToG[256], VP8kUToG[256]; 198static uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN]; 199 200static void yuv_init_tables() { 201 int i; 202 203 for (i = 0; i < 256; ++i) { 204 VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX; 205 VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF; 206 VP8kVToG[i] = -45773 * (i - 128); 207 VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX; 208 } 209 for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) { 210 const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX; 211 VP8kClip[i - YUV_RANGE_MIN] = (k < 0) ? 0 : (k > 255) ? 255 : k; 212 } 213} 214 215// Static global mutex to protect Webp initialization 216static SkMutex gYUVMutex; 217static bool gYUVReady = false; 218 219static bool yuv_init() { 220 if (!gYUVReady) { 221 gYUVMutex.acquire(); 222 if (!gYUVReady) { 223 yuv_init_tables(); 224 gYUVReady = true; 225 } 226 gYUVMutex.release(); 227 } 228 229 return gYUVReady; 230} 231 232#define PutRGBA(p,r,g,b) (((SkPMColor*) (p))[0] = SkPackARGB32(0xff,(r),(g),(b))) 233#define PutRGB565(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackRGB16((r)>>3,(g)>>2,(b)>>3)) 234#define PutRGBA4444(p,r,g,b) (((SkPMColor16*) (p))[0] = SkPackARGB4444(0xf,(r)>>4,(g)>>4,(b)>>4)) 235 236#define CRGBA(p,y,roff,goff,boff) PutRGBA(p, \ 237 VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ 238 VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ 239 VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) 240#define CRGB565(p,y,roff,goff,boff) PutRGB565(p, \ 241 VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ 242 VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ 243 VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) 244#define CRGBA4444(p,y,roff,goff,boff) PutRGBA4444(p, \ 245 VP8kClip[(y) + (roff) - YUV_RANGE_MIN], \ 246 VP8kClip[(y) + (goff) - YUV_RANGE_MIN], \ 247 VP8kClip[(y) + (boff) - YUV_RANGE_MIN]) 248 249static void block_put(const VP8Io* io) { 250 WEBPImage *p = (WEBPImage*) io->opaque; 251 SkBitmap* decodedBitmap = p->image; 252 253 const int w = io->width; 254 const int mb_h = io->mb_h; 255 256 const uint8_t *y, *y2, *u, *v; 257 const uint8_t *py, *py2, *pu, *pv; 258 259 uint8_t* pout; 260 uint8_t* pout2; 261 262 int i, j; 263 const int ystride2 = io->y_stride * 2; 264 int bpp; 265 SkBitmap::Config config = decodedBitmap->config(); 266 267 //SkASSERT(!(io->mb_y & 1)); 268 269 y = io->y; 270 u = io->u; 271 v = io->v; 272 273 switch (config) { 274 case SkBitmap::kARGB_8888_Config: 275 bpp = 4; 276 break; 277 case SkBitmap::kRGB_565_Config: 278 bpp = 2; 279 break; 280 case SkBitmap::kARGB_4444_Config: 281 bpp = 2; 282 break; 283 default: 284 // Unsupported config 285 return; 286 } 287 288 for (j = 0; j < mb_h;) { 289 pout = decodedBitmap->getAddr8(0, io->mb_y + j); 290 if (j + 1 < mb_h) { 291 y2 = y + io->y_stride; 292 pout2 = decodedBitmap->getAddr8(0, io->mb_y + j + 1); 293 } else { 294 y2 = NULL; 295 pout2 = NULL; 296 } 297 298 // Copy YUV into target buffer 299 py = y; 300 pu = u; 301 pv = v; 302 303 py2 = y2; 304 305 // Leave test for config out of inner loop. This implies some redundancy in code, 306 // but help in supporting several configs without degrading performance. 307 // As a reminder, one must *NOT* put py increment into parameters (i.e. *py++) in the hope to 308 // improve performance or code readability. Since it is used as argument of a macro which uses it 309 // several times in its expression, so this would end up in having it too much incremented 310 switch (config) { 311 case SkBitmap::kARGB_8888_Config: 312 for (i = 0; i < w; i += 2) { 313 // U and V are common for up to 4 pixels 314 const int r_off = VP8kVToR[*pv]; 315 const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; 316 const int b_off = VP8kUToB[*pu]; 317 318 CRGBA(pout, *py, r_off, g_off, b_off); 319 pout += bpp; 320 py++; 321 322 // Width shouldn't be odd, so this should always be true 323 if (i + 1 < w) { 324 CRGBA(pout, *py, r_off, g_off, b_off); 325 pout += bpp; 326 py++; 327 } 328 329 if (pout2) { 330 CRGBA(pout2, *py2, r_off, g_off, b_off); 331 pout2 += bpp; 332 py2++; 333 334 // Width shouldn't be odd, so this should always be true 335 if (i + 1 < w) { 336 CRGBA(pout2, *py2, r_off, g_off, b_off); 337 pout2 += bpp; 338 py2++; 339 } 340 } 341 342 pu++; 343 pv++; 344 } 345 break; 346 case SkBitmap::kRGB_565_Config: 347 for (i = 0; i < w; i += 2) { 348 // U and V are common for up to 4 pixels 349 const int r_off = VP8kVToR[*pv]; 350 const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; 351 const int b_off = VP8kUToB[*pu]; 352 353 CRGB565(pout, *py, r_off, g_off, b_off); 354 pout += bpp; 355 py++; 356 357 // Width shouldn't be odd, so this should always be true 358 if (i + 1 < w) { 359 CRGB565(pout, *py, r_off, g_off, b_off); 360 pout += bpp; 361 py++; 362 } 363 364 if (pout2) { 365 CRGB565(pout2, *py2, r_off, g_off, b_off); 366 pout2 += bpp; 367 py2++; 368 369 // Width shouldn't be odd, so this should always be true 370 if (i + 1 < w) { 371 CRGB565(pout2, *py2, r_off, g_off, b_off); 372 pout2 += bpp; 373 py2++; 374 } 375 } 376 377 pu++; 378 pv++; 379 } 380 break; 381 case SkBitmap::kARGB_4444_Config: 382 for (i = 0; i < w; i += 2) { 383 // U and V are common for up to 4 pixels 384 const int r_off = VP8kVToR[*pv]; 385 const int g_off = (VP8kVToG[*pv] + VP8kUToG[*pu]) >> YUV_FIX; 386 const int b_off = VP8kUToB[*pu]; 387 388 CRGBA4444(pout, *py, r_off, g_off, b_off); 389 pout += bpp; 390 py++; 391 392 // Width shouldn't be odd, so this should always be true 393 if (i + 1 < w) { 394 CRGBA4444(pout, *py, r_off, g_off, b_off); 395 pout += bpp; 396 py++; 397 } 398 399 if (pout2) { 400 CRGBA4444(pout2, *py2, r_off, g_off, b_off); 401 pout2 += bpp; 402 py2++; 403 404 // Width shouldn't be odd, so this should always be true 405 if (i + 1 < w) { 406 CRGBA4444(pout2, *py2, r_off, g_off, b_off); 407 pout2 += bpp; 408 py2++; 409 } 410 } 411 412 pu++; 413 pv++; 414 } 415 break; 416 default: 417 // Unsupported config (can't happen, but prevents compiler warning) 418 SkASSERT(0); 419 break; 420 } 421 422 if (y2) { 423 // Scanned and populated two rows 424 y += ystride2; 425 y2 += ystride2; 426 j += 2; 427 } else { 428 // Skip to next row 429 y += io->y_stride; 430 j++; 431 } 432 433 u += io->uv_stride; 434 v += io->uv_stride; 435 } 436 437 return; 438} 439 440static int block_setup(VP8Io* io) { 441 yuv_init(); 442 return 1; 443} 444 445static void block_teardown(const VP8Io* io) { 446} 447 448bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, Mode mode) { 449#ifdef TIME_DECODE 450 AutoTimeMillis atm("WEBP Decode"); 451#endif 452 453 // libwebp doesn't provide a way to override all I/O with a custom 454 // implementation. For initial implementation, let's go the "dirty" 455 // way, by loading image file content in memory before decoding it 456 int origWidth, origHeight; 457 size_t len; 458 459 bool hasAlpha = false; 460 461 uint32_t contentSize; 462 unsigned char buffer[VP8_HEADER_SIZE]; 463 unsigned char *input; 464 465 // Check header 466 contentSize = webp_parse_header(stream); 467 if (contentSize <= VP8_HEADER_SIZE) { 468 return false; 469 } 470 471 //* Extract information 472 len = stream->read(buffer, VP8_HEADER_SIZE); 473 if (len != VP8_HEADER_SIZE) { 474 return false; 475 } 476 477 // check signature 478 if (buffer[3] != 0x9d || buffer[4] != 0x01 || buffer[5] != 0x2a) { 479 return false; 480 } 481 482 const uint32_t bits = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16); 483 const int key_frame = !(bits & 1); 484 485 origWidth = ((buffer[7] << 8) | buffer[6]) & 0x3fff; 486 origHeight = ((buffer[9] << 8) | buffer[8]) & 0x3fff; 487 488 if (origWidth <= 0 || origHeight <= 0) { 489 return false; 490 } 491 492 if (!key_frame) { 493 // Not a keyframe 494 return false; 495 } 496 497 if (((bits >> 1) & 7) > 3) { 498 // unknown profile 499 return false; 500 } 501 if (!((bits >> 4) & 1)) { 502 // first frame is invisible 503 return false; 504 } 505 if (((bits >> 5)) >= contentSize) { 506 // partition_length inconsistent size information 507 return false; 508 } 509 510 SkBitmap::Config config; 511 config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha); 512 513 // only accept prefConfig if it makes sense for us. YUV converter 514 // supports output in RGB565, RGBA4444 and RGBA8888 formats. 515 if (hasAlpha) { 516 if (config != SkBitmap::kARGB_4444_Config) { 517 config = SkBitmap::kARGB_8888_Config; 518 } 519 } else { 520 if (config != SkBitmap::kRGB_565_Config && config != SkBitmap::kARGB_4444_Config) { 521 config = SkBitmap::kARGB_8888_Config; 522 } 523 } 524 525 // sanity check for size 526 { 527 Sk64 size; 528 size.setMul(origWidth, origHeight); 529 if (size.isNeg() || !size.is32()) { 530 return false; 531 } 532 // now check that if we are 4-bytes per pixel, we also don't overflow 533 if (size.get32() > (0x7FFFFFFF >> 2)) { 534 return false; 535 } 536 } 537 538 if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { 539 return false; 540 } 541 542 // TODO: may add support for sampler, to load previeww/thumbnails faster 543 // Note that, as documented, an image decoder may decide to ignore sample hint from requested 544 // config, so this implementation is still valid and safe even without handling it at all. Several 545 // other Skia image decoders just ignore this optional feature as well. 546#if 0 547 SkScaledBitmapSampler sampler(origWidth, origHeight, getSampleSize()); 548 decodedBitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0); 549#else 550 decodedBitmap->setConfig(config, origWidth, origHeight, 0); 551#endif 552 553 // If only bounds are requested, done 554 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 555 return true; 556 } 557 558 // Current WEBP specification has no support for alpha layer. 559 decodedBitmap->setIsOpaque(true); 560 561 if (!this->allocPixelRef(decodedBitmap, NULL)) { 562 return return_false(*decodedBitmap, "allocPixelRef"); 563 } 564 SkAutoLockPixels alp(*decodedBitmap); 565 566 // libwebp doesn't provide a way to override all I/O with a custom 567 // implementation. For initial implementation, let's go the "dirty" 568 // way, by loading image file content (actually only VP8 chunk) into 569 // memory before decoding it */ 570 SkAutoMalloc srcStorage(contentSize); 571 input = (uint8_t*) srcStorage.get(); 572 if (input == NULL) { 573 return return_false(*decodedBitmap, "failed to allocate read buffer"); 574 } 575 576 len = stream->read(input + VP8_HEADER_SIZE, contentSize - VP8_HEADER_SIZE); 577#ifdef WEBPCONV_MISSING_PADDING 578 // Some early (yet widely spread) version of webpconv utility 579 // had a bug causing mandatory padding byte to not be written to 580 // file when content size was odd, while total size was reporting 581 // actual file size correctly. Since many webp around may have been 582 // generated with this version of webpconv, work around this issue 583 // by adding padding here. */ 584 // TODO: remove this whenever work-around may be considered obsolete 585 if (len == contentSize - VP8_HEADER_SIZE - 1) { 586 input[VP8_HEADER_SIZE + len] = 0; 587 len++; 588 } 589#endif 590 if (len != contentSize - VP8_HEADER_SIZE) { 591 return false; 592 } 593 memcpy(input, buffer, VP8_HEADER_SIZE); 594 595 WEBPImage pSrc; 596 VP8Decoder* dec; 597 VP8Io io; 598 599 // Custom Put callback need reference to target image 600 pSrc.image = decodedBitmap; 601 602 // Keep reference to input stream, in case we find a way to not preload stream 603 // content in memory. So far, stream content has already been consumed, and this 604 // won't be used, but this is left for future usage. 605 pSrc.stream = stream; 606 607 dec = VP8New(); 608 if (dec == NULL) { 609 return false; 610 } 611 612 VP8InitIo(&io); 613 io.data = input; 614 io.data_size = contentSize; 615 616 io.opaque = (void*) &pSrc; 617 io.put = block_put; 618 io.setup = block_setup; 619 io.teardown = block_teardown; 620 621 if (!VP8GetHeaders(dec, &io)) { 622 VP8Delete(dec); 623 return false; 624 } 625 626 if (!VP8Decode(dec, &io)) { 627 VP8Delete(dec); 628 return false; 629 } 630 631 VP8Delete(dec); 632 633 // SkDebugf("------------------- bm2 size %d [%d %d] %d\n", 634 // bm->getSize(), bm->width(), bm->height(), bm->config()); 635 return true; 636} 637 638/////////////////////////////////////////////////////////////////////////////// 639 640#include "SkTRegistry.h" 641 642static SkImageDecoder* DFactory(SkStream* stream) { 643 if (webp_parse_header(stream) <= 0) { 644 return NULL; 645 } 646 647 // Magic matches, call decoder 648 return SkNEW(SkWEBPImageDecoder); 649} 650 651SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) { 652 return DFactory(stream); 653} 654 655static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory); 656