blob: 21046d4b5ead34e92e3e96886f1f9fb22fcc2f48 [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"
scroggof24f2242015-03-03 08:59:20 -080012#include "SkColorTable.h"
Hal Canary22be4c42018-06-12 12:37:31 -040013#include "SkMacros.h"
scroggof24f2242015-03-03 08:59:20 -080014#include "SkMath.h"
msarett13a91232016-02-01 08:03:29 -080015#include "SkOpts.h"
msarettbe1d5552016-01-21 09:05:23 -080016#include "SkPngCodec.h"
Mike Reedd6cb11e2017-11-30 15:33:04 -050017#include "SkPngPriv.h"
msarett55447952016-07-22 14:07:23 -070018#include "SkPoint3.h"
scroggof24f2242015-03-03 08:59:20 -080019#include "SkSize.h"
20#include "SkStream.h"
21#include "SkSwizzler.h"
scroggo565901d2015-12-10 10:44:13 -080022#include "SkTemplates.h"
bungeman5d2cd6e2016-02-23 07:34:25 -080023#include "SkUtils.h"
scroggof24f2242015-03-03 08:59:20 -080024
mtklein63213812016-08-24 09:55:56 -070025#include "png.h"
Leon Scroggins III83239652017-04-21 13:47:12 -040026#include <algorithm>
mtklein63213812016-08-24 09:55:56 -070027
mtkleindc90b532016-07-28 14:45:28 -070028// This warning triggers false postives way too often in here.
29#if defined(__GNUC__) && !defined(__clang__)
30 #pragma GCC diagnostic ignored "-Wclobbered"
31#endif
32
scroggo8e6c7ad2016-09-16 08:20:38 -070033// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6
34#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x)
35
scroggof24f2242015-03-03 08:59:20 -080036///////////////////////////////////////////////////////////////////////////////
scroggof24f2242015-03-03 08:59:20 -080037// Callback functions
38///////////////////////////////////////////////////////////////////////////////
39
scroggo8e6c7ad2016-09-16 08:20:38 -070040// When setjmp is first called, it returns 0, meaning longjmp was not called.
41constexpr int kSetJmpOkay = 0;
42// An error internal to libpng.
43constexpr int kPngError = 1;
44// Passed to longjmp when we have decoded as many lines as we need.
45constexpr int kStopDecoding = 2;
46
scroggof24f2242015-03-03 08:59:20 -080047static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070048 SkCodecPrintf("------ png error %s\n", msg);
scroggo8e6c7ad2016-09-16 08:20:38 -070049 longjmp(PNG_JMPBUF(png_ptr), kPngError);
scroggof24f2242015-03-03 08:59:20 -080050}
51
scroggo0eed6df2015-03-26 10:07:56 -070052void sk_warning_fn(png_structp, png_const_charp msg) {
53 SkCodecPrintf("----- png warning %s\n", msg);
54}
55
scroggocf98fa92015-11-23 08:14:40 -080056#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
57static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
58 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
59 // readChunk() returning true means continue decoding
60 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
61}
62#endif
63
scroggof24f2242015-03-03 08:59:20 -080064///////////////////////////////////////////////////////////////////////////////
65// Helpers
66///////////////////////////////////////////////////////////////////////////////
67
68class AutoCleanPng : public SkNoncopyable {
69public:
scroggo8e6c7ad2016-09-16 08:20:38 -070070 /*
71 * This class does not take ownership of stream or reader, but if codecPtr
72 * is non-NULL, and decodeBounds succeeds, it will have created a new
73 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as
74 * the png_ptr and info_ptr.
75 */
76 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader,
77 SkCodec** codecPtr)
scroggof24f2242015-03-03 08:59:20 -080078 : fPng_ptr(png_ptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070079 , fInfo_ptr(nullptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070080 , fStream(stream)
81 , fChunkReader(reader)
82 , fOutCodec(codecPtr)
83 {}
scroggof24f2242015-03-03 08:59:20 -080084
85 ~AutoCleanPng() {
halcanary96fcdcc2015-08-27 07:41:13 -070086 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
scroggof24f2242015-03-03 08:59:20 -080087 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070088 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
msarett13a91232016-02-01 08:03:29 -080089 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr);
scroggof24f2242015-03-03 08:59:20 -080090 }
91 }
92
93 void setInfoPtr(png_infop info_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070094 SkASSERT(nullptr == fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -080095 fInfo_ptr = info_ptr;
96 }
97
scroggo8e6c7ad2016-09-16 08:20:38 -070098 /**
99 * Reads enough of the input stream to decode the bounds.
100 * @return false if the stream is not a valid PNG (or too short).
101 * true if it read enough of the stream to determine the bounds.
102 * In the latter case, the stream may have been read beyond the
103 * point to determine the bounds, and the png_ptr will have saved
104 * any extra data. Further, if the codecPtr supplied to the
105 * constructor was not NULL, it will now point to a new SkCodec,
106 * which owns (or refs, in the case of the SkPngChunkReader) the
107 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are
108 * unowned, and it is up to the caller to destroy them.
109 */
110 bool decodeBounds();
111
112private:
113 png_structp fPng_ptr;
114 png_infop fInfo_ptr;
scroggo8e6c7ad2016-09-16 08:20:38 -0700115 SkStream* fStream;
116 SkPngChunkReader* fChunkReader;
117 SkCodec** fOutCodec;
118
Leon Scroggins III83239652017-04-21 13:47:12 -0400119 void infoCallback(size_t idatLength);
scroggo8e6c7ad2016-09-16 08:20:38 -0700120
scroggo8e6c7ad2016-09-16 08:20:38 -0700121 void releasePngPtrs() {
halcanary96fcdcc2015-08-27 07:41:13 -0700122 fPng_ptr = nullptr;
123 fInfo_ptr = nullptr;
scroggof24f2242015-03-03 08:59:20 -0800124 }
scroggof24f2242015-03-03 08:59:20 -0800125};
126#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
127
Leon Scroggins III83239652017-04-21 13:47:12 -0400128static inline bool is_chunk(const png_byte* chunk, const char* tag) {
129 return memcmp(chunk + 4, tag, 4) == 0;
130}
131
132static inline bool process_data(png_structp png_ptr, png_infop info_ptr,
133 SkStream* stream, void* buffer, size_t bufferSize, size_t length) {
134 while (length > 0) {
135 const size_t bytesToProcess = std::min(bufferSize, length);
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400136 const size_t bytesRead = stream->read(buffer, bytesToProcess);
137 png_process_data(png_ptr, info_ptr, (png_bytep) buffer, bytesRead);
138 if (bytesRead < bytesToProcess) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400139 return false;
140 }
Leon Scroggins III83239652017-04-21 13:47:12 -0400141 length -= bytesToProcess;
142 }
143 return true;
144}
145
scroggo8e6c7ad2016-09-16 08:20:38 -0700146bool AutoCleanPng::decodeBounds() {
147 if (setjmp(PNG_JMPBUF(fPng_ptr))) {
148 return false;
149 }
150
Leon Scroggins III83239652017-04-21 13:47:12 -0400151 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700152
153 // Arbitrary buffer size, though note that it matches (below)
154 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of
155 // the PNG header?
156 constexpr size_t kBufferSize = 4096;
157 char buffer[kBufferSize];
158
Leon Scroggins III83239652017-04-21 13:47:12 -0400159 {
160 // Parse the signature.
161 if (fStream->read(buffer, 8) < 8) {
162 return false;
163 }
164
165 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, 8);
166 }
167
scroggo8e6c7ad2016-09-16 08:20:38 -0700168 while (true) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400169 // Parse chunk length and type.
170 if (fStream->read(buffer, 8) < 8) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700171 // We have read to the end of the input without decoding bounds.
172 break;
173 }
174
Leon Scroggins III83239652017-04-21 13:47:12 -0400175 png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
176 const size_t length = png_get_uint_32(chunk);
177
178 if (is_chunk(chunk, "IDAT")) {
179 this->infoCallback(length);
180 return true;
181 }
182
183 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
184 // Process the full chunk + CRC.
185 if (!process_data(fPng_ptr, fInfo_ptr, fStream, buffer, kBufferSize, length + 4)) {
186 return false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700187 }
188 }
189
Leon Scroggins III83239652017-04-21 13:47:12 -0400190 return false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700191}
192
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530193bool SkPngCodec::processData() {
scroggo8e6c7ad2016-09-16 08:20:38 -0700194 switch (setjmp(PNG_JMPBUF(fPng_ptr))) {
195 case kPngError:
196 // There was an error. Stop processing data.
197 // FIXME: Do we need to discard png_ptr?
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530198 return false;;
scroggo8e6c7ad2016-09-16 08:20:38 -0700199 case kStopDecoding:
200 // We decoded all the lines we want.
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530201 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -0700202 case kSetJmpOkay:
203 // Everything is okay.
204 break;
205 default:
206 // No other values should be passed to longjmp.
207 SkASSERT(false);
208 }
209
210 // Arbitrary buffer size
211 constexpr size_t kBufferSize = 4096;
212 char buffer[kBufferSize];
213
Leon Scroggins III83239652017-04-21 13:47:12 -0400214 bool iend = false;
scroggo8e6c7ad2016-09-16 08:20:38 -0700215 while (true) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400216 size_t length;
217 if (fDecodedIdat) {
218 // Parse chunk length and type.
219 if (this->stream()->read(buffer, 8) < 8) {
220 break;
221 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700222
Leon Scroggins III83239652017-04-21 13:47:12 -0400223 png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
224 png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
225 if (is_chunk(chunk, "IEND")) {
226 iend = true;
227 }
228
229 length = png_get_uint_32(chunk);
230 } else {
231 length = fIdatLength;
232 png_byte idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
233 png_save_uint_32(idat, length);
234 png_process_data(fPng_ptr, fInfo_ptr, idat, 8);
235 fDecodedIdat = true;
236 }
237
238 // Process the full chunk + CRC.
239 if (!process_data(fPng_ptr, fInfo_ptr, this->stream(), buffer, kBufferSize, length + 4)
240 || iend) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700241 break;
242 }
243 }
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530244
245 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -0700246}
247
Leon Scroggins III862c1962017-10-02 16:28:49 -0400248static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
Matt Sarett562e6812016-11-08 16:13:43 -0500249
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400250static inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) {
251 return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha;
252}
253
msarettd1ec89b2016-08-03 12:59:27 -0700254// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
Leon Scroggins571b30f2017-07-11 17:35:31 +0000255bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
scroggof24f2242015-03-03 08:59:20 -0800256
msarett13a91232016-02-01 08:03:29 -0800257 int numColors;
258 png_color* palette;
259 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
scroggo05245902015-03-25 11:11:52 -0700260 return false;
scroggof24f2242015-03-03 08:59:20 -0800261 }
262
msarettdcd5e652016-08-22 08:48:40 -0700263 // Contents depend on tableColorType and our choice of if/when to premultiply:
264 // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
265 SkPMColor colorTable[256];
Matt Sarett562e6812016-11-08 16:13:43 -0500266 SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType();
scroggof24f2242015-03-03 08:59:20 -0800267
msarett13a91232016-02-01 08:03:29 -0800268 png_bytep alphas;
269 int numColorsWithAlpha = 0;
270 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400271 bool premultiply = needs_premul(dstInfo.alphaType(), 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
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400345// If there is no color profile information, it will use sRGB.
346std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(png_structp png_ptr,
347 png_infop info_ptr) {
msarett6a738212016-03-04 13:27:35 -0800348
msarette2443222016-03-04 14:20:49 -0800349#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
msarett6a738212016-03-04 13:27:35 -0800350 // First check for an ICC profile
351 png_bytep profile;
352 png_uint_32 length;
353 // The below variables are unused, however, we need to pass them in anyway or
354 // png_get_iCCP() will return nothing.
355 // Could knowing the |name| of the profile ever be interesting? Maybe for debugging?
356 png_charp name;
357 // The |compression| is uninteresting since:
358 // (1) libpng has already decompressed the profile for us.
359 // (2) "deflate" is the only mode of decompression that libpng supports.
360 int compression;
361 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
362 &length)) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400363 auto data = SkData::MakeWithCopy(profile, length);
364 return SkEncodedInfo::ICCProfile::Make(std::move(data));
msarett6a738212016-03-04 13:27:35 -0800365 }
366
367 // Second, check for sRGB.
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400368 // Note that Blink does this first. This code checks ICC first, with the thinking that
369 // an image has both truly wants the potentially more specific ICC chunk, with sRGB as a
370 // backup in case the decoder does not support full color management.
msarett6a738212016-03-04 13:27:35 -0800371 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
msarett6a738212016-03-04 13:27:35 -0800372 // sRGB chunks also store a rendering intent: Absolute, Relative,
373 // Perceptual, and Saturation.
374 // FIXME (msarett): Extract this information from the sRGB chunk once
375 // we are able to handle this information in
376 // SkColorSpace.
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400377 return SkEncodedInfo::ICCProfile::MakeSRGB();
msarett6a738212016-03-04 13:27:35 -0800378 }
379
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400380 // Default to SRGB gamut.
381 skcms_Matrix3x3 toXYZD50 = skcms_sRGB_profile()->toXYZD50;
msarett6a738212016-03-04 13:27:35 -0800382 // Next, check for chromaticities.
msaretta5a31dd2016-10-11 09:41:16 -0700383 png_fixed_point chrm[8];
msarett6a738212016-03-04 13:27:35 -0800384 png_fixed_point gamma;
msaretta5a31dd2016-10-11 09:41:16 -0700385 if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4],
386 &chrm[5], &chrm[6], &chrm[7]))
msarett55447952016-07-22 14:07:23 -0700387 {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400388 float rx = png_fixed_point_to_float(chrm[2]);
389 float ry = png_fixed_point_to_float(chrm[3]);
390 float gx = png_fixed_point_to_float(chrm[4]);
391 float gy = png_fixed_point_to_float(chrm[5]);
392 float bx = png_fixed_point_to_float(chrm[6]);
393 float by = png_fixed_point_to_float(chrm[7]);
394 float wx = png_fixed_point_to_float(chrm[0]);
395 float wy = png_fixed_point_to_float(chrm[1]);
msarett55447952016-07-22 14:07:23 -0700396
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400397 skcms_Matrix3x3 tmp;
398 if (skcms_PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, &tmp)) {
399 toXYZD50 = tmp;
400 } else {
401 // Note that Blink simply returns nullptr in this case. We'll fall
402 // back to srgb.
msarett55447952016-07-22 14:07:23 -0700403 }
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400404 }
msarett6a738212016-03-04 13:27:35 -0800405
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400406 skcms_TransferFunction fn;
407 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
408 fn.a = 1.0f;
409 fn.b = fn.c = fn.d = fn.e = fn.f = 0.0f;
410 fn.g = png_inverted_fixed_point_to_float(gamma);
411 } else {
msarettc4ce6b52016-06-16 07:37:41 -0700412 // Default to sRGB gamma if the image has color space information,
413 // but does not specify gamma.
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400414 // Note that Blink would again return nullptr in this case.
415 fn = *skcms_sRGB_TransferFunction();
msarett6a738212016-03-04 13:27:35 -0800416 }
417
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400418 skcms_ICCProfile skcmsProfile;
419 skcms_Init(&skcmsProfile);
420 skcms_SetTransferFunction(&skcmsProfile, &fn);
421 skcms_SetXYZD50(&skcmsProfile, &toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800422
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400423 return SkEncodedInfo::ICCProfile::Make(skcmsProfile);
424#else // LIBPNG >= 1.6
425 return SkEncodedInfo::ICCProfile::MakeSRGB();
msarette2443222016-03-04 14:20:49 -0800426#endif // LIBPNG >= 1.6
msarett6a738212016-03-04 13:27:35 -0800427}
428
msarett400a93b2016-09-01 18:32:52 -0700429void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
430 switch (fXformMode) {
431 case kSwizzleOnly_XformMode:
msarett400a93b2016-09-01 18:32:52 -0700432 break;
433 case kColorOnly_XformMode:
434 // Intentional fall through. A swizzler hasn't been created yet, but one will
435 // be created later if we are sampling. We'll go ahead and allocate
436 // enough memory to swizzle if necessary.
437 case kSwizzleColor_XformMode: {
Matt Sarett34c69d62017-01-19 17:42:23 -0500438 const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel();
439
440 // If we have more than 8-bits (per component) of precision, we will keep that
441 // extra precision. Otherwise, we will swizzle to RGBA_8888 before transforming.
442 const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4;
443 const size_t colorXformBytes = dstInfo.width() * bytesPerPixel;
scroggo8e6c7ad2016-09-16 08:20:38 -0700444 fStorage.reset(colorXformBytes);
Matt Sarett379938e2017-01-12 18:34:29 -0500445 fColorXformSrcRow = fStorage.get();
msarett400a93b2016-09-01 18:32:52 -0700446 break;
447 }
448 }
msarettd1ec89b2016-08-03 12:59:27 -0700449}
450
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400451static skcms_PixelFormat png_select_xform_format(const SkEncodedInfo& info) {
Matt Sarett34c69d62017-01-19 17:42:23 -0500452 // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
453 if (16 == info.bitsPerComponent()) {
454 if (SkEncodedInfo::kRGBA_Color == info.color()) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400455 return skcms_PixelFormat_RGBA_16161616;
Matt Sarett34c69d62017-01-19 17:42:23 -0500456 } else if (SkEncodedInfo::kRGB_Color == info.color()) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400457 return skcms_PixelFormat_RGB_161616;
Matt Sarett34c69d62017-01-19 17:42:23 -0500458 }
Matt Sarett379938e2017-01-12 18:34:29 -0500459 }
460
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400461 return skcms_PixelFormat_RGBA_8888;
Matt Sarett379938e2017-01-12 18:34:29 -0500462}
463
scroggo8e6c7ad2016-09-16 08:20:38 -0700464void SkPngCodec::applyXformRow(void* dst, const void* src) {
msarett400a93b2016-09-01 18:32:52 -0700465 switch (fXformMode) {
466 case kSwizzleOnly_XformMode:
467 fSwizzler->swizzle(dst, (const uint8_t*) src);
468 break;
469 case kColorOnly_XformMode:
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400470 this->applyColorXform(dst, src, fXformWidth);
msarett400a93b2016-09-01 18:32:52 -0700471 break;
472 case kSwizzleColor_XformMode:
473 fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400474 this->applyColorXform(dst, fColorXformSrcRow, fXformWidth);
msarett400a93b2016-09-01 18:32:52 -0700475 break;
476 }
msarettdcd5e652016-08-22 08:48:40 -0700477}
478
scroggo8e6c7ad2016-09-16 08:20:38 -0700479class SkPngNormalDecoder : public SkPngCodec {
scroggo05245902015-03-25 11:11:52 -0700480public:
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400481 SkPngNormalDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
482 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
483 : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
scroggoff9f7bb2016-10-10 11:35:01 -0700484 , fRowsWrittenToOutput(0)
scroggo8e6c7ad2016-09-16 08:20:38 -0700485 , fDst(nullptr)
486 , fRowBytes(0)
487 , fFirstRow(0)
488 , fLastRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700489 {}
490
scroggo8e6c7ad2016-09-16 08:20:38 -0700491 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
492 GetDecoder(png_ptr)->allRowsCallback(row, rowNum);
scroggo05245902015-03-25 11:11:52 -0700493 }
494
scroggo8e6c7ad2016-09-16 08:20:38 -0700495 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
496 GetDecoder(png_ptr)->rowCallback(row, rowNum);
msarettd1ec89b2016-08-03 12:59:27 -0700497 }
498
emmaleer0a4c3cb2015-06-22 10:40:21 -0700499private:
scroggoff9f7bb2016-10-10 11:35:01 -0700500 int fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700501 void* fDst;
502 size_t fRowBytes;
503
504 // Variables for partial decode
505 int fFirstRow; // FIXME: Move to baseclass?
506 int fLastRow;
scroggo4f2a88c2016-10-17 14:32:41 -0700507 int fRowsNeeded;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700508
scroggo46c57472015-09-30 08:57:13 -0700509 typedef SkPngCodec INHERITED;
scroggo8e6c7ad2016-09-16 08:20:38 -0700510
511 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) {
512 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr));
513 }
514
515 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
516 const int height = this->getInfo().height();
Leon Scroggins III83239652017-04-21 13:47:12 -0400517 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700518 fDst = dst;
519 fRowBytes = rowBytes;
520
scroggoff9f7bb2016-10-10 11:35:01 -0700521 fRowsWrittenToOutput = 0;
scroggoc46cdd42016-10-10 06:45:32 -0700522 fFirstRow = 0;
523 fLastRow = height - 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700524
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530525 if (!this->processData()) {
526 return kErrorInInput;
527 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700528
scroggoff9f7bb2016-10-10 11:35:01 -0700529 if (fRowsWrittenToOutput == height) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700530 return SkCodec::kSuccess;
531 }
532
533 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700534 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700535 }
536
537 return SkCodec::kIncompleteInput;
538 }
539
540 void allRowsCallback(png_bytep row, int rowNum) {
scroggoff9f7bb2016-10-10 11:35:01 -0700541 SkASSERT(rowNum == fRowsWrittenToOutput);
542 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700543 this->applyXformRow(fDst, row);
544 fDst = SkTAddOffset<void>(fDst, fRowBytes);
545 }
546
547 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
Leon Scroggins III83239652017-04-21 13:47:12 -0400548 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700549 fFirstRow = firstRow;
550 fLastRow = lastRow;
551 fDst = dst;
552 fRowBytes = rowBytes;
scroggoff9f7bb2016-10-10 11:35:01 -0700553 fRowsWrittenToOutput = 0;
scroggo4f2a88c2016-10-17 14:32:41 -0700554 fRowsNeeded = fLastRow - fFirstRow + 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700555 }
556
557 SkCodec::Result decode(int* rowsDecoded) override {
scroggo4f2a88c2016-10-17 14:32:41 -0700558 if (this->swizzler()) {
559 const int sampleY = this->swizzler()->sampleY();
560 fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
561 }
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530562
563 if (!this->processData()) {
564 return kErrorInInput;
565 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700566
scroggo4f2a88c2016-10-17 14:32:41 -0700567 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700568 return SkCodec::kSuccess;
569 }
570
571 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700572 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700573 }
574
575 return SkCodec::kIncompleteInput;
576 }
577
578 void rowCallback(png_bytep row, int rowNum) {
579 if (rowNum < fFirstRow) {
580 // Ignore this row.
581 return;
582 }
583
584 SkASSERT(rowNum <= fLastRow);
scroggo4f2a88c2016-10-17 14:32:41 -0700585 SkASSERT(fRowsWrittenToOutput < fRowsNeeded);
scroggo8e6c7ad2016-09-16 08:20:38 -0700586
587 // If there is no swizzler, all rows are needed.
scroggo4f2a88c2016-10-17 14:32:41 -0700588 if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700589 this->applyXformRow(fDst, row);
590 fDst = SkTAddOffset<void>(fDst, fRowBytes);
scroggoff9f7bb2016-10-10 11:35:01 -0700591 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700592 }
593
scroggo4f2a88c2016-10-17 14:32:41 -0700594 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700595 // Fake error to stop decoding scanlines.
596 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
597 }
598 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700599};
600
scroggo8e6c7ad2016-09-16 08:20:38 -0700601class SkPngInterlacedDecoder : public SkPngCodec {
602public:
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400603 SkPngInterlacedDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
604 SkPngChunkReader* reader, png_structp png_ptr,
Mike Reedede7bac2017-07-23 15:30:02 -0400605 png_infop info_ptr, int bitDepth, int numberPasses)
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400606 : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
scroggo8e6c7ad2016-09-16 08:20:38 -0700607 , fNumberPasses(numberPasses)
608 , fFirstRow(0)
609 , fLastRow(0)
610 , fLinesDecoded(0)
611 , fInterlacedComplete(false)
612 , fPng_rowbytes(0)
613 {}
614
615 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) {
616 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr));
617 decoder->interlacedRowCallback(row, rowNum, pass);
618 }
619
scroggo8e6c7ad2016-09-16 08:20:38 -0700620private:
621 const int fNumberPasses;
622 int fFirstRow;
623 int fLastRow;
624 void* fDst;
625 size_t fRowBytes;
626 int fLinesDecoded;
627 bool fInterlacedComplete;
628 size_t fPng_rowbytes;
629 SkAutoTMalloc<png_byte> fInterlaceBuffer;
630
631 typedef SkPngCodec INHERITED;
632
scroggo8e6c7ad2016-09-16 08:20:38 -0700633 // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
634 // as expensive as the subset version of non-interlaced, but it still does extra
635 // work.
636 void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
Leon Scroggins III600effb2017-04-24 15:44:45 -0400637 if (rowNum < fFirstRow || rowNum > fLastRow || fInterlacedComplete) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700638 // Ignore this row
639 return;
640 }
641
642 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
643 png_progressive_combine_row(this->png_ptr(), oldRow, row);
644
645 if (0 == pass) {
646 // The first pass initializes all rows.
647 SkASSERT(row);
648 SkASSERT(fLinesDecoded == rowNum - fFirstRow);
649 fLinesDecoded++;
650 } else {
651 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
652 if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
Leon Scroggins III600effb2017-04-24 15:44:45 -0400653 // Last pass, and we have read all of the rows we care about.
scroggo8e6c7ad2016-09-16 08:20:38 -0700654 fInterlacedComplete = true;
Leon Scroggins III600effb2017-04-24 15:44:45 -0400655 if (fLastRow != this->getInfo().height() - 1 ||
656 (this->swizzler() && this->swizzler()->sampleY() != 1)) {
657 // Fake error to stop decoding scanlines. Only stop if we're not decoding the
658 // whole image, in which case processing the rest of the image might be
659 // expensive. When decoding the whole image, read through the IEND chunk to
660 // preserve Android behavior of leaving the input stream in the right place.
661 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
662 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700663 }
664 }
665 }
666
667 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
668 const int height = this->getInfo().height();
669 this->setUpInterlaceBuffer(height);
Leon Scroggins III83239652017-04-21 13:47:12 -0400670 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback,
scroggo8e6c7ad2016-09-16 08:20:38 -0700671 nullptr);
672
673 fFirstRow = 0;
674 fLastRow = height - 1;
675 fLinesDecoded = 0;
676
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530677 if (!this->processData()) {
678 return kErrorInInput;
679 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700680
681 png_bytep srcRow = fInterlaceBuffer.get();
682 // FIXME: When resuming, this may rewrite rows that did not change.
683 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) {
684 this->applyXformRow(dst, srcRow);
685 dst = SkTAddOffset<void>(dst, rowBytes);
686 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes);
687 }
688 if (fInterlacedComplete) {
689 return SkCodec::kSuccess;
690 }
691
692 if (rowsDecoded) {
693 *rowsDecoded = fLinesDecoded;
694 }
695
696 return SkCodec::kIncompleteInput;
697 }
698
699 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
700 // FIXME: We could skip rows in the interlace buffer that we won't put in the output.
701 this->setUpInterlaceBuffer(lastRow - firstRow + 1);
Leon Scroggins III83239652017-04-21 13:47:12 -0400702 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700703 fFirstRow = firstRow;
704 fLastRow = lastRow;
705 fDst = dst;
706 fRowBytes = rowBytes;
707 fLinesDecoded = 0;
708 }
709
710 SkCodec::Result decode(int* rowsDecoded) override {
nagarajan.ndd7ffa52017-09-29 08:23:32 +0530711 if (this->processData() == false) {
712 return kErrorInInput;
713 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700714
715 // Now apply Xforms on all the rows that were decoded.
716 if (!fLinesDecoded) {
scroggoe61b3b42016-10-10 07:17:32 -0700717 if (rowsDecoded) {
718 *rowsDecoded = 0;
719 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700720 return SkCodec::kIncompleteInput;
721 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700722
scroggo4f2a88c2016-10-17 14:32:41 -0700723 const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1;
724 const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
scroggoff9f7bb2016-10-10 11:35:01 -0700725 int rowsWrittenToOutput = 0;
726
scroggo8e6c7ad2016-09-16 08:20:38 -0700727 // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it
728 // may be too tricky/expensive to handle that correctly.
scroggoe9ea3492016-10-18 09:14:11 -0700729
730 // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow,
731 // since the first row in fInterlaceBuffer corresponds to fFirstRow.
732 png_bytep srcRow = SkTAddOffset<png_byte>(fInterlaceBuffer.get(),
733 fPng_rowbytes * get_start_coord(sampleY));
scroggo8e6c7ad2016-09-16 08:20:38 -0700734 void* dst = fDst;
scroggoe9ea3492016-10-18 09:14:11 -0700735 for (; rowsWrittenToOutput < rowsNeeded; rowsWrittenToOutput++) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700736 this->applyXformRow(dst, srcRow);
737 dst = SkTAddOffset<void>(dst, fRowBytes);
738 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY);
739 }
740
741 if (fInterlacedComplete) {
742 return SkCodec::kSuccess;
743 }
744
745 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700746 *rowsDecoded = rowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700747 }
748 return SkCodec::kIncompleteInput;
749 }
750
751 void setUpInterlaceBuffer(int height) {
752 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr());
753 fInterlaceBuffer.reset(fPng_rowbytes * height);
754 fInterlacedComplete = false;
755 }
756};
757
msarettac6c7502016-04-25 09:30:24 -0700758// Reads the header and initializes the output fields, if not NULL.
759//
760// @param stream Input data. Will be read to get enough information to properly
761// setup the codec.
762// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
763// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
764// expected to continue to own it for the lifetime of the png_ptr.
765// @param outCodec Optional output variable. If non-NULL, will be set to a new
766// SkPngCodec on success.
767// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
768// png_structp on success.
769// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
770// png_infop on success;
Leon Scroggins III588fb042017-07-14 16:32:31 -0400771// @return if kSuccess, the caller is responsible for calling
msarettac6c7502016-04-25 09:30:24 -0700772// png_destroy_read_struct(png_ptrp, info_ptrp).
Leon Scroggins III588fb042017-07-14 16:32:31 -0400773// Otherwise, the passed in fields (except stream) are unchanged.
774static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader,
775 SkCodec** outCodec,
776 png_structp* png_ptrp, png_infop* info_ptrp) {
msarettac6c7502016-04-25 09:30:24 -0700777 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
778 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
779 sk_error_fn, sk_warning_fn);
780 if (!png_ptr) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400781 return SkCodec::kInternalError;
msarettac6c7502016-04-25 09:30:24 -0700782 }
783
Leon Scroggins III9b1a8862018-03-01 15:57:32 -0500784#ifdef PNG_SET_OPTION_SUPPORTED
Leon Scroggins III9e8a5942018-02-28 16:24:18 -0500785 // This setting ensures that we display images with incorrect CMF bytes.
786 // See crbug.com/807324.
787 png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
Leon Scroggins III9b1a8862018-03-01 15:57:32 -0500788#endif
Leon Scroggins III9e8a5942018-02-28 16:24:18 -0500789
scroggo8e6c7ad2016-09-16 08:20:38 -0700790 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
msarettac6c7502016-04-25 09:30:24 -0700791
792 png_infop info_ptr = png_create_info_struct(png_ptr);
793 if (info_ptr == nullptr) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400794 return SkCodec::kInternalError;
msarettac6c7502016-04-25 09:30:24 -0700795 }
796
797 autoClean.setInfoPtr(info_ptr);
798
scroggo8e6c7ad2016-09-16 08:20:38 -0700799 if (setjmp(PNG_JMPBUF(png_ptr))) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400800 return SkCodec::kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -0700801 }
802
msarettac6c7502016-04-25 09:30:24 -0700803#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
804 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
805 // This needs to be installed before we read the png header. Android may store ninepatch
806 // chunks in the header.
807 if (chunkReader) {
808 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
809 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
810 }
811#endif
812
scroggo8e6c7ad2016-09-16 08:20:38 -0700813 const bool decodedBounds = autoClean.decodeBounds();
814
815 if (!decodedBounds) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400816 return SkCodec::kIncompleteInput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700817 }
818
819 // On success, decodeBounds releases ownership of png_ptr and info_ptr.
820 if (png_ptrp) {
821 *png_ptrp = png_ptr;
822 }
823 if (info_ptrp) {
824 *info_ptrp = info_ptr;
825 }
826
827 // decodeBounds takes care of setting outCodec
828 if (outCodec) {
829 SkASSERT(*outCodec);
830 }
Leon Scroggins III588fb042017-07-14 16:32:31 -0400831 return SkCodec::kSuccess;
scroggo8e6c7ad2016-09-16 08:20:38 -0700832}
833
Leon Scroggins III83239652017-04-21 13:47:12 -0400834void AutoCleanPng::infoCallback(size_t idatLength) {
msarettac6c7502016-04-25 09:30:24 -0700835 png_uint_32 origWidth, origHeight;
836 int bitDepth, encodedColorType;
Leon Scroggins III83239652017-04-21 13:47:12 -0400837 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
msarettac6c7502016-04-25 09:30:24 -0700838 &encodedColorType, nullptr, nullptr, nullptr);
839
Matt Sarett7a1cc672016-12-14 11:48:31 -0500840 // TODO: Should we support 16-bits of precision for gray images?
841 if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType ||
842 PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) {
843 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400844 png_set_strip_16(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700845 }
846
847 // Now determine the default colorType and alphaType and set the required transforms.
848 // Often, we depend on SkSwizzler to perform any transforms that we need. However, we
849 // still depend on libpng for many of the rare and PNG-specific cases.
850 SkEncodedInfo::Color color;
851 SkEncodedInfo::Alpha alpha;
852 switch (encodedColorType) {
853 case PNG_COLOR_TYPE_PALETTE:
854 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
855 // byte into separate bytes (useful for paletted and grayscale images).
856 if (bitDepth < 8) {
857 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500858 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400859 png_set_packing(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700860 }
861
862 color = SkEncodedInfo::kPalette_Color;
863 // Set the alpha depending on if a transparency chunk exists.
Leon Scroggins III83239652017-04-21 13:47:12 -0400864 alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ?
msarettac6c7502016-04-25 09:30:24 -0700865 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
866 break;
867 case PNG_COLOR_TYPE_RGB:
Leon Scroggins III83239652017-04-21 13:47:12 -0400868 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
msarettac6c7502016-04-25 09:30:24 -0700869 // Convert to RGBA if transparency chunk exists.
Leon Scroggins III83239652017-04-21 13:47:12 -0400870 png_set_tRNS_to_alpha(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700871 color = SkEncodedInfo::kRGBA_Color;
872 alpha = SkEncodedInfo::kBinary_Alpha;
873 } else {
874 color = SkEncodedInfo::kRGB_Color;
875 alpha = SkEncodedInfo::kOpaque_Alpha;
876 }
877 break;
878 case PNG_COLOR_TYPE_GRAY:
879 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
880 if (bitDepth < 8) {
881 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500882 bitDepth = 8;
Leon Scroggins III83239652017-04-21 13:47:12 -0400883 png_set_expand_gray_1_2_4_to_8(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700884 }
885
Leon Scroggins III83239652017-04-21 13:47:12 -0400886 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
887 png_set_tRNS_to_alpha(fPng_ptr);
msarettac6c7502016-04-25 09:30:24 -0700888 color = SkEncodedInfo::kGrayAlpha_Color;
889 alpha = SkEncodedInfo::kBinary_Alpha;
890 } else {
891 color = SkEncodedInfo::kGray_Color;
892 alpha = SkEncodedInfo::kOpaque_Alpha;
893 }
894 break;
895 case PNG_COLOR_TYPE_GRAY_ALPHA:
896 color = SkEncodedInfo::kGrayAlpha_Color;
897 alpha = SkEncodedInfo::kUnpremul_Alpha;
898 break;
899 case PNG_COLOR_TYPE_RGBA:
900 color = SkEncodedInfo::kRGBA_Color;
901 alpha = SkEncodedInfo::kUnpremul_Alpha;
902 break;
903 default:
904 // All the color types have been covered above.
905 SkASSERT(false);
906 color = SkEncodedInfo::kRGBA_Color;
907 alpha = SkEncodedInfo::kUnpremul_Alpha;
908 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700909
910 const int numberPasses = png_set_interlace_handling(fPng_ptr);
911
scroggo8e6c7ad2016-09-16 08:20:38 -0700912 if (fOutCodec) {
913 SkASSERT(nullptr == *fOutCodec);
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400914 auto profile = read_color_profile(fPng_ptr, fInfo_ptr);
915 if (profile) {
916 switch (profile->profile()->data_color_space) {
917 case skcms_Signature_CMYK:
918 profile = nullptr;
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400919 break;
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400920 case skcms_Signature_Gray:
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400921 if (SkEncodedInfo::kGray_Color != color &&
922 SkEncodedInfo::kGrayAlpha_Color != color)
923 {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400924 profile = nullptr;
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400925 }
926 break;
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400927 default:
Leon Scroggins IIIf78b55c2017-10-31 13:49:14 -0400928 break;
929 }
Matt Sarett523116d2017-01-12 18:36:38 -0500930 }
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400931 if (!profile) {
raftiasd737bee2016-12-08 10:53:24 -0500932 // Treat unsupported/invalid color spaces as sRGB.
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400933 profile = SkEncodedInfo::ICCProfile::MakeSRGB();
msarettf34cd632016-05-25 10:13:53 -0700934 }
scroggod8d68552016-06-06 11:26:17 -0700935
Mike Reedd6cb11e2017-11-30 15:33:04 -0500936 if (encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
937 png_color_8p sigBits;
938 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
939 if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400940 color = SkEncodedInfo::kXAlpha_Color;
Mike Reedd6cb11e2017-11-30 15:33:04 -0500941 }
942 }
943 } else if (SkEncodedInfo::kOpaque_Alpha == alpha) {
msarett549ca322016-08-17 08:54:08 -0700944 png_color_8p sigBits;
scroggo8e6c7ad2016-09-16 08:20:38 -0700945 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
msarett549ca322016-08-17 08:54:08 -0700946 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
947 // Recommend a decode to 565 if the sBIT indicates 565.
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400948 color = SkEncodedInfo::k565_Color;
msarett549ca322016-08-17 08:54:08 -0700949 }
950 }
951 }
scroggod8d68552016-06-06 11:26:17 -0700952
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400953 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(origWidth, origHeight, color, alpha,
954 bitDepth, std::move(profile));
msarettac6c7502016-04-25 09:30:24 -0700955 if (1 == numberPasses) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400956 *fOutCodec = new SkPngNormalDecoder(std::move(encodedInfo),
Mike Reedede7bac2017-07-23 15:30:02 -0400957 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
msarettac6c7502016-04-25 09:30:24 -0700958 } else {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400959 *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo),
Mike Reedede7bac2017-07-23 15:30:02 -0400960 std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
961 numberPasses);
msarettac6c7502016-04-25 09:30:24 -0700962 }
Leon Scroggins III83239652017-04-21 13:47:12 -0400963 static_cast<SkPngCodec*>(*fOutCodec)->setIdatLength(idatLength);
msarettac6c7502016-04-25 09:30:24 -0700964 }
965
scroggo8e6c7ad2016-09-16 08:20:38 -0700966 // Release the pointers, which are now owned by the codec or the caller is expected to
967 // take ownership.
968 this->releasePngPtrs();
msarettac6c7502016-04-25 09:30:24 -0700969}
970
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400971SkPngCodec::SkPngCodec(SkEncodedInfo&& encodedInfo, std::unique_ptr<SkStream> stream,
972 SkPngChunkReader* chunkReader, void* png_ptr, void* info_ptr, int bitDepth)
973 : INHERITED(std::move(encodedInfo), png_select_xform_format(encodedInfo), std::move(stream))
msarettac6c7502016-04-25 09:30:24 -0700974 , fPngChunkReader(SkSafeRef(chunkReader))
975 , fPng_ptr(png_ptr)
976 , fInfo_ptr(info_ptr)
msarettd1ec89b2016-08-03 12:59:27 -0700977 , fColorXformSrcRow(nullptr)
msarettac6c7502016-04-25 09:30:24 -0700978 , fBitDepth(bitDepth)
Leon Scroggins III83239652017-04-21 13:47:12 -0400979 , fIdatLength(0)
980 , fDecodedIdat(false)
msarettac6c7502016-04-25 09:30:24 -0700981{}
982
983SkPngCodec::~SkPngCodec() {
984 this->destroyReadStruct();
985}
986
987void SkPngCodec::destroyReadStruct() {
988 if (fPng_ptr) {
989 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
990 SkASSERT(fInfo_ptr);
mtklein6dc5b9a2016-08-24 12:22:32 -0700991 png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr);
msarettac6c7502016-04-25 09:30:24 -0700992 fPng_ptr = nullptr;
993 fInfo_ptr = nullptr;
994 }
995}
996
997///////////////////////////////////////////////////////////////////////////////
998// Getting the pixels
999///////////////////////////////////////////////////////////////////////////////
1000
Leon Scroggins571b30f2017-07-11 17:35:31 +00001001SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001002 if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
msarettd1ec89b2016-08-03 12:59:27 -07001003 SkCodecPrintf("Failed on png_read_update_info.\n");
Matt Sarettd59948a2017-04-26 10:59:48 -04001004 return kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -07001005 }
1006 png_read_update_info(fPng_ptr, fInfo_ptr);
1007
Matt Sarett313c4632016-10-20 12:35:23 -04001008 // Reset fSwizzler and this->colorXform(). We can't do this in onRewind() because the
msarett400a93b2016-09-01 18:32:52 -07001009 // interlaced scanline decoder may need to rewind.
1010 fSwizzler.reset(nullptr);
msarett400a93b2016-09-01 18:32:52 -07001011
Matt Sarett34c69d62017-01-19 17:42:23 -05001012 // If SkColorSpaceXform directly supports the encoded PNG format, we should skip format
1013 // conversion in the swizzler (or skip swizzling altogether).
1014 bool skipFormatConversion = false;
1015 switch (this->getEncodedInfo().color()) {
1016 case SkEncodedInfo::kRGB_Color:
1017 if (this->getEncodedInfo().bitsPerComponent() != 16) {
1018 break;
1019 }
1020
1021 // Fall through
1022 case SkEncodedInfo::kRGBA_Color:
1023 skipFormatConversion = this->colorXform();
1024 break;
1025 default:
1026 break;
1027 }
Matt Sarett379938e2017-01-12 18:34:29 -05001028 if (skipFormatConversion && !options.fSubset) {
msarett400a93b2016-09-01 18:32:52 -07001029 fXformMode = kColorOnly_XformMode;
Matt Sarettd59948a2017-04-26 10:59:48 -04001030 return kSuccess;
msarettd1ec89b2016-08-03 12:59:27 -07001031 }
1032
msarettac6c7502016-04-25 09:30:24 -07001033 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001034 if (!this->createColorTable(dstInfo)) {
Matt Sarettd59948a2017-04-26 10:59:48 -04001035 return kInvalidInput;
msarettac6c7502016-04-25 09:30:24 -07001036 }
1037 }
1038
Matt Sarett379938e2017-01-12 18:34:29 -05001039 this->initializeSwizzler(dstInfo, options, skipFormatConversion);
Matt Sarettd59948a2017-04-26 10:59:48 -04001040 return kSuccess;
msarett400a93b2016-09-01 18:32:52 -07001041}
1042
msarettc0444612016-09-16 11:45:58 -07001043void SkPngCodec::initializeXformParams() {
1044 switch (fXformMode) {
1045 case kColorOnly_XformMode:
msarettc0444612016-09-16 11:45:58 -07001046 fXformWidth = this->dstInfo().width();
1047 break;
1048 case kSwizzleColor_XformMode:
msarettc0444612016-09-16 11:45:58 -07001049 fXformWidth = this->swizzler()->swizzleWidth();
1050 break;
1051 default:
1052 break;
1053 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001054}
1055
Matt Sarett379938e2017-01-12 18:34:29 -05001056void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
1057 bool skipFormatConversion) {
msarett400a93b2016-09-01 18:32:52 -07001058 SkImageInfo swizzlerInfo = dstInfo;
1059 Options swizzlerOptions = options;
1060 fXformMode = kSwizzleOnly_XformMode;
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -04001061 if (this->colorXform() && this->xformOnDecode()) {
Matt Sarett562e6812016-11-08 16:13:43 -05001062 swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
msarett400a93b2016-09-01 18:32:52 -07001063 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
1064 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
1065 }
1066
1067 fXformMode = kSwizzleColor_XformMode;
1068
1069 // Here, we swizzle into temporary memory, which is not zero initialized.
1070 // FIXME (msarett):
1071 // Is this a problem?
1072 swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
1073 }
1074
msarettac6c7502016-04-25 09:30:24 -07001075 const SkPMColor* colors = get_color_ptr(fColorTable.get());
msarettd1ec89b2016-08-03 12:59:27 -07001076 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo,
Matt Sarett379938e2017-01-12 18:34:29 -05001077 swizzlerOptions, nullptr, skipFormatConversion));
msarettac6c7502016-04-25 09:30:24 -07001078 SkASSERT(fSwizzler);
msarett400a93b2016-09-01 18:32:52 -07001079}
1080
1081SkSampler* SkPngCodec::getSampler(bool createIfNecessary) {
1082 if (fSwizzler || !createIfNecessary) {
Ben Wagner145dbcd2016-11-03 14:40:50 -04001083 return fSwizzler.get();
msarett400a93b2016-09-01 18:32:52 -07001084 }
1085
Matt Sarett379938e2017-01-12 18:34:29 -05001086 this->initializeSwizzler(this->dstInfo(), this->options(), true);
Ben Wagner145dbcd2016-11-03 14:40:50 -04001087 return fSwizzler.get();
msarettac6c7502016-04-25 09:30:24 -07001088}
1089
msarettac6c7502016-04-25 09:30:24 -07001090bool SkPngCodec::onRewind() {
1091 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
1092 // succeeds, they will be repopulated, and if it fails, they will
1093 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
1094 // come through this function which will rewind and again attempt
1095 // to reinitialize them.
1096 this->destroyReadStruct();
1097
scroggo46c57472015-09-30 08:57:13 -07001098 png_structp png_ptr;
1099 png_infop info_ptr;
Leon Scroggins III588fb042017-07-14 16:32:31 -04001100 if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr,
1101 &png_ptr, &info_ptr)) {
msarettac6c7502016-04-25 09:30:24 -07001102 return false;
scroggo05245902015-03-25 11:11:52 -07001103 }
1104
msarettac6c7502016-04-25 09:30:24 -07001105 fPng_ptr = png_ptr;
1106 fInfo_ptr = info_ptr;
Leon Scroggins III83239652017-04-21 13:47:12 -04001107 fDecodedIdat = false;
msarettac6c7502016-04-25 09:30:24 -07001108 return true;
1109}
msarett6a738212016-03-04 13:27:35 -08001110
msarettd1ec89b2016-08-03 12:59:27 -07001111SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
1112 size_t rowBytes, const Options& options,
msarettac6c7502016-04-25 09:30:24 -07001113 int* rowsDecoded) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001114 Result result = this->initializeXforms(dstInfo, options);
Matt Sarettd59948a2017-04-26 10:59:48 -04001115 if (kSuccess != result) {
1116 return result;
1117 }
1118
msarettac6c7502016-04-25 09:30:24 -07001119 if (options.fSubset) {
msarettac6c7502016-04-25 09:30:24 -07001120 return kUnimplemented;
scroggo05245902015-03-25 11:11:52 -07001121 }
1122
msarett400a93b2016-09-01 18:32:52 -07001123 this->allocateStorage(dstInfo);
msarettc0444612016-09-16 11:45:58 -07001124 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001125 return this->decodeAllRows(dst, rowBytes, rowsDecoded);
1126}
msarettac6c7502016-04-25 09:30:24 -07001127
scroggo8e6c7ad2016-09-16 08:20:38 -07001128SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
Leon Scroggins571b30f2017-07-11 17:35:31 +00001129 void* dst, size_t rowBytes, const SkCodec::Options& options) {
Leon Scroggins571b30f2017-07-11 17:35:31 +00001130 Result result = this->initializeXforms(dstInfo, options);
Matt Sarettd59948a2017-04-26 10:59:48 -04001131 if (kSuccess != result) {
1132 return result;
1133 }
1134
scroggo8e6c7ad2016-09-16 08:20:38 -07001135 this->allocateStorage(dstInfo);
1136
1137 int firstRow, lastRow;
1138 if (options.fSubset) {
1139 firstRow = options.fSubset->top();
1140 lastRow = options.fSubset->bottom() - 1;
1141 } else {
1142 firstRow = 0;
1143 lastRow = dstInfo.height() - 1;
1144 }
1145 this->setRange(firstRow, lastRow, dst, rowBytes);
scroggod8d68552016-06-06 11:26:17 -07001146 return kSuccess;
scroggo6fb23912016-06-02 14:16:43 -07001147}
1148
scroggo8e6c7ad2016-09-16 08:20:38 -07001149SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) {
1150 // FIXME: Only necessary on the first call.
msarettc0444612016-09-16 11:45:58 -07001151 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001152
1153 return this->decode(rowsDecoded);
1154}
1155
Mike Reedede7bac2017-07-23 15:30:02 -04001156std::unique_ptr<SkCodec> SkPngCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
1157 Result* result, SkPngChunkReader* chunkReader) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001158 SkCodec* outCodec = nullptr;
Mike Reedede7bac2017-07-23 15:30:02 -04001159 *result = read_header(stream.get(), chunkReader, &outCodec, nullptr, nullptr);
Leon Scroggins III588fb042017-07-14 16:32:31 -04001160 if (kSuccess == *result) {
msarettac6c7502016-04-25 09:30:24 -07001161 // Codec has taken ownership of the stream.
1162 SkASSERT(outCodec);
Mike Reedede7bac2017-07-23 15:30:02 -04001163 stream.release();
msarettac6c7502016-04-25 09:30:24 -07001164 }
Mike Reedede7bac2017-07-23 15:30:02 -04001165 return std::unique_ptr<SkCodec>(outCodec);
scroggo05245902015-03-25 11:11:52 -07001166}