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