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