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