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 Result 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 void print_webp_error(const SkBitmap& bm, const char msg[]) { 166 SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); 167} 168 169static bool return_false(const SkBitmap& bm, const char msg[]) { 170 print_webp_error(bm, msg); 171 return false; // must always return false 172} 173 174static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) { 175 print_webp_error(bm, msg); 176 return SkImageDecoder::kFailure; // must always return kFailure 177} 178 179/////////////////////////////////////////////////////////////////////////////// 180 181static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) { 182 WEBP_CSP_MODE mode = MODE_LAST; 183 const SkColorType ct = decodedBitmap->colorType(); 184 185 if (ct == kN32_SkColorType) { 186 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 187 mode = premultiply ? MODE_bgrA : MODE_BGRA; 188 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 189 mode = premultiply ? MODE_rgbA : MODE_RGBA; 190 #else 191 #error "Skia uses BGRA or RGBA byte order" 192 #endif 193 } else if (ct == kARGB_4444_SkColorType) { 194 mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444; 195 } else if (ct == kRGB_565_SkColorType) { 196 mode = MODE_RGB_565; 197 } 198 SkASSERT(MODE_LAST != mode); 199 return mode; 200} 201 202// Incremental WebP image decoding. Reads input buffer of 64K size iteratively 203// and decodes this block to appropriate color-space as per config object. 204static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { 205 WebPIDecoder* idec = WebPIDecode(NULL, 0, config); 206 if (NULL == idec) { 207 WebPFreeDecBuffer(&config->output); 208 return false; 209 } 210 211 if (!stream->rewind()) { 212 SkDebugf("Failed to rewind webp stream!"); 213 return false; 214 } 215 const size_t readBufferSize = stream->hasLength() ? 216 SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; 217 SkAutoMalloc srcStorage(readBufferSize); 218 unsigned char* input = (uint8_t*)srcStorage.get(); 219 if (NULL == input) { 220 WebPIDelete(idec); 221 WebPFreeDecBuffer(&config->output); 222 return false; 223 } 224 225 bool success = true; 226 VP8StatusCode status = VP8_STATUS_SUSPENDED; 227 do { 228 const size_t bytesRead = stream->read(input, readBufferSize); 229 if (0 == bytesRead) { 230 success = false; 231 break; 232 } 233 234 status = WebPIAppend(idec, input, bytesRead); 235 if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) { 236 success = false; 237 break; 238 } 239 } while (VP8_STATUS_OK != status); 240 srcStorage.free(); 241 WebPIDelete(idec); 242 WebPFreeDecBuffer(&config->output); 243 244 return success; 245} 246 247static bool webp_get_config_resize(WebPDecoderConfig* config, 248 SkBitmap* decodedBitmap, 249 int width, int height, bool premultiply) { 250 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply); 251 if (MODE_LAST == mode) { 252 return false; 253 } 254 255 if (0 == WebPInitDecoderConfig(config)) { 256 return false; 257 } 258 259 config->output.colorspace = mode; 260 config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); 261 config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes(); 262 config->output.u.RGBA.size = decodedBitmap->getSize(); 263 config->output.is_external_memory = 1; 264 265 if (width != decodedBitmap->width() || height != decodedBitmap->height()) { 266 config->options.use_scaling = 1; 267 config->options.scaled_width = decodedBitmap->width(); 268 config->options.scaled_height = decodedBitmap->height(); 269 } 270 271 return true; 272} 273 274static bool webp_get_config_resize_crop(WebPDecoderConfig* config, 275 SkBitmap* decodedBitmap, 276 const SkIRect& region, bool premultiply) { 277 278 if (!webp_get_config_resize(config, decodedBitmap, region.width(), 279 region.height(), premultiply)) { 280 return false; 281 } 282 283 config->options.use_cropping = 1; 284 config->options.crop_left = region.fLeft; 285 config->options.crop_top = region.fTop; 286 config->options.crop_width = region.width(); 287 config->options.crop_height = region.height(); 288 289 return true; 290} 291 292bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) { 293 SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha)); 294 295 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. 296 if (fHasAlpha) { 297 if (colorType != kARGB_4444_SkColorType) { 298 colorType = kN32_SkColorType; 299 } 300 } else { 301 if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) { 302 colorType = kN32_SkColorType; 303 } 304 } 305 306#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER 307 if (!this->chooseFromOneChoice(colorType, width, height)) { 308 return false; 309 } 310#endif 311 312 SkAlphaType alphaType = kOpaque_SkAlphaType; 313 if (SkToBool(fHasAlpha)) { 314 if (this->getRequireUnpremultipliedColors()) { 315 alphaType = kUnpremul_SkAlphaType; 316 } else { 317 alphaType = kPremul_SkAlphaType; 318 } 319 } 320 return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); 321} 322 323bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, 324 int *width, int *height) { 325 int origWidth, origHeight, hasAlpha; 326 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 327 return false; 328 } 329 330 if (!stream->rewind()) { 331 SkDebugf("Failed to rewind webp stream!"); 332 return false; 333 } 334 335 *width = origWidth; 336 *height = origHeight; 337 338 SkRefCnt_SafeAssign(this->fInputStream, stream); 339 this->fOrigWidth = origWidth; 340 this->fOrigHeight = origHeight; 341 this->fHasAlpha = hasAlpha; 342 343 return true; 344} 345 346static bool is_config_compatible(const SkBitmap& bitmap) { 347 const SkColorType ct = bitmap.colorType(); 348 return ct == kARGB_4444_SkColorType || ct == kRGB_565_SkColorType || ct == kN32_SkColorType; 349} 350 351bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap, 352 const SkIRect& region) { 353 SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight); 354 355 if (!rect.intersect(region)) { 356 // If the requested region is entirely outsides the image, return false 357 return false; 358 } 359 360 const int sampleSize = this->getSampleSize(); 361 SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); 362 const int width = sampler.scaledWidth(); 363 const int height = sampler.scaledHeight(); 364 365 // The image can be decoded directly to decodedBitmap if 366 // 1. the region is within the image range 367 // 2. bitmap's config is compatible 368 // 3. bitmap's size is same as the required region (after sampled) 369 bool directDecode = (rect == region) && 370 (decodedBitmap->isNull() || 371 (is_config_compatible(*decodedBitmap) && 372 (decodedBitmap->width() == width) && 373 (decodedBitmap->height() == height))); 374 375 SkBitmap tmpBitmap; 376 SkBitmap *bitmap = decodedBitmap; 377 378 if (!directDecode) { 379 bitmap = &tmpBitmap; 380 } 381 382 if (bitmap->isNull()) { 383 if (!setDecodeConfig(bitmap, width, height)) { 384 return false; 385 } 386 // alloc from native heap if it is a temp bitmap. (prevent GC) 387 bool allocResult = (bitmap == decodedBitmap) 388 ? allocPixelRef(bitmap, NULL) 389 : bitmap->allocPixels(); 390 if (!allocResult) { 391 return return_false(*decodedBitmap, "allocPixelRef"); 392 } 393#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER 394 } else { 395 // This is also called in setDecodeConfig in above block. 396 // i.e., when bitmap->isNull() is true. 397 if (!chooseFromOneChoice(bitmap->colorType(), width, height)) { 398 return false; 399 } 400#endif 401 } 402 403 SkAutoLockPixels alp(*bitmap); 404 WebPDecoderConfig config; 405 if (!webp_get_config_resize_crop(&config, bitmap, rect, 406 this->shouldPremultiply())) { 407 return false; 408 } 409 410 // Decode the WebP image data stream using WebP incremental decoding for 411 // the specified cropped image-region. 412 if (!webp_idecode(this->fInputStream, &config)) { 413 return false; 414 } 415 416 if (!directDecode) { 417 cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), 418 region.width(), region.height(), rect.x(), rect.y()); 419 } 420 return true; 421} 422 423SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, 424 Mode mode) { 425#ifdef TIME_DECODE 426 AutoTimeMillis atm("WEBP Decode"); 427#endif 428 429 int origWidth, origHeight, hasAlpha; 430 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 431 return kFailure; 432 } 433 this->fHasAlpha = hasAlpha; 434 435 const int sampleSize = this->getSampleSize(); 436 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); 437 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 438 sampler.scaledHeight())) { 439 return kFailure; 440 } 441 442 // If only bounds are requested, done 443 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 444 return kSuccess; 445 } 446 447 if (!this->allocPixelRef(decodedBitmap, NULL)) { 448 return return_failure(*decodedBitmap, "allocPixelRef"); 449 } 450 451 SkAutoLockPixels alp(*decodedBitmap); 452 453 WebPDecoderConfig config; 454 if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, 455 this->shouldPremultiply())) { 456 return kFailure; 457 } 458 459 // Decode the WebP image data stream using WebP incremental decoding. 460 return webp_idecode(stream, &config) ? kSuccess : kFailure; 461} 462 463/////////////////////////////////////////////////////////////////////////////// 464 465#include "SkUnPreMultiply.h" 466 467typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, 468 const SkPMColor* SK_RESTRICT ctable); 469 470static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 471 const SkPMColor*) { 472 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 473 for (int i = 0; i < width; ++i) { 474 const uint32_t c = *src++; 475 rgb[0] = SkGetPackedR32(c); 476 rgb[1] = SkGetPackedG32(c); 477 rgb[2] = SkGetPackedB32(c); 478 rgb += 3; 479 } 480} 481 482static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 483 const SkPMColor*) { 484 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 485 const SkUnPreMultiply::Scale* SK_RESTRICT table = 486 SkUnPreMultiply::GetScaleTable(); 487 for (int i = 0; i < width; ++i) { 488 const uint32_t c = *src++; 489 uint8_t a = SkGetPackedA32(c); 490 uint8_t r = SkGetPackedR32(c); 491 uint8_t g = SkGetPackedG32(c); 492 uint8_t b = SkGetPackedB32(c); 493 if (0 != a && 255 != a) { 494 SkUnPreMultiply::Scale scale = table[a]; 495 r = SkUnPreMultiply::ApplyScale(scale, r); 496 g = SkUnPreMultiply::ApplyScale(scale, g); 497 b = SkUnPreMultiply::ApplyScale(scale, b); 498 } 499 rgb[0] = r; 500 rgb[1] = g; 501 rgb[2] = b; 502 rgb[3] = a; 503 rgb += 4; 504 } 505} 506 507static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 508 const SkPMColor*) { 509 const uint16_t* SK_RESTRICT src = (const uint16_t*)in; 510 for (int i = 0; i < width; ++i) { 511 const uint16_t c = *src++; 512 rgb[0] = SkPacked16ToR32(c); 513 rgb[1] = SkPacked16ToG32(c); 514 rgb[2] = SkPacked16ToB32(c); 515 rgb += 3; 516 } 517} 518 519static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 520 const SkPMColor*) { 521 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 522 for (int i = 0; i < width; ++i) { 523 const SkPMColor16 c = *src++; 524 rgb[0] = SkPacked4444ToR32(c); 525 rgb[1] = SkPacked4444ToG32(c); 526 rgb[2] = SkPacked4444ToB32(c); 527 rgb += 3; 528 } 529} 530 531static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 532 const SkPMColor*) { 533 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 534 const SkUnPreMultiply::Scale* SK_RESTRICT table = 535 SkUnPreMultiply::GetScaleTable(); 536 for (int i = 0; i < width; ++i) { 537 const SkPMColor16 c = *src++; 538 uint8_t a = SkPacked4444ToA32(c); 539 uint8_t r = SkPacked4444ToR32(c); 540 uint8_t g = SkPacked4444ToG32(c); 541 uint8_t b = SkPacked4444ToB32(c); 542 if (0 != a && 255 != a) { 543 SkUnPreMultiply::Scale scale = table[a]; 544 r = SkUnPreMultiply::ApplyScale(scale, r); 545 g = SkUnPreMultiply::ApplyScale(scale, g); 546 b = SkUnPreMultiply::ApplyScale(scale, b); 547 } 548 rgb[0] = r; 549 rgb[1] = g; 550 rgb[2] = b; 551 rgb[3] = a; 552 rgb += 4; 553 } 554} 555 556static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 557 const SkPMColor* SK_RESTRICT ctable) { 558 const uint8_t* SK_RESTRICT src = (const uint8_t*)in; 559 for (int i = 0; i < width; ++i) { 560 const uint32_t c = ctable[*src++]; 561 rgb[0] = SkGetPackedR32(c); 562 rgb[1] = SkGetPackedG32(c); 563 rgb[2] = SkGetPackedB32(c); 564 rgb += 3; 565 } 566} 567 568static ScanlineImporter ChooseImporter(SkColorType ct, bool hasAlpha, int* bpp) { 569 switch (ct) { 570 case kN32_SkColorType: 571 if (hasAlpha) { 572 *bpp = 4; 573 return ARGB_8888_To_RGBA; 574 } else { 575 *bpp = 3; 576 return ARGB_8888_To_RGB; 577 } 578 case kARGB_4444_SkColorType: 579 if (hasAlpha) { 580 *bpp = 4; 581 return ARGB_4444_To_RGBA; 582 } else { 583 *bpp = 3; 584 return ARGB_4444_To_RGB; 585 } 586 case kRGB_565_SkColorType: 587 *bpp = 3; 588 return RGB_565_To_RGB; 589 case kIndex_8_SkColorType: 590 *bpp = 3; 591 return Index8_To_RGB; 592 default: 593 return NULL; 594 } 595} 596 597static int stream_writer(const uint8_t* data, size_t data_size, 598 const WebPPicture* const picture) { 599 SkWStream* const stream = (SkWStream*)picture->custom_ptr; 600 return stream->write(data, data_size) ? 1 : 0; 601} 602 603class SkWEBPImageEncoder : public SkImageEncoder { 604protected: 605 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE; 606 607private: 608 typedef SkImageEncoder INHERITED; 609}; 610 611bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, 612 int quality) { 613 const bool hasAlpha = !bm.isOpaque(); 614 int bpp = -1; 615 const ScanlineImporter scanline_import = ChooseImporter(bm.colorType(), hasAlpha, &bpp); 616 if (NULL == scanline_import) { 617 return false; 618 } 619 if (-1 == bpp) { 620 return false; 621 } 622 623 SkAutoLockPixels alp(bm); 624 SkAutoLockColors ctLocker; 625 if (NULL == bm.getPixels()) { 626 return false; 627 } 628 629 WebPConfig webp_config; 630 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) { 631 return false; 632 } 633 634 WebPPicture pic; 635 WebPPictureInit(&pic); 636 pic.width = bm.width(); 637 pic.height = bm.height(); 638 pic.writer = stream_writer; 639 pic.custom_ptr = (void*)stream; 640 641 const SkPMColor* colors = ctLocker.lockColors(bm); 642 const uint8_t* src = (uint8_t*)bm.getPixels(); 643 const int rgbStride = pic.width * bpp; 644 645 // Import (for each scanline) the bit-map image (in appropriate color-space) 646 // to RGB color space. 647 uint8_t* rgb = new uint8_t[rgbStride * pic.height]; 648 for (int y = 0; y < pic.height; ++y) { 649 scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, 650 pic.width, colors); 651 } 652 653 bool ok; 654 if (bpp == 3) { 655 ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride)); 656 } else { 657 ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride)); 658 } 659 delete[] rgb; 660 661 ok = ok && WebPEncode(&webp_config, &pic); 662 WebPPictureFree(&pic); 663 664 return ok; 665} 666 667 668/////////////////////////////////////////////////////////////////////////////// 669DEFINE_DECODER_CREATOR(WEBPImageDecoder); 670DEFINE_ENCODER_CREATOR(WEBPImageEncoder); 671/////////////////////////////////////////////////////////////////////////////// 672 673static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) { 674 int width, height, hasAlpha; 675 if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { 676 return NULL; 677 } 678 679 // Magic matches, call decoder 680 return SkNEW(SkWEBPImageDecoder); 681} 682 683static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) { 684 int width, height, hasAlpha; 685 if (webp_parse_header(stream, &width, &height, &hasAlpha)) { 686 return SkImageDecoder::kWEBP_Format; 687 } 688 return SkImageDecoder::kUnknown_Format; 689} 690 691static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { 692 return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL; 693} 694 695static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory); 696static SkImageDecoder_FormatReg gFormatReg(get_format_webp); 697static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory); 698