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