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 int64_t size = sk_64_mul(*width, *height); 84 if (!sk_64_isS32(size)) { 85 return false; 86 } 87 // now check that if we are 4-bytes per pixel, we also don't overflow 88 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { 89 return false; 90 } 91 } 92 return true; 93} 94 95class SkWEBPImageDecoder: public SkImageDecoder { 96public: 97 SkWEBPImageDecoder() { 98 fInputStream = NULL; 99 fOrigWidth = 0; 100 fOrigHeight = 0; 101 fHasAlpha = 0; 102 } 103 virtual ~SkWEBPImageDecoder() { 104 SkSafeUnref(fInputStream); 105 } 106 107 virtual Format getFormat() const SK_OVERRIDE { 108 return kWEBP_Format; 109 } 110 111protected: 112 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE; 113 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE; 114 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; 115 116private: 117 /** 118 * Called when determining the output config to request to webp. 119 * If the image does not have alpha, there is no need to premultiply. 120 * If the caller wants unpremultiplied colors, that is respected. 121 */ 122 bool shouldPremultiply() const { 123 return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors(); 124 } 125 126 bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); 127 128 SkStream* fInputStream; 129 int fOrigWidth; 130 int fOrigHeight; 131 int fHasAlpha; 132 133 typedef SkImageDecoder INHERITED; 134}; 135 136////////////////////////////////////////////////////////////////////////// 137 138#ifdef TIME_DECODE 139 140#include "SkTime.h" 141 142class AutoTimeMillis { 143public: 144 AutoTimeMillis(const char label[]) : 145 fLabel(label) { 146 if (NULL == fLabel) { 147 fLabel = ""; 148 } 149 fNow = SkTime::GetMSecs(); 150 } 151 ~AutoTimeMillis() { 152 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 153 } 154private: 155 const char* fLabel; 156 SkMSec fNow; 157}; 158 159#endif 160 161/////////////////////////////////////////////////////////////////////////////// 162 163// This guy exists just to aid in debugging, as it allows debuggers to just 164// set a break-point in one place to see all error exists. 165static bool return_false(const SkBitmap& bm, const char msg[]) { 166 SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); 167 return false; // must always return false 168} 169 170static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) { 171 WEBP_CSP_MODE mode = MODE_LAST; 172 const SkColorType ct = decodedBitmap->colorType(); 173 174 if (ct == kN32_SkColorType) { 175 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 176 mode = premultiply ? MODE_bgrA : MODE_BGRA; 177 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 178 mode = premultiply ? MODE_rgbA : MODE_RGBA; 179 #else 180 #error "Skia uses BGRA or RGBA byte order" 181 #endif 182 } else if (ct == kARGB_4444_SkColorType) { 183 mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444; 184 } else if (ct == kRGB_565_SkColorType) { 185 mode = MODE_RGB_565; 186 } 187 SkASSERT(MODE_LAST != mode); 188 return mode; 189} 190 191// Incremental WebP image decoding. Reads input buffer of 64K size iteratively 192// and decodes this block to appropriate color-space as per config object. 193static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { 194 WebPIDecoder* idec = WebPIDecode(NULL, 0, config); 195 if (NULL == idec) { 196 WebPFreeDecBuffer(&config->output); 197 return false; 198 } 199 200 if (!stream->rewind()) { 201 SkDebugf("Failed to rewind webp stream!"); 202 return false; 203 } 204 const size_t readBufferSize = stream->hasLength() ? 205 SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; 206 SkAutoMalloc srcStorage(readBufferSize); 207 unsigned char* input = (uint8_t*)srcStorage.get(); 208 if (NULL == input) { 209 WebPIDelete(idec); 210 WebPFreeDecBuffer(&config->output); 211 return false; 212 } 213 214 bool success = true; 215 VP8StatusCode status = VP8_STATUS_SUSPENDED; 216 do { 217 const size_t bytesRead = stream->read(input, readBufferSize); 218 if (0 == bytesRead) { 219 success = false; 220 break; 221 } 222 223 status = WebPIAppend(idec, input, bytesRead); 224 if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) { 225 success = false; 226 break; 227 } 228 } while (VP8_STATUS_OK != status); 229 srcStorage.free(); 230 WebPIDelete(idec); 231 WebPFreeDecBuffer(&config->output); 232 233 return success; 234} 235 236static bool webp_get_config_resize(WebPDecoderConfig* config, 237 SkBitmap* decodedBitmap, 238 int width, int height, bool premultiply) { 239 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply); 240 if (MODE_LAST == mode) { 241 return false; 242 } 243 244 if (0 == WebPInitDecoderConfig(config)) { 245 return false; 246 } 247 248 config->output.colorspace = mode; 249 config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); 250 config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes(); 251 config->output.u.RGBA.size = decodedBitmap->getSize(); 252 config->output.is_external_memory = 1; 253 254 if (width != decodedBitmap->width() || height != decodedBitmap->height()) { 255 config->options.use_scaling = 1; 256 config->options.scaled_width = decodedBitmap->width(); 257 config->options.scaled_height = decodedBitmap->height(); 258 } 259 260 return true; 261} 262 263static bool webp_get_config_resize_crop(WebPDecoderConfig* config, 264 SkBitmap* decodedBitmap, 265 const SkIRect& region, bool premultiply) { 266 267 if (!webp_get_config_resize(config, decodedBitmap, region.width(), 268 region.height(), premultiply)) { 269 return false; 270 } 271 272 config->options.use_cropping = 1; 273 config->options.crop_left = region.fLeft; 274 config->options.crop_top = region.fTop; 275 config->options.crop_width = region.width(); 276 config->options.crop_height = region.height(); 277 278 return true; 279} 280 281bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) { 282 SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha)); 283 284 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. 285 if (fHasAlpha) { 286 if (colorType != kARGB_4444_SkColorType) { 287 colorType = kN32_SkColorType; 288 } 289 } else { 290 if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) { 291 colorType = kN32_SkColorType; 292 } 293 } 294 295#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER 296 if (!this->chooseFromOneChoice(colorType, width, height)) { 297 return false; 298 } 299#endif 300 301 SkAlphaType alphaType = kOpaque_SkAlphaType; 302 if (SkToBool(fHasAlpha)) { 303 if (this->getRequireUnpremultipliedColors()) { 304 alphaType = kUnpremul_SkAlphaType; 305 } else { 306 alphaType = kPremul_SkAlphaType; 307 } 308 } 309 return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); 310} 311 312bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, 313 int *width, int *height) { 314 int origWidth, origHeight, hasAlpha; 315 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 316 return false; 317 } 318 319 if (!stream->rewind()) { 320 SkDebugf("Failed to rewind webp stream!"); 321 return false; 322 } 323 324 *width = origWidth; 325 *height = origHeight; 326 327 SkRefCnt_SafeAssign(this->fInputStream, stream); 328 this->fOrigWidth = origWidth; 329 this->fOrigHeight = origHeight; 330 this->fHasAlpha = hasAlpha; 331 332 return true; 333} 334 335static bool is_config_compatible(const SkBitmap& bitmap) { 336 const SkColorType ct = bitmap.colorType(); 337 return ct == kARGB_4444_SkColorType || ct == kRGB_565_SkColorType || ct == kN32_SkColorType; 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->tryAllocPixels(); 379 if (!allocResult) { 380 return return_false(*decodedBitmap, "allocPixelRef"); 381 } 382#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER 383 } else { 384 // This is also called in setDecodeConfig in above block. 385 // i.e., when bitmap->isNull() is true. 386 if (!chooseFromOneChoice(bitmap->colorType(), width, height)) { 387 return false; 388 } 389#endif 390 } 391 392 SkAutoLockPixels alp(*bitmap); 393 WebPDecoderConfig config; 394 if (!webp_get_config_resize_crop(&config, bitmap, rect, 395 this->shouldPremultiply())) { 396 return false; 397 } 398 399 // Decode the WebP image data stream using WebP incremental decoding for 400 // the specified cropped image-region. 401 if (!webp_idecode(this->fInputStream, &config)) { 402 return false; 403 } 404 405 if (!directDecode) { 406 cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), 407 region.width(), region.height(), rect.x(), rect.y()); 408 } 409 return true; 410} 411 412bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, 413 Mode mode) { 414#ifdef TIME_DECODE 415 AutoTimeMillis atm("WEBP Decode"); 416#endif 417 418 int origWidth, origHeight, hasAlpha; 419 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 420 return false; 421 } 422 this->fHasAlpha = hasAlpha; 423 424 const int sampleSize = this->getSampleSize(); 425 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); 426 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 427 sampler.scaledHeight())) { 428 return false; 429 } 430 431 // If only bounds are requested, done 432 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 433 return true; 434 } 435 436 if (!this->allocPixelRef(decodedBitmap, NULL)) { 437 return return_false(*decodedBitmap, "allocPixelRef"); 438 } 439 440 SkAutoLockPixels alp(*decodedBitmap); 441 442 WebPDecoderConfig config; 443 if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, 444 this->shouldPremultiply())) { 445 return false; 446 } 447 448 // Decode the WebP image data stream using WebP incremental decoding. 449 return webp_idecode(stream, &config); 450} 451 452/////////////////////////////////////////////////////////////////////////////// 453 454#include "SkUnPreMultiply.h" 455 456typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, 457 const SkPMColor* SK_RESTRICT ctable); 458 459static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 460 const SkPMColor*) { 461 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 462 for (int i = 0; i < width; ++i) { 463 const uint32_t c = *src++; 464 rgb[0] = SkGetPackedR32(c); 465 rgb[1] = SkGetPackedG32(c); 466 rgb[2] = SkGetPackedB32(c); 467 rgb += 3; 468 } 469} 470 471static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 472 const SkPMColor*) { 473 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 474 const SkUnPreMultiply::Scale* SK_RESTRICT table = 475 SkUnPreMultiply::GetScaleTable(); 476 for (int i = 0; i < width; ++i) { 477 const uint32_t c = *src++; 478 uint8_t a = SkGetPackedA32(c); 479 uint8_t r = SkGetPackedR32(c); 480 uint8_t g = SkGetPackedG32(c); 481 uint8_t b = SkGetPackedB32(c); 482 if (0 != a && 255 != a) { 483 SkUnPreMultiply::Scale scale = table[a]; 484 r = SkUnPreMultiply::ApplyScale(scale, r); 485 g = SkUnPreMultiply::ApplyScale(scale, g); 486 b = SkUnPreMultiply::ApplyScale(scale, b); 487 } 488 rgb[0] = r; 489 rgb[1] = g; 490 rgb[2] = b; 491 rgb[3] = a; 492 rgb += 4; 493 } 494} 495 496static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 497 const SkPMColor*) { 498 const uint16_t* SK_RESTRICT src = (const uint16_t*)in; 499 for (int i = 0; i < width; ++i) { 500 const uint16_t c = *src++; 501 rgb[0] = SkPacked16ToR32(c); 502 rgb[1] = SkPacked16ToG32(c); 503 rgb[2] = SkPacked16ToB32(c); 504 rgb += 3; 505 } 506} 507 508static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 509 const SkPMColor*) { 510 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 511 for (int i = 0; i < width; ++i) { 512 const SkPMColor16 c = *src++; 513 rgb[0] = SkPacked4444ToR32(c); 514 rgb[1] = SkPacked4444ToG32(c); 515 rgb[2] = SkPacked4444ToB32(c); 516 rgb += 3; 517 } 518} 519 520static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 521 const SkPMColor*) { 522 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 523 const SkUnPreMultiply::Scale* SK_RESTRICT table = 524 SkUnPreMultiply::GetScaleTable(); 525 for (int i = 0; i < width; ++i) { 526 const SkPMColor16 c = *src++; 527 uint8_t a = SkPacked4444ToA32(c); 528 uint8_t r = SkPacked4444ToR32(c); 529 uint8_t g = SkPacked4444ToG32(c); 530 uint8_t b = SkPacked4444ToB32(c); 531 if (0 != a && 255 != a) { 532 SkUnPreMultiply::Scale scale = table[a]; 533 r = SkUnPreMultiply::ApplyScale(scale, r); 534 g = SkUnPreMultiply::ApplyScale(scale, g); 535 b = SkUnPreMultiply::ApplyScale(scale, b); 536 } 537 rgb[0] = r; 538 rgb[1] = g; 539 rgb[2] = b; 540 rgb[3] = a; 541 rgb += 4; 542 } 543} 544 545static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 546 const SkPMColor* SK_RESTRICT ctable) { 547 const uint8_t* SK_RESTRICT src = (const uint8_t*)in; 548 for (int i = 0; i < width; ++i) { 549 const uint32_t c = ctable[*src++]; 550 rgb[0] = SkGetPackedR32(c); 551 rgb[1] = SkGetPackedG32(c); 552 rgb[2] = SkGetPackedB32(c); 553 rgb += 3; 554 } 555} 556 557static ScanlineImporter ChooseImporter(SkColorType ct, bool hasAlpha, int* bpp) { 558 switch (ct) { 559 case kN32_SkColorType: 560 if (hasAlpha) { 561 *bpp = 4; 562 return ARGB_8888_To_RGBA; 563 } else { 564 *bpp = 3; 565 return ARGB_8888_To_RGB; 566 } 567 case kARGB_4444_SkColorType: 568 if (hasAlpha) { 569 *bpp = 4; 570 return ARGB_4444_To_RGBA; 571 } else { 572 *bpp = 3; 573 return ARGB_4444_To_RGB; 574 } 575 case kRGB_565_SkColorType: 576 *bpp = 3; 577 return RGB_565_To_RGB; 578 case kIndex_8_SkColorType: 579 *bpp = 3; 580 return Index8_To_RGB; 581 default: 582 return NULL; 583 } 584} 585 586static int stream_writer(const uint8_t* data, size_t data_size, 587 const WebPPicture* const picture) { 588 SkWStream* const stream = (SkWStream*)picture->custom_ptr; 589 return stream->write(data, data_size) ? 1 : 0; 590} 591 592class SkWEBPImageEncoder : public SkImageEncoder { 593protected: 594 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE; 595 596private: 597 typedef SkImageEncoder INHERITED; 598}; 599 600bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, 601 int quality) { 602 const bool hasAlpha = !bm.isOpaque(); 603 int bpp = -1; 604 const ScanlineImporter scanline_import = ChooseImporter(bm.colorType(), hasAlpha, &bpp); 605 if (NULL == scanline_import) { 606 return false; 607 } 608 if (-1 == bpp) { 609 return false; 610 } 611 612 SkAutoLockPixels alp(bm); 613 SkAutoLockColors ctLocker; 614 if (NULL == bm.getPixels()) { 615 return false; 616 } 617 618 WebPConfig webp_config; 619 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) { 620 return false; 621 } 622 623 WebPPicture pic; 624 WebPPictureInit(&pic); 625 pic.width = bm.width(); 626 pic.height = bm.height(); 627 pic.writer = stream_writer; 628 pic.custom_ptr = (void*)stream; 629 630 const SkPMColor* colors = ctLocker.lockColors(bm); 631 const uint8_t* src = (uint8_t*)bm.getPixels(); 632 const int rgbStride = pic.width * bpp; 633 634 // Import (for each scanline) the bit-map image (in appropriate color-space) 635 // to RGB color space. 636 uint8_t* rgb = new uint8_t[rgbStride * pic.height]; 637 for (int y = 0; y < pic.height; ++y) { 638 scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, 639 pic.width, colors); 640 } 641 642 bool ok; 643 if (bpp == 3) { 644 ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride)); 645 } else { 646 ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride)); 647 } 648 delete[] rgb; 649 650 ok = ok && WebPEncode(&webp_config, &pic); 651 WebPPictureFree(&pic); 652 653 return ok; 654} 655 656 657/////////////////////////////////////////////////////////////////////////////// 658DEFINE_DECODER_CREATOR(WEBPImageDecoder); 659DEFINE_ENCODER_CREATOR(WEBPImageEncoder); 660/////////////////////////////////////////////////////////////////////////////// 661 662static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) { 663 int width, height, hasAlpha; 664 if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { 665 return NULL; 666 } 667 668 // Magic matches, call decoder 669 return SkNEW(SkWEBPImageDecoder); 670} 671 672static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) { 673 int width, height, hasAlpha; 674 if (webp_parse_header(stream, &width, &height, &hasAlpha)) { 675 return SkImageDecoder::kWEBP_Format; 676 } 677 return SkImageDecoder::kUnknown_Format; 678} 679 680static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { 681 return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL; 682} 683 684static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory); 685static SkImageDecoder_FormatReg gFormatReg(get_format_webp); 686static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory); 687