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