1/* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkCodec_libpng.h" 9#include "SkCodecPriv.h" 10#include "SkColorPriv.h" 11#include "SkColorTable.h" 12#include "SkBitmap.h" 13#include "SkMath.h" 14#include "SkScanlineDecoder.h" 15#include "SkSize.h" 16#include "SkStream.h" 17#include "SkSwizzler.h" 18 19/////////////////////////////////////////////////////////////////////////////// 20// Helper macros 21/////////////////////////////////////////////////////////////////////////////// 22 23#ifndef png_jmpbuf 24# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) 25#endif 26 27/* These were dropped in libpng >= 1.4 */ 28#ifndef png_infopp_NULL 29 #define png_infopp_NULL NULL 30#endif 31 32#ifndef png_bytepp_NULL 33 #define png_bytepp_NULL NULL 34#endif 35 36#ifndef int_p_NULL 37 #define int_p_NULL NULL 38#endif 39 40#ifndef png_flush_ptr_NULL 41 #define png_flush_ptr_NULL NULL 42#endif 43 44/////////////////////////////////////////////////////////////////////////////// 45// Callback functions 46/////////////////////////////////////////////////////////////////////////////// 47 48static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { 49 SkCodecPrintf("------ png error %s\n", msg); 50 longjmp(png_jmpbuf(png_ptr), 1); 51} 52 53void sk_warning_fn(png_structp, png_const_charp msg) { 54 SkCodecPrintf("----- png warning %s\n", msg); 55} 56 57static void sk_read_fn(png_structp png_ptr, png_bytep data, 58 png_size_t length) { 59 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr)); 60 const size_t bytes = stream->read(data, length); 61 if (bytes != length) { 62 // FIXME: We want to report the fact that the stream was truncated. 63 // One way to do that might be to pass a enum to longjmp so setjmp can 64 // specify the failure. 65 png_error(png_ptr, "Read Error!"); 66 } 67} 68 69/////////////////////////////////////////////////////////////////////////////// 70// Helpers 71/////////////////////////////////////////////////////////////////////////////// 72 73class AutoCleanPng : public SkNoncopyable { 74public: 75 AutoCleanPng(png_structp png_ptr) 76 : fPng_ptr(png_ptr) 77 , fInfo_ptr(NULL) {} 78 79 ~AutoCleanPng() { 80 // fInfo_ptr will never be non-NULL unless fPng_ptr is. 81 if (fPng_ptr) { 82 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL; 83 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL); 84 } 85 } 86 87 void setInfoPtr(png_infop info_ptr) { 88 SkASSERT(NULL == fInfo_ptr); 89 fInfo_ptr = info_ptr; 90 } 91 92 void detach() { 93 fPng_ptr = NULL; 94 fInfo_ptr = NULL; 95 } 96 97private: 98 png_structp fPng_ptr; 99 png_infop fInfo_ptr; 100}; 101#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) 102 103// call only if color_type is PALETTE. Returns true if the ctable has alpha 104static bool has_transparency_in_palette(png_structp png_ptr, 105 png_infop info_ptr) { 106 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 107 return false; 108 } 109 110 png_bytep trans; 111 int num_trans; 112 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); 113 return num_trans > 0; 114} 115 116// Method for coverting to either an SkPMColor or a similarly packed 117// unpremultiplied color. 118typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); 119 120// Note: SkColorTable claims to store SkPMColors, which is not necessarily 121// the case here. 122bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount) { 123 int numPalette; 124 png_colorp palette; 125 png_bytep trans; 126 127 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { 128 return false; 129 } 130 131 // Note: These are not necessarily SkPMColors 132 SkPMColor colorStorage[256]; // worst-case storage 133 SkPMColor* colorPtr = colorStorage; 134 135 int numTrans; 136 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { 137 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL); 138 } else { 139 numTrans = 0; 140 } 141 142 // check for bad images that might make us crash 143 if (numTrans > numPalette) { 144 numTrans = numPalette; 145 } 146 147 int index = 0; 148 int transLessThanFF = 0; 149 150 // Choose which function to use to create the color table. If the final destination's 151 // colortype is unpremultiplied, the color table will store unpremultiplied colors. 152 PackColorProc proc; 153 if (premultiply) { 154 proc = &SkPreMultiplyARGB; 155 } else { 156 proc = &SkPackARGB32NoCheck; 157 } 158 for (; index < numTrans; index++) { 159 transLessThanFF |= (int)*trans - 0xFF; 160 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue); 161 palette++; 162 } 163 164 fReallyHasAlpha = transLessThanFF < 0; 165 166 for (; index < numPalette; index++) { 167 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); 168 palette++; 169 } 170 171 /* BUGGY IMAGE WORKAROUND 172 Invalid images could contain pixel values that are greater than the number of palette 173 entries. Since we use pixel values as indices into the palette this could result in reading 174 beyond the end of the palette which could leak the contents of uninitialized memory. To 175 ensure this doesn't happen, we grow the colortable to the maximum size that can be 176 addressed by the bitdepth of the image and fill it with the last palette color or black if 177 the palette is empty (really broken image). 178 */ 179 int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8)); 180 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0); 181 for (; index < colorCount; index++) { 182 *colorPtr++ = lastColor; 183 } 184 185 // Set the new color count 186 if (ctableCount != NULL) { 187 *ctableCount = colorCount; 188 } 189 190 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount))); 191 return true; 192} 193 194/////////////////////////////////////////////////////////////////////////////// 195// Creation 196/////////////////////////////////////////////////////////////////////////////// 197 198#define PNG_BYTES_TO_CHECK 4 199 200bool SkPngCodec::IsPng(SkStream* stream) { 201 char buf[PNG_BYTES_TO_CHECK]; 202 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) { 203 return false; 204 } 205 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { 206 return false; 207 } 208 return true; 209} 210 211// Reads the header, and initializes the passed in fields, if not NULL (except 212// stream, which is passed to the read function). 213// Returns true on success, in which case the caller is responsible for calling 214// png_destroy_read_struct. If it returns false, the passed in fields (except 215// stream) are unchanged. 216static bool read_header(SkStream* stream, png_structp* png_ptrp, 217 png_infop* info_ptrp, SkImageInfo* imageInfo) { 218 // The image is known to be a PNG. Decode enough to know the SkImageInfo. 219 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 220 sk_error_fn, sk_warning_fn); 221 if (!png_ptr) { 222 return false; 223 } 224 225 AutoCleanPng autoClean(png_ptr); 226 227 png_infop info_ptr = png_create_info_struct(png_ptr); 228 if (info_ptr == NULL) { 229 return false; 230 } 231 232 autoClean.setInfoPtr(info_ptr); 233 234 // FIXME: Could we use the return value of setjmp to specify the type of 235 // error? 236 if (setjmp(png_jmpbuf(png_ptr))) { 237 return false; 238 } 239 240 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); 241 242 // FIXME: This is where the old code hooks up the Peeker. Does it need to 243 // be set this early? (i.e. where are the user chunks? early in the stream, 244 // potentially?) 245 // If it does, we need to figure out a way to set it here. 246 247 // The call to png_read_info() gives us all of the information from the 248 // PNG file before the first IDAT (image data chunk). 249 png_read_info(png_ptr, info_ptr); 250 png_uint_32 origWidth, origHeight; 251 int bitDepth, colorType; 252 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, 253 &colorType, int_p_NULL, int_p_NULL, int_p_NULL); 254 255 // sanity check for size 256 { 257 int64_t size = sk_64_mul(origWidth, origHeight); 258 // now check that if we are 4-bytes per pixel, we also don't overflow 259 if (size < 0 || size > (0x7FFFFFFF >> 2)) { 260 return false; 261 } 262 } 263 264 // Tell libpng to strip 16 bit/color files down to 8 bits/color 265 if (bitDepth == 16) { 266 png_set_strip_16(png_ptr); 267 } 268#ifdef PNG_READ_PACK_SUPPORTED 269 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single 270 // byte into separate bytes (useful for paletted and grayscale images). 271 if (bitDepth < 8) { 272 png_set_packing(png_ptr); 273 } 274#endif 275 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. 276 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { 277 png_set_expand_gray_1_2_4_to_8(png_ptr); 278 } 279 280 281 // Now determine the default SkColorType and SkAlphaType. 282 SkColorType skColorType; 283 SkAlphaType skAlphaType; 284 switch (colorType) { 285 case PNG_COLOR_TYPE_PALETTE: 286 skColorType = kIndex_8_SkColorType; 287 skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ? 288 kUnpremul_SkAlphaType : kOpaque_SkAlphaType; 289 break; 290 case PNG_COLOR_TYPE_GRAY: 291 if (false) { 292 // FIXME: Is this the wrong default behavior? This means if the 293 // caller supplies the info we gave them, they'll get Alpha 8. 294 skColorType = kAlpha_8_SkColorType; 295 // FIXME: Strangely, the canonical type for Alpha 8 is Premul. 296 skAlphaType = kPremul_SkAlphaType; 297 } else { 298 skColorType = kN32_SkColorType; 299 skAlphaType = kOpaque_SkAlphaType; 300 } 301 break; 302 default: 303 // Note: This *almost* mimics the code in SkImageDecoder_libpng. 304 // has_transparency_in_palette makes an additional check - whether 305 // numTrans is greater than 0. Why does the other code not make that 306 // check? 307 if (has_transparency_in_palette(png_ptr, info_ptr) 308 || PNG_COLOR_TYPE_RGB_ALPHA == colorType 309 || PNG_COLOR_TYPE_GRAY_ALPHA == colorType) 310 { 311 skAlphaType = kUnpremul_SkAlphaType; 312 } else { 313 skAlphaType = kOpaque_SkAlphaType; 314 } 315 skColorType = kN32_SkColorType; 316 break; 317 } 318 319 { 320 // FIXME: Again, this block needs to go into onGetPixels. 321 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType; 322 323 // Unless the user is requesting A8, convert a grayscale image into RGB. 324 // GRAY_ALPHA will always be converted to RGB 325 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { 326 png_set_gray_to_rgb(png_ptr); 327 } 328 329 // Add filler (or alpha) byte (after each RGB triplet) if necessary. 330 // FIXME: It seems like we could just use RGB as the SrcConfig here. 331 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) { 332 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); 333 } 334 } 335 336 // FIXME: Also need to check for sRGB (skbug.com/3471). 337 338 if (imageInfo) { 339 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, 340 skAlphaType); 341 } 342 autoClean.detach(); 343 if (png_ptrp) { 344 *png_ptrp = png_ptr; 345 } 346 if (info_ptrp) { 347 *info_ptrp = info_ptr; 348 } 349 return true; 350} 351 352SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { 353 SkAutoTDelete<SkStream> streamDeleter(stream); 354 png_structp png_ptr; 355 png_infop info_ptr; 356 SkImageInfo imageInfo; 357 if (read_header(stream, &png_ptr, &info_ptr, &imageInfo)) { 358 return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(), png_ptr, info_ptr)); 359 } 360 return NULL; 361} 362 363#define INVALID_NUMBER_PASSES -1 364SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, 365 png_structp png_ptr, png_infop info_ptr) 366 : INHERITED(info, stream) 367 , fPng_ptr(png_ptr) 368 , fInfo_ptr(info_ptr) 369 , fSrcConfig(SkSwizzler::kUnknown) 370 , fNumberPasses(INVALID_NUMBER_PASSES) 371 , fReallyHasAlpha(false) 372{} 373 374SkPngCodec::~SkPngCodec() { 375 this->destroyReadStruct(); 376} 377 378void SkPngCodec::destroyReadStruct() { 379 if (fPng_ptr) { 380 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr 381 SkASSERT(fInfo_ptr); 382 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); 383 fPng_ptr = NULL; 384 fInfo_ptr = NULL; 385 } 386} 387 388/////////////////////////////////////////////////////////////////////////////// 389// Getting the pixels 390/////////////////////////////////////////////////////////////////////////////// 391 392static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { 393 // TODO: Support other conversions 394 if (dst.profileType() != src.profileType()) { 395 return false; 396 } 397 398 // Check for supported alpha types 399 if (src.alphaType() != dst.alphaType()) { 400 if (kOpaque_SkAlphaType == src.alphaType()) { 401 // If the source is opaque, we must decode to opaque 402 return false; 403 } 404 405 // The source is not opaque 406 switch (dst.alphaType()) { 407 case kPremul_SkAlphaType: 408 case kUnpremul_SkAlphaType: 409 // The source is not opaque, so either of these is okay 410 break; 411 default: 412 // We cannot decode a non-opaque image to opaque (or unknown) 413 return false; 414 } 415 } 416 417 // Check for supported color types 418 switch (dst.colorType()) { 419 // Allow output to kN32 from any type of input 420 case kN32_SkColorType: 421 return true; 422 default: 423 return dst.colorType() == src.colorType(); 424 } 425} 426 427SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, 428 void* dst, size_t rowBytes, 429 const Options& options, 430 SkPMColor ctable[], 431 int* ctableCount) { 432 // FIXME: Could we use the return value of setjmp to specify the type of 433 // error? 434 if (setjmp(png_jmpbuf(fPng_ptr))) { 435 SkCodecPrintf("setjmp long jump!\n"); 436 return kInvalidInput; 437 } 438 439 // FIXME: We already retrieved this information. Store it in SkPngCodec? 440 png_uint_32 origWidth, origHeight; 441 int bitDepth, pngColorType, interlaceType; 442 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, 443 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL); 444 445 fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ? 446 png_set_interlace_handling(fPng_ptr) : 1; 447 448 // Set to the default before calling decodePalette, which may change it. 449 fReallyHasAlpha = false; 450 if (PNG_COLOR_TYPE_PALETTE == pngColorType) { 451 fSrcConfig = SkSwizzler::kIndex; 452 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), bitDepth, 453 ctableCount)) { 454 return kInvalidInput; 455 } 456 } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) { 457 // Note: we check the destination, since otherwise we would have 458 // told png to upscale. 459 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType); 460 fSrcConfig = SkSwizzler::kGray; 461 } else if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { 462 fSrcConfig = SkSwizzler::kRGBX; 463 } else { 464 fSrcConfig = SkSwizzler::kRGBA; 465 } 466 467 // Copy the color table to the client if they request kIndex8 mode 468 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); 469 470 // Create the swizzler. SkPngCodec retains ownership of the color table. 471 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL; 472 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, 473 dst, rowBytes, options.fZeroInitialized)); 474 if (!fSwizzler) { 475 // FIXME: CreateSwizzler could fail for another reason. 476 return kUnimplemented; 477 } 478 479 // FIXME: Here is where we should likely insert some of the modifications 480 // made in the factory. 481 png_read_update_info(fPng_ptr, fInfo_ptr); 482 483 return kSuccess; 484} 485 486bool SkPngCodec::handleRewind() { 487 switch (this->rewindIfNeeded()) { 488 case kNoRewindNecessary_RewindState: 489 return true; 490 case kCouldNotRewind_RewindState: 491 return false; 492 case kRewound_RewindState: { 493 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header 494 // succeeds, they will be repopulated, and if it fails, they will 495 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will 496 // come through this function which will rewind and again attempt 497 // to reinitialize them. 498 this->destroyReadStruct(); 499 png_structp png_ptr; 500 png_infop info_ptr; 501 if (read_header(this->stream(), &png_ptr, &info_ptr, NULL)) { 502 fPng_ptr = png_ptr; 503 fInfo_ptr = info_ptr; 504 return true; 505 } 506 return false; 507 } 508 default: 509 SkASSERT(false); 510 return false; 511 } 512} 513 514SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, 515 size_t rowBytes, const Options& options, 516 SkPMColor ctable[], int* ctableCount) { 517 if (!this->handleRewind()) { 518 return kCouldNotRewind; 519 } 520 if (requestedInfo.dimensions() != this->getInfo().dimensions()) { 521 return kInvalidScale; 522 } 523 if (!conversion_possible(requestedInfo, this->getInfo())) { 524 return kInvalidConversion; 525 } 526 527 // Note that ctable and ctableCount may be modified if there is a color table 528 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes, 529 options, ctable, ctableCount); 530 531 if (result != kSuccess) { 532 return result; 533 } 534 535 // FIXME: Could we use the return value of setjmp to specify the type of 536 // error? 537 if (setjmp(png_jmpbuf(fPng_ptr))) { 538 SkCodecPrintf("setjmp long jump!\n"); 539 return kInvalidInput; 540 } 541 542 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); 543 SkAutoMalloc storage; 544 if (fNumberPasses > 1) { 545 const int width = requestedInfo.width(); 546 const int height = requestedInfo.height(); 547 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); 548 const size_t rowBytes = width * bpp; 549 550 storage.reset(width * height * bpp); 551 uint8_t* const base = static_cast<uint8_t*>(storage.get()); 552 553 for (int i = 0; i < fNumberPasses; i++) { 554 uint8_t* row = base; 555 for (int y = 0; y < height; y++) { 556 uint8_t* bmRow = row; 557 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1); 558 row += rowBytes; 559 } 560 } 561 562 // Now swizzle it. 563 uint8_t* row = base; 564 for (int y = 0; y < height; y++) { 565 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row)); 566 row += rowBytes; 567 } 568 } else { 569 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig)); 570 uint8_t* srcRow = static_cast<uint8_t*>(storage.get()); 571 for (int y = 0; y < requestedInfo.height(); y++) { 572 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); 573 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow)); 574 } 575 } 576 577 // FIXME: do we need substituteTranspColor? Note that we cannot do it for 578 // scanline decoding, but we could do it here. Alternatively, we could do 579 // it as we go, instead of in post-processing like SkPNGImageDecoder. 580 581 this->finish(); 582 return kSuccess; 583} 584 585void SkPngCodec::finish() { 586 if (setjmp(png_jmpbuf(fPng_ptr))) { 587 // We've already read all the scanlines. This is a success. 588 return; 589 } 590 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ 591 png_read_end(fPng_ptr, fInfo_ptr); 592} 593 594class SkPngScanlineDecoder : public SkScanlineDecoder { 595public: 596 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) 597 : INHERITED(dstInfo) 598 , fCodec(codec) 599 , fHasAlpha(false) 600 { 601 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); 602 fSrcRow = static_cast<uint8_t*>(fStorage.get()); 603 } 604 605 SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { 606 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { 607 SkCodecPrintf("setjmp long jump!\n"); 608 return SkImageGenerator::kInvalidInput; 609 } 610 611 for (int i = 0; i < count; i++) { 612 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); 613 fCodec->fSwizzler->setDstRow(dst); 614 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow)); 615 dst = SkTAddOffset<void>(dst, rowBytes); 616 } 617 return SkImageGenerator::kSuccess; 618 } 619 620 SkImageGenerator::Result onSkipScanlines(int count) override { 621 // FIXME: Could we use the return value of setjmp to specify the type of 622 // error? 623 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { 624 SkCodecPrintf("setjmp long jump!\n"); 625 return SkImageGenerator::kInvalidInput; 626 } 627 628 png_read_rows(fCodec->fPng_ptr, png_bytepp_NULL, png_bytepp_NULL, count); 629 return SkImageGenerator::kSuccess; 630 } 631 632 void onFinish() override { 633 fCodec->finish(); 634 } 635 636 bool onReallyHasAlpha() const override { return fHasAlpha; } 637 638private: 639 SkPngCodec* fCodec; // Unowned. 640 bool fHasAlpha; 641 SkAutoMalloc fStorage; 642 uint8_t* fSrcRow; 643 644 typedef SkScanlineDecoder INHERITED; 645}; 646 647SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, 648 const Options& options, SkPMColor ctable[], int* ctableCount) { 649 if (!this->handleRewind()) { 650 return NULL; 651 } 652 653 // Check to see if scaling was requested. 654 if (dstInfo.dimensions() != this->getInfo().dimensions()) { 655 return NULL; 656 } 657 658 if (!conversion_possible(dstInfo, this->getInfo())) { 659 SkCodecPrintf("no conversion possible\n"); 660 return NULL; 661 } 662 663 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed, 664 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to 665 // be at least dstInfo.minRowBytes. 666 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable, 667 ctableCount) != kSuccess) { 668 SkCodecPrintf("failed to initialize the swizzler.\n"); 669 return NULL; 670 } 671 672 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); 673 if (fNumberPasses > 1) { 674 // We cannot efficiently do scanline decoding. 675 return NULL; 676 } 677 678 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this)); 679} 680 681