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