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