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 SkASSERT(stream->isAtEnd()); 63 break; 64 } 65 bytesToRead -= bytesRead; 66 totalBytesRead += bytesRead; 67 SkASSERT(bytesToRead + totalBytesRead == WEBP_VP8_HEADER_SIZE); 68 } while (!stream->isAtEnd() && bytesToRead > 0); 69 70 WebPBitstreamFeatures features; 71 VP8StatusCode status = WebPGetFeatures(buffer, totalBytesRead, &features); 72 if (VP8_STATUS_OK != status) { 73 return false; // Invalid WebP file. 74 } 75 *width = features.width; 76 *height = features.height; 77 *alpha = features.has_alpha; 78 79 // sanity check for image size that's about to be decoded. 80 { 81 int64_t size = sk_64_mul(*width, *height); 82 if (!sk_64_isS32(size)) { 83 return false; 84 } 85 // now check that if we are 4-bytes per pixel, we also don't overflow 86 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { 87 return false; 88 } 89 } 90 return true; 91} 92 93class SkWEBPImageDecoder: public SkImageDecoder { 94public: 95 SkWEBPImageDecoder() { 96 fOrigWidth = 0; 97 fOrigHeight = 0; 98 fHasAlpha = 0; 99 } 100 101 Format getFormat() const override { 102 return kWEBP_Format; 103 } 104 105protected: 106 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; 107 108private: 109 /** 110 * Called when determining the output config to request to webp. 111 * If the image does not have alpha, there is no need to premultiply. 112 * If the caller wants unpremultiplied colors, that is respected. 113 */ 114 bool shouldPremultiply() const { 115 return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors(); 116 } 117 118 bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); 119 120 SkAutoTDelete<SkStream> fInputStream; 121 int fOrigWidth; 122 int fOrigHeight; 123 int fHasAlpha; 124 125 typedef SkImageDecoder INHERITED; 126}; 127 128////////////////////////////////////////////////////////////////////////// 129 130#ifdef TIME_DECODE 131 132#include "SkTime.h" 133 134class AutoTimeMillis { 135public: 136 AutoTimeMillis(const char label[]) : 137 fLabel(label) { 138 if (nullptr == fLabel) { 139 fLabel = ""; 140 } 141 fNow = SkTime::GetMSecs(); 142 } 143 ~AutoTimeMillis() { 144 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 145 } 146private: 147 const char* fLabel; 148 SkMSec fNow; 149}; 150 151#endif 152 153/////////////////////////////////////////////////////////////////////////////// 154 155// This guy exists just to aid in debugging, as it allows debuggers to just 156// set a break-point in one place to see all error exists. 157static void print_webp_error(const SkBitmap& bm, const char msg[]) { 158 SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); 159} 160 161static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) { 162 print_webp_error(bm, msg); 163 return SkImageDecoder::kFailure; // must always return kFailure 164} 165 166/////////////////////////////////////////////////////////////////////////////// 167 168static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) { 169 WEBP_CSP_MODE mode = MODE_LAST; 170 const SkColorType ct = decodedBitmap->colorType(); 171 172 if (ct == kN32_SkColorType) { 173 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 174 mode = premultiply ? MODE_bgrA : MODE_BGRA; 175 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 176 mode = premultiply ? MODE_rgbA : MODE_RGBA; 177 #else 178 #error "Skia uses BGRA or RGBA byte order" 179 #endif 180 } else if (ct == kARGB_4444_SkColorType) { 181 mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444; 182 } else if (ct == kRGB_565_SkColorType) { 183 mode = MODE_RGB_565; 184 } 185 SkASSERT(MODE_LAST != mode); 186 return mode; 187} 188 189// Incremental WebP image decoding. Reads input buffer of 64K size iteratively 190// and decodes this block to appropriate color-space as per config object. 191static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { 192 WebPIDecoder* idec = WebPIDecode(nullptr, 0, config); 193 if (nullptr == idec) { 194 WebPFreeDecBuffer(&config->output); 195 return false; 196 } 197 198 if (!stream->rewind()) { 199 SkDebugf("Failed to rewind webp stream!"); 200 return false; 201 } 202 const size_t readBufferSize = stream->hasLength() ? 203 SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; 204 SkAutoTMalloc<unsigned char> srcStorage(readBufferSize); 205 unsigned char* input = srcStorage.get(); 206 if (nullptr == input) { 207 WebPIDelete(idec); 208 WebPFreeDecBuffer(&config->output); 209 return false; 210 } 211 212 bool success = true; 213 VP8StatusCode status = VP8_STATUS_SUSPENDED; 214 do { 215 const size_t bytesRead = stream->read(input, readBufferSize); 216 if (0 == bytesRead) { 217 success = false; 218 break; 219 } 220 221 status = WebPIAppend(idec, input, bytesRead); 222 if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) { 223 success = false; 224 break; 225 } 226 } while (VP8_STATUS_OK != status); 227 srcStorage.free(); 228 WebPIDelete(idec); 229 WebPFreeDecBuffer(&config->output); 230 231 return success; 232} 233 234static bool webp_get_config_resize(WebPDecoderConfig* config, 235 SkBitmap* decodedBitmap, 236 int width, int height, bool premultiply) { 237 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply); 238 if (MODE_LAST == mode) { 239 return false; 240 } 241 242 if (0 == WebPInitDecoderConfig(config)) { 243 return false; 244 } 245 246 config->output.colorspace = mode; 247 config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); 248 config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes(); 249 config->output.u.RGBA.size = decodedBitmap->getSize(); 250 config->output.is_external_memory = 1; 251 252 if (width != decodedBitmap->width() || height != decodedBitmap->height()) { 253 config->options.use_scaling = 1; 254 config->options.scaled_width = decodedBitmap->width(); 255 config->options.scaled_height = decodedBitmap->height(); 256 } 257 258 return true; 259} 260 261bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) { 262 SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha)); 263 264 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. 265 if (fHasAlpha) { 266 if (colorType != kARGB_4444_SkColorType) { 267 colorType = kN32_SkColorType; 268 } 269 } else { 270 if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) { 271 colorType = kN32_SkColorType; 272 } 273 } 274 275 SkAlphaType alphaType = kOpaque_SkAlphaType; 276 if (SkToBool(fHasAlpha)) { 277 if (this->getRequireUnpremultipliedColors()) { 278 alphaType = kUnpremul_SkAlphaType; 279 } else { 280 alphaType = kPremul_SkAlphaType; 281 } 282 } 283 return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); 284} 285 286SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, 287 Mode mode) { 288#ifdef TIME_DECODE 289 AutoTimeMillis atm("WEBP Decode"); 290#endif 291 292 int origWidth, origHeight, hasAlpha; 293 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { 294 return kFailure; 295 } 296 this->fHasAlpha = hasAlpha; 297 298 const int sampleSize = this->getSampleSize(); 299 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); 300 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), 301 sampler.scaledHeight())) { 302 return kFailure; 303 } 304 305 // If only bounds are requested, done 306 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 307 return kSuccess; 308 } 309 310 if (!this->allocPixelRef(decodedBitmap, nullptr)) { 311 return return_failure(*decodedBitmap, "allocPixelRef"); 312 } 313 314 SkAutoLockPixels alp(*decodedBitmap); 315 316 WebPDecoderConfig config; 317 if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, 318 this->shouldPremultiply())) { 319 return kFailure; 320 } 321 322 // Decode the WebP image data stream using WebP incremental decoding. 323 return webp_idecode(stream, &config) ? kSuccess : kFailure; 324} 325 326/////////////////////////////////////////////////////////////////////////////// 327 328#include "SkUnPreMultiply.h" 329 330typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, 331 const SkPMColor* SK_RESTRICT ctable); 332 333static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 334 const SkPMColor*) { 335 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 336 for (int i = 0; i < width; ++i) { 337 const uint32_t c = *src++; 338 rgb[0] = SkGetPackedR32(c); 339 rgb[1] = SkGetPackedG32(c); 340 rgb[2] = SkGetPackedB32(c); 341 rgb += 3; 342 } 343} 344 345static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 346 const SkPMColor*) { 347 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; 348 const SkUnPreMultiply::Scale* SK_RESTRICT table = 349 SkUnPreMultiply::GetScaleTable(); 350 for (int i = 0; i < width; ++i) { 351 const uint32_t c = *src++; 352 uint8_t a = SkGetPackedA32(c); 353 uint8_t r = SkGetPackedR32(c); 354 uint8_t g = SkGetPackedG32(c); 355 uint8_t b = SkGetPackedB32(c); 356 if (0 != a && 255 != a) { 357 SkUnPreMultiply::Scale scale = table[a]; 358 r = SkUnPreMultiply::ApplyScale(scale, r); 359 g = SkUnPreMultiply::ApplyScale(scale, g); 360 b = SkUnPreMultiply::ApplyScale(scale, b); 361 } 362 rgb[0] = r; 363 rgb[1] = g; 364 rgb[2] = b; 365 rgb[3] = a; 366 rgb += 4; 367 } 368} 369 370static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 371 const SkPMColor*) { 372 const uint16_t* SK_RESTRICT src = (const uint16_t*)in; 373 for (int i = 0; i < width; ++i) { 374 const uint16_t c = *src++; 375 rgb[0] = SkPacked16ToR32(c); 376 rgb[1] = SkPacked16ToG32(c); 377 rgb[2] = SkPacked16ToB32(c); 378 rgb += 3; 379 } 380} 381 382static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 383 const SkPMColor*) { 384 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 385 for (int i = 0; i < width; ++i) { 386 const SkPMColor16 c = *src++; 387 rgb[0] = SkPacked4444ToR32(c); 388 rgb[1] = SkPacked4444ToG32(c); 389 rgb[2] = SkPacked4444ToB32(c); 390 rgb += 3; 391 } 392} 393 394static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, 395 const SkPMColor*) { 396 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; 397 const SkUnPreMultiply::Scale* SK_RESTRICT table = 398 SkUnPreMultiply::GetScaleTable(); 399 for (int i = 0; i < width; ++i) { 400 const SkPMColor16 c = *src++; 401 uint8_t a = SkPacked4444ToA32(c); 402 uint8_t r = SkPacked4444ToR32(c); 403 uint8_t g = SkPacked4444ToG32(c); 404 uint8_t b = SkPacked4444ToB32(c); 405 if (0 != a && 255 != a) { 406 SkUnPreMultiply::Scale scale = table[a]; 407 r = SkUnPreMultiply::ApplyScale(scale, r); 408 g = SkUnPreMultiply::ApplyScale(scale, g); 409 b = SkUnPreMultiply::ApplyScale(scale, b); 410 } 411 rgb[0] = r; 412 rgb[1] = g; 413 rgb[2] = b; 414 rgb[3] = a; 415 rgb += 4; 416 } 417} 418 419static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, 420 const SkPMColor* SK_RESTRICT ctable) { 421 const uint8_t* SK_RESTRICT src = (const uint8_t*)in; 422 for (int i = 0; i < width; ++i) { 423 const uint32_t c = ctable[*src++]; 424 rgb[0] = SkGetPackedR32(c); 425 rgb[1] = SkGetPackedG32(c); 426 rgb[2] = SkGetPackedB32(c); 427 rgb += 3; 428 } 429} 430 431static ScanlineImporter ChooseImporter(SkColorType ct, bool hasAlpha, int* bpp) { 432 switch (ct) { 433 case kN32_SkColorType: 434 if (hasAlpha) { 435 *bpp = 4; 436 return ARGB_8888_To_RGBA; 437 } else { 438 *bpp = 3; 439 return ARGB_8888_To_RGB; 440 } 441 case kARGB_4444_SkColorType: 442 if (hasAlpha) { 443 *bpp = 4; 444 return ARGB_4444_To_RGBA; 445 } else { 446 *bpp = 3; 447 return ARGB_4444_To_RGB; 448 } 449 case kRGB_565_SkColorType: 450 *bpp = 3; 451 return RGB_565_To_RGB; 452 case kIndex_8_SkColorType: 453 *bpp = 3; 454 return Index8_To_RGB; 455 default: 456 return nullptr; 457 } 458} 459 460static int stream_writer(const uint8_t* data, size_t data_size, 461 const WebPPicture* const picture) { 462 SkWStream* const stream = (SkWStream*)picture->custom_ptr; 463 return stream->write(data, data_size) ? 1 : 0; 464} 465 466class SkWEBPImageEncoder : public SkImageEncoder { 467protected: 468 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; 469 470private: 471 typedef SkImageEncoder INHERITED; 472}; 473 474bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, 475 int quality) { 476 const bool hasAlpha = !bm.isOpaque(); 477 int bpp = -1; 478 const ScanlineImporter scanline_import = ChooseImporter(bm.colorType(), hasAlpha, &bpp); 479 if (nullptr == scanline_import) { 480 return false; 481 } 482 if (-1 == bpp) { 483 return false; 484 } 485 486 SkAutoLockPixels alp(bm); 487 if (nullptr == bm.getPixels()) { 488 return false; 489 } 490 491 WebPConfig webp_config; 492 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) { 493 return false; 494 } 495 496 WebPPicture pic; 497 WebPPictureInit(&pic); 498 pic.width = bm.width(); 499 pic.height = bm.height(); 500 pic.writer = stream_writer; 501 pic.custom_ptr = (void*)stream; 502 503 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr; 504 const uint8_t* src = (uint8_t*)bm.getPixels(); 505 const int rgbStride = pic.width * bpp; 506 507 // Import (for each scanline) the bit-map image (in appropriate color-space) 508 // to RGB color space. 509 uint8_t* rgb = new uint8_t[rgbStride * pic.height]; 510 for (int y = 0; y < pic.height; ++y) { 511 scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, 512 pic.width, colors); 513 } 514 515 bool ok; 516 if (bpp == 3) { 517 ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride)); 518 } else { 519 ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride)); 520 } 521 delete[] rgb; 522 523 ok = ok && WebPEncode(&webp_config, &pic); 524 WebPPictureFree(&pic); 525 526 return ok; 527} 528 529 530/////////////////////////////////////////////////////////////////////////////// 531DEFINE_DECODER_CREATOR(WEBPImageDecoder); 532DEFINE_ENCODER_CREATOR(WEBPImageEncoder); 533/////////////////////////////////////////////////////////////////////////////// 534 535static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) { 536 int width, height, hasAlpha; 537 if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { 538 return nullptr; 539 } 540 541 // Magic matches, call decoder 542 return new SkWEBPImageDecoder; 543} 544 545static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) { 546 int width, height, hasAlpha; 547 if (webp_parse_header(stream, &width, &height, &hasAlpha)) { 548 return SkImageDecoder::kWEBP_Format; 549 } 550 return SkImageDecoder::kUnknown_Format; 551} 552 553static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { 554 return (SkImageEncoder::kWEBP_Type == t) ? new SkWEBPImageEncoder : nullptr; 555} 556 557static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory); 558static SkImageDecoder_FormatReg gFormatReg(get_format_webp); 559static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory); 560