blob: 1cb1c74621cca1fb3e1cc9cf9d75820d6dc5a3f7 [file] [log] [blame]
scroggof24f2242015-03-03 08:59:20 -08001/*
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
msarettad8bcfe2016-03-07 07:09:03 -08008#include "SkBitmap.h"
msarett74114382015-03-16 11:55:18 -07009#include "SkCodecPriv.h"
Cary Clarka4083c92017-09-15 11:59:23 -040010#include "SkColorData.h"
Matt Sarettc434f512016-10-13 18:24:20 -040011#include "SkColorSpace.h"
Matt Sarett5dfb4e42017-01-04 09:41:09 -050012#include "SkColorSpacePriv.h"
scroggof24f2242015-03-03 08:59:20 -080013#include "SkColorTable.h"
Hal Canary22be4c42018-06-12 12:37:31 -040014#include "SkMacros.h"
scroggof24f2242015-03-03 08:59:20 -080015#include "SkMath.h"
msarett13a91232016-02-01 08:03:29 -080016#include "SkOpts.h"
msarettbe1d5552016-01-21 09:05:23 -080017#include "SkPngCodec.h"
Mike Reedd6cb11e2017-11-30 15:33:04 -050018#include "SkPngPriv.h"
msarett55447952016-07-22 14:07:23 -070019#include "SkPoint3.h"
scroggof24f2242015-03-03 08:59:20 -080020#include "SkSize.h"
21#include "SkStream.h"
22#include "SkSwizzler.h"
scroggo565901d2015-12-10 10:44:13 -080023#include "SkTemplates.h"
bungeman5d2cd6e2016-02-23 07:34:25 -080024#include "SkUtils.h"
scroggof24f2242015-03-03 08:59:20 -080025
mtklein63213812016-08-24 09:55:56 -070026#include "png.h"
Leon Scroggins III83239652017-04-21 13:47:12 -040027#include <algorithm>
mtklein63213812016-08-24 09:55:56 -070028
mtkleindc90b532016-07-28 14:45:28 -070029// This warning triggers false postives way too often in here.
30#if defined(__GNUC__) && !defined(__clang__)
31 #pragma GCC diagnostic ignored "-Wclobbered"
32#endif
33
scroggo8e6c7ad2016-09-16 08:20:38 -070034// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6
35#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x)
36
scroggof24f2242015-03-03 08:59:20 -080037///////////////////////////////////////////////////////////////////////////////
scroggof24f2242015-03-03 08:59:20 -080038// Callback functions
39///////////////////////////////////////////////////////////////////////////////
40
scroggo8e6c7ad2016-09-16 08:20:38 -070041// When setjmp is first called, it returns 0, meaning longjmp was not called.
42constexpr int kSetJmpOkay = 0;
43// An error internal to libpng.
44constexpr int kPngError = 1;
45// Passed to longjmp when we have decoded as many lines as we need.
46constexpr int kStopDecoding = 2;
47
scroggof24f2242015-03-03 08:59:20 -080048static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070049 SkCodecPrintf("------ png error %s\n", msg);
scroggo8e6c7ad2016-09-16 08:20:38 -070050 longjmp(PNG_JMPBUF(png_ptr), kPngError);
scroggof24f2242015-03-03 08:59:20 -080051}
52
scroggo0eed6df2015-03-26 10:07:56 -070053void sk_warning_fn(png_structp, png_const_charp msg) {
54 SkCodecPrintf("----- png warning %s\n", msg);
55}
56
scroggocf98fa92015-11-23 08:14:40 -080057#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
58static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
59 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
60 // readChunk() returning true means continue decoding
61 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
62}
63#endif
64
scroggof24f2242015-03-03 08:59:20 -080065///////////////////////////////////////////////////////////////////////////////
66// Helpers
67///////////////////////////////////////////////////////////////////////////////
68
69class AutoCleanPng : public SkNoncopyable {
70public:
scroggo8e6c7ad2016-09-16 08:20:38 -070071 /*
72 * This class does not take ownership of stream or reader, but if codecPtr
73 * is non-NULL, and decodeBounds succeeds, it will have created a new
74 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as
75 * the png_ptr and info_ptr.
76 */
77 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader,
78 SkCodec** codecPtr)
scroggof24f2242015-03-03 08:59:20 -080079 : fPng_ptr(png_ptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070080 , fInfo_ptr(nullptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070081 , fStream(stream)
82 , fChunkReader(reader)
83 , fOutCodec(codecPtr)
84 {}
scroggof24f2242015-03-03 08:59:20 -080085
86 ~AutoCleanPng() {
halcanary96fcdcc2015-08-27 07:41:13 -070087 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
scroggof24f2242015-03-03 08:59:20 -080088 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070089 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
msarett13a91232016-02-01 08:03:29 -080090 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr);
scroggof24f2242015-03-03 08:59:20 -080091 }
92 }
93
94 void setInfoPtr(png_infop info_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070095 SkASSERT(nullptr == fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -080096 fInfo_ptr = info_ptr;
97 }
98
scroggo8e6c7ad2016-09-16 08:20:38 -070099 /**
100 * Reads enough of the input stream to decode the bounds.
101 * @return false if the stream is not a valid PNG (or too short).
102 * true if it read enough of the stream to determine the bounds.
103 * In the latter case, the stream may have been read beyond the
104 * point to determine the bounds, and the png_ptr will have saved
105 * any extra data. Further, if the codecPtr supplied to the
106 * constructor was not NULL, it will now point to a new SkCodec,
107 * which owns (or refs, in the case of the SkPngChunkReader) the
108 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are
109 * unowned, and it is up to the caller to destroy them.
110 */
111 bool decodeBounds();
112
113private:
114 png_structp fPng_ptr;
115 png_infop fInfo_ptr;
scroggo8e6c7ad2016-09-16 08:20:38 -0700116 SkStream* fStream;
117 SkPngChunkReader* fChunkReader;
118 SkCodec** fOutCodec;
119
Leon Scroggins III83239652017-04-21 13:47:12 -0400120 void infoCallback(size_t idatLength);
scroggo8e6c7ad2016-09-16 08:20:38 -0700121
scroggo8e6c7ad2016-09-16 08:20:38 -0700122 void releasePngPtrs() {
halcanary96fcdcc2015-08-27 07:41:13 -0700123 fPng_ptr = nullptr;
124 fInfo_ptr = nullptr;
scroggof24f2242015-03-03 08:59:20 -0800125 }
scroggof24f2242015-03-03 08:59:20 -0800126};
127#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
128
Leon Scroggins III83239652017-04-21 13:47:12 -0400129static inline bool is_chunk(const png_byte* chunk, const char* tag) {
130 return memcmp(chunk + 4, tag, 4) == 0;
131}
132
133static inline bool process_data(png_structp png_ptr, png_infop info_ptr,
134 SkStream* stream, void* buffer, size_t bufferSize, size_t length) {
135 while (length > 0) {
136 const size_t bytesToProcess = std::min(bufferSize, length);
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400137 const size_t bytesRead = stream->read(buffer, bytesToProcess);
138 png_process_data(png_ptr, info_ptr, (png_bytep) buffer, bytesRead);
139 if (bytesRead < bytesToProcess) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400140 return false;
141 }
Leon Scroggins III83239652017-04-21 13:47:12 -0400142 length -= bytesToProcess;
143 }
144 return true;
145}
146
scroggo8e6c7ad2016-09-16 08:20:38 -0700147bool AutoCleanPng::decodeBounds() {
148 if (setjmp(PNG_JMPBUF(fPng_ptr))) {
149 return false;
150 }
151
Leon Scroggins III83239652017-04-21 13:47:12 -0400152 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700153
154 // Arbitrary buffer size, though note that it matches (below)
155 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of
156 // the PNG header?
157 constexpr size_t kBufferSize = 4096;
158 char buffer[kBufferSize];
159
Leon Scroggins III83239652017-04-21 13:47:12 -0400160 {
161 // Parse the signature.
162 if (fStream->read(buffer, 8) < 8) {
163 return false;
164 }
165
166 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, 8);
167 }
168
scroggo8e6c7ad2016-09-16 08:20:38 -0700169 while (true) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400170 // Parse chunk length and type.
171 if (fStream->read(buffer, 8) < 8) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700172 // We have read to the end of the input without decoding bounds.
173 break;
174 }
175
Leon Scroggins III83239652017-04-21 13:47:12 -0400176 png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
177 const size_t length = png_get_uint_32(chunk);
178
179 if (is_chunk(chunk, "IDAT")) {
180 this->infoCallback(length);
181 return true;
182 }
183
184 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
185 // Process the full chunk + CRC.
186 if (!process_data(fPng_ptr, fInfo_ptr, fStream, buffer, kBufferSize, length + 4)) {
187 return false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700188 }
189 }
190
Leon Scroggins III83239652017-04-21 13:47:12 -0400191 return false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700192}
193
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530194bool SkPngCodec::processData() {
scroggo8e6c7ad2016-09-16 08:20:38 -0700195 switch (setjmp(PNG_JMPBUF(fPng_ptr))) {
196 case kPngError:
197 // There was an error. Stop processing data.
198 // FIXME: Do we need to discard png_ptr?
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530199 return false;;
scroggo8e6c7ad2016-09-16 08:20:38 -0700200 case kStopDecoding:
201 // We decoded all the lines we want.
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530202 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -0700203 case kSetJmpOkay:
204 // Everything is okay.
205 break;
206 default:
207 // No other values should be passed to longjmp.
208 SkASSERT(false);
209 }
210
211 // Arbitrary buffer size
212 constexpr size_t kBufferSize = 4096;
213 char buffer[kBufferSize];
214
Leon Scroggins III83239652017-04-21 13:47:12 -0400215 bool iend = false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700216 while (true) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400217 size_t length;
218 if (fDecodedIdat) {
219 // Parse chunk length and type.
220 if (this->stream()->read(buffer, 8) < 8) {
221 break;
222 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700223
Leon Scroggins III83239652017-04-21 13:47:12 -0400224 png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
225 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
226 if (is_chunk(chunk, "IEND")) {
227 iend = true;
228 }
229
230 length = png_get_uint_32(chunk);
231 } else {
232 length = fIdatLength;
233 png_byte idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
234 png_save_uint_32(idat, length);
235 png_process_data(fPng_ptr, fInfo_ptr, idat, 8);
236 fDecodedIdat = true;
237 }
238
239 // Process the full chunk + CRC.
240 if (!process_data(fPng_ptr, fInfo_ptr, this->stream(), buffer, kBufferSize, length + 4)
241 || iend) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700242 break;
243 }
244 }
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530245
246 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -0700247}
248
Leon Scroggins III862c1962017-10-02 16:28:49 -0400249static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
Matt Sarett562e6812016-11-08 16:13:43 -0500250
msarettd1ec89b2016-08-03 12:59:27 -0700251// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
Leon Scroggins571b30f2017-07-11 17:35:31 +0000252bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
scroggof24f2242015-03-03 08:59:20 -0800253
msarett13a91232016-02-01 08:03:29 -0800254 int numColors;
255 png_color* palette;
256 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
scroggo05245902015-03-25 11:11:52 -0700257 return false;
scroggof24f2242015-03-03 08:59:20 -0800258 }
259
msarettdcd5e652016-08-22 08:48:40 -0700260 // Contents depend on tableColorType and our choice of if/when to premultiply:
261 // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
262 SkPMColor colorTable[256];
Matt Sarett562e6812016-11-08 16:13:43 -0500263 SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType();
scroggof24f2242015-03-03 08:59:20 -0800264
msarett13a91232016-02-01 08:03:29 -0800265 png_bytep alphas;
266 int numColorsWithAlpha = 0;
267 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
msarettdcd5e652016-08-22 08:48:40 -0700268 // If we are performing a color xform, it will handle the premultiply. Otherwise,
269 // we'll do it here.
Leon Scroggins IIIae79f322017-08-18 10:53:24 -0400270 bool premultiply = !this->colorXform() && needs_premul(dstInfo.alphaType(),
271 this->getEncodedInfo().alpha());
msarettdcd5e652016-08-22 08:48:40 -0700272
msarett13a91232016-02-01 08:03:29 -0800273 // Choose which function to use to create the color table. If the final destination's
274 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
msarettdcd5e652016-08-22 08:48:40 -0700275 PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType);
msarett13a91232016-02-01 08:03:29 -0800276
277 for (int i = 0; i < numColorsWithAlpha; i++) {
278 // We don't have a function in SkOpts that combines a set of alphas with a set
279 // of RGBs. We could write one, but it's hardly worth it, given that this
280 // is such a small fraction of the total decode time.
msarettdcd5e652016-08-22 08:48:40 -0700281 colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue);
msarett13a91232016-02-01 08:03:29 -0800282 palette++;
283 }
scroggof24f2242015-03-03 08:59:20 -0800284 }
285
msarett13a91232016-02-01 08:03:29 -0800286 if (numColorsWithAlpha < numColors) {
287 // The optimized code depends on a 3-byte png_color struct with the colors
288 // in RGB order. These checks make sure it is safe to use.
289 static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken.");
290#ifdef SK_DEBUG
291 SkASSERT(&palette->red < &palette->green);
292 SkASSERT(&palette->green < &palette->blue);
293#endif
294
msarettdcd5e652016-08-22 08:48:40 -0700295 if (is_rgba(tableColorType)) {
296 SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700297 numColors - numColorsWithAlpha);
298 } else {
msarettdcd5e652016-08-22 08:48:40 -0700299 SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700300 numColors - numColorsWithAlpha);
301 }
scroggof24f2242015-03-03 08:59:20 -0800302 }
303
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400304 if (this->colorXform() && !this->xformOnDecode()) {
305 this->applyColorXform(colorTable, colorTable, numColors);
msarettdcd5e652016-08-22 08:48:40 -0700306 }
307
msarett13a91232016-02-01 08:03:29 -0800308 // Pad the color table with the last color in the table (or black) in the case that
309 // invalid pixel indices exceed the number of colors in the table.
310 const int maxColors = 1 << fBitDepth;
311 if (numColors < maxColors) {
msarettdcd5e652016-08-22 08:48:40 -0700312 SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK;
313 sk_memset32(colorTable + numColors, lastColor, maxColors - numColors);
scroggof24f2242015-03-03 08:59:20 -0800314 }
315
msarettdcd5e652016-08-22 08:48:40 -0700316 fColorTable.reset(new SkColorTable(colorTable, maxColors));
scroggo05245902015-03-25 11:11:52 -0700317 return true;
scroggof24f2242015-03-03 08:59:20 -0800318}
319
320///////////////////////////////////////////////////////////////////////////////
321// Creation
322///////////////////////////////////////////////////////////////////////////////
323
scroggodb30be22015-12-08 18:54:13 -0800324bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
325 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
scroggof24f2242015-03-03 08:59:20 -0800326}
327
scroggo8e6c7ad2016-09-16 08:20:38 -0700328#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
329
msarett6a738212016-03-04 13:27:35 -0800330static float png_fixed_point_to_float(png_fixed_point x) {
331 // We multiply by the same factor that libpng used to convert
332 // fixed point -> double. Since we want floats, we choose to
333 // do the conversion ourselves rather than convert
334 // fixed point -> double -> float.
335 return ((float) x) * 0.00001f;
336}
337
msarett128245c2016-03-30 12:01:47 -0700338static float png_inverted_fixed_point_to_float(png_fixed_point x) {
339 // This is necessary because the gAMA chunk actually stores 1/gamma.
340 return 1.0f / png_fixed_point_to_float(x);
341}
342
scroggo8e6c7ad2016-09-16 08:20:38 -0700343#endif // LIBPNG >= 1.6
344
msarett6a738212016-03-04 13:27:35 -0800345// Returns a colorSpace object that represents any color space information in
raftiasd737bee2016-12-08 10:53:24 -0500346// the encoded data. If the encoded data contains an invalid/unsupported color space,
347// this will return NULL. If there is no color space information, it will guess sRGB
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400348sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
msarett6a738212016-03-04 13:27:35 -0800349
msarette2443222016-03-04 14:20:49 -0800350#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
351
msarett6a738212016-03-04 13:27:35 -0800352 // First check for an ICC profile
353 png_bytep profile;
354 png_uint_32 length;
355 // The below variables are unused, however, we need to pass them in anyway or
356 // png_get_iCCP() will return nothing.
357 // Could knowing the |name| of the profile ever be interesting? Maybe for debugging?
358 png_charp name;
359 // The |compression| is uninteresting since:
360 // (1) libpng has already decompressed the profile for us.
361 // (2) "deflate" is the only mode of decompression that libpng supports.
362 int compression;
363 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
364 &length)) {
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400365 return SkColorSpace::MakeICC(profile, length);
msarett6a738212016-03-04 13:27:35 -0800366 }
367
368 // Second, check for sRGB.
369 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
370
371 // sRGB chunks also store a rendering intent: Absolute, Relative,
372 // Perceptual, and Saturation.
373 // FIXME (msarett): Extract this information from the sRGB chunk once
374 // we are able to handle this information in
375 // SkColorSpace.
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500376 return SkColorSpace::MakeSRGB();
msarett6a738212016-03-04 13:27:35 -0800377 }
378
379 // Next, check for chromaticities.
msaretta5a31dd2016-10-11 09:41:16 -0700380 png_fixed_point chrm[8];
msarett6a738212016-03-04 13:27:35 -0800381 png_fixed_point gamma;
msaretta5a31dd2016-10-11 09:41:16 -0700382 if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4],
383 &chrm[5], &chrm[6], &chrm[7]))
msarett55447952016-07-22 14:07:23 -0700384 {
msaretta5a31dd2016-10-11 09:41:16 -0700385 SkColorSpacePrimaries primaries;
386 primaries.fRX = png_fixed_point_to_float(chrm[2]);
387 primaries.fRY = png_fixed_point_to_float(chrm[3]);
388 primaries.fGX = png_fixed_point_to_float(chrm[4]);
389 primaries.fGY = png_fixed_point_to_float(chrm[5]);
390 primaries.fBX = png_fixed_point_to_float(chrm[6]);
391 primaries.fBY = png_fixed_point_to_float(chrm[7]);
392 primaries.fWX = png_fixed_point_to_float(chrm[0]);
393 primaries.fWY = png_fixed_point_to_float(chrm[1]);
msarett55447952016-07-22 14:07:23 -0700394
395 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
msaretta5a31dd2016-10-11 09:41:16 -0700396 if (!primaries.toXYZD50(&toXYZD50)) {
msarett55447952016-07-22 14:07:23 -0700397 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
398 }
msarett6a738212016-03-04 13:27:35 -0800399
msarett128245c2016-03-30 12:01:47 -0700400 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400401 SkColorSpaceTransferFn fn;
402 fn.fA = 1.0f;
403 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
404 fn.fG = png_inverted_fixed_point_to_float(gamma);
msarettffc2aea2016-05-02 11:12:14 -0700405
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400406 return SkColorSpace::MakeRGB(fn, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800407 }
msarett128245c2016-03-30 12:01:47 -0700408
msarettc4ce6b52016-06-16 07:37:41 -0700409 // Default to sRGB gamma if the image has color space information,
410 // but does not specify gamma.
Brian Osman526972e2016-10-24 09:24:02 -0400411 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800412 }
413
414 // Last, check for gamma.
415 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400416 SkColorSpaceTransferFn fn;
417 fn.fA = 1.0f;
418 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
419 fn.fG = png_inverted_fixed_point_to_float(gamma);
msarett6a738212016-03-04 13:27:35 -0800420
msarettc4ce6b52016-06-16 07:37:41 -0700421 // Since there is no cHRM, we will guess sRGB gamut.
msarett55447952016-07-22 14:07:23 -0700422 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
423 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
msarettc4ce6b52016-06-16 07:37:41 -0700424
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400425 return SkColorSpace::MakeRGB(fn, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800426 }
427
msarette2443222016-03-04 14:20:49 -0800428#endif // LIBPNG >= 1.6
429
raftiasd737bee2016-12-08 10:53:24 -0500430 // Report that there is no color space information in the PNG.
431 // Guess sRGB in this case.
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500432 return SkColorSpace::MakeSRGB();
msarett6a738212016-03-04 13:27:35 -0800433}
434
msarett400a93b2016-09-01 18:32:52 -0700435void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
436 switch (fXformMode) {
437 case kSwizzleOnly_XformMode:
msarett400a93b2016-09-01 18:32:52 -0700438 break;
439 case kColorOnly_XformMode:
440 // Intentional fall through. A swizzler hasn't been created yet, but one will
441 // be created later if we are sampling. We'll go ahead and allocate
442 // enough memory to swizzle if necessary.
443 case kSwizzleColor_XformMode: {
Matt Sarett34c69d62017-01-19 17:42:23 -0500444 const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel();
445
446 // If we have more than 8-bits (per component) of precision, we will keep that
447 // extra precision. Otherwise, we will swizzle to RGBA_8888 before transforming.
448 const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4;
449 const size_t colorXformBytes = dstInfo.width() * bytesPerPixel;
scroggo8e6c7ad2016-09-16 08:20:38 -0700450 fStorage.reset(colorXformBytes);
Matt Sarett379938e2017-01-12 18:34:29 -0500451 fColorXformSrcRow = fStorage.get();
msarett400a93b2016-09-01 18:32:52 -0700452 break;
453 }
454 }
msarettd1ec89b2016-08-03 12:59:27 -0700455}
456
Matt Sarett379938e2017-01-12 18:34:29 -0500457static SkColorSpaceXform::ColorFormat png_select_xform_format(const SkEncodedInfo& info) {
Matt Sarett34c69d62017-01-19 17:42:23 -0500458 // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
459 if (16 == info.bitsPerComponent()) {
460 if (SkEncodedInfo::kRGBA_Color == info.color()) {
461 return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat;
462 } else if (SkEncodedInfo::kRGB_Color == info.color()) {
463 return SkColorSpaceXform::kRGB_U16_BE_ColorFormat;
464 }
Matt Sarett379938e2017-01-12 18:34:29 -0500465 }
466
467 return SkColorSpaceXform::kRGBA_8888_ColorFormat;
468}
469
scroggo8e6c7ad2016-09-16 08:20:38 -0700470void SkPngCodec::applyXformRow(void* dst, const void* src) {
msarett400a93b2016-09-01 18:32:52 -0700471 switch (fXformMode) {
472 case kSwizzleOnly_XformMode:
473 fSwizzler->swizzle(dst, (const uint8_t*) src);
474 break;
475 case kColorOnly_XformMode:
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400476 this->applyColorXform(dst, src, fXformWidth);
msarett400a93b2016-09-01 18:32:52 -0700477 break;
478 case kSwizzleColor_XformMode:
479 fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400480 this->applyColorXform(dst, fColorXformSrcRow, fXformWidth);
msarett400a93b2016-09-01 18:32:52 -0700481 break;
482 }
msarettdcd5e652016-08-22 08:48:40 -0700483}
484
scroggo8e6c7ad2016-09-16 08:20:38 -0700485class SkPngNormalDecoder : public SkPngCodec {
scroggo05245902015-03-25 11:11:52 -0700486public:
Mike Reedede7bac2017-07-23 15:30:02 -0400487 SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
488 std::unique_ptr<SkStream> stream, SkPngChunkReader* reader,
489 png_structp png_ptr, png_infop info_ptr, int bitDepth)
490 : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
scroggoff9f7bb2016-10-10 11:35:01 -0700491 , fRowsWrittenToOutput(0)
scroggo8e6c7ad2016-09-16 08:20:38 -0700492 , fDst(nullptr)
493 , fRowBytes(0)
494 , fFirstRow(0)
495 , fLastRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700496 {}
497
scroggo8e6c7ad2016-09-16 08:20:38 -0700498 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
499 GetDecoder(png_ptr)->allRowsCallback(row, rowNum);
scroggo05245902015-03-25 11:11:52 -0700500 }
501
scroggo8e6c7ad2016-09-16 08:20:38 -0700502 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
503 GetDecoder(png_ptr)->rowCallback(row, rowNum);
msarettd1ec89b2016-08-03 12:59:27 -0700504 }
505
emmaleer0a4c3cb2015-06-22 10:40:21 -0700506private:
scroggoff9f7bb2016-10-10 11:35:01 -0700507 int fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700508 void* fDst;
509 size_t fRowBytes;
510
511 // Variables for partial decode
512 int fFirstRow; // FIXME: Move to baseclass?
513 int fLastRow;
scroggo4f2a88c2016-10-17 14:32:41 -0700514 int fRowsNeeded;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700515
scroggo46c57472015-09-30 08:57:13 -0700516 typedef SkPngCodec INHERITED;
scroggo8e6c7ad2016-09-16 08:20:38 -0700517
518 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) {
519 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr));
520 }
521
522 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
523 const int height = this->getInfo().height();
Leon Scroggins III83239652017-04-21 13:47:12 -0400524 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700525 fDst = dst;
526 fRowBytes = rowBytes;
527
scroggoff9f7bb2016-10-10 11:35:01 -0700528 fRowsWrittenToOutput = 0;
scroggoc46cdd42016-10-10 06:45:32 -0700529 fFirstRow = 0;
530 fLastRow = height - 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700531
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530532 if (!this->processData()) {
533 return kErrorInInput;
534 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700535
scroggoff9f7bb2016-10-10 11:35:01 -0700536 if (fRowsWrittenToOutput == height) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700537 return SkCodec::kSuccess;
538 }
539
540 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700541 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700542 }
543
544 return SkCodec::kIncompleteInput;
545 }
546
547 void allRowsCallback(png_bytep row, int rowNum) {
scroggoff9f7bb2016-10-10 11:35:01 -0700548 SkASSERT(rowNum == fRowsWrittenToOutput);
549 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700550 this->applyXformRow(fDst, row);
551 fDst = SkTAddOffset<void>(fDst, fRowBytes);
552 }
553
554 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
Leon Scroggins III83239652017-04-21 13:47:12 -0400555 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700556 fFirstRow = firstRow;
557 fLastRow = lastRow;
558 fDst = dst;
559 fRowBytes = rowBytes;
scroggoff9f7bb2016-10-10 11:35:01 -0700560 fRowsWrittenToOutput = 0;
scroggo4f2a88c2016-10-17 14:32:41 -0700561 fRowsNeeded = fLastRow - fFirstRow + 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700562 }
563
564 SkCodec::Result decode(int* rowsDecoded) override {
scroggo4f2a88c2016-10-17 14:32:41 -0700565 if (this->swizzler()) {
566 const int sampleY = this->swizzler()->sampleY();
567 fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
568 }
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530569
570 if (!this->processData()) {
571 return kErrorInInput;
572 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700573
scroggo4f2a88c2016-10-17 14:32:41 -0700574 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700575 return SkCodec::kSuccess;
576 }
577
578 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700579 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700580 }
581
582 return SkCodec::kIncompleteInput;
583 }
584
585 void rowCallback(png_bytep row, int rowNum) {
586 if (rowNum < fFirstRow) {
587 // Ignore this row.
588 return;
589 }
590
591 SkASSERT(rowNum <= fLastRow);
scroggo4f2a88c2016-10-17 14:32:41 -0700592 SkASSERT(fRowsWrittenToOutput < fRowsNeeded);
scroggo8e6c7ad2016-09-16 08:20:38 -0700593
594 // If there is no swizzler, all rows are needed.
scroggo4f2a88c2016-10-17 14:32:41 -0700595 if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700596 this->applyXformRow(fDst, row);
597 fDst = SkTAddOffset<void>(fDst, fRowBytes);
scroggoff9f7bb2016-10-10 11:35:01 -0700598 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700599 }
600
scroggo4f2a88c2016-10-17 14:32:41 -0700601 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700602 // Fake error to stop decoding scanlines.
603 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
604 }
605 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700606};
607
scroggo8e6c7ad2016-09-16 08:20:38 -0700608class SkPngInterlacedDecoder : public SkPngCodec {
609public:
610 SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
Mike Reedede7bac2017-07-23 15:30:02 -0400611 std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, png_structp png_ptr,
612 png_infop info_ptr, int bitDepth, int numberPasses)
613 : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
scroggo8e6c7ad2016-09-16 08:20:38 -0700614 , fNumberPasses(numberPasses)
615 , fFirstRow(0)
616 , fLastRow(0)
617 , fLinesDecoded(0)
618 , fInterlacedComplete(false)
619 , fPng_rowbytes(0)
620 {}
621
622 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) {
623 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr));
624 decoder->interlacedRowCallback(row, rowNum, pass);
625 }
626
scroggo8e6c7ad2016-09-16 08:20:38 -0700627private:
628 const int fNumberPasses;
629 int fFirstRow;
630 int fLastRow;
631 void* fDst;
632 size_t fRowBytes;
633 int fLinesDecoded;
634 bool fInterlacedComplete;
635 size_t fPng_rowbytes;
636 SkAutoTMalloc<png_byte> fInterlaceBuffer;
637
638 typedef SkPngCodec INHERITED;
639
scroggo8e6c7ad2016-09-16 08:20:38 -0700640 // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
641 // as expensive as the subset version of non-interlaced, but it still does extra
642 // work.
643 void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
Leon Scroggins III600effb2017-04-24 15:44:45 -0400644 if (rowNum < fFirstRow || rowNum > fLastRow || fInterlacedComplete) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700645 // Ignore this row
646 return;
647 }
648
649 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
650 png_progressive_combine_row(this->png_ptr(), oldRow, row);
651
652 if (0 == pass) {
653 // The first pass initializes all rows.
654 SkASSERT(row);
655 SkASSERT(fLinesDecoded == rowNum - fFirstRow);
656 fLinesDecoded++;
657 } else {
658 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
659 if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
Leon Scroggins III600effb2017-04-24 15:44:45 -0400660 // Last pass, and we have read all of the rows we care about.
scroggo8e6c7ad2016-09-16 08:20:38 -0700661 fInterlacedComplete = true;
Leon Scroggins III600effb2017-04-24 15:44:45 -0400662 if (fLastRow != this->getInfo().height() - 1 ||
663 (this->swizzler() && this->swizzler()->sampleY() != 1)) {
664 // Fake error to stop decoding scanlines. Only stop if we're not decoding the
665 // whole image, in which case processing the rest of the image might be
666 // expensive. When decoding the whole image, read through the IEND chunk to
667 // preserve Android behavior of leaving the input stream in the right place.
668 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
669 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700670 }
671 }
672 }
673
674 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
675 const int height = this->getInfo().height();
676 this->setUpInterlaceBuffer(height);
Leon Scroggins III83239652017-04-21 13:47:12 -0400677 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback,
scroggo8e6c7ad2016-09-16 08:20:38 -0700678 nullptr);
679
680 fFirstRow = 0;
681 fLastRow = height - 1;
682 fLinesDecoded = 0;
683
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530684 if (!this->processData()) {
685 return kErrorInInput;
686 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700687
688 png_bytep srcRow = fInterlaceBuffer.get();
689 // FIXME: When resuming, this may rewrite rows that did not change.
690 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) {
691 this->applyXformRow(dst, srcRow);
692 dst = SkTAddOffset<void>(dst, rowBytes);
693 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes);
694 }
695 if (fInterlacedComplete) {
696 return SkCodec::kSuccess;
697 }
698
699 if (rowsDecoded) {
700 *rowsDecoded = fLinesDecoded;
701 }
702
703 return SkCodec::kIncompleteInput;
704 }
705
706 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
707 // FIXME: We could skip rows in the interlace buffer that we won't put in the output.
708 this->setUpInterlaceBuffer(lastRow - firstRow + 1);
Leon Scroggins III83239652017-04-21 13:47:12 -0400709 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700710 fFirstRow = firstRow;
711 fLastRow = lastRow;
712 fDst = dst;
713 fRowBytes = rowBytes;
714 fLinesDecoded = 0;
715 }
716
717 SkCodec::Result decode(int* rowsDecoded) override {
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530718 if (this->processData() == false) {
719 return kErrorInInput;
720 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700721
722 // Now apply Xforms on all the rows that were decoded.
723 if (!fLinesDecoded) {
scroggoe61b3b42016-10-10 07:17:32 -0700724 if (rowsDecoded) {
725 *rowsDecoded = 0;
726 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700727 return SkCodec::kIncompleteInput;
728 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700729
scroggo4f2a88c2016-10-17 14:32:41 -0700730 const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1;
731 const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
scroggoff9f7bb2016-10-10 11:35:01 -0700732 int rowsWrittenToOutput = 0;
733
scroggo8e6c7ad2016-09-16 08:20:38 -0700734 // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it
735 // may be too tricky/expensive to handle that correctly.
scroggoe9ea3492016-10-18 09:14:11 -0700736
737 // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow,
738 // since the first row in fInterlaceBuffer corresponds to fFirstRow.
739 png_bytep srcRow = SkTAddOffset<png_byte>(fInterlaceBuffer.get(),
740 fPng_rowbytes * get_start_coord(sampleY));
scroggo8e6c7ad2016-09-16 08:20:38 -0700741 void* dst = fDst;
scroggoe9ea3492016-10-18 09:14:11 -0700742 for (; rowsWrittenToOutput < rowsNeeded; rowsWrittenToOutput++) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700743 this->applyXformRow(dst, srcRow);
744 dst = SkTAddOffset<void>(dst, fRowBytes);
745 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY);
746 }
747
748 if (fInterlacedComplete) {
749 return SkCodec::kSuccess;
750 }
751
752 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700753 *rowsDecoded = rowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700754 }
755 return SkCodec::kIncompleteInput;
756 }
757
758 void setUpInterlaceBuffer(int height) {
759 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr());
760 fInterlaceBuffer.reset(fPng_rowbytes * height);
761 fInterlacedComplete = false;
762 }
763};
764
msarettac6c7502016-04-25 09:30:24 -0700765// Reads the header and initializes the output fields, if not NULL.
766//
767// @param stream Input data. Will be read to get enough information to properly
768// setup the codec.
769// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
770// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
771// expected to continue to own it for the lifetime of the png_ptr.
772// @param outCodec Optional output variable. If non-NULL, will be set to a new
773// SkPngCodec on success.
774// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
775// png_structp on success.
776// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
777// png_infop on success;
Leon Scroggins III588fb042017-07-14 16:32:31 -0400778// @return if kSuccess, the caller is responsible for calling
msarettac6c7502016-04-25 09:30:24 -0700779// png_destroy_read_struct(png_ptrp, info_ptrp).
Leon Scroggins III588fb042017-07-14 16:32:31 -0400780// Otherwise, the passed in fields (except stream) are unchanged.
781static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader,
782 SkCodec** outCodec,
783 png_structp* png_ptrp, png_infop* info_ptrp) {
msarettac6c7502016-04-25 09:30:24 -0700784 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
785 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
786 sk_error_fn, sk_warning_fn);
787 if (!png_ptr) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400788 return SkCodec::kInternalError;
msarettac6c7502016-04-25 09:30:24 -0700789 }
790
Leon Scroggins III9b1a8862018-03-01 15:57:32 -0500791#ifdef PNG_SET_OPTION_SUPPORTED
Leon Scroggins III9e8a5942018-02-28 16:24:18 -0500792 // This setting ensures that we display images with incorrect CMF bytes.
793 // See crbug.com/807324.
794 png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
Leon Scroggins III9b1a8862018-03-01 15:57:32 -0500795#endif
Leon Scroggins III9e8a5942018-02-28 16:24:18 -0500796
scroggo8e6c7ad2016-09-16 08:20:38 -0700797 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
msarettac6c7502016-04-25 09:30:24 -0700798
799 png_infop info_ptr = png_create_info_struct(png_ptr);
800 if (info_ptr == nullptr) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400801 return SkCodec::kInternalError;
msarettac6c7502016-04-25 09:30:24 -0700802 }
803
804 autoClean.setInfoPtr(info_ptr);
805
scroggo8e6c7ad2016-09-16 08:20:38 -0700806 if (setjmp(PNG_JMPBUF(png_ptr))) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400807 return SkCodec::kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -0700808 }
809
msarettac6c7502016-04-25 09:30:24 -0700810#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
811 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
812 // This needs to be installed before we read the png header. Android may store ninepatch
813 // chunks in the header.
814 if (chunkReader) {
815 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
816 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
817 }
818#endif
819
scroggo8e6c7ad2016-09-16 08:20:38 -0700820 const bool decodedBounds = autoClean.decodeBounds();
821
822 if (!decodedBounds) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400823 return SkCodec::kIncompleteInput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700824 }
825
826 // On success, decodeBounds releases ownership of png_ptr and info_ptr.
827 if (png_ptrp) {
828 *png_ptrp = png_ptr;
829 }
830 if (info_ptrp) {
831 *info_ptrp = info_ptr;
832 }
833
834 // decodeBounds takes care of setting outCodec
835 if (outCodec) {
836 SkASSERT(*outCodec);
837 }
Leon Scroggins III588fb042017-07-14 16:32:31 -0400838 return SkCodec::kSuccess;
scroggo8e6c7ad2016-09-16 08:20:38 -0700839}
840
Leon Scroggins III83239652017-04-21 13:47:12 -0400841void AutoCleanPng::infoCallback(size_t idatLength) {
msarettac6c7502016-04-25 09:30:24 -0700842 png_uint_32 origWidth, origHeight;
843 int bitDepth, encodedColorType;
Leon Scroggins III83239652017-04-21 13:47:12 -0400844 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
msarettac6c7502016-04-25 09:30:24 -0700845 &encodedColorType, nullptr, nullptr, nullptr);
846
Matt Sarett7a1cc672016-12-14 11:48:31 -0500847 // TODO: Should we support 16-bits of precision for gray images?
848 if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType ||
849 PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) {
850 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400851 png_set_strip_16(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700852 }
853
854 // Now determine the default colorType and alphaType and set the required transforms.
855 // Often, we depend on SkSwizzler to perform any transforms that we need. However, we
856 // still depend on libpng for many of the rare and PNG-specific cases.
857 SkEncodedInfo::Color color;
858 SkEncodedInfo::Alpha alpha;
859 switch (encodedColorType) {
860 case PNG_COLOR_TYPE_PALETTE:
861 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
862 // byte into separate bytes (useful for paletted and grayscale images).
863 if (bitDepth < 8) {
864 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500865 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400866 png_set_packing(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700867 }
868
869 color = SkEncodedInfo::kPalette_Color;
870 // Set the alpha depending on if a transparency chunk exists.
Leon Scroggins III83239652017-04-21 13:47:12 -0400871 alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ?
msarettac6c7502016-04-25 09:30:24 -0700872 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
873 break;
874 case PNG_COLOR_TYPE_RGB:
Leon Scroggins III83239652017-04-21 13:47:12 -0400875 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
msarettac6c7502016-04-25 09:30:24 -0700876 // Convert to RGBA if transparency chunk exists.
Leon Scroggins III83239652017-04-21 13:47:12 -0400877 png_set_tRNS_to_alpha(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700878 color = SkEncodedInfo::kRGBA_Color;
879 alpha = SkEncodedInfo::kBinary_Alpha;
880 } else {
881 color = SkEncodedInfo::kRGB_Color;
882 alpha = SkEncodedInfo::kOpaque_Alpha;
883 }
884 break;
885 case PNG_COLOR_TYPE_GRAY:
886 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
887 if (bitDepth < 8) {
888 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500889 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400890 png_set_expand_gray_1_2_4_to_8(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700891 }
892
Leon Scroggins III83239652017-04-21 13:47:12 -0400893 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
894 png_set_tRNS_to_alpha(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700895 color = SkEncodedInfo::kGrayAlpha_Color;
896 alpha = SkEncodedInfo::kBinary_Alpha;
897 } else {
898 color = SkEncodedInfo::kGray_Color;
899 alpha = SkEncodedInfo::kOpaque_Alpha;
900 }
901 break;
902 case PNG_COLOR_TYPE_GRAY_ALPHA:
903 color = SkEncodedInfo::kGrayAlpha_Color;
904 alpha = SkEncodedInfo::kUnpremul_Alpha;
905 break;
906 case PNG_COLOR_TYPE_RGBA:
907 color = SkEncodedInfo::kRGBA_Color;
908 alpha = SkEncodedInfo::kUnpremul_Alpha;
909 break;
910 default:
911 // All the color types have been covered above.
912 SkASSERT(false);
913 color = SkEncodedInfo::kRGBA_Color;
914 alpha = SkEncodedInfo::kUnpremul_Alpha;
915 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700916
917 const int numberPasses = png_set_interlace_handling(fPng_ptr);
918
scroggo8e6c7ad2016-09-16 08:20:38 -0700919 if (fOutCodec) {
920 SkASSERT(nullptr == *fOutCodec);
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400921 sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr);
922 if (colorSpace) {
923 switch (colorSpace->type()) {
924 case SkColorSpace::kCMYK_Type:
925 colorSpace = nullptr;
926 break;
927 case SkColorSpace::kGray_Type:
928 if (SkEncodedInfo::kGray_Color != color &&
929 SkEncodedInfo::kGrayAlpha_Color != color)
930 {
931 colorSpace = nullptr;
932 }
933 break;
934 case SkColorSpace::kRGB_Type:
935 break;
936 }
Matt Sarett523116d2017-01-12 18:36:38 -0500937 }
msarettf34cd632016-05-25 10:13:53 -0700938 if (!colorSpace) {
raftiasd737bee2016-12-08 10:53:24 -0500939 // Treat unsupported/invalid color spaces as sRGB.
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500940 colorSpace = SkColorSpace::MakeSRGB();
msarettf34cd632016-05-25 10:13:53 -0700941 }
scroggod8d68552016-06-06 11:26:17 -0700942
Matt Sarett7a1cc672016-12-14 11:48:31 -0500943 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, bitDepth);
msarett549ca322016-08-17 08:54:08 -0700944 SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace);
945
Mike Reedd6cb11e2017-11-30 15:33:04 -0500946 if (encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
947 png_color_8p sigBits;
948 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
949 if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) {
950 imageInfo = imageInfo.makeColorType(kAlpha_8_SkColorType);
951 }
952 }
953 } else if (SkEncodedInfo::kOpaque_Alpha == alpha) {
msarett549ca322016-08-17 08:54:08 -0700954 png_color_8p sigBits;
scroggo8e6c7ad2016-09-16 08:20:38 -0700955 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
msarett549ca322016-08-17 08:54:08 -0700956 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
957 // Recommend a decode to 565 if the sBIT indicates 565.
958 imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
959 }
960 }
961 }
scroggod8d68552016-06-06 11:26:17 -0700962
msarettac6c7502016-04-25 09:30:24 -0700963 if (1 == numberPasses) {
Mike Reedede7bac2017-07-23 15:30:02 -0400964 *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo,
965 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
msarettac6c7502016-04-25 09:30:24 -0700966 } else {
Mike Reedede7bac2017-07-23 15:30:02 -0400967 *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo,
968 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
969 numberPasses);
msarettac6c7502016-04-25 09:30:24 -0700970 }
Leon Scroggins III83239652017-04-21 13:47:12 -0400971 static_cast<SkPngCodec*>(*fOutCodec)->setIdatLength(idatLength);
msarettac6c7502016-04-25 09:30:24 -0700972 }
973
scroggo8e6c7ad2016-09-16 08:20:38 -0700974 // Release the pointers, which are now owned by the codec or the caller is expected to
975 // take ownership.
976 this->releasePngPtrs();
msarettac6c7502016-04-25 09:30:24 -0700977}
978
msarett549ca322016-08-17 08:54:08 -0700979SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
Mike Reedede7bac2017-07-23 15:30:02 -0400980 std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader,
981 void* png_ptr, void* info_ptr, int bitDepth)
982 : INHERITED(encodedInfo, imageInfo, png_select_xform_format(encodedInfo), std::move(stream))
msarettac6c7502016-04-25 09:30:24 -0700983 , fPngChunkReader(SkSafeRef(chunkReader))
984 , fPng_ptr(png_ptr)
985 , fInfo_ptr(info_ptr)
msarettd1ec89b2016-08-03 12:59:27 -0700986 , fColorXformSrcRow(nullptr)
msarettac6c7502016-04-25 09:30:24 -0700987 , fBitDepth(bitDepth)
Leon Scroggins III83239652017-04-21 13:47:12 -0400988 , fIdatLength(0)
989 , fDecodedIdat(false)
msarettac6c7502016-04-25 09:30:24 -0700990{}
991
992SkPngCodec::~SkPngCodec() {
993 this->destroyReadStruct();
994}
995
996void SkPngCodec::destroyReadStruct() {
997 if (fPng_ptr) {
998 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
999 SkASSERT(fInfo_ptr);
mtklein6dc5b9a2016-08-24 12:22:32 -07001000 png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr);
msarettac6c7502016-04-25 09:30:24 -07001001 fPng_ptr = nullptr;
1002 fInfo_ptr = nullptr;
1003 }
1004}
1005
1006///////////////////////////////////////////////////////////////////////////////
1007// Getting the pixels
1008///////////////////////////////////////////////////////////////////////////////
1009
Leon Scroggins571b30f2017-07-11 17:35:31 +00001010SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001011 if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
msarettd1ec89b2016-08-03 12:59:27 -07001012 SkCodecPrintf("Failed on png_read_update_info.\n");
Matt Sarettd59948a2017-04-26 10:59:48 -04001013 return kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -07001014 }
1015 png_read_update_info(fPng_ptr, fInfo_ptr);
1016
Matt Sarett313c4632016-10-20 12:35:23 -04001017 // Reset fSwizzler and this->colorXform(). We can't do this in onRewind() because the
msarett400a93b2016-09-01 18:32:52 -07001018 // interlaced scanline decoder may need to rewind.
1019 fSwizzler.reset(nullptr);
msarett400a93b2016-09-01 18:32:52 -07001020
Matt Sarett34c69d62017-01-19 17:42:23 -05001021 // If SkColorSpaceXform directly supports the encoded PNG format, we should skip format
1022 // conversion in the swizzler (or skip swizzling altogether).
1023 bool skipFormatConversion = false;
1024 switch (this->getEncodedInfo().color()) {
1025 case SkEncodedInfo::kRGB_Color:
1026 if (this->getEncodedInfo().bitsPerComponent() != 16) {
1027 break;
1028 }
1029
1030 // Fall through
1031 case SkEncodedInfo::kRGBA_Color:
1032 skipFormatConversion = this->colorXform();
1033 break;
1034 default:
1035 break;
1036 }
Matt Sarett379938e2017-01-12 18:34:29 -05001037 if (skipFormatConversion && !options.fSubset) {
msarett400a93b2016-09-01 18:32:52 -07001038 fXformMode = kColorOnly_XformMode;
Matt Sarettd59948a2017-04-26 10:59:48 -04001039 return kSuccess;
msarettd1ec89b2016-08-03 12:59:27 -07001040 }
1041
msarettac6c7502016-04-25 09:30:24 -07001042 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001043 if (!this->createColorTable(dstInfo)) {
Matt Sarettd59948a2017-04-26 10:59:48 -04001044 return kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -07001045 }
1046 }
1047
Matt Sarett379938e2017-01-12 18:34:29 -05001048 this->initializeSwizzler(dstInfo, options, skipFormatConversion);
Matt Sarettd59948a2017-04-26 10:59:48 -04001049 return kSuccess;
msarett400a93b2016-09-01 18:32:52 -07001050}
1051
msarettc0444612016-09-16 11:45:58 -07001052void SkPngCodec::initializeXformParams() {
1053 switch (fXformMode) {
1054 case kColorOnly_XformMode:
msarettc0444612016-09-16 11:45:58 -07001055 fXformWidth = this->dstInfo().width();
1056 break;
1057 case kSwizzleColor_XformMode:
msarettc0444612016-09-16 11:45:58 -07001058 fXformWidth = this->swizzler()->swizzleWidth();
1059 break;
1060 default:
1061 break;
1062 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001063}
1064
Matt Sarett379938e2017-01-12 18:34:29 -05001065void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
1066 bool skipFormatConversion) {
msarett400a93b2016-09-01 18:32:52 -07001067 SkImageInfo swizzlerInfo = dstInfo;
1068 Options swizzlerOptions = options;
1069 fXformMode = kSwizzleOnly_XformMode;
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -04001070 if (this->colorXform() && this->xformOnDecode()) {
Matt Sarett562e6812016-11-08 16:13:43 -05001071 swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
msarett400a93b2016-09-01 18:32:52 -07001072 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
1073 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
1074 }
1075
1076 fXformMode = kSwizzleColor_XformMode;
1077
1078 // Here, we swizzle into temporary memory, which is not zero initialized.
1079 // FIXME (msarett):
1080 // Is this a problem?
1081 swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
1082 }
1083
msarettac6c7502016-04-25 09:30:24 -07001084 const SkPMColor* colors = get_color_ptr(fColorTable.get());
msarettd1ec89b2016-08-03 12:59:27 -07001085 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo,
Matt Sarett379938e2017-01-12 18:34:29 -05001086 swizzlerOptions, nullptr, skipFormatConversion));
msarettac6c7502016-04-25 09:30:24 -07001087 SkASSERT(fSwizzler);
msarett400a93b2016-09-01 18:32:52 -07001088}
1089
1090SkSampler* SkPngCodec::getSampler(bool createIfNecessary) {
1091 if (fSwizzler || !createIfNecessary) {
Ben Wagner145dbcd2016-11-03 14:40:50 -04001092 return fSwizzler.get();
msarett400a93b2016-09-01 18:32:52 -07001093 }
1094
Matt Sarett379938e2017-01-12 18:34:29 -05001095 this->initializeSwizzler(this->dstInfo(), this->options(), true);
Ben Wagner145dbcd2016-11-03 14:40:50 -04001096 return fSwizzler.get();
msarettac6c7502016-04-25 09:30:24 -07001097}
1098
msarettac6c7502016-04-25 09:30:24 -07001099bool SkPngCodec::onRewind() {
1100 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
1101 // succeeds, they will be repopulated, and if it fails, they will
1102 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
1103 // come through this function which will rewind and again attempt
1104 // to reinitialize them.
1105 this->destroyReadStruct();
1106
scroggo46c57472015-09-30 08:57:13 -07001107 png_structp png_ptr;
1108 png_infop info_ptr;
Leon Scroggins III588fb042017-07-14 16:32:31 -04001109 if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr,
1110 &png_ptr, &info_ptr)) {
msarettac6c7502016-04-25 09:30:24 -07001111 return false;
scroggo05245902015-03-25 11:11:52 -07001112 }
1113
msarettac6c7502016-04-25 09:30:24 -07001114 fPng_ptr = png_ptr;
1115 fInfo_ptr = info_ptr;
Leon Scroggins III83239652017-04-21 13:47:12 -04001116 fDecodedIdat = false;
msarettac6c7502016-04-25 09:30:24 -07001117 return true;
1118}
msarett6a738212016-03-04 13:27:35 -08001119
msarettd1ec89b2016-08-03 12:59:27 -07001120SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
1121 size_t rowBytes, const Options& options,
msarettac6c7502016-04-25 09:30:24 -07001122 int* rowsDecoded) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001123 Result result = this->initializeXforms(dstInfo, options);
Matt Sarettd59948a2017-04-26 10:59:48 -04001124 if (kSuccess != result) {
1125 return result;
1126 }
1127
msarettac6c7502016-04-25 09:30:24 -07001128 if (options.fSubset) {
msarettac6c7502016-04-25 09:30:24 -07001129 return kUnimplemented;
scroggo05245902015-03-25 11:11:52 -07001130 }
1131
msarett400a93b2016-09-01 18:32:52 -07001132 this->allocateStorage(dstInfo);
msarettc0444612016-09-16 11:45:58 -07001133 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001134 return this->decodeAllRows(dst, rowBytes, rowsDecoded);
1135}
msarettac6c7502016-04-25 09:30:24 -07001136
scroggo8e6c7ad2016-09-16 08:20:38 -07001137SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
Leon Scroggins571b30f2017-07-11 17:35:31 +00001138 void* dst, size_t rowBytes, const SkCodec::Options& options) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001139 Result result = this->initializeXforms(dstInfo, options);
Matt Sarettd59948a2017-04-26 10:59:48 -04001140 if (kSuccess != result) {
1141 return result;
1142 }
1143
scroggo8e6c7ad2016-09-16 08:20:38 -07001144 this->allocateStorage(dstInfo);
1145
1146 int firstRow, lastRow;
1147 if (options.fSubset) {
1148 firstRow = options.fSubset->top();
1149 lastRow = options.fSubset->bottom() - 1;
1150 } else {
1151 firstRow = 0;
1152 lastRow = dstInfo.height() - 1;
1153 }
1154 this->setRange(firstRow, lastRow, dst, rowBytes);
scroggod8d68552016-06-06 11:26:17 -07001155 return kSuccess;
scroggo6fb23912016-06-02 14:16:43 -07001156}
1157
scroggo8e6c7ad2016-09-16 08:20:38 -07001158SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) {
1159 // FIXME: Only necessary on the first call.
msarettc0444612016-09-16 11:45:58 -07001160 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001161
1162 return this->decode(rowsDecoded);
1163}
1164
msarettf7eb6fc2016-09-13 09:04:11 -07001165uint64_t SkPngCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
msarettac6c7502016-04-25 09:30:24 -07001166 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
1167 if (colorPtr) {
msarettc0444612016-09-16 11:45:58 -07001168 SkAlphaType alphaType = select_xform_alpha(dstInfo.alphaType(),
msarettf7eb6fc2016-09-13 09:04:11 -07001169 this->getInfo().alphaType());
1170 return get_color_table_fill_value(dstInfo.colorType(), alphaType, colorPtr, 0,
Matt Sarett19aff5d2017-04-03 16:01:10 -04001171 this->colorXform(), true);
msarettac6c7502016-04-25 09:30:24 -07001172 }
msarettf7eb6fc2016-09-13 09:04:11 -07001173 return INHERITED::onGetFillValue(dstInfo);
msarettac6c7502016-04-25 09:30:24 -07001174}
1175
Mike Reedede7bac2017-07-23 15:30:02 -04001176std::unique_ptr<SkCodec> SkPngCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
1177 Result* result, SkPngChunkReader* chunkReader) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001178 SkCodec* outCodec = nullptr;
Mike Reedede7bac2017-07-23 15:30:02 -04001179 *result = read_header(stream.get(), chunkReader, &outCodec, nullptr, nullptr);
Leon Scroggins III588fb042017-07-14 16:32:31 -04001180 if (kSuccess == *result) {
msarettac6c7502016-04-25 09:30:24 -07001181 // Codec has taken ownership of the stream.
1182 SkASSERT(outCodec);
Mike Reedede7bac2017-07-23 15:30:02 -04001183 stream.release();
msarettac6c7502016-04-25 09:30:24 -07001184 }
Mike Reedede7bac2017-07-23 15:30:02 -04001185 return std::unique_ptr<SkCodec>(outCodec);
scroggo05245902015-03-25 11:11:52 -07001186}