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 "include/core/SkBitmap.h" 9#include "include/core/SkColorSpace.h" 10#include "include/core/SkMath.h" 11#include "include/core/SkPoint3.h" 12#include "include/core/SkSize.h" 13#include "include/core/SkStream.h" 14#include "include/private/SkColorData.h" 15#include "include/private/SkMacros.h" 16#include "include/private/SkTemplates.h" 17#include "src/codec/SkCodecPriv.h" 18#include "src/codec/SkColorTable.h" 19#include "src/codec/SkPngCodec.h" 20#include "src/codec/SkPngPriv.h" 21#include "src/codec/SkSwizzler.h" 22#include "src/core/SkOpts.h" 23 24#include "png.h" 25#include <algorithm> 26 27#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 28 #include "include/android/SkAndroidFrameworkUtils.h" 29#endif 30 31// This warning triggers false postives way too often in here. 32#if defined(__GNUC__) && !defined(__clang__) 33 #pragma GCC diagnostic ignored "-Wclobbered" 34#endif 35 36// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6 37#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x) 38 39/////////////////////////////////////////////////////////////////////////////// 40// Callback functions 41/////////////////////////////////////////////////////////////////////////////// 42 43// When setjmp is first called, it returns 0, meaning longjmp was not called. 44constexpr int kSetJmpOkay = 0; 45// An error internal to libpng. 46constexpr int kPngError = 1; 47// Passed to longjmp when we have decoded as many lines as we need. 48constexpr int kStopDecoding = 2; 49 50static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { 51 SkCodecPrintf("------ png error %s\n", msg); 52 longjmp(PNG_JMPBUF(png_ptr), kPngError); 53} 54 55void sk_warning_fn(png_structp, png_const_charp msg) { 56 SkCodecPrintf("----- png warning %s\n", msg); 57} 58 59#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 60static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { 61 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr); 62 // readChunk() returning true means continue decoding 63 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1; 64} 65#endif 66 67/////////////////////////////////////////////////////////////////////////////// 68// Helpers 69/////////////////////////////////////////////////////////////////////////////// 70 71class AutoCleanPng : public SkNoncopyable { 72public: 73 /* 74 * This class does not take ownership of stream or reader, but if codecPtr 75 * is non-NULL, and decodeBounds succeeds, it will have created a new 76 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as 77 * the png_ptr and info_ptr. 78 */ 79 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader, 80 SkCodec** codecPtr) 81 : fPng_ptr(png_ptr) 82 , fInfo_ptr(nullptr) 83 , fStream(stream) 84 , fChunkReader(reader) 85 , fOutCodec(codecPtr) 86 {} 87 88 ~AutoCleanPng() { 89 // fInfo_ptr will never be non-nullptr unless fPng_ptr is. 90 if (fPng_ptr) { 91 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; 92 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr); 93 } 94 } 95 96 void setInfoPtr(png_infop info_ptr) { 97 SkASSERT(nullptr == fInfo_ptr); 98 fInfo_ptr = info_ptr; 99 } 100 101 /** 102 * Reads enough of the input stream to decode the bounds. 103 * @return false if the stream is not a valid PNG (or too short). 104 * true if it read enough of the stream to determine the bounds. 105 * In the latter case, the stream may have been read beyond the 106 * point to determine the bounds, and the png_ptr will have saved 107 * any extra data. Further, if the codecPtr supplied to the 108 * constructor was not NULL, it will now point to a new SkCodec, 109 * which owns (or refs, in the case of the SkPngChunkReader) the 110 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are 111 * unowned, and it is up to the caller to destroy them. 112 */ 113 bool decodeBounds(); 114 115private: 116 png_structp fPng_ptr; 117 png_infop fInfo_ptr; 118 SkStream* fStream; 119 SkPngChunkReader* fChunkReader; 120 SkCodec** fOutCodec; 121 122 void infoCallback(size_t idatLength); 123 124 void releasePngPtrs() { 125 fPng_ptr = nullptr; 126 fInfo_ptr = nullptr; 127 } 128}; 129 130static inline bool is_chunk(const png_byte* chunk, const char* tag) { 131 return memcmp(chunk + 4, tag, 4) == 0; 132} 133 134static inline bool process_data(png_structp png_ptr, png_infop info_ptr, 135 SkStream* stream, void* buffer, size_t bufferSize, size_t length) { 136 while (length > 0) { 137 const size_t bytesToProcess = std::min(bufferSize, length); 138 const size_t bytesRead = stream->read(buffer, bytesToProcess); 139 png_process_data(png_ptr, info_ptr, (png_bytep) buffer, bytesRead); 140 if (bytesRead < bytesToProcess) { 141 return false; 142 } 143 length -= bytesToProcess; 144 } 145 return true; 146} 147 148bool AutoCleanPng::decodeBounds() { 149 if (setjmp(PNG_JMPBUF(fPng_ptr))) { 150 return false; 151 } 152 153 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr); 154 155 // Arbitrary buffer size, though note that it matches (below) 156 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of 157 // the PNG header? 158 constexpr size_t kBufferSize = 4096; 159 char buffer[kBufferSize]; 160 161 { 162 // Parse the signature. 163 if (fStream->read(buffer, 8) < 8) { 164 return false; 165 } 166 167 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, 8); 168 } 169 170 while (true) { 171 // Parse chunk length and type. 172 if (fStream->read(buffer, 8) < 8) { 173 // We have read to the end of the input without decoding bounds. 174 break; 175 } 176 177 png_byte* chunk = reinterpret_cast<png_byte*>(buffer); 178 const size_t length = png_get_uint_32(chunk); 179 180 if (is_chunk(chunk, "IDAT")) { 181 this->infoCallback(length); 182 return true; 183 } 184 185 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8); 186 // Process the full chunk + CRC. 187 if (!process_data(fPng_ptr, fInfo_ptr, fStream, buffer, kBufferSize, length + 4)) { 188 return false; 189 } 190 } 191 192 return false; 193} 194 195bool SkPngCodec::processData() { 196 switch (setjmp(PNG_JMPBUF(fPng_ptr))) { 197 case kPngError: 198 // There was an error. Stop processing data. 199 // FIXME: Do we need to discard png_ptr? 200 return false; 201 case kStopDecoding: 202 // We decoded all the lines we want. 203 return true; 204 case kSetJmpOkay: 205 // Everything is okay. 206 break; 207 default: 208 // No other values should be passed to longjmp. 209 SkASSERT(false); 210 } 211 212 // Arbitrary buffer size 213#ifdef TURBO_PNG_MULTY_LINE_OPT 214 // OH ISSUE: png optimize 215 constexpr size_t kBufferSize = 65536; // 65536, expand buffer to improve performance 216#else 217 constexpr size_t kBufferSize = 4096; 218#endif 219 char buffer[kBufferSize]; 220 221 bool iend = false; 222 while (true) { 223 size_t length; 224 if (fDecodedIdat) { 225 // Parse chunk length and type. 226 if (this->stream()->read(buffer, 8) < 8) { 227 break; 228 } 229 230 png_byte* chunk = reinterpret_cast<png_byte*>(buffer); 231 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8); 232 if (is_chunk(chunk, "IEND")) { 233 iend = true; 234 } 235 236 length = png_get_uint_32(chunk); 237 } else { 238 length = fIdatLength; 239 png_byte idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'}; 240 png_save_uint_32(idat, length); 241 png_process_data(fPng_ptr, fInfo_ptr, idat, 8); 242 fDecodedIdat = true; 243 } 244 245 // Process the full chunk + CRC. 246 if (!process_data(fPng_ptr, fInfo_ptr, this->stream(), buffer, kBufferSize, length + 4) 247 || iend) { 248 break; 249 } 250 } 251 252 return true; 253} 254 255static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType; 256 257static inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) { 258 return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha; 259} 260 261// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here. 262bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) { 263 264 int numColors; 265 png_color* palette; 266 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { 267 return false; 268 } 269 270 // Contents depend on tableColorType and our choice of if/when to premultiply: 271 // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA } 272 SkPMColor colorTable[256]; 273 SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType(); 274 275 png_bytep alphas; 276 int numColorsWithAlpha = 0; 277 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) { 278 bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha()); 279 280 // Choose which function to use to create the color table. If the final destination's 281 // colortype is unpremultiplied, the color table will store unpremultiplied colors. 282 PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType); 283 284 for (int i = 0; i < numColorsWithAlpha; i++) { 285 // We don't have a function in SkOpts that combines a set of alphas with a set 286 // of RGBs. We could write one, but it's hardly worth it, given that this 287 // is such a small fraction of the total decode time. 288 colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue); 289 palette++; 290 } 291 } 292 293 if (numColorsWithAlpha < numColors) { 294 // The optimized code depends on a 3-byte png_color struct with the colors 295 // in RGB order. These checks make sure it is safe to use. 296 static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken."); 297#ifdef SK_DEBUG 298 SkASSERT(&palette->red < &palette->green); 299 SkASSERT(&palette->green < &palette->blue); 300#endif 301 302 if (is_rgba(tableColorType)) { 303 SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, (const uint8_t*)palette, 304 numColors - numColorsWithAlpha); 305 } else { 306 SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, (const uint8_t*)palette, 307 numColors - numColorsWithAlpha); 308 } 309 } 310 311 if (this->colorXform() && !this->xformOnDecode()) { 312 this->applyColorXform(colorTable, colorTable, numColors); 313 } 314 315 // Pad the color table with the last color in the table (or black) in the case that 316 // invalid pixel indices exceed the number of colors in the table. 317 const int maxColors = 1 << fBitDepth; 318 if (numColors < maxColors) { 319 SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK; 320 sk_memset32(colorTable + numColors, lastColor, maxColors - numColors); 321 } 322 323 fColorTable.reset(new SkColorTable(colorTable, maxColors)); 324 return true; 325} 326 327/////////////////////////////////////////////////////////////////////////////// 328// Creation 329/////////////////////////////////////////////////////////////////////////////// 330 331bool SkPngCodec::IsPng(const void* buf, size_t bytesRead) { 332 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead); 333} 334 335#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) 336 337static float png_fixed_point_to_float(png_fixed_point x) { 338 // We multiply by the same factor that libpng used to convert 339 // fixed point -> double. Since we want floats, we choose to 340 // do the conversion ourselves rather than convert 341 // fixed point -> double -> float. 342 return ((float) x) * 0.00001f; 343} 344 345static float png_inverted_fixed_point_to_float(png_fixed_point x) { 346 // This is necessary because the gAMA chunk actually stores 1/gamma. 347 return 1.0f / png_fixed_point_to_float(x); 348} 349 350#endif // LIBPNG >= 1.6 351 352// If there is no color profile information, it will use sRGB. 353std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(png_structp png_ptr, 354 png_infop info_ptr) { 355 356#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) 357 // First check for an ICC profile 358 png_bytep profile; 359 png_uint_32 length; 360 // The below variables are unused, however, we need to pass them in anyway or 361 // png_get_iCCP() will return nothing. 362 // Could knowing the |name| of the profile ever be interesting? Maybe for debugging? 363 png_charp name; 364 // The |compression| is uninteresting since: 365 // (1) libpng has already decompressed the profile for us. 366 // (2) "deflate" is the only mode of decompression that libpng supports. 367 int compression; 368 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile, 369 &length)) { 370 auto data = SkData::MakeWithCopy(profile, length); 371 return SkEncodedInfo::ICCProfile::Make(std::move(data)); 372 } 373 374 // Second, check for sRGB. 375 // Note that Blink does this first. This code checks ICC first, with the thinking that 376 // an image has both truly wants the potentially more specific ICC chunk, with sRGB as a 377 // backup in case the decoder does not support full color management. 378 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { 379 // sRGB chunks also store a rendering intent: Absolute, Relative, 380 // Perceptual, and Saturation. 381 // FIXME (scroggo): Extract this information from the sRGB chunk once 382 // we are able to handle this information in 383 // skcms_ICCProfile 384 return nullptr; 385 } 386 387 // Default to SRGB gamut. 388 skcms_Matrix3x3 toXYZD50 = skcms_sRGB_profile()->toXYZD50; 389 // Next, check for chromaticities. 390 png_fixed_point chrm[8]; 391 png_fixed_point gamma; 392 if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4], 393 &chrm[5], &chrm[6], &chrm[7])) 394 { 395 float rx = png_fixed_point_to_float(chrm[2]); 396 float ry = png_fixed_point_to_float(chrm[3]); 397 float gx = png_fixed_point_to_float(chrm[4]); 398 float gy = png_fixed_point_to_float(chrm[5]); 399 float bx = png_fixed_point_to_float(chrm[6]); 400 float by = png_fixed_point_to_float(chrm[7]); 401 float wx = png_fixed_point_to_float(chrm[0]); 402 float wy = png_fixed_point_to_float(chrm[1]); 403 404 skcms_Matrix3x3 tmp; 405 if (skcms_PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, &tmp)) { 406 toXYZD50 = tmp; 407 } else { 408 // Note that Blink simply returns nullptr in this case. We'll fall 409 // back to srgb. 410 } 411 } 412 413 skcms_TransferFunction fn; 414 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { 415 fn.a = 1.0f; 416 fn.b = fn.c = fn.d = fn.e = fn.f = 0.0f; 417 fn.g = png_inverted_fixed_point_to_float(gamma); 418 } else { 419 // Default to sRGB gamma if the image has color space information, 420 // but does not specify gamma. 421 // Note that Blink would again return nullptr in this case. 422 fn = *skcms_sRGB_TransferFunction(); 423 } 424 425 skcms_ICCProfile skcmsProfile; 426 skcms_Init(&skcmsProfile); 427 skcms_SetTransferFunction(&skcmsProfile, &fn); 428 skcms_SetXYZD50(&skcmsProfile, &toXYZD50); 429 430 return SkEncodedInfo::ICCProfile::Make(skcmsProfile); 431#else // LIBPNG >= 1.6 432 return nullptr; 433#endif // LIBPNG >= 1.6 434} 435 436void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) { 437 switch (fXformMode) { 438 case kSwizzleOnly_XformMode: 439 break; 440 case kColorOnly_XformMode: 441 // Intentional fall through. A swizzler hasn't been created yet, but one will 442 // be created later if we are sampling. We'll go ahead and allocate 443 // enough memory to swizzle if necessary. 444 case kSwizzleColor_XformMode: { 445 const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel(); 446 447 // If we have more than 8-bits (per component) of precision, we will keep that 448 // extra precision. Otherwise, we will swizzle to RGBA_8888 before transforming. 449 const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4; 450 const size_t colorXformBytes = dstInfo.width() * bytesPerPixel; 451 fStorage.reset(colorXformBytes); 452 fColorXformSrcRow = fStorage.get(); 453 break; 454 } 455 } 456} 457 458static skcms_PixelFormat png_select_xform_format(const SkEncodedInfo& info) { 459 // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA. 460 if (16 == info.bitsPerComponent()) { 461 if (SkEncodedInfo::kRGBA_Color == info.color()) { 462 return skcms_PixelFormat_RGBA_16161616BE; 463 } else if (SkEncodedInfo::kRGB_Color == info.color()) { 464 return skcms_PixelFormat_RGB_161616BE; 465 } 466 } else if (SkEncodedInfo::kGray_Color == info.color()) { 467 return skcms_PixelFormat_G_8; 468 } 469 470 return skcms_PixelFormat_RGBA_8888; 471} 472 473void SkPngCodec::applyXformRow(void* dst, const void* src) { 474 switch (fXformMode) { 475 case kSwizzleOnly_XformMode: 476 fSwizzler->swizzle(dst, (const uint8_t*) src); 477 break; 478 case kColorOnly_XformMode: 479 this->applyColorXform(dst, src, fXformWidth); 480 break; 481 case kSwizzleColor_XformMode: 482 fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src); 483 this->applyColorXform(dst, fColorXformSrcRow, fXformWidth); 484 break; 485 } 486} 487 488static SkCodec::Result log_and_return_error(bool success) { 489 if (success) return SkCodec::kIncompleteInput; 490#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 491 SkAndroidFrameworkUtils::SafetyNetLog("117838472"); 492#endif 493 return SkCodec::kErrorInInput; 494} 495 496class SkPngNormalDecoder : public SkPngCodec { 497public: 498 SkPngNormalDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream, 499 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth) 500 : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth) 501 , fRowsWrittenToOutput(0) 502 , fDst(nullptr) 503 , fRowBytes(0) 504 , fFirstRow(0) 505 , fLastRow(0) 506 {} 507 508 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) { 509 GetDecoder(png_ptr)->allRowsCallback(row, rowNum); 510 } 511 512 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) { 513 GetDecoder(png_ptr)->rowCallback(row, rowNum); 514 } 515 516private: 517 int fRowsWrittenToOutput; 518 void* fDst; 519 size_t fRowBytes; 520 521 // Variables for partial decode 522 int fFirstRow; // FIXME: Move to baseclass? 523 int fLastRow; 524 int fRowsNeeded; 525 526 using INHERITED = SkPngCodec; 527 528 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) { 529 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr)); 530 } 531 532 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override { 533 const int height = this->dimensions().height(); 534 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallback, nullptr); 535 fDst = dst; 536 fRowBytes = rowBytes; 537 538 fRowsWrittenToOutput = 0; 539 fFirstRow = 0; 540 fLastRow = height - 1; 541 542 const bool success = this->processData(); 543 if (success && fRowsWrittenToOutput == height) { 544 return kSuccess; 545 } 546 547 if (rowsDecoded) { 548 *rowsDecoded = fRowsWrittenToOutput; 549 } 550 551 return log_and_return_error(success); 552 } 553 554 void allRowsCallback(png_bytep row, int rowNum) { 555 SkASSERT(rowNum == fRowsWrittenToOutput); 556 fRowsWrittenToOutput++; 557 this->applyXformRow(fDst, row); 558 fDst = SkTAddOffset<void>(fDst, fRowBytes); 559 } 560 561 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override { 562 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback, nullptr); 563 fFirstRow = firstRow; 564 fLastRow = lastRow; 565 fDst = dst; 566 fRowBytes = rowBytes; 567 fRowsWrittenToOutput = 0; 568 fRowsNeeded = fLastRow - fFirstRow + 1; 569 } 570 571 Result decode(int* rowsDecoded) override { 572 if (this->swizzler()) { 573 const int sampleY = this->swizzler()->sampleY(); 574 fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY); 575 } 576 577 const bool success = this->processData(); 578 if (success && fRowsWrittenToOutput == fRowsNeeded) { 579 return kSuccess; 580 } 581 582 if (rowsDecoded) { 583 *rowsDecoded = fRowsWrittenToOutput; 584 } 585 586 return log_and_return_error(success); 587 } 588 589 void rowCallback(png_bytep row, int rowNum) { 590 if (rowNum < fFirstRow) { 591 // Ignore this row. 592 return; 593 } 594 595 SkASSERT(rowNum <= fLastRow); 596 SkASSERT(fRowsWrittenToOutput < fRowsNeeded); 597 598 // If there is no swizzler, all rows are needed. 599 if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) { 600 this->applyXformRow(fDst, row); 601 fDst = SkTAddOffset<void>(fDst, fRowBytes); 602 fRowsWrittenToOutput++; 603 } 604 605 if (fRowsWrittenToOutput == fRowsNeeded) { 606 // Fake error to stop decoding scanlines. 607 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding); 608 } 609 } 610}; 611 612class SkPngInterlacedDecoder : public SkPngCodec { 613public: 614 SkPngInterlacedDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream, 615 SkPngChunkReader* reader, png_structp png_ptr, 616 png_infop info_ptr, int bitDepth, int numberPasses) 617 : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth) 618 , fNumberPasses(numberPasses) 619 , fFirstRow(0) 620 , fLastRow(0) 621 , fLinesDecoded(0) 622 , fInterlacedComplete(false) 623 , fPng_rowbytes(0) 624 {} 625 626 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) { 627 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr)); 628 decoder->interlacedRowCallback(row, rowNum, pass); 629 } 630 631private: 632 const int fNumberPasses; 633 int fFirstRow; 634 int fLastRow; 635 void* fDst; 636 size_t fRowBytes; 637 int fLinesDecoded; 638 bool fInterlacedComplete; 639 size_t fPng_rowbytes; 640 SkAutoTMalloc<png_byte> fInterlaceBuffer; 641 642 using INHERITED = SkPngCodec; 643 644 // FIXME: Currently sharing interlaced callback for all rows and subset. It's not 645 // as expensive as the subset version of non-interlaced, but it still does extra 646 // work. 647 void interlacedRowCallback(png_bytep row, int rowNum, int pass) { 648 if (rowNum < fFirstRow || rowNum > fLastRow || fInterlacedComplete) { 649 // Ignore this row 650 return; 651 } 652 653 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes; 654 png_progressive_combine_row(this->png_ptr(), oldRow, row); 655 656 if (0 == pass) { 657 // The first pass initializes all rows. 658 SkASSERT(row); 659 SkASSERT(fLinesDecoded == rowNum - fFirstRow); 660 fLinesDecoded++; 661 } else { 662 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1); 663 if (fNumberPasses - 1 == pass && rowNum == fLastRow) { 664 // Last pass, and we have read all of the rows we care about. 665 fInterlacedComplete = true; 666 if (fLastRow != this->dimensions().height() - 1 || 667 (this->swizzler() && this->swizzler()->sampleY() != 1)) { 668 // Fake error to stop decoding scanlines. Only stop if we're not decoding the 669 // whole image, in which case processing the rest of the image might be 670 // expensive. When decoding the whole image, read through the IEND chunk to 671 // preserve Android behavior of leaving the input stream in the right place. 672 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding); 673 } 674 } 675 } 676 } 677 678 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override { 679 const int height = this->dimensions().height(); 680 this->setUpInterlaceBuffer(height); 681 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, 682 nullptr); 683 684 fFirstRow = 0; 685 fLastRow = height - 1; 686 fLinesDecoded = 0; 687 688 const bool success = this->processData(); 689 png_bytep srcRow = fInterlaceBuffer.get(); 690 // FIXME: When resuming, this may rewrite rows that did not change. 691 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) { 692 this->applyXformRow(dst, srcRow); 693 dst = SkTAddOffset<void>(dst, rowBytes); 694 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes); 695 } 696 if (success && fInterlacedComplete) { 697 return kSuccess; 698 } 699 700 if (rowsDecoded) { 701 *rowsDecoded = fLinesDecoded; 702 } 703 704 return log_and_return_error(success); 705 } 706 707 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override { 708 // FIXME: We could skip rows in the interlace buffer that we won't put in the output. 709 this->setUpInterlaceBuffer(lastRow - firstRow + 1); 710 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, nullptr); 711 fFirstRow = firstRow; 712 fLastRow = lastRow; 713 fDst = dst; 714 fRowBytes = rowBytes; 715 fLinesDecoded = 0; 716 } 717 718 Result decode(int* rowsDecoded) override { 719 const bool success = this->processData(); 720 721 // Now apply Xforms on all the rows that were decoded. 722 if (!fLinesDecoded) { 723 if (rowsDecoded) { 724 *rowsDecoded = 0; 725 } 726 return log_and_return_error(success); 727 } 728 729 const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1; 730 const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY); 731 732 // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it 733 // may be too tricky/expensive to handle that correctly. 734 735 // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow, 736 // since the first row in fInterlaceBuffer corresponds to fFirstRow. 737 int srcRow = get_start_coord(sampleY); 738 void* dst = fDst; 739 int rowsWrittenToOutput = 0; 740 while (rowsWrittenToOutput < rowsNeeded && srcRow < fLinesDecoded) { 741 png_bytep src = SkTAddOffset<png_byte>(fInterlaceBuffer.get(), fPng_rowbytes * srcRow); 742 this->applyXformRow(dst, src); 743 dst = SkTAddOffset<void>(dst, fRowBytes); 744 745 rowsWrittenToOutput++; 746 srcRow += sampleY; 747 } 748 749 if (success && fInterlacedComplete) { 750 return kSuccess; 751 } 752 753 if (rowsDecoded) { 754 *rowsDecoded = rowsWrittenToOutput; 755 } 756 return log_and_return_error(success); 757 } 758 759 void setUpInterlaceBuffer(int height) { 760 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr()); 761 fInterlaceBuffer.reset(fPng_rowbytes * height); 762 fInterlacedComplete = false; 763 } 764}; 765 766// Reads the header and initializes the output fields, if not NULL. 767// 768// @param stream Input data. Will be read to get enough information to properly 769// setup the codec. 770// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. 771// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is 772// expected to continue to own it for the lifetime of the png_ptr. 773// @param outCodec Optional output variable. If non-NULL, will be set to a new 774// SkPngCodec on success. 775// @param png_ptrp Optional output variable. If non-NULL, will be set to a new 776// png_structp on success. 777// @param info_ptrp Optional output variable. If non-NULL, will be set to a new 778// png_infop on success; 779// @return if kSuccess, the caller is responsible for calling 780// png_destroy_read_struct(png_ptrp, info_ptrp). 781// Otherwise, the passed in fields (except stream) are unchanged. 782static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader, 783 SkCodec** outCodec, 784 png_structp* png_ptrp, png_infop* info_ptrp) { 785 // The image is known to be a PNG. Decode enough to know the SkImageInfo. 786 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, 787 sk_error_fn, sk_warning_fn); 788 if (!png_ptr) { 789 return SkCodec::kInternalError; 790 } 791 792#ifdef PNG_SET_OPTION_SUPPORTED 793 // This setting ensures that we display images with incorrect CMF bytes. 794 // See crbug.com/807324. 795 png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); 796#endif 797 798 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec); 799 800 png_infop info_ptr = png_create_info_struct(png_ptr); 801 if (info_ptr == nullptr) { 802 return SkCodec::kInternalError; 803 } 804 805 autoClean.setInfoPtr(info_ptr); 806 807 if (setjmp(PNG_JMPBUF(png_ptr))) { 808 return SkCodec::kInvalidInput; 809 } 810 811#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 812 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. 813 // This needs to be installed before we read the png header. Android may store ninepatch 814 // chunks in the header. 815 if (chunkReader) { 816 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); 817 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); 818 } 819#endif 820 821 const bool decodedBounds = autoClean.decodeBounds(); 822 823 if (!decodedBounds) { 824 return SkCodec::kIncompleteInput; 825 } 826 827 // On success, decodeBounds releases ownership of png_ptr and info_ptr. 828 if (png_ptrp) { 829 *png_ptrp = png_ptr; 830 } 831 if (info_ptrp) { 832 *info_ptrp = info_ptr; 833 } 834 835 // decodeBounds takes care of setting outCodec 836 if (outCodec) { 837 SkASSERT(*outCodec); 838 } 839 return SkCodec::kSuccess; 840} 841 842void AutoCleanPng::infoCallback(size_t idatLength) { 843 png_uint_32 origWidth, origHeight; 844 int bitDepth, encodedColorType; 845 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, 846 &encodedColorType, nullptr, nullptr, nullptr); 847 848 // TODO: Should we support 16-bits of precision for gray images? 849 if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType || 850 PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) { 851 bitDepth = 8; 852 png_set_strip_16(fPng_ptr); 853 } 854 855 // Now determine the default colorType and alphaType and set the required transforms. 856 // Often, we depend on SkSwizzler to perform any transforms that we need. However, we 857 // still depend on libpng for many of the rare and PNG-specific cases. 858 SkEncodedInfo::Color color; 859 SkEncodedInfo::Alpha alpha; 860 switch (encodedColorType) { 861 case PNG_COLOR_TYPE_PALETTE: 862 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single 863 // byte into separate bytes (useful for paletted and grayscale images). 864 if (bitDepth < 8) { 865 // TODO: Should we use SkSwizzler here? 866 bitDepth = 8; 867 png_set_packing(fPng_ptr); 868 } 869 870 color = SkEncodedInfo::kPalette_Color; 871 // Set the alpha depending on if a transparency chunk exists. 872 alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ? 873 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; 874 break; 875 case PNG_COLOR_TYPE_RGB: 876 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { 877 // Convert to RGBA if transparency chunk exists. 878 png_set_tRNS_to_alpha(fPng_ptr); 879 color = SkEncodedInfo::kRGBA_Color; 880 alpha = SkEncodedInfo::kBinary_Alpha; 881 } else { 882 color = SkEncodedInfo::kRGB_Color; 883 alpha = SkEncodedInfo::kOpaque_Alpha; 884 } 885 break; 886 case PNG_COLOR_TYPE_GRAY: 887 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. 888 if (bitDepth < 8) { 889 // TODO: Should we use SkSwizzler here? 890 bitDepth = 8; 891 png_set_expand_gray_1_2_4_to_8(fPng_ptr); 892 } 893 894 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { 895 png_set_tRNS_to_alpha(fPng_ptr); 896 color = SkEncodedInfo::kGrayAlpha_Color; 897 alpha = SkEncodedInfo::kBinary_Alpha; 898 } else { 899 color = SkEncodedInfo::kGray_Color; 900 alpha = SkEncodedInfo::kOpaque_Alpha; 901 } 902 break; 903 case PNG_COLOR_TYPE_GRAY_ALPHA: 904 color = SkEncodedInfo::kGrayAlpha_Color; 905 alpha = SkEncodedInfo::kUnpremul_Alpha; 906 break; 907 case PNG_COLOR_TYPE_RGBA: 908 color = SkEncodedInfo::kRGBA_Color; 909 alpha = SkEncodedInfo::kUnpremul_Alpha; 910 break; 911 default: 912 // All the color types have been covered above. 913 SkASSERT(false); 914 color = SkEncodedInfo::kRGBA_Color; 915 alpha = SkEncodedInfo::kUnpremul_Alpha; 916 } 917 918 const int numberPasses = png_set_interlace_handling(fPng_ptr); 919 920 if (fOutCodec) { 921 SkASSERT(nullptr == *fOutCodec); 922 auto profile = read_color_profile(fPng_ptr, fInfo_ptr); 923 if (profile) { 924 switch (profile->profile()->data_color_space) { 925 case skcms_Signature_CMYK: 926 profile = nullptr; 927 break; 928 case skcms_Signature_Gray: 929 if (SkEncodedInfo::kGray_Color != color && 930 SkEncodedInfo::kGrayAlpha_Color != color) 931 { 932 profile = nullptr; 933 } 934 break; 935 default: 936 break; 937 } 938 } 939 940 switch (encodedColorType) { 941 case PNG_COLOR_TYPE_GRAY_ALPHA:{ 942 png_color_8p sigBits; 943 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { 944 if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) { 945 color = SkEncodedInfo::kXAlpha_Color; 946 } 947 } 948 break; 949 } 950 case PNG_COLOR_TYPE_RGB:{ 951 png_color_8p sigBits; 952 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { 953 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { 954 // Recommend a decode to 565 if the sBIT indicates 565. 955 color = SkEncodedInfo::k565_Color; 956 } 957 } 958 break; 959 } 960 } 961 962#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 963 if (encodedColorType != PNG_COLOR_TYPE_GRAY_ALPHA 964 && SkEncodedInfo::kOpaque_Alpha == alpha) { 965 png_color_8p sigBits; 966 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { 967 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { 968 SkAndroidFrameworkUtils::SafetyNetLog("190188264"); 969 } 970 } 971 } 972#endif // SK_BUILD_FOR_ANDROID_FRAMEWORK 973 974 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(origWidth, origHeight, color, alpha, 975 bitDepth, std::move(profile)); 976 if (1 == numberPasses) { 977 *fOutCodec = new SkPngNormalDecoder(std::move(encodedInfo), 978 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth); 979 } else { 980 *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo), 981 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, 982 numberPasses); 983 } 984 static_cast<SkPngCodec*>(*fOutCodec)->setIdatLength(idatLength); 985 } 986 987 // Release the pointers, which are now owned by the codec or the caller is expected to 988 // take ownership. 989 this->releasePngPtrs(); 990} 991 992SkPngCodec::SkPngCodec(SkEncodedInfo&& encodedInfo, std::unique_ptr<SkStream> stream, 993 SkPngChunkReader* chunkReader, void* png_ptr, void* info_ptr, int bitDepth) 994 : INHERITED(std::move(encodedInfo), png_select_xform_format(encodedInfo), std::move(stream)) 995 , fPngChunkReader(SkSafeRef(chunkReader)) 996 , fPng_ptr(png_ptr) 997 , fInfo_ptr(info_ptr) 998 , fColorXformSrcRow(nullptr) 999 , fBitDepth(bitDepth) 1000 , fIdatLength(0) 1001 , fDecodedIdat(false) 1002{} 1003 1004SkPngCodec::~SkPngCodec() { 1005 this->destroyReadStruct(); 1006} 1007 1008void SkPngCodec::destroyReadStruct() { 1009 if (fPng_ptr) { 1010 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr 1011 SkASSERT(fInfo_ptr); 1012 png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr); 1013 fPng_ptr = nullptr; 1014 fInfo_ptr = nullptr; 1015 } 1016} 1017 1018/////////////////////////////////////////////////////////////////////////////// 1019// Getting the pixels 1020/////////////////////////////////////////////////////////////////////////////// 1021 1022SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) { 1023 if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) { 1024 SkCodecPrintf("Failed on png_read_update_info.\n"); 1025 return kInvalidInput; 1026 } 1027 png_read_update_info(fPng_ptr, fInfo_ptr); 1028 1029 // Reset fSwizzler and this->colorXform(). We can't do this in onRewind() because the 1030 // interlaced scanline decoder may need to rewind. 1031 fSwizzler.reset(nullptr); 1032 1033 // If skcms directly supports the encoded PNG format, we should skip format 1034 // conversion in the swizzler (or skip swizzling altogether). 1035 bool skipFormatConversion = false; 1036 switch (this->getEncodedInfo().color()) { 1037 case SkEncodedInfo::kRGB_Color: 1038 if (this->getEncodedInfo().bitsPerComponent() != 16) { 1039 break; 1040 } 1041 [[fallthrough]]; 1042 case SkEncodedInfo::kRGBA_Color: 1043 case SkEncodedInfo::kGray_Color: 1044 skipFormatConversion = this->colorXform(); 1045 break; 1046 default: 1047 break; 1048 } 1049 if (skipFormatConversion && !options.fSubset) { 1050 fXformMode = kColorOnly_XformMode; 1051 return kSuccess; 1052 } 1053 1054 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { 1055 if (!this->createColorTable(dstInfo)) { 1056 return kInvalidInput; 1057 } 1058 } 1059 1060 this->initializeSwizzler(dstInfo, options, skipFormatConversion); 1061 return kSuccess; 1062} 1063 1064void SkPngCodec::initializeXformParams() { 1065 switch (fXformMode) { 1066 case kColorOnly_XformMode: 1067 fXformWidth = this->dstInfo().width(); 1068 break; 1069 case kSwizzleColor_XformMode: 1070 fXformWidth = this->swizzler()->swizzleWidth(); 1071 break; 1072 default: 1073 break; 1074 } 1075} 1076 1077void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options, 1078 bool skipFormatConversion) { 1079 SkImageInfo swizzlerInfo = dstInfo; 1080 Options swizzlerOptions = options; 1081 fXformMode = kSwizzleOnly_XformMode; 1082 if (this->colorXform() && this->xformOnDecode()) { 1083 if (SkEncodedInfo::kGray_Color == this->getEncodedInfo().color()) { 1084 swizzlerInfo = swizzlerInfo.makeColorType(kGray_8_SkColorType); 1085 } else { 1086 swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType); 1087 } 1088 if (kPremul_SkAlphaType == dstInfo.alphaType()) { 1089 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType); 1090 } 1091 1092 fXformMode = kSwizzleColor_XformMode; 1093 1094 // Here, we swizzle into temporary memory, which is not zero initialized. 1095 // FIXME (msarett): 1096 // Is this a problem? 1097 swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized; 1098 } 1099 1100 if (skipFormatConversion) { 1101 // We cannot skip format conversion when there is a color table. 1102 SkASSERT(!fColorTable); 1103 int srcBPP = 0; 1104 switch (this->getEncodedInfo().color()) { 1105 case SkEncodedInfo::kRGB_Color: 1106 SkASSERT(this->getEncodedInfo().bitsPerComponent() == 16); 1107 srcBPP = 6; 1108 break; 1109 case SkEncodedInfo::kRGBA_Color: 1110 srcBPP = this->getEncodedInfo().bitsPerComponent() / 2; 1111 break; 1112 case SkEncodedInfo::kGray_Color: 1113 srcBPP = 1; 1114 break; 1115 default: 1116 SkASSERT(false); 1117 break; 1118 } 1119 fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerInfo, swizzlerOptions); 1120 } else { 1121 const SkPMColor* colors = get_color_ptr(fColorTable.get()); 1122 fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), colors, swizzlerInfo, 1123 swizzlerOptions); 1124 } 1125 SkASSERT(fSwizzler); 1126} 1127 1128SkSampler* SkPngCodec::getSampler(bool createIfNecessary) { 1129 if (fSwizzler || !createIfNecessary) { 1130 return fSwizzler.get(); 1131 } 1132 1133 this->initializeSwizzler(this->dstInfo(), this->options(), true); 1134 return fSwizzler.get(); 1135} 1136 1137bool SkPngCodec::onRewind() { 1138 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header 1139 // succeeds, they will be repopulated, and if it fails, they will 1140 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will 1141 // come through this function which will rewind and again attempt 1142 // to reinitialize them. 1143 this->destroyReadStruct(); 1144 1145 png_structp png_ptr; 1146 png_infop info_ptr; 1147 if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr, 1148 &png_ptr, &info_ptr)) { 1149 return false; 1150 } 1151 1152 fPng_ptr = png_ptr; 1153 fInfo_ptr = info_ptr; 1154 fDecodedIdat = false; 1155 return true; 1156} 1157 1158SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, 1159 size_t rowBytes, const Options& options, 1160 int* rowsDecoded) { 1161 Result result = this->initializeXforms(dstInfo, options); 1162 if (kSuccess != result) { 1163 return result; 1164 } 1165 1166 if (options.fSubset) { 1167 return kUnimplemented; 1168 } 1169 1170 this->allocateStorage(dstInfo); 1171 this->initializeXformParams(); 1172 return this->decodeAllRows(dst, rowBytes, rowsDecoded); 1173} 1174 1175SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, 1176 void* dst, size_t rowBytes, const SkCodec::Options& options) { 1177 Result result = this->initializeXforms(dstInfo, options); 1178 if (kSuccess != result) { 1179 return result; 1180 } 1181 1182 this->allocateStorage(dstInfo); 1183 1184 int firstRow, lastRow; 1185 if (options.fSubset) { 1186 firstRow = options.fSubset->top(); 1187 lastRow = options.fSubset->bottom() - 1; 1188 } else { 1189 firstRow = 0; 1190 lastRow = dstInfo.height() - 1; 1191 } 1192 this->setRange(firstRow, lastRow, dst, rowBytes); 1193 return kSuccess; 1194} 1195 1196SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) { 1197 // FIXME: Only necessary on the first call. 1198 this->initializeXformParams(); 1199 1200 return this->decode(rowsDecoded); 1201} 1202 1203std::unique_ptr<SkCodec> SkPngCodec::MakeFromStream(std::unique_ptr<SkStream> stream, 1204 Result* result, SkPngChunkReader* chunkReader) { 1205 SkCodec* outCodec = nullptr; 1206 *result = read_header(stream.get(), chunkReader, &outCodec, nullptr, nullptr); 1207 if (kSuccess == *result) { 1208 // Codec has taken ownership of the stream. 1209 SkASSERT(outCodec); 1210 stream.release(); 1211 } 1212 return std::unique_ptr<SkCodec>(outCodec); 1213} 1214