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