blob: 468b0b873162804c76b6d86a1087e99447e04a81 [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"
scroggof24f2242015-03-03 08:59:20 -080010#include "SkColorPriv.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"
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"
msarett55447952016-07-22 14:07:23 -070017#include "SkPoint3.h"
scroggof24f2242015-03-03 08:59:20 -080018#include "SkSize.h"
19#include "SkStream.h"
20#include "SkSwizzler.h"
scroggo565901d2015-12-10 10:44:13 -080021#include "SkTemplates.h"
bungeman5d2cd6e2016-02-23 07:34:25 -080022#include "SkUtils.h"
scroggof24f2242015-03-03 08:59:20 -080023
mtklein63213812016-08-24 09:55:56 -070024#include "png.h"
25
mtkleindc90b532016-07-28 14:45:28 -070026// This warning triggers false postives way too often in here.
27#if defined(__GNUC__) && !defined(__clang__)
28 #pragma GCC diagnostic ignored "-Wclobbered"
29#endif
30
scroggo8e6c7ad2016-09-16 08:20:38 -070031#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
32 // This is not needed with version 1.5
33 #undef SK_GOOGLE3_PNG_HACK
34#endif
35
36// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6
37#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x)
38
scroggof24f2242015-03-03 08:59:20 -080039///////////////////////////////////////////////////////////////////////////////
scroggof24f2242015-03-03 08:59:20 -080040// Callback functions
41///////////////////////////////////////////////////////////////////////////////
42
scroggo8e6c7ad2016-09-16 08:20:38 -070043// When setjmp is first called, it returns 0, meaning longjmp was not called.
44constexpr int kSetJmpOkay = 0;
45// An error internal to libpng.
46constexpr int kPngError = 1;
47// Passed to longjmp when we have decoded as many lines as we need.
48constexpr int kStopDecoding = 2;
49
scroggof24f2242015-03-03 08:59:20 -080050static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070051 SkCodecPrintf("------ png error %s\n", msg);
scroggo8e6c7ad2016-09-16 08:20:38 -070052 longjmp(PNG_JMPBUF(png_ptr), kPngError);
scroggof24f2242015-03-03 08:59:20 -080053}
54
scroggo0eed6df2015-03-26 10:07:56 -070055void sk_warning_fn(png_structp, png_const_charp msg) {
56 SkCodecPrintf("----- png warning %s\n", msg);
57}
58
scroggocf98fa92015-11-23 08:14:40 -080059#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
60static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
61 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
62 // readChunk() returning true means continue decoding
63 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
64}
65#endif
66
scroggof24f2242015-03-03 08:59:20 -080067///////////////////////////////////////////////////////////////////////////////
68// Helpers
69///////////////////////////////////////////////////////////////////////////////
70
71class AutoCleanPng : public SkNoncopyable {
72public:
scroggo8e6c7ad2016-09-16 08:20:38 -070073 /*
74 * This class does not take ownership of stream or reader, but if codecPtr
75 * is non-NULL, and decodeBounds succeeds, it will have created a new
76 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as
77 * the png_ptr and info_ptr.
78 */
79 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader,
80 SkCodec** codecPtr)
scroggof24f2242015-03-03 08:59:20 -080081 : fPng_ptr(png_ptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070082 , fInfo_ptr(nullptr)
83 , fDecodedBounds(false)
84 , fReadHeader(false)
85 , fStream(stream)
86 , fChunkReader(reader)
87 , fOutCodec(codecPtr)
88 {}
scroggof24f2242015-03-03 08:59:20 -080089
90 ~AutoCleanPng() {
halcanary96fcdcc2015-08-27 07:41:13 -070091 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
scroggof24f2242015-03-03 08:59:20 -080092 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070093 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
msarett13a91232016-02-01 08:03:29 -080094 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr);
scroggof24f2242015-03-03 08:59:20 -080095 }
96 }
97
98 void setInfoPtr(png_infop info_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070099 SkASSERT(nullptr == fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800100 fInfo_ptr = info_ptr;
101 }
102
scroggo8e6c7ad2016-09-16 08:20:38 -0700103 /**
104 * Reads enough of the input stream to decode the bounds.
105 * @return false if the stream is not a valid PNG (or too short).
106 * true if it read enough of the stream to determine the bounds.
107 * In the latter case, the stream may have been read beyond the
108 * point to determine the bounds, and the png_ptr will have saved
109 * any extra data. Further, if the codecPtr supplied to the
110 * constructor was not NULL, it will now point to a new SkCodec,
111 * which owns (or refs, in the case of the SkPngChunkReader) the
112 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are
113 * unowned, and it is up to the caller to destroy them.
114 */
115 bool decodeBounds();
116
117private:
118 png_structp fPng_ptr;
119 png_infop fInfo_ptr;
120 bool fDecodedBounds;
121 bool fReadHeader;
122 SkStream* fStream;
123 SkPngChunkReader* fChunkReader;
124 SkCodec** fOutCodec;
125
126 /**
127 * Supplied to libpng to call when it has read enough data to determine
128 * bounds.
129 */
130 static void InfoCallback(png_structp png_ptr, png_infop) {
131 // png_get_progressive_ptr returns the pointer we set on the png_ptr with
132 // png_set_progressive_read_fn
133 static_cast<AutoCleanPng*>(png_get_progressive_ptr(png_ptr))->infoCallback();
134 }
135
136 void infoCallback();
137
138#ifdef SK_GOOGLE3_PNG_HACK
139// public so it can be called by SkPngCodec::rereadHeaderIfNecessary().
140public:
141#endif
142 void releasePngPtrs() {
halcanary96fcdcc2015-08-27 07:41:13 -0700143 fPng_ptr = nullptr;
144 fInfo_ptr = nullptr;
scroggof24f2242015-03-03 08:59:20 -0800145 }
scroggof24f2242015-03-03 08:59:20 -0800146};
147#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
148
scroggo8e6c7ad2016-09-16 08:20:38 -0700149bool AutoCleanPng::decodeBounds() {
150 if (setjmp(PNG_JMPBUF(fPng_ptr))) {
151 return false;
152 }
153
154 png_set_progressive_read_fn(fPng_ptr, this, InfoCallback, nullptr, nullptr);
155
156 // Arbitrary buffer size, though note that it matches (below)
157 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of
158 // the PNG header?
159 constexpr size_t kBufferSize = 4096;
160 char buffer[kBufferSize];
161
162 while (true) {
163 const size_t bytesRead = fStream->read(buffer, kBufferSize);
164 if (!bytesRead) {
165 // We have read to the end of the input without decoding bounds.
166 break;
167 }
168
169 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead);
170 if (fReadHeader) {
171 break;
172 }
173 }
174
175 // For safety, clear the pointer to this object.
176 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr);
177 return fDecodedBounds;
178}
179
180void SkPngCodec::processData() {
181 switch (setjmp(PNG_JMPBUF(fPng_ptr))) {
182 case kPngError:
183 // There was an error. Stop processing data.
184 // FIXME: Do we need to discard png_ptr?
185 return;
186 case kStopDecoding:
187 // We decoded all the lines we want.
188 return;
189 case kSetJmpOkay:
190 // Everything is okay.
191 break;
192 default:
193 // No other values should be passed to longjmp.
194 SkASSERT(false);
195 }
196
197 // Arbitrary buffer size
198 constexpr size_t kBufferSize = 4096;
199 char buffer[kBufferSize];
200
201 while (true) {
202 const size_t bytesRead = this->stream()->read(buffer, kBufferSize);
203 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead);
204
205 if (!bytesRead) {
206 // We have read to the end of the input. Note that we quit *after*
207 // calling png_process_data, because decodeBounds may have told
208 // libpng to save the remainder of the buffer, in which case
209 // png_process_data will process the saved buffer, though the
210 // stream has no more to read.
211 break;
212 }
213 }
214}
215
Matt Sarett562e6812016-11-08 16:13:43 -0500216static const SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
217
msarettd1ec89b2016-08-03 12:59:27 -0700218// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
msarettdcd5e652016-08-22 08:48:40 -0700219bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800220
msarett13a91232016-02-01 08:03:29 -0800221 int numColors;
222 png_color* palette;
223 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
scroggo05245902015-03-25 11:11:52 -0700224 return false;
scroggof24f2242015-03-03 08:59:20 -0800225 }
226
msarettdcd5e652016-08-22 08:48:40 -0700227 // Contents depend on tableColorType and our choice of if/when to premultiply:
228 // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
229 SkPMColor colorTable[256];
Matt Sarett562e6812016-11-08 16:13:43 -0500230 SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType();
scroggof24f2242015-03-03 08:59:20 -0800231
msarett13a91232016-02-01 08:03:29 -0800232 png_bytep alphas;
233 int numColorsWithAlpha = 0;
234 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
msarettdcd5e652016-08-22 08:48:40 -0700235 // If we are performing a color xform, it will handle the premultiply. Otherwise,
236 // we'll do it here.
Matt Sarett61eedeb2016-11-04 13:19:48 -0400237 bool premultiply = !this->colorXform() && needs_premul(dstInfo, this->getEncodedInfo());
msarettdcd5e652016-08-22 08:48:40 -0700238
msarett13a91232016-02-01 08:03:29 -0800239 // Choose which function to use to create the color table. If the final destination's
240 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
msarettdcd5e652016-08-22 08:48:40 -0700241 PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType);
msarett13a91232016-02-01 08:03:29 -0800242
243 for (int i = 0; i < numColorsWithAlpha; i++) {
244 // We don't have a function in SkOpts that combines a set of alphas with a set
245 // of RGBs. We could write one, but it's hardly worth it, given that this
246 // is such a small fraction of the total decode time.
msarettdcd5e652016-08-22 08:48:40 -0700247 colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue);
msarett13a91232016-02-01 08:03:29 -0800248 palette++;
249 }
scroggof24f2242015-03-03 08:59:20 -0800250 }
251
msarett13a91232016-02-01 08:03:29 -0800252 if (numColorsWithAlpha < numColors) {
253 // The optimized code depends on a 3-byte png_color struct with the colors
254 // in RGB order. These checks make sure it is safe to use.
255 static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken.");
256#ifdef SK_DEBUG
257 SkASSERT(&palette->red < &palette->green);
258 SkASSERT(&palette->green < &palette->blue);
259#endif
260
msarettdcd5e652016-08-22 08:48:40 -0700261 if (is_rgba(tableColorType)) {
262 SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700263 numColors - numColorsWithAlpha);
264 } else {
msarettdcd5e652016-08-22 08:48:40 -0700265 SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700266 numColors - numColorsWithAlpha);
267 }
scroggof24f2242015-03-03 08:59:20 -0800268 }
269
msarettdcd5e652016-08-22 08:48:40 -0700270 // If we are not decoding to F16, we can color xform now and store the results
271 // in the color table.
Matt Sarett313c4632016-10-20 12:35:23 -0400272 if (this->colorXform() && kRGBA_F16_SkColorType != dstInfo.colorType()) {
Matt Sarett562e6812016-11-08 16:13:43 -0500273 const SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType());
274 const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType);
275 const SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
276 this->getInfo().alphaType());
277 SkAssertResult(this->colorXform()->apply(dstFormat, colorTable, srcFormat, colorTable,
278 numColors, xformAlphaType));
msarettdcd5e652016-08-22 08:48:40 -0700279 }
280
msarett13a91232016-02-01 08:03:29 -0800281 // Pad the color table with the last color in the table (or black) in the case that
282 // invalid pixel indices exceed the number of colors in the table.
283 const int maxColors = 1 << fBitDepth;
284 if (numColors < maxColors) {
msarettdcd5e652016-08-22 08:48:40 -0700285 SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK;
286 sk_memset32(colorTable + numColors, lastColor, maxColors - numColors);
scroggof24f2242015-03-03 08:59:20 -0800287 }
288
msarett13a91232016-02-01 08:03:29 -0800289 // Set the new color count.
halcanary96fcdcc2015-08-27 07:41:13 -0700290 if (ctableCount != nullptr) {
msarett13a91232016-02-01 08:03:29 -0800291 *ctableCount = maxColors;
scroggof24f2242015-03-03 08:59:20 -0800292 }
293
msarettdcd5e652016-08-22 08:48:40 -0700294 fColorTable.reset(new SkColorTable(colorTable, maxColors));
scroggo05245902015-03-25 11:11:52 -0700295 return true;
scroggof24f2242015-03-03 08:59:20 -0800296}
297
298///////////////////////////////////////////////////////////////////////////////
299// Creation
300///////////////////////////////////////////////////////////////////////////////
301
scroggodb30be22015-12-08 18:54:13 -0800302bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
303 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
scroggof24f2242015-03-03 08:59:20 -0800304}
305
scroggo8e6c7ad2016-09-16 08:20:38 -0700306#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
307
msarett6a738212016-03-04 13:27:35 -0800308static float png_fixed_point_to_float(png_fixed_point x) {
309 // We multiply by the same factor that libpng used to convert
310 // fixed point -> double. Since we want floats, we choose to
311 // do the conversion ourselves rather than convert
312 // fixed point -> double -> float.
313 return ((float) x) * 0.00001f;
314}
315
msarett128245c2016-03-30 12:01:47 -0700316static float png_inverted_fixed_point_to_float(png_fixed_point x) {
317 // This is necessary because the gAMA chunk actually stores 1/gamma.
318 return 1.0f / png_fixed_point_to_float(x);
319}
320
scroggo8e6c7ad2016-09-16 08:20:38 -0700321#endif // LIBPNG >= 1.6
322
msarett6a738212016-03-04 13:27:35 -0800323// Returns a colorSpace object that represents any color space information in
raftiasd737bee2016-12-08 10:53:24 -0500324// the encoded data. If the encoded data contains an invalid/unsupported color space,
325// this will return NULL. If there is no color space information, it will guess sRGB
Matt Sarett523116d2017-01-12 18:36:38 -0500326sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr,
327 SkColorSpace_Base::ICCTypeFlag iccType) {
msarett6a738212016-03-04 13:27:35 -0800328
msarette2443222016-03-04 14:20:49 -0800329#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
330
msarett6a738212016-03-04 13:27:35 -0800331 // First check for an ICC profile
332 png_bytep profile;
333 png_uint_32 length;
334 // The below variables are unused, however, we need to pass them in anyway or
335 // png_get_iCCP() will return nothing.
336 // Could knowing the |name| of the profile ever be interesting? Maybe for debugging?
337 png_charp name;
338 // The |compression| is uninteresting since:
339 // (1) libpng has already decompressed the profile for us.
340 // (2) "deflate" is the only mode of decompression that libpng supports.
341 int compression;
342 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
343 &length)) {
Matt Sarett523116d2017-01-12 18:36:38 -0500344 return SkColorSpace_Base::MakeICC(profile, length, iccType);
msarett6a738212016-03-04 13:27:35 -0800345 }
346
347 // Second, check for sRGB.
348 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
349
350 // sRGB chunks also store a rendering intent: Absolute, Relative,
351 // Perceptual, and Saturation.
352 // FIXME (msarett): Extract this information from the sRGB chunk once
353 // we are able to handle this information in
354 // SkColorSpace.
Brian Osman526972e2016-10-24 09:24:02 -0400355 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
msarett6a738212016-03-04 13:27:35 -0800356 }
357
358 // Next, check for chromaticities.
msaretta5a31dd2016-10-11 09:41:16 -0700359 png_fixed_point chrm[8];
msarett6a738212016-03-04 13:27:35 -0800360 png_fixed_point gamma;
msaretta5a31dd2016-10-11 09:41:16 -0700361 if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4],
362 &chrm[5], &chrm[6], &chrm[7]))
msarett55447952016-07-22 14:07:23 -0700363 {
msaretta5a31dd2016-10-11 09:41:16 -0700364 SkColorSpacePrimaries primaries;
365 primaries.fRX = png_fixed_point_to_float(chrm[2]);
366 primaries.fRY = png_fixed_point_to_float(chrm[3]);
367 primaries.fGX = png_fixed_point_to_float(chrm[4]);
368 primaries.fGY = png_fixed_point_to_float(chrm[5]);
369 primaries.fBX = png_fixed_point_to_float(chrm[6]);
370 primaries.fBY = png_fixed_point_to_float(chrm[7]);
371 primaries.fWX = png_fixed_point_to_float(chrm[0]);
372 primaries.fWY = png_fixed_point_to_float(chrm[1]);
msarett55447952016-07-22 14:07:23 -0700373
374 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
msaretta5a31dd2016-10-11 09:41:16 -0700375 if (!primaries.toXYZD50(&toXYZD50)) {
msarett55447952016-07-22 14:07:23 -0700376 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
377 }
msarett6a738212016-03-04 13:27:35 -0800378
msarett128245c2016-03-30 12:01:47 -0700379 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400380 SkColorSpaceTransferFn fn;
381 fn.fA = 1.0f;
382 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
383 fn.fG = png_inverted_fixed_point_to_float(gamma);
msarettffc2aea2016-05-02 11:12:14 -0700384
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400385 return SkColorSpace::MakeRGB(fn, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800386 }
msarett128245c2016-03-30 12:01:47 -0700387
msarettc4ce6b52016-06-16 07:37:41 -0700388 // Default to sRGB gamma if the image has color space information,
389 // but does not specify gamma.
Brian Osman526972e2016-10-24 09:24:02 -0400390 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800391 }
392
393 // Last, check for gamma.
394 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400395 SkColorSpaceTransferFn fn;
396 fn.fA = 1.0f;
397 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
398 fn.fG = png_inverted_fixed_point_to_float(gamma);
msarett6a738212016-03-04 13:27:35 -0800399
msarettc4ce6b52016-06-16 07:37:41 -0700400 // Since there is no cHRM, we will guess sRGB gamut.
msarett55447952016-07-22 14:07:23 -0700401 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
402 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
msarettc4ce6b52016-06-16 07:37:41 -0700403
Matt Sarett99e3f7d2016-10-28 12:51:08 -0400404 return SkColorSpace::MakeRGB(fn, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800405 }
406
msarette2443222016-03-04 14:20:49 -0800407#endif // LIBPNG >= 1.6
408
raftiasd737bee2016-12-08 10:53:24 -0500409 // Report that there is no color space information in the PNG.
410 // Guess sRGB in this case.
411 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
msarett6a738212016-03-04 13:27:35 -0800412}
413
msarett400a93b2016-09-01 18:32:52 -0700414void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
415 switch (fXformMode) {
416 case kSwizzleOnly_XformMode:
msarett400a93b2016-09-01 18:32:52 -0700417 break;
418 case kColorOnly_XformMode:
419 // Intentional fall through. A swizzler hasn't been created yet, but one will
420 // be created later if we are sampling. We'll go ahead and allocate
421 // enough memory to swizzle if necessary.
422 case kSwizzleColor_XformMode: {
Matt Sarett34c69d62017-01-19 17:42:23 -0500423 const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel();
424
425 // If we have more than 8-bits (per component) of precision, we will keep that
426 // extra precision. Otherwise, we will swizzle to RGBA_8888 before transforming.
427 const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4;
428 const size_t colorXformBytes = dstInfo.width() * bytesPerPixel;
scroggo8e6c7ad2016-09-16 08:20:38 -0700429 fStorage.reset(colorXformBytes);
Matt Sarett379938e2017-01-12 18:34:29 -0500430 fColorXformSrcRow = fStorage.get();
msarett400a93b2016-09-01 18:32:52 -0700431 break;
432 }
433 }
msarettd1ec89b2016-08-03 12:59:27 -0700434}
435
Matt Sarett379938e2017-01-12 18:34:29 -0500436static SkColorSpaceXform::ColorFormat png_select_xform_format(const SkEncodedInfo& info) {
Matt Sarett34c69d62017-01-19 17:42:23 -0500437 // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
438 if (16 == info.bitsPerComponent()) {
439 if (SkEncodedInfo::kRGBA_Color == info.color()) {
440 return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat;
441 } else if (SkEncodedInfo::kRGB_Color == info.color()) {
442 return SkColorSpaceXform::kRGB_U16_BE_ColorFormat;
443 }
Matt Sarett379938e2017-01-12 18:34:29 -0500444 }
445
446 return SkColorSpaceXform::kRGBA_8888_ColorFormat;
447}
448
scroggo8e6c7ad2016-09-16 08:20:38 -0700449void SkPngCodec::applyXformRow(void* dst, const void* src) {
Matt Sarett379938e2017-01-12 18:34:29 -0500450 const SkColorSpaceXform::ColorFormat srcColorFormat =
451 png_select_xform_format(this->getEncodedInfo());
msarett400a93b2016-09-01 18:32:52 -0700452 switch (fXformMode) {
453 case kSwizzleOnly_XformMode:
454 fSwizzler->swizzle(dst, (const uint8_t*) src);
455 break;
456 case kColorOnly_XformMode:
Matt Sarett313c4632016-10-20 12:35:23 -0400457 SkAssertResult(this->colorXform()->apply(fXformColorFormat, dst, srcColorFormat, src,
458 fXformWidth, fXformAlphaType));
msarett400a93b2016-09-01 18:32:52 -0700459 break;
460 case kSwizzleColor_XformMode:
461 fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
Matt Sarett313c4632016-10-20 12:35:23 -0400462 SkAssertResult(this->colorXform()->apply(fXformColorFormat, dst, srcColorFormat,
463 fColorXformSrcRow, fXformWidth, fXformAlphaType));
msarett400a93b2016-09-01 18:32:52 -0700464 break;
465 }
msarettdcd5e652016-08-22 08:48:40 -0700466}
467
scroggo8e6c7ad2016-09-16 08:20:38 -0700468class SkPngNormalDecoder : public SkPngCodec {
scroggo05245902015-03-25 11:11:52 -0700469public:
scroggo8e6c7ad2016-09-16 08:20:38 -0700470 SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkStream* stream,
471 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
472 : INHERITED(info, imageInfo, stream, reader, png_ptr, info_ptr, bitDepth)
scroggoff9f7bb2016-10-10 11:35:01 -0700473 , fRowsWrittenToOutput(0)
scroggo8e6c7ad2016-09-16 08:20:38 -0700474 , fDst(nullptr)
475 , fRowBytes(0)
476 , fFirstRow(0)
477 , fLastRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700478 {}
479
scroggo8e6c7ad2016-09-16 08:20:38 -0700480 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
481 GetDecoder(png_ptr)->allRowsCallback(row, rowNum);
scroggo05245902015-03-25 11:11:52 -0700482 }
483
scroggo8e6c7ad2016-09-16 08:20:38 -0700484 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
485 GetDecoder(png_ptr)->rowCallback(row, rowNum);
msarettd1ec89b2016-08-03 12:59:27 -0700486 }
487
scroggo8e6c7ad2016-09-16 08:20:38 -0700488#ifdef SK_GOOGLE3_PNG_HACK
489 static void RereadInfoCallback(png_structp png_ptr, png_infop) {
490 GetDecoder(png_ptr)->rereadInfoCallback();
scroggo05245902015-03-25 11:11:52 -0700491 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700492#endif
emmaleer8f4ba762015-08-14 07:44:46 -0700493
emmaleer0a4c3cb2015-06-22 10:40:21 -0700494private:
scroggoff9f7bb2016-10-10 11:35:01 -0700495 int fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700496 void* fDst;
497 size_t fRowBytes;
498
499 // Variables for partial decode
500 int fFirstRow; // FIXME: Move to baseclass?
501 int fLastRow;
scroggo4f2a88c2016-10-17 14:32:41 -0700502 int fRowsNeeded;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700503
scroggo46c57472015-09-30 08:57:13 -0700504 typedef SkPngCodec INHERITED;
scroggo8e6c7ad2016-09-16 08:20:38 -0700505
506 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) {
507 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr));
508 }
509
510 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
511 const int height = this->getInfo().height();
512 png_progressive_info_ptr callback = nullptr;
513#ifdef SK_GOOGLE3_PNG_HACK
514 callback = RereadInfoCallback;
515#endif
516 png_set_progressive_read_fn(this->png_ptr(), this, callback, AllRowsCallback, nullptr);
517 fDst = dst;
518 fRowBytes = rowBytes;
519
scroggoff9f7bb2016-10-10 11:35:01 -0700520 fRowsWrittenToOutput = 0;
scroggoc46cdd42016-10-10 06:45:32 -0700521 fFirstRow = 0;
522 fLastRow = height - 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700523
524 this->processData();
525
scroggoff9f7bb2016-10-10 11:35:01 -0700526 if (fRowsWrittenToOutput == height) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700527 return SkCodec::kSuccess;
528 }
529
530 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700531 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700532 }
533
534 return SkCodec::kIncompleteInput;
535 }
536
537 void allRowsCallback(png_bytep row, int rowNum) {
scroggoff9f7bb2016-10-10 11:35:01 -0700538 SkASSERT(rowNum == fRowsWrittenToOutput);
539 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700540 this->applyXformRow(fDst, row);
541 fDst = SkTAddOffset<void>(fDst, fRowBytes);
542 }
543
544 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
545 png_progressive_info_ptr callback = nullptr;
546#ifdef SK_GOOGLE3_PNG_HACK
547 callback = RereadInfoCallback;
548#endif
549 png_set_progressive_read_fn(this->png_ptr(), this, callback, RowCallback, nullptr);
550 fFirstRow = firstRow;
551 fLastRow = lastRow;
552 fDst = dst;
553 fRowBytes = rowBytes;
scroggoff9f7bb2016-10-10 11:35:01 -0700554 fRowsWrittenToOutput = 0;
scroggo4f2a88c2016-10-17 14:32:41 -0700555 fRowsNeeded = fLastRow - fFirstRow + 1;
scroggo8e6c7ad2016-09-16 08:20:38 -0700556 }
557
558 SkCodec::Result decode(int* rowsDecoded) override {
scroggo4f2a88c2016-10-17 14:32:41 -0700559 if (this->swizzler()) {
560 const int sampleY = this->swizzler()->sampleY();
561 fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
562 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700563 this->processData();
564
scroggo4f2a88c2016-10-17 14:32:41 -0700565 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700566 return SkCodec::kSuccess;
567 }
568
569 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700570 *rowsDecoded = fRowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700571 }
572
573 return SkCodec::kIncompleteInput;
574 }
575
576 void rowCallback(png_bytep row, int rowNum) {
577 if (rowNum < fFirstRow) {
578 // Ignore this row.
579 return;
580 }
581
582 SkASSERT(rowNum <= fLastRow);
scroggo4f2a88c2016-10-17 14:32:41 -0700583 SkASSERT(fRowsWrittenToOutput < fRowsNeeded);
scroggo8e6c7ad2016-09-16 08:20:38 -0700584
585 // If there is no swizzler, all rows are needed.
scroggo4f2a88c2016-10-17 14:32:41 -0700586 if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700587 this->applyXformRow(fDst, row);
588 fDst = SkTAddOffset<void>(fDst, fRowBytes);
scroggoff9f7bb2016-10-10 11:35:01 -0700589 fRowsWrittenToOutput++;
scroggo8e6c7ad2016-09-16 08:20:38 -0700590 }
591
scroggo4f2a88c2016-10-17 14:32:41 -0700592 if (fRowsWrittenToOutput == fRowsNeeded) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700593 // Fake error to stop decoding scanlines.
594 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
595 }
596 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700597};
598
scroggo8e6c7ad2016-09-16 08:20:38 -0700599class SkPngInterlacedDecoder : public SkPngCodec {
600public:
601 SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
602 SkStream* stream, SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr,
603 int bitDepth, int numberPasses)
604 : INHERITED(info, imageInfo, stream, reader, png_ptr, info_ptr, bitDepth)
605 , fNumberPasses(numberPasses)
606 , fFirstRow(0)
607 , fLastRow(0)
608 , fLinesDecoded(0)
609 , fInterlacedComplete(false)
610 , fPng_rowbytes(0)
611 {}
612
613 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) {
614 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr));
615 decoder->interlacedRowCallback(row, rowNum, pass);
616 }
617
618#ifdef SK_GOOGLE3_PNG_HACK
619 static void RereadInfoInterlacedCallback(png_structp png_ptr, png_infop) {
620 static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr))->rereadInfoInterlaced();
621 }
622#endif
623
624private:
625 const int fNumberPasses;
626 int fFirstRow;
627 int fLastRow;
628 void* fDst;
629 size_t fRowBytes;
630 int fLinesDecoded;
631 bool fInterlacedComplete;
632 size_t fPng_rowbytes;
633 SkAutoTMalloc<png_byte> fInterlaceBuffer;
634
635 typedef SkPngCodec INHERITED;
636
637#ifdef SK_GOOGLE3_PNG_HACK
638 void rereadInfoInterlaced() {
639 this->rereadInfoCallback();
640 // Note: This allocates more memory than necessary, if we are sampling/subset.
641 this->setUpInterlaceBuffer(this->getInfo().height());
642 }
643#endif
644
645 // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
646 // as expensive as the subset version of non-interlaced, but it still does extra
647 // work.
648 void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
649 if (rowNum < fFirstRow || rowNum > fLastRow) {
650 // Ignore this row
651 return;
652 }
653
654 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
655 png_progressive_combine_row(this->png_ptr(), oldRow, row);
656
657 if (0 == pass) {
658 // The first pass initializes all rows.
659 SkASSERT(row);
660 SkASSERT(fLinesDecoded == rowNum - fFirstRow);
661 fLinesDecoded++;
662 } else {
663 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
664 if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
665 // Last pass, and we have read all of the rows we care about. Note that
666 // we do not care about reading anything beyond the end of the image (or
667 // beyond the last scanline requested).
668 fInterlacedComplete = true;
669 // Fake error to stop decoding scanlines.
670 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
671 }
672 }
673 }
674
675 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
676 const int height = this->getInfo().height();
677 this->setUpInterlaceBuffer(height);
678 png_progressive_info_ptr callback = nullptr;
679#ifdef SK_GOOGLE3_PNG_HACK
680 callback = RereadInfoInterlacedCallback;
681#endif
682 png_set_progressive_read_fn(this->png_ptr(), this, callback, InterlacedRowCallback,
683 nullptr);
684
685 fFirstRow = 0;
686 fLastRow = height - 1;
687 fLinesDecoded = 0;
688
689 this->processData();
690
691 png_bytep srcRow = fInterlaceBuffer.get();
692 // FIXME: When resuming, this may rewrite rows that did not change.
693 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) {
694 this->applyXformRow(dst, srcRow);
695 dst = SkTAddOffset<void>(dst, rowBytes);
696 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes);
697 }
698 if (fInterlacedComplete) {
699 return SkCodec::kSuccess;
700 }
701
702 if (rowsDecoded) {
703 *rowsDecoded = fLinesDecoded;
704 }
705
706 return SkCodec::kIncompleteInput;
707 }
708
709 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
710 // FIXME: We could skip rows in the interlace buffer that we won't put in the output.
711 this->setUpInterlaceBuffer(lastRow - firstRow + 1);
712 png_progressive_info_ptr callback = nullptr;
713#ifdef SK_GOOGLE3_PNG_HACK
714 callback = RereadInfoInterlacedCallback;
715#endif
716 png_set_progressive_read_fn(this->png_ptr(), this, callback, InterlacedRowCallback, nullptr);
717 fFirstRow = firstRow;
718 fLastRow = lastRow;
719 fDst = dst;
720 fRowBytes = rowBytes;
721 fLinesDecoded = 0;
722 }
723
724 SkCodec::Result decode(int* rowsDecoded) override {
725 this->processData();
726
727 // Now apply Xforms on all the rows that were decoded.
728 if (!fLinesDecoded) {
scroggoe61b3b42016-10-10 07:17:32 -0700729 if (rowsDecoded) {
730 *rowsDecoded = 0;
731 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700732 return SkCodec::kIncompleteInput;
733 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700734
scroggo4f2a88c2016-10-17 14:32:41 -0700735 const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1;
736 const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
scroggoff9f7bb2016-10-10 11:35:01 -0700737 int rowsWrittenToOutput = 0;
738
scroggo8e6c7ad2016-09-16 08:20:38 -0700739 // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it
740 // may be too tricky/expensive to handle that correctly.
scroggoe9ea3492016-10-18 09:14:11 -0700741
742 // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow,
743 // since the first row in fInterlaceBuffer corresponds to fFirstRow.
744 png_bytep srcRow = SkTAddOffset<png_byte>(fInterlaceBuffer.get(),
745 fPng_rowbytes * get_start_coord(sampleY));
scroggo8e6c7ad2016-09-16 08:20:38 -0700746 void* dst = fDst;
scroggoe9ea3492016-10-18 09:14:11 -0700747 for (; rowsWrittenToOutput < rowsNeeded; rowsWrittenToOutput++) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700748 this->applyXformRow(dst, srcRow);
749 dst = SkTAddOffset<void>(dst, fRowBytes);
750 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY);
751 }
752
753 if (fInterlacedComplete) {
754 return SkCodec::kSuccess;
755 }
756
757 if (rowsDecoded) {
scroggoff9f7bb2016-10-10 11:35:01 -0700758 *rowsDecoded = rowsWrittenToOutput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700759 }
760 return SkCodec::kIncompleteInput;
761 }
762
763 void setUpInterlaceBuffer(int height) {
764 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr());
765 fInterlaceBuffer.reset(fPng_rowbytes * height);
766 fInterlacedComplete = false;
767 }
768};
769
770#ifdef SK_GOOGLE3_PNG_HACK
771bool SkPngCodec::rereadHeaderIfNecessary() {
772 if (!fNeedsToRereadHeader) {
773 return true;
774 }
775
776 // On the first call, we'll need to rewind ourselves. Future calls will
777 // have already rewound in rewindIfNecessary.
778 if (this->stream()->getPosition() > 0) {
779 this->stream()->rewind();
780 }
781
782 this->destroyReadStruct();
783 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
784 sk_error_fn, sk_warning_fn);
785 if (!png_ptr) {
786 return false;
787 }
788
789 // Only use the AutoCleanPng to delete png_ptr as necessary.
790 // (i.e. not for reading bounds etc.)
791 AutoCleanPng autoClean(png_ptr, nullptr, nullptr, nullptr);
792
793 png_infop info_ptr = png_create_info_struct(png_ptr);
794 if (info_ptr == nullptr) {
795 return false;
796 }
797
798 autoClean.setInfoPtr(info_ptr);
799
800#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
801 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
802 // This needs to be installed before we read the png header. Android may store ninepatch
803 // chunks in the header.
804 if (fPngChunkReader.get()) {
805 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
806 png_set_read_user_chunk_fn(png_ptr, (png_voidp) fPngChunkReader.get(), sk_read_user_chunk);
807 }
808#endif
809
810 fPng_ptr = png_ptr;
811 fInfo_ptr = info_ptr;
812 autoClean.releasePngPtrs();
813 fNeedsToRereadHeader = false;
814 return true;
815}
816#endif // SK_GOOGLE3_PNG_HACK
817
msarettac6c7502016-04-25 09:30:24 -0700818// Reads the header and initializes the output fields, if not NULL.
819//
820// @param stream Input data. Will be read to get enough information to properly
821// setup the codec.
822// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
823// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
824// expected to continue to own it for the lifetime of the png_ptr.
825// @param outCodec Optional output variable. If non-NULL, will be set to a new
826// SkPngCodec on success.
827// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
828// png_structp on success.
829// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
830// png_infop on success;
831// @return true on success, in which case the caller is responsible for calling
832// png_destroy_read_struct(png_ptrp, info_ptrp).
833// If it returns false, the passed in fields (except stream) are unchanged.
834static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec,
835 png_structp* png_ptrp, png_infop* info_ptrp) {
836 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
837 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
838 sk_error_fn, sk_warning_fn);
839 if (!png_ptr) {
840 return false;
841 }
842
scroggo8e6c7ad2016-09-16 08:20:38 -0700843 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
msarettac6c7502016-04-25 09:30:24 -0700844
845 png_infop info_ptr = png_create_info_struct(png_ptr);
846 if (info_ptr == nullptr) {
847 return false;
848 }
849
850 autoClean.setInfoPtr(info_ptr);
851
852 // FIXME: Could we use the return value of setjmp to specify the type of
853 // error?
scroggo8e6c7ad2016-09-16 08:20:38 -0700854 if (setjmp(PNG_JMPBUF(png_ptr))) {
msarettac6c7502016-04-25 09:30:24 -0700855 return false;
856 }
857
msarettac6c7502016-04-25 09:30:24 -0700858#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
859 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
860 // This needs to be installed before we read the png header. Android may store ninepatch
861 // chunks in the header.
862 if (chunkReader) {
863 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
864 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
865 }
866#endif
867
scroggo8e6c7ad2016-09-16 08:20:38 -0700868 const bool decodedBounds = autoClean.decodeBounds();
869
870 if (!decodedBounds) {
871 return false;
872 }
873
874 // On success, decodeBounds releases ownership of png_ptr and info_ptr.
875 if (png_ptrp) {
876 *png_ptrp = png_ptr;
877 }
878 if (info_ptrp) {
879 *info_ptrp = info_ptr;
880 }
881
882 // decodeBounds takes care of setting outCodec
883 if (outCodec) {
884 SkASSERT(*outCodec);
885 }
886 return true;
887}
888
889// FIXME (scroggo): Once SK_GOOGLE3_PNG_HACK is no more, this method can be inline in
890// AutoCleanPng::infoCallback
891static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
Matt Sarett7a1cc672016-12-14 11:48:31 -0500892 SkEncodedInfo::Color* outColor, SkEncodedInfo::Alpha* outAlpha,
893 int* outBitDepth) {
msarettac6c7502016-04-25 09:30:24 -0700894 png_uint_32 origWidth, origHeight;
895 int bitDepth, encodedColorType;
scroggod8d68552016-06-06 11:26:17 -0700896 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
msarettac6c7502016-04-25 09:30:24 -0700897 &encodedColorType, nullptr, nullptr, nullptr);
898
Matt Sarett7a1cc672016-12-14 11:48:31 -0500899 // TODO: Should we support 16-bits of precision for gray images?
900 if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType ||
901 PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) {
902 bitDepth = 8;
scroggod8d68552016-06-06 11:26:17 -0700903 png_set_strip_16(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700904 }
905
906 // Now determine the default colorType and alphaType and set the required transforms.
907 // Often, we depend on SkSwizzler to perform any transforms that we need. However, we
908 // still depend on libpng for many of the rare and PNG-specific cases.
909 SkEncodedInfo::Color color;
910 SkEncodedInfo::Alpha alpha;
911 switch (encodedColorType) {
912 case PNG_COLOR_TYPE_PALETTE:
913 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
914 // byte into separate bytes (useful for paletted and grayscale images).
915 if (bitDepth < 8) {
916 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500917 bitDepth = 8;
scroggod8d68552016-06-06 11:26:17 -0700918 png_set_packing(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700919 }
920
921 color = SkEncodedInfo::kPalette_Color;
922 // Set the alpha depending on if a transparency chunk exists.
scroggod8d68552016-06-06 11:26:17 -0700923 alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ?
msarettac6c7502016-04-25 09:30:24 -0700924 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
925 break;
926 case PNG_COLOR_TYPE_RGB:
scroggod8d68552016-06-06 11:26:17 -0700927 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
msarettac6c7502016-04-25 09:30:24 -0700928 // Convert to RGBA if transparency chunk exists.
scroggod8d68552016-06-06 11:26:17 -0700929 png_set_tRNS_to_alpha(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700930 color = SkEncodedInfo::kRGBA_Color;
931 alpha = SkEncodedInfo::kBinary_Alpha;
932 } else {
933 color = SkEncodedInfo::kRGB_Color;
934 alpha = SkEncodedInfo::kOpaque_Alpha;
935 }
936 break;
937 case PNG_COLOR_TYPE_GRAY:
938 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
939 if (bitDepth < 8) {
940 // TODO: Should we use SkSwizzler here?
Matt Sarett7a1cc672016-12-14 11:48:31 -0500941 bitDepth = 8;
scroggod8d68552016-06-06 11:26:17 -0700942 png_set_expand_gray_1_2_4_to_8(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700943 }
944
scroggod8d68552016-06-06 11:26:17 -0700945 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
946 png_set_tRNS_to_alpha(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700947 color = SkEncodedInfo::kGrayAlpha_Color;
948 alpha = SkEncodedInfo::kBinary_Alpha;
949 } else {
950 color = SkEncodedInfo::kGray_Color;
951 alpha = SkEncodedInfo::kOpaque_Alpha;
952 }
953 break;
954 case PNG_COLOR_TYPE_GRAY_ALPHA:
955 color = SkEncodedInfo::kGrayAlpha_Color;
956 alpha = SkEncodedInfo::kUnpremul_Alpha;
957 break;
958 case PNG_COLOR_TYPE_RGBA:
959 color = SkEncodedInfo::kRGBA_Color;
960 alpha = SkEncodedInfo::kUnpremul_Alpha;
961 break;
962 default:
963 // All the color types have been covered above.
964 SkASSERT(false);
965 color = SkEncodedInfo::kRGBA_Color;
966 alpha = SkEncodedInfo::kUnpremul_Alpha;
967 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700968 if (outColor) {
969 *outColor = color;
scroggo9a89a092016-05-31 13:52:47 -0700970 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700971 if (outAlpha) {
972 *outAlpha = alpha;
scroggod8d68552016-06-06 11:26:17 -0700973 }
Matt Sarett7a1cc672016-12-14 11:48:31 -0500974 if (outBitDepth) {
975 *outBitDepth = bitDepth;
976 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700977}
scroggod8d68552016-06-06 11:26:17 -0700978
scroggo8e6c7ad2016-09-16 08:20:38 -0700979#ifdef SK_GOOGLE3_PNG_HACK
980void SkPngCodec::rereadInfoCallback() {
Matt Sarett7a1cc672016-12-14 11:48:31 -0500981 general_info_callback(fPng_ptr, fInfo_ptr, nullptr, nullptr, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700982 png_set_interlace_handling(fPng_ptr);
983 png_read_update_info(fPng_ptr, fInfo_ptr);
984}
985#endif
986
987void AutoCleanPng::infoCallback() {
988 SkEncodedInfo::Color color;
989 SkEncodedInfo::Alpha alpha;
Matt Sarett7a1cc672016-12-14 11:48:31 -0500990 int bitDepth;
991 general_info_callback(fPng_ptr, fInfo_ptr, &color, &alpha, &bitDepth);
scroggo8e6c7ad2016-09-16 08:20:38 -0700992
993 const int numberPasses = png_set_interlace_handling(fPng_ptr);
994
995 fReadHeader = true;
996 fDecodedBounds = true;
997#ifndef SK_GOOGLE3_PNG_HACK
998 // 1 tells libpng to save any extra data. We may be able to be more efficient by saving
999 // it ourselves.
1000 png_process_data_pause(fPng_ptr, 1);
1001#else
1002 // Hack to make png_process_data stop.
1003 fPng_ptr->buffer_size = 0;
1004#endif
1005 if (fOutCodec) {
1006 SkASSERT(nullptr == *fOutCodec);
Matt Sarett523116d2017-01-12 18:36:38 -05001007 SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag;
1008 if (SkEncodedInfo::kGray_Color == color || SkEncodedInfo::kGrayAlpha_Color == color) {
1009 iccType |= SkColorSpace_Base::kGray_ICCTypeFlag;
1010 }
1011 sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr, iccType);
raftiasd737bee2016-12-08 10:53:24 -05001012 const bool unsupportedICC = !colorSpace;
msarettf34cd632016-05-25 10:13:53 -07001013 if (!colorSpace) {
raftiasd737bee2016-12-08 10:53:24 -05001014 // Treat unsupported/invalid color spaces as sRGB.
Brian Osman526972e2016-10-24 09:24:02 -04001015 colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
msarettf34cd632016-05-25 10:13:53 -07001016 }
scroggod8d68552016-06-06 11:26:17 -07001017
Matt Sarett7a1cc672016-12-14 11:48:31 -05001018 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, bitDepth);
scroggo8e6c7ad2016-09-16 08:20:38 -07001019 // FIXME (scroggo): Once we get rid of SK_GOOGLE3_PNG_HACK, general_info_callback can
1020 // be inlined, so these values will already be set.
1021 png_uint_32 origWidth = png_get_image_width(fPng_ptr, fInfo_ptr);
1022 png_uint_32 origHeight = png_get_image_height(fPng_ptr, fInfo_ptr);
1023 png_byte bitDepth = png_get_bit_depth(fPng_ptr, fInfo_ptr);
msarett549ca322016-08-17 08:54:08 -07001024 SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace);
1025
1026 if (SkEncodedInfo::kOpaque_Alpha == alpha) {
1027 png_color_8p sigBits;
scroggo8e6c7ad2016-09-16 08:20:38 -07001028 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
msarett549ca322016-08-17 08:54:08 -07001029 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
1030 // Recommend a decode to 565 if the sBIT indicates 565.
1031 imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
1032 }
1033 }
1034 }
scroggod8d68552016-06-06 11:26:17 -07001035
msarettac6c7502016-04-25 09:30:24 -07001036 if (1 == numberPasses) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001037 *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo, fStream,
1038 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
msarettac6c7502016-04-25 09:30:24 -07001039 } else {
scroggo8e6c7ad2016-09-16 08:20:38 -07001040 *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo, fStream,
1041 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, numberPasses);
msarettac6c7502016-04-25 09:30:24 -07001042 }
raftiasd737bee2016-12-08 10:53:24 -05001043 (*fOutCodec)->setUnsupportedICC(unsupportedICC);
msarettac6c7502016-04-25 09:30:24 -07001044 }
1045
scroggo8e6c7ad2016-09-16 08:20:38 -07001046
1047 // Release the pointers, which are now owned by the codec or the caller is expected to
1048 // take ownership.
1049 this->releasePngPtrs();
msarettac6c7502016-04-25 09:30:24 -07001050}
1051
msarett549ca322016-08-17 08:54:08 -07001052SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
mtklein6dc5b9a2016-08-24 12:22:32 -07001053 SkStream* stream, SkPngChunkReader* chunkReader, void* png_ptr,
scroggo8e6c7ad2016-09-16 08:20:38 -07001054 void* info_ptr, int bitDepth)
msarett549ca322016-08-17 08:54:08 -07001055 : INHERITED(encodedInfo, imageInfo, stream)
msarettac6c7502016-04-25 09:30:24 -07001056 , fPngChunkReader(SkSafeRef(chunkReader))
1057 , fPng_ptr(png_ptr)
1058 , fInfo_ptr(info_ptr)
msarettd1ec89b2016-08-03 12:59:27 -07001059 , fColorXformSrcRow(nullptr)
msarettac6c7502016-04-25 09:30:24 -07001060 , fBitDepth(bitDepth)
scroggo8e6c7ad2016-09-16 08:20:38 -07001061#ifdef SK_GOOGLE3_PNG_HACK
1062 , fNeedsToRereadHeader(true)
1063#endif
msarettac6c7502016-04-25 09:30:24 -07001064{}
1065
1066SkPngCodec::~SkPngCodec() {
1067 this->destroyReadStruct();
1068}
1069
1070void SkPngCodec::destroyReadStruct() {
1071 if (fPng_ptr) {
1072 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
1073 SkASSERT(fInfo_ptr);
mtklein6dc5b9a2016-08-24 12:22:32 -07001074 png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr);
msarettac6c7502016-04-25 09:30:24 -07001075 fPng_ptr = nullptr;
1076 fInfo_ptr = nullptr;
1077 }
1078}
1079
1080///////////////////////////////////////////////////////////////////////////////
1081// Getting the pixels
1082///////////////////////////////////////////////////////////////////////////////
1083
msarettd1ec89b2016-08-03 12:59:27 -07001084bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options,
1085 SkPMColor ctable[], int* ctableCount) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001086 if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
msarettd1ec89b2016-08-03 12:59:27 -07001087 SkCodecPrintf("Failed on png_read_update_info.\n");
1088 return false;
msarettac6c7502016-04-25 09:30:24 -07001089 }
1090 png_read_update_info(fPng_ptr, fInfo_ptr);
1091
Matt Sarett313c4632016-10-20 12:35:23 -04001092 // Reset fSwizzler and this->colorXform(). We can't do this in onRewind() because the
msarett400a93b2016-09-01 18:32:52 -07001093 // interlaced scanline decoder may need to rewind.
1094 fSwizzler.reset(nullptr);
msarett400a93b2016-09-01 18:32:52 -07001095
Matt Sarett313c4632016-10-20 12:35:23 -04001096 if (!this->initializeColorXform(dstInfo)) {
1097 return false;
msarett400a93b2016-09-01 18:32:52 -07001098 }
msarettd3317422016-08-22 13:00:05 -07001099
Matt Sarett34c69d62017-01-19 17:42:23 -05001100 // If SkColorSpaceXform directly supports the encoded PNG format, we should skip format
1101 // conversion in the swizzler (or skip swizzling altogether).
1102 bool skipFormatConversion = false;
1103 switch (this->getEncodedInfo().color()) {
1104 case SkEncodedInfo::kRGB_Color:
1105 if (this->getEncodedInfo().bitsPerComponent() != 16) {
1106 break;
1107 }
1108
1109 // Fall through
1110 case SkEncodedInfo::kRGBA_Color:
1111 skipFormatConversion = this->colorXform();
1112 break;
1113 default:
1114 break;
1115 }
Matt Sarett379938e2017-01-12 18:34:29 -05001116 if (skipFormatConversion && !options.fSubset) {
msarett400a93b2016-09-01 18:32:52 -07001117 fXformMode = kColorOnly_XformMode;
1118 return true;
msarettd1ec89b2016-08-03 12:59:27 -07001119 }
1120
msarettac6c7502016-04-25 09:30:24 -07001121 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
msarettdcd5e652016-08-22 08:48:40 -07001122 if (!this->createColorTable(dstInfo, ctableCount)) {
msarettd1ec89b2016-08-03 12:59:27 -07001123 return false;
msarettac6c7502016-04-25 09:30:24 -07001124 }
1125 }
1126
msarett400a93b2016-09-01 18:32:52 -07001127 // Copy the color table to the client if they request kIndex8 mode.
Hal Canary67b39de2016-11-07 11:47:44 -05001128 copy_color_table(dstInfo, fColorTable.get(), ctable, ctableCount);
msarettac6c7502016-04-25 09:30:24 -07001129
Matt Sarett379938e2017-01-12 18:34:29 -05001130 this->initializeSwizzler(dstInfo, options, skipFormatConversion);
msarett400a93b2016-09-01 18:32:52 -07001131 return true;
1132}
1133
msarettc0444612016-09-16 11:45:58 -07001134void SkPngCodec::initializeXformParams() {
1135 switch (fXformMode) {
1136 case kColorOnly_XformMode:
1137 fXformColorFormat = select_xform_format(this->dstInfo().colorType());
1138 fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
1139 this->getInfo().alphaType());
1140 fXformWidth = this->dstInfo().width();
1141 break;
1142 case kSwizzleColor_XformMode:
1143 fXformColorFormat = select_xform_format(this->dstInfo().colorType());
1144 fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
1145 this->getInfo().alphaType());
1146 fXformWidth = this->swizzler()->swizzleWidth();
1147 break;
1148 default:
1149 break;
1150 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001151}
1152
Matt Sarett379938e2017-01-12 18:34:29 -05001153void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
1154 bool skipFormatConversion) {
msarett400a93b2016-09-01 18:32:52 -07001155 SkImageInfo swizzlerInfo = dstInfo;
1156 Options swizzlerOptions = options;
1157 fXformMode = kSwizzleOnly_XformMode;
Matt Sarett313c4632016-10-20 12:35:23 -04001158 if (this->colorXform() &&
1159 apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color()))
1160 {
Matt Sarett562e6812016-11-08 16:13:43 -05001161 swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
msarett400a93b2016-09-01 18:32:52 -07001162 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
1163 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
1164 }
1165
1166 fXformMode = kSwizzleColor_XformMode;
1167
1168 // Here, we swizzle into temporary memory, which is not zero initialized.
1169 // FIXME (msarett):
1170 // Is this a problem?
1171 swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
1172 }
1173
msarettac6c7502016-04-25 09:30:24 -07001174 const SkPMColor* colors = get_color_ptr(fColorTable.get());
msarettd1ec89b2016-08-03 12:59:27 -07001175 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo,
Matt Sarett379938e2017-01-12 18:34:29 -05001176 swizzlerOptions, nullptr, skipFormatConversion));
msarettac6c7502016-04-25 09:30:24 -07001177 SkASSERT(fSwizzler);
msarett400a93b2016-09-01 18:32:52 -07001178}
1179
1180SkSampler* SkPngCodec::getSampler(bool createIfNecessary) {
1181 if (fSwizzler || !createIfNecessary) {
Ben Wagner145dbcd2016-11-03 14:40:50 -04001182 return fSwizzler.get();
msarett400a93b2016-09-01 18:32:52 -07001183 }
1184
Matt Sarett379938e2017-01-12 18:34:29 -05001185 this->initializeSwizzler(this->dstInfo(), this->options(), true);
Ben Wagner145dbcd2016-11-03 14:40:50 -04001186 return fSwizzler.get();
msarettac6c7502016-04-25 09:30:24 -07001187}
1188
msarettac6c7502016-04-25 09:30:24 -07001189bool SkPngCodec::onRewind() {
scroggo8e6c7ad2016-09-16 08:20:38 -07001190#ifdef SK_GOOGLE3_PNG_HACK
1191 fNeedsToRereadHeader = true;
1192 return true;
1193#else
msarettac6c7502016-04-25 09:30:24 -07001194 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
1195 // succeeds, they will be repopulated, and if it fails, they will
1196 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
1197 // come through this function which will rewind and again attempt
1198 // to reinitialize them.
1199 this->destroyReadStruct();
1200
scroggo46c57472015-09-30 08:57:13 -07001201 png_structp png_ptr;
1202 png_infop info_ptr;
msarettac6c7502016-04-25 09:30:24 -07001203 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &info_ptr)) {
1204 return false;
scroggo05245902015-03-25 11:11:52 -07001205 }
1206
msarettac6c7502016-04-25 09:30:24 -07001207 fPng_ptr = png_ptr;
1208 fInfo_ptr = info_ptr;
1209 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -07001210#endif
msarettac6c7502016-04-25 09:30:24 -07001211}
msarett6a738212016-03-04 13:27:35 -08001212
msarettd1ec89b2016-08-03 12:59:27 -07001213SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
1214 size_t rowBytes, const Options& options,
msarettac6c7502016-04-25 09:30:24 -07001215 SkPMColor ctable[], int* ctableCount,
1216 int* rowsDecoded) {
msarett2ecc35f2016-09-08 11:55:16 -07001217 if (!conversion_possible(dstInfo, this->getInfo()) ||
msarettd1ec89b2016-08-03 12:59:27 -07001218 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
1219 {
msarettac6c7502016-04-25 09:30:24 -07001220 return kInvalidConversion;
1221 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001222#ifdef SK_GOOGLE3_PNG_HACK
1223 // Note that this is done after initializeXforms. Otherwise that method
1224 // would not have png_ptr to use.
1225 if (!this->rereadHeaderIfNecessary()) {
1226 return kCouldNotRewind;
1227 }
1228#endif
msarettd1ec89b2016-08-03 12:59:27 -07001229
msarettac6c7502016-04-25 09:30:24 -07001230 if (options.fSubset) {
msarettac6c7502016-04-25 09:30:24 -07001231 return kUnimplemented;
scroggo05245902015-03-25 11:11:52 -07001232 }
1233
msarett400a93b2016-09-01 18:32:52 -07001234 this->allocateStorage(dstInfo);
msarettc0444612016-09-16 11:45:58 -07001235 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001236 return this->decodeAllRows(dst, rowBytes, rowsDecoded);
1237}
msarettac6c7502016-04-25 09:30:24 -07001238
scroggo8e6c7ad2016-09-16 08:20:38 -07001239SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
1240 void* dst, size_t rowBytes, const SkCodec::Options& options,
1241 SkPMColor* ctable, int* ctableCount) {
1242 if (!conversion_possible(dstInfo, this->getInfo()) ||
1243 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
1244 {
1245 return kInvalidConversion;
1246 }
1247#ifdef SK_GOOGLE3_PNG_HACK
1248 // See note in onGetPixels.
1249 if (!this->rereadHeaderIfNecessary()) {
1250 return kCouldNotRewind;
1251 }
1252#endif
1253
1254 this->allocateStorage(dstInfo);
1255
1256 int firstRow, lastRow;
1257 if (options.fSubset) {
1258 firstRow = options.fSubset->top();
1259 lastRow = options.fSubset->bottom() - 1;
1260 } else {
1261 firstRow = 0;
1262 lastRow = dstInfo.height() - 1;
1263 }
1264 this->setRange(firstRow, lastRow, dst, rowBytes);
scroggod8d68552016-06-06 11:26:17 -07001265 return kSuccess;
scroggo6fb23912016-06-02 14:16:43 -07001266}
1267
scroggo8e6c7ad2016-09-16 08:20:38 -07001268SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) {
1269 // FIXME: Only necessary on the first call.
msarettc0444612016-09-16 11:45:58 -07001270 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001271
1272 return this->decode(rowsDecoded);
1273}
1274
msarettf7eb6fc2016-09-13 09:04:11 -07001275uint64_t SkPngCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
msarettac6c7502016-04-25 09:30:24 -07001276 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
1277 if (colorPtr) {
msarettc0444612016-09-16 11:45:58 -07001278 SkAlphaType alphaType = select_xform_alpha(dstInfo.alphaType(),
msarettf7eb6fc2016-09-13 09:04:11 -07001279 this->getInfo().alphaType());
1280 return get_color_table_fill_value(dstInfo.colorType(), alphaType, colorPtr, 0,
Matt Sarett313c4632016-10-20 12:35:23 -04001281 this->colorXform());
msarettac6c7502016-04-25 09:30:24 -07001282 }
msarettf7eb6fc2016-09-13 09:04:11 -07001283 return INHERITED::onGetFillValue(dstInfo);
msarettac6c7502016-04-25 09:30:24 -07001284}
1285
1286SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
Ben Wagner145dbcd2016-11-03 14:40:50 -04001287 std::unique_ptr<SkStream> streamDeleter(stream);
msarettac6c7502016-04-25 09:30:24 -07001288
scroggo8e6c7ad2016-09-16 08:20:38 -07001289 SkCodec* outCodec = nullptr;
1290 if (read_header(streamDeleter.get(), chunkReader, &outCodec, nullptr, nullptr)) {
msarettac6c7502016-04-25 09:30:24 -07001291 // Codec has taken ownership of the stream.
1292 SkASSERT(outCodec);
1293 streamDeleter.release();
1294 return outCodec;
1295 }
1296
1297 return nullptr;
scroggo05245902015-03-25 11:11:52 -07001298}