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