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