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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/third_party/skia/src/codec/SkPngCodec.cpp