blob: 1e633eaaed3b76b5e9aae380e8a1605b498d84cf [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"
msarettc4ce6b52016-06-16 07:37:41 -070011#include "SkColorSpace_Base.h"
scroggof24f2242015-03-03 08:59:20 -080012#include "SkColorTable.h"
scroggof24f2242015-03-03 08:59:20 -080013#include "SkMath.h"
msarett13a91232016-02-01 08:03:29 -080014#include "SkOpts.h"
msarettbe1d5552016-01-21 09:05:23 -080015#include "SkPngCodec.h"
msarett55447952016-07-22 14:07:23 -070016#include "SkPoint3.h"
scroggof24f2242015-03-03 08:59:20 -080017#include "SkSize.h"
18#include "SkStream.h"
19#include "SkSwizzler.h"
scroggo565901d2015-12-10 10:44:13 -080020#include "SkTemplates.h"
bungeman5d2cd6e2016-02-23 07:34:25 -080021#include "SkUtils.h"
scroggof24f2242015-03-03 08:59:20 -080022
mtklein63213812016-08-24 09:55:56 -070023#include "png.h"
24
mtkleindc90b532016-07-28 14:45:28 -070025// This warning triggers false postives way too often in here.
26#if defined(__GNUC__) && !defined(__clang__)
27 #pragma GCC diagnostic ignored "-Wclobbered"
28#endif
29
scroggo8e6c7ad2016-09-16 08:20:38 -070030#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
31 // This is not needed with version 1.5
32 #undef SK_GOOGLE3_PNG_HACK
33#endif
34
35// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6
36#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x)
37
scroggof24f2242015-03-03 08:59:20 -080038///////////////////////////////////////////////////////////////////////////////
scroggof24f2242015-03-03 08:59:20 -080039// Callback functions
40///////////////////////////////////////////////////////////////////////////////
41
scroggo8e6c7ad2016-09-16 08:20:38 -070042// When setjmp is first called, it returns 0, meaning longjmp was not called.
43constexpr int kSetJmpOkay = 0;
44// An error internal to libpng.
45constexpr int kPngError = 1;
46// Passed to longjmp when we have decoded as many lines as we need.
47constexpr int kStopDecoding = 2;
48
scroggof24f2242015-03-03 08:59:20 -080049static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070050 SkCodecPrintf("------ png error %s\n", msg);
scroggo8e6c7ad2016-09-16 08:20:38 -070051 longjmp(PNG_JMPBUF(png_ptr), kPngError);
scroggof24f2242015-03-03 08:59:20 -080052}
53
scroggo0eed6df2015-03-26 10:07:56 -070054void sk_warning_fn(png_structp, png_const_charp msg) {
55 SkCodecPrintf("----- png warning %s\n", msg);
56}
57
scroggocf98fa92015-11-23 08:14:40 -080058#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
59static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
60 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
61 // readChunk() returning true means continue decoding
62 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
63}
64#endif
65
scroggof24f2242015-03-03 08:59:20 -080066///////////////////////////////////////////////////////////////////////////////
67// Helpers
68///////////////////////////////////////////////////////////////////////////////
69
70class AutoCleanPng : public SkNoncopyable {
71public:
scroggo8e6c7ad2016-09-16 08:20:38 -070072 /*
73 * This class does not take ownership of stream or reader, but if codecPtr
74 * is non-NULL, and decodeBounds succeeds, it will have created a new
75 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as
76 * the png_ptr and info_ptr.
77 */
78 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader,
79 SkCodec** codecPtr)
scroggof24f2242015-03-03 08:59:20 -080080 : fPng_ptr(png_ptr)
scroggo8e6c7ad2016-09-16 08:20:38 -070081 , fInfo_ptr(nullptr)
82 , fDecodedBounds(false)
83 , fReadHeader(false)
84 , fStream(stream)
85 , fChunkReader(reader)
86 , fOutCodec(codecPtr)
87 {}
scroggof24f2242015-03-03 08:59:20 -080088
89 ~AutoCleanPng() {
halcanary96fcdcc2015-08-27 07:41:13 -070090 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
scroggof24f2242015-03-03 08:59:20 -080091 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070092 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
msarett13a91232016-02-01 08:03:29 -080093 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr);
scroggof24f2242015-03-03 08:59:20 -080094 }
95 }
96
97 void setInfoPtr(png_infop info_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070098 SkASSERT(nullptr == fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -080099 fInfo_ptr = info_ptr;
100 }
101
scroggo8e6c7ad2016-09-16 08:20:38 -0700102 /**
103 * Reads enough of the input stream to decode the bounds.
104 * @return false if the stream is not a valid PNG (or too short).
105 * true if it read enough of the stream to determine the bounds.
106 * In the latter case, the stream may have been read beyond the
107 * point to determine the bounds, and the png_ptr will have saved
108 * any extra data. Further, if the codecPtr supplied to the
109 * constructor was not NULL, it will now point to a new SkCodec,
110 * which owns (or refs, in the case of the SkPngChunkReader) the
111 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are
112 * unowned, and it is up to the caller to destroy them.
113 */
114 bool decodeBounds();
115
116private:
117 png_structp fPng_ptr;
118 png_infop fInfo_ptr;
119 bool fDecodedBounds;
120 bool fReadHeader;
121 SkStream* fStream;
122 SkPngChunkReader* fChunkReader;
123 SkCodec** fOutCodec;
124
125 /**
126 * Supplied to libpng to call when it has read enough data to determine
127 * bounds.
128 */
129 static void InfoCallback(png_structp png_ptr, png_infop) {
130 // png_get_progressive_ptr returns the pointer we set on the png_ptr with
131 // png_set_progressive_read_fn
132 static_cast<AutoCleanPng*>(png_get_progressive_ptr(png_ptr))->infoCallback();
133 }
134
135 void infoCallback();
136
137#ifdef SK_GOOGLE3_PNG_HACK
138// public so it can be called by SkPngCodec::rereadHeaderIfNecessary().
139public:
140#endif
141 void releasePngPtrs() {
halcanary96fcdcc2015-08-27 07:41:13 -0700142 fPng_ptr = nullptr;
143 fInfo_ptr = nullptr;
scroggof24f2242015-03-03 08:59:20 -0800144 }
scroggof24f2242015-03-03 08:59:20 -0800145};
146#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
147
scroggo8e6c7ad2016-09-16 08:20:38 -0700148bool AutoCleanPng::decodeBounds() {
149 if (setjmp(PNG_JMPBUF(fPng_ptr))) {
150 return false;
151 }
152
153 png_set_progressive_read_fn(fPng_ptr, this, InfoCallback, nullptr, nullptr);
154
155 // Arbitrary buffer size, though note that it matches (below)
156 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of
157 // the PNG header?
158 constexpr size_t kBufferSize = 4096;
159 char buffer[kBufferSize];
160
161 while (true) {
162 const size_t bytesRead = fStream->read(buffer, kBufferSize);
163 if (!bytesRead) {
164 // We have read to the end of the input without decoding bounds.
165 break;
166 }
167
168 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead);
169 if (fReadHeader) {
170 break;
171 }
172 }
173
174 // For safety, clear the pointer to this object.
175 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr);
176 return fDecodedBounds;
177}
178
179void SkPngCodec::processData() {
180 switch (setjmp(PNG_JMPBUF(fPng_ptr))) {
181 case kPngError:
182 // There was an error. Stop processing data.
183 // FIXME: Do we need to discard png_ptr?
184 return;
185 case kStopDecoding:
186 // We decoded all the lines we want.
187 return;
188 case kSetJmpOkay:
189 // Everything is okay.
190 break;
191 default:
192 // No other values should be passed to longjmp.
193 SkASSERT(false);
194 }
195
196 // Arbitrary buffer size
197 constexpr size_t kBufferSize = 4096;
198 char buffer[kBufferSize];
199
200 while (true) {
201 const size_t bytesRead = this->stream()->read(buffer, kBufferSize);
202 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead);
203
204 if (!bytesRead) {
205 // We have read to the end of the input. Note that we quit *after*
206 // calling png_process_data, because decodeBounds may have told
207 // libpng to save the remainder of the buffer, in which case
208 // png_process_data will process the saved buffer, though the
209 // stream has no more to read.
210 break;
211 }
212 }
213}
214
msarettd1ec89b2016-08-03 12:59:27 -0700215// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
msarettdcd5e652016-08-22 08:48:40 -0700216bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800217
msarett13a91232016-02-01 08:03:29 -0800218 int numColors;
219 png_color* palette;
220 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
scroggo05245902015-03-25 11:11:52 -0700221 return false;
scroggof24f2242015-03-03 08:59:20 -0800222 }
223
msarettdcd5e652016-08-22 08:48:40 -0700224 // Contents depend on tableColorType and our choice of if/when to premultiply:
225 // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
226 SkPMColor colorTable[256];
227 SkColorType tableColorType = fColorXform ? kRGBA_8888_SkColorType : dstInfo.colorType();
scroggof24f2242015-03-03 08:59:20 -0800228
msarett13a91232016-02-01 08:03:29 -0800229 png_bytep alphas;
230 int numColorsWithAlpha = 0;
231 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
msarettdcd5e652016-08-22 08:48:40 -0700232 // If we are performing a color xform, it will handle the premultiply. Otherwise,
233 // we'll do it here.
234 bool premultiply = !fColorXform && needs_premul(dstInfo, this->getInfo());
235
msarett13a91232016-02-01 08:03:29 -0800236 // Choose which function to use to create the color table. If the final destination's
237 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
msarettdcd5e652016-08-22 08:48:40 -0700238 PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType);
msarett13a91232016-02-01 08:03:29 -0800239
240 for (int i = 0; i < numColorsWithAlpha; i++) {
241 // We don't have a function in SkOpts that combines a set of alphas with a set
242 // of RGBs. We could write one, but it's hardly worth it, given that this
243 // is such a small fraction of the total decode time.
msarettdcd5e652016-08-22 08:48:40 -0700244 colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue);
msarett13a91232016-02-01 08:03:29 -0800245 palette++;
246 }
scroggof24f2242015-03-03 08:59:20 -0800247 }
248
msarett13a91232016-02-01 08:03:29 -0800249 if (numColorsWithAlpha < numColors) {
250 // The optimized code depends on a 3-byte png_color struct with the colors
251 // in RGB order. These checks make sure it is safe to use.
252 static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken.");
253#ifdef SK_DEBUG
254 SkASSERT(&palette->red < &palette->green);
255 SkASSERT(&palette->green < &palette->blue);
256#endif
257
msarettdcd5e652016-08-22 08:48:40 -0700258 if (is_rgba(tableColorType)) {
259 SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700260 numColors - numColorsWithAlpha);
261 } else {
msarettdcd5e652016-08-22 08:48:40 -0700262 SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, palette,
msarett34e0ec42016-04-22 16:27:24 -0700263 numColors - numColorsWithAlpha);
264 }
scroggof24f2242015-03-03 08:59:20 -0800265 }
266
msarettdcd5e652016-08-22 08:48:40 -0700267 // If we are not decoding to F16, we can color xform now and store the results
268 // in the color table.
269 if (fColorXform && kRGBA_F16_SkColorType != dstInfo.colorType()) {
msarettc0444612016-09-16 11:45:58 -0700270 SkColorSpaceXform::ColorFormat xformColorFormat = is_rgba(dstInfo.colorType()) ?
271 SkColorSpaceXform::kRGBA_8888_ColorFormat :
272 SkColorSpaceXform::kBGRA_8888_ColorFormat;
273 SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
msarette99883f2016-09-08 06:05:35 -0700274 this->getInfo().alphaType());
msarettc0444612016-09-16 11:45:58 -0700275 fColorXform->apply(colorTable, colorTable, numColors, xformColorFormat, xformAlphaType);
msarettdcd5e652016-08-22 08:48:40 -0700276 }
277
msarett13a91232016-02-01 08:03:29 -0800278 // Pad the color table with the last color in the table (or black) in the case that
279 // invalid pixel indices exceed the number of colors in the table.
280 const int maxColors = 1 << fBitDepth;
281 if (numColors < maxColors) {
msarettdcd5e652016-08-22 08:48:40 -0700282 SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK;
283 sk_memset32(colorTable + numColors, lastColor, maxColors - numColors);
scroggof24f2242015-03-03 08:59:20 -0800284 }
285
msarett13a91232016-02-01 08:03:29 -0800286 // Set the new color count.
halcanary96fcdcc2015-08-27 07:41:13 -0700287 if (ctableCount != nullptr) {
msarett13a91232016-02-01 08:03:29 -0800288 *ctableCount = maxColors;
scroggof24f2242015-03-03 08:59:20 -0800289 }
290
msarettdcd5e652016-08-22 08:48:40 -0700291 fColorTable.reset(new SkColorTable(colorTable, maxColors));
scroggo05245902015-03-25 11:11:52 -0700292 return true;
scroggof24f2242015-03-03 08:59:20 -0800293}
294
295///////////////////////////////////////////////////////////////////////////////
296// Creation
297///////////////////////////////////////////////////////////////////////////////
298
scroggodb30be22015-12-08 18:54:13 -0800299bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
300 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
scroggof24f2242015-03-03 08:59:20 -0800301}
302
scroggo8e6c7ad2016-09-16 08:20:38 -0700303#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
304
msarett6a738212016-03-04 13:27:35 -0800305static float png_fixed_point_to_float(png_fixed_point x) {
306 // We multiply by the same factor that libpng used to convert
307 // fixed point -> double. Since we want floats, we choose to
308 // do the conversion ourselves rather than convert
309 // fixed point -> double -> float.
310 return ((float) x) * 0.00001f;
311}
312
msarett128245c2016-03-30 12:01:47 -0700313static float png_inverted_fixed_point_to_float(png_fixed_point x) {
314 // This is necessary because the gAMA chunk actually stores 1/gamma.
315 return 1.0f / png_fixed_point_to_float(x);
316}
317
msarettc4ce6b52016-06-16 07:37:41 -0700318static constexpr float gSRGB_toXYZD50[] {
brianosmande68d6c2016-09-09 10:36:17 -0700319 0.4358f, 0.3853f, 0.1430f, // Rx, Gx, Bx
320 0.2224f, 0.7170f, 0.0606f, // Ry, Gy, Gz
321 0.0139f, 0.0971f, 0.7139f, // Rz, Gz, Bz
msarettc4ce6b52016-06-16 07:37:41 -0700322};
323
msarett55447952016-07-22 14:07:23 -0700324static bool convert_to_D50(SkMatrix44* toXYZD50, float toXYZ[9], float whitePoint[2]) {
325 float wX = whitePoint[0];
326 float wY = whitePoint[1];
327 if (wX < 0.0f || wY < 0.0f || (wX + wY > 1.0f)) {
328 return false;
329 }
330
331 // Calculate the XYZ illuminant. Call this the src illuminant.
332 float wZ = 1.0f - wX - wY;
333 float scale = 1.0f / wY;
334 // TODO (msarett):
335 // What are common src illuminants? I'm guessing we will almost always see D65. Should
336 // we go ahead and save a precomputed D65->D50 Bradford matrix? Should we exit early if
337 // if the src illuminant is D50?
338 SkVector3 srcXYZ = SkVector3::Make(wX * scale, 1.0f, wZ * scale);
339
340 // The D50 illuminant.
341 SkVector3 dstXYZ = SkVector3::Make(0.96422f, 1.0f, 0.82521f);
342
343 // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus
344 // the matrices below. The Bradford method is used by Adobe and is widely considered
345 // to be the best.
346 // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
347 SkMatrix mA, mAInv;
348 mA.setAll(0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -0.0685f, 1.0296f);
349 mAInv.setAll(0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f, 0.0492912f,
350 -0.0085287f, 0.0400428f, 0.9684867f);
351
352 // Map illuminant into cone response domain.
353 SkVector3 srcCone;
354 srcCone.fX = mA[0] * srcXYZ.fX + mA[1] * srcXYZ.fY + mA[2] * srcXYZ.fZ;
355 srcCone.fY = mA[3] * srcXYZ.fX + mA[4] * srcXYZ.fY + mA[5] * srcXYZ.fZ;
356 srcCone.fZ = mA[6] * srcXYZ.fX + mA[7] * srcXYZ.fY + mA[8] * srcXYZ.fZ;
357 SkVector3 dstCone;
358 dstCone.fX = mA[0] * dstXYZ.fX + mA[1] * dstXYZ.fY + mA[2] * dstXYZ.fZ;
359 dstCone.fY = mA[3] * dstXYZ.fX + mA[4] * dstXYZ.fY + mA[5] * dstXYZ.fZ;
360 dstCone.fZ = mA[6] * dstXYZ.fX + mA[7] * dstXYZ.fY + mA[8] * dstXYZ.fZ;
361
362 SkMatrix DXToD50;
363 DXToD50.setIdentity();
364 DXToD50[0] = dstCone.fX / srcCone.fX;
365 DXToD50[4] = dstCone.fY / srcCone.fY;
366 DXToD50[8] = dstCone.fZ / srcCone.fZ;
367 DXToD50.postConcat(mAInv);
368 DXToD50.preConcat(mA);
369
370 SkMatrix toXYZ3x3;
371 toXYZ3x3.setAll(toXYZ[0], toXYZ[3], toXYZ[6], toXYZ[1], toXYZ[4], toXYZ[7], toXYZ[2], toXYZ[5],
372 toXYZ[8]);
373 toXYZ3x3.postConcat(DXToD50);
374
brianosmande68d6c2016-09-09 10:36:17 -0700375 toXYZD50->set3x3(toXYZ3x3[0], toXYZ3x3[3], toXYZ3x3[6],
376 toXYZ3x3[1], toXYZ3x3[4], toXYZ3x3[7],
377 toXYZ3x3[2], toXYZ3x3[5], toXYZ3x3[8]);
msarett55447952016-07-22 14:07:23 -0700378 return true;
379}
380
scroggo8e6c7ad2016-09-16 08:20:38 -0700381#endif // LIBPNG >= 1.6
382
msarett6a738212016-03-04 13:27:35 -0800383// Returns a colorSpace object that represents any color space information in
384// the encoded data. If the encoded data contains no color space, this will
385// return NULL.
msarettad8bcfe2016-03-07 07:09:03 -0800386sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
msarett6a738212016-03-04 13:27:35 -0800387
msarette2443222016-03-04 14:20:49 -0800388#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
389
msarett6a738212016-03-04 13:27:35 -0800390 // First check for an ICC profile
391 png_bytep profile;
392 png_uint_32 length;
393 // The below variables are unused, however, we need to pass them in anyway or
394 // png_get_iCCP() will return nothing.
395 // Could knowing the |name| of the profile ever be interesting? Maybe for debugging?
396 png_charp name;
397 // The |compression| is uninteresting since:
398 // (1) libpng has already decompressed the profile for us.
399 // (2) "deflate" is the only mode of decompression that libpng supports.
400 int compression;
401 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
402 &length)) {
403 return SkColorSpace::NewICC(profile, length);
404 }
405
406 // Second, check for sRGB.
407 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
408
409 // sRGB chunks also store a rendering intent: Absolute, Relative,
410 // Perceptual, and Saturation.
411 // FIXME (msarett): Extract this information from the sRGB chunk once
412 // we are able to handle this information in
413 // SkColorSpace.
414 return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
415 }
416
417 // Next, check for chromaticities.
msarett55447952016-07-22 14:07:23 -0700418 png_fixed_point toXYZFixed[9];
419 float toXYZ[9];
420 png_fixed_point whitePointFixed[2];
421 float whitePoint[2];
msarett6a738212016-03-04 13:27:35 -0800422 png_fixed_point gamma;
msarettbb9f7742016-05-17 09:31:20 -0700423 float gammas[3];
msarett55447952016-07-22 14:07:23 -0700424 if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &toXYZFixed[0], &toXYZFixed[1], &toXYZFixed[2],
425 &toXYZFixed[3], &toXYZFixed[4], &toXYZFixed[5], &toXYZFixed[6],
426 &toXYZFixed[7], &toXYZFixed[8]) &&
427 png_get_cHRM_fixed(png_ptr, info_ptr, &whitePointFixed[0], &whitePointFixed[1], nullptr,
428 nullptr, nullptr, nullptr, nullptr, nullptr))
429 {
msarett6a738212016-03-04 13:27:35 -0800430 for (int i = 0; i < 9; i++) {
msarett55447952016-07-22 14:07:23 -0700431 toXYZ[i] = png_fixed_point_to_float(toXYZFixed[i]);
msarett6a738212016-03-04 13:27:35 -0800432 }
msarett55447952016-07-22 14:07:23 -0700433 whitePoint[0] = png_fixed_point_to_float(whitePointFixed[0]);
434 whitePoint[1] = png_fixed_point_to_float(whitePointFixed[1]);
435
436 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
437 if (!convert_to_D50(&toXYZD50, toXYZ, whitePoint)) {
438 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
439 }
msarett6a738212016-03-04 13:27:35 -0800440
msarett128245c2016-03-30 12:01:47 -0700441 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
msarettffc2aea2016-05-02 11:12:14 -0700442 float value = png_inverted_fixed_point_to_float(gamma);
msarettbb9f7742016-05-17 09:31:20 -0700443 gammas[0] = value;
444 gammas[1] = value;
445 gammas[2] = value;
msarettffc2aea2016-05-02 11:12:14 -0700446
msarett55447952016-07-22 14:07:23 -0700447 return SkColorSpace_Base::NewRGB(gammas, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800448 }
msarett128245c2016-03-30 12:01:47 -0700449
msarettc4ce6b52016-06-16 07:37:41 -0700450 // Default to sRGB gamma if the image has color space information,
451 // but does not specify gamma.
msarett48ba2b82016-09-07 18:55:49 -0700452 return SkColorSpace::NewRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800453 }
454
455 // Last, check for gamma.
456 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
457
msarett6a738212016-03-04 13:27:35 -0800458 // Set the gammas.
msarettffc2aea2016-05-02 11:12:14 -0700459 float value = png_inverted_fixed_point_to_float(gamma);
msarettbb9f7742016-05-17 09:31:20 -0700460 gammas[0] = value;
461 gammas[1] = value;
462 gammas[2] = value;
msarett6a738212016-03-04 13:27:35 -0800463
msarettc4ce6b52016-06-16 07:37:41 -0700464 // Since there is no cHRM, we will guess sRGB gamut.
msarett55447952016-07-22 14:07:23 -0700465 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
466 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
msarettc4ce6b52016-06-16 07:37:41 -0700467
msarett55447952016-07-22 14:07:23 -0700468 return SkColorSpace_Base::NewRGB(gammas, toXYZD50);
msarett6a738212016-03-04 13:27:35 -0800469 }
470
msarette2443222016-03-04 14:20:49 -0800471#endif // LIBPNG >= 1.6
472
msarettc4ce6b52016-06-16 07:37:41 -0700473 // Report that there is no color space information in the PNG. SkPngCodec is currently
474 // implemented to guess sRGB in this case.
msarett6a738212016-03-04 13:27:35 -0800475 return nullptr;
476}
477
msarett400a93b2016-09-01 18:32:52 -0700478void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
479 switch (fXformMode) {
480 case kSwizzleOnly_XformMode:
msarett400a93b2016-09-01 18:32:52 -0700481 break;
482 case kColorOnly_XformMode:
483 // Intentional fall through. A swizzler hasn't been created yet, but one will
484 // be created later if we are sampling. We'll go ahead and allocate
485 // enough memory to swizzle if necessary.
486 case kSwizzleColor_XformMode: {
scroggo8e6c7ad2016-09-16 08:20:38 -0700487 const size_t colorXformBytes = dstInfo.width() * sizeof(uint32_t);
488 fStorage.reset(colorXformBytes);
489 fColorXformSrcRow = (uint32_t*) fStorage.get();
msarett400a93b2016-09-01 18:32:52 -0700490 break;
491 }
492 }
msarettd1ec89b2016-08-03 12:59:27 -0700493}
494
scroggo8e6c7ad2016-09-16 08:20:38 -0700495void SkPngCodec::applyXformRow(void* dst, const void* src) {
msarett400a93b2016-09-01 18:32:52 -0700496 switch (fXformMode) {
497 case kSwizzleOnly_XformMode:
498 fSwizzler->swizzle(dst, (const uint8_t*) src);
499 break;
500 case kColorOnly_XformMode:
msarettc0444612016-09-16 11:45:58 -0700501 fColorXform->apply(dst, (const uint32_t*) src, fXformWidth, fXformColorFormat,
502 fXformAlphaType);
msarett400a93b2016-09-01 18:32:52 -0700503 break;
504 case kSwizzleColor_XformMode:
505 fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
msarettc0444612016-09-16 11:45:58 -0700506 fColorXform->apply(dst, fColorXformSrcRow, fXformWidth, fXformColorFormat,
507 fXformAlphaType);
msarett400a93b2016-09-01 18:32:52 -0700508 break;
509 }
msarettdcd5e652016-08-22 08:48:40 -0700510}
511
scroggo8e6c7ad2016-09-16 08:20:38 -0700512class SkPngNormalDecoder : public SkPngCodec {
scroggo05245902015-03-25 11:11:52 -0700513public:
scroggo8e6c7ad2016-09-16 08:20:38 -0700514 SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkStream* stream,
515 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
516 : INHERITED(info, imageInfo, stream, reader, png_ptr, info_ptr, bitDepth)
517 , fLinesDecoded(0)
518 , fDst(nullptr)
519 , fRowBytes(0)
520 , fFirstRow(0)
521 , fLastRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700522 {}
523
scroggo8e6c7ad2016-09-16 08:20:38 -0700524 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
525 GetDecoder(png_ptr)->allRowsCallback(row, rowNum);
scroggo05245902015-03-25 11:11:52 -0700526 }
527
scroggo8e6c7ad2016-09-16 08:20:38 -0700528 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
529 GetDecoder(png_ptr)->rowCallback(row, rowNum);
msarettd1ec89b2016-08-03 12:59:27 -0700530 }
531
scroggo8e6c7ad2016-09-16 08:20:38 -0700532#ifdef SK_GOOGLE3_PNG_HACK
533 static void RereadInfoCallback(png_structp png_ptr, png_infop) {
534 GetDecoder(png_ptr)->rereadInfoCallback();
scroggo05245902015-03-25 11:11:52 -0700535 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700536#endif
emmaleer8f4ba762015-08-14 07:44:46 -0700537
emmaleer0a4c3cb2015-06-22 10:40:21 -0700538private:
scroggo8e6c7ad2016-09-16 08:20:38 -0700539 int fLinesDecoded; // FIXME: Move to baseclass?
540 void* fDst;
541 size_t fRowBytes;
542
543 // Variables for partial decode
544 int fFirstRow; // FIXME: Move to baseclass?
545 int fLastRow;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700546
scroggo46c57472015-09-30 08:57:13 -0700547 typedef SkPngCodec INHERITED;
scroggo8e6c7ad2016-09-16 08:20:38 -0700548
549 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) {
550 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr));
551 }
552
553 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
554 const int height = this->getInfo().height();
555 png_progressive_info_ptr callback = nullptr;
556#ifdef SK_GOOGLE3_PNG_HACK
557 callback = RereadInfoCallback;
558#endif
559 png_set_progressive_read_fn(this->png_ptr(), this, callback, AllRowsCallback, nullptr);
560 fDst = dst;
561 fRowBytes = rowBytes;
562
563 fLinesDecoded = 0;
564
565 this->processData();
566
567 if (fLinesDecoded == height) {
568 return SkCodec::kSuccess;
569 }
570
571 if (rowsDecoded) {
572 *rowsDecoded = fLinesDecoded;
573 }
574
575 return SkCodec::kIncompleteInput;
576 }
577
578 void allRowsCallback(png_bytep row, int rowNum) {
579 SkASSERT(rowNum - fFirstRow == fLinesDecoded);
580 fLinesDecoded++;
581 this->applyXformRow(fDst, row);
582 fDst = SkTAddOffset<void>(fDst, fRowBytes);
583 }
584
585 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
586 png_progressive_info_ptr callback = nullptr;
587#ifdef SK_GOOGLE3_PNG_HACK
588 callback = RereadInfoCallback;
589#endif
590 png_set_progressive_read_fn(this->png_ptr(), this, callback, RowCallback, nullptr);
591 fFirstRow = firstRow;
592 fLastRow = lastRow;
593 fDst = dst;
594 fRowBytes = rowBytes;
595 fLinesDecoded = 0;
596 }
597
598 SkCodec::Result decode(int* rowsDecoded) override {
599 this->processData();
600
601 if (fLinesDecoded == fLastRow - fFirstRow + 1) {
602 return SkCodec::kSuccess;
603 }
604
605 if (rowsDecoded) {
606 *rowsDecoded = fLinesDecoded;
607 }
608
609 return SkCodec::kIncompleteInput;
610 }
611
612 void rowCallback(png_bytep row, int rowNum) {
613 if (rowNum < fFirstRow) {
614 // Ignore this row.
615 return;
616 }
617
618 SkASSERT(rowNum <= fLastRow);
619
620 // If there is no swizzler, all rows are needed.
621 if (!this->swizzler() || this->swizzler()->rowNeeded(fLinesDecoded)) {
622 this->applyXformRow(fDst, row);
623 fDst = SkTAddOffset<void>(fDst, fRowBytes);
624 }
625
626 fLinesDecoded++;
627
628 if (rowNum == fLastRow) {
629 // Fake error to stop decoding scanlines.
630 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
631 }
632 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700633};
634
scroggo8e6c7ad2016-09-16 08:20:38 -0700635class SkPngInterlacedDecoder : public SkPngCodec {
636public:
637 SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
638 SkStream* stream, SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr,
639 int bitDepth, int numberPasses)
640 : INHERITED(info, imageInfo, stream, reader, png_ptr, info_ptr, bitDepth)
641 , fNumberPasses(numberPasses)
642 , fFirstRow(0)
643 , fLastRow(0)
644 , fLinesDecoded(0)
645 , fInterlacedComplete(false)
646 , fPng_rowbytes(0)
647 {}
648
649 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) {
650 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr));
651 decoder->interlacedRowCallback(row, rowNum, pass);
652 }
653
654#ifdef SK_GOOGLE3_PNG_HACK
655 static void RereadInfoInterlacedCallback(png_structp png_ptr, png_infop) {
656 static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr))->rereadInfoInterlaced();
657 }
658#endif
659
660private:
661 const int fNumberPasses;
662 int fFirstRow;
663 int fLastRow;
664 void* fDst;
665 size_t fRowBytes;
666 int fLinesDecoded;
667 bool fInterlacedComplete;
668 size_t fPng_rowbytes;
669 SkAutoTMalloc<png_byte> fInterlaceBuffer;
670
671 typedef SkPngCodec INHERITED;
672
673#ifdef SK_GOOGLE3_PNG_HACK
674 void rereadInfoInterlaced() {
675 this->rereadInfoCallback();
676 // Note: This allocates more memory than necessary, if we are sampling/subset.
677 this->setUpInterlaceBuffer(this->getInfo().height());
678 }
679#endif
680
681 // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
682 // as expensive as the subset version of non-interlaced, but it still does extra
683 // work.
684 void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
685 if (rowNum < fFirstRow || rowNum > fLastRow) {
686 // Ignore this row
687 return;
688 }
689
690 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
691 png_progressive_combine_row(this->png_ptr(), oldRow, row);
692
693 if (0 == pass) {
694 // The first pass initializes all rows.
695 SkASSERT(row);
696 SkASSERT(fLinesDecoded == rowNum - fFirstRow);
697 fLinesDecoded++;
698 } else {
699 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
700 if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
701 // Last pass, and we have read all of the rows we care about. Note that
702 // we do not care about reading anything beyond the end of the image (or
703 // beyond the last scanline requested).
704 fInterlacedComplete = true;
705 // Fake error to stop decoding scanlines.
706 longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
707 }
708 }
709 }
710
711 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
712 const int height = this->getInfo().height();
713 this->setUpInterlaceBuffer(height);
714 png_progressive_info_ptr callback = nullptr;
715#ifdef SK_GOOGLE3_PNG_HACK
716 callback = RereadInfoInterlacedCallback;
717#endif
718 png_set_progressive_read_fn(this->png_ptr(), this, callback, InterlacedRowCallback,
719 nullptr);
720
721 fFirstRow = 0;
722 fLastRow = height - 1;
723 fLinesDecoded = 0;
724
725 this->processData();
726
727 png_bytep srcRow = fInterlaceBuffer.get();
728 // FIXME: When resuming, this may rewrite rows that did not change.
729 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) {
730 this->applyXformRow(dst, srcRow);
731 dst = SkTAddOffset<void>(dst, rowBytes);
732 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes);
733 }
734 if (fInterlacedComplete) {
735 return SkCodec::kSuccess;
736 }
737
738 if (rowsDecoded) {
739 *rowsDecoded = fLinesDecoded;
740 }
741
742 return SkCodec::kIncompleteInput;
743 }
744
745 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
746 // FIXME: We could skip rows in the interlace buffer that we won't put in the output.
747 this->setUpInterlaceBuffer(lastRow - firstRow + 1);
748 png_progressive_info_ptr callback = nullptr;
749#ifdef SK_GOOGLE3_PNG_HACK
750 callback = RereadInfoInterlacedCallback;
751#endif
752 png_set_progressive_read_fn(this->png_ptr(), this, callback, InterlacedRowCallback, nullptr);
753 fFirstRow = firstRow;
754 fLastRow = lastRow;
755 fDst = dst;
756 fRowBytes = rowBytes;
757 fLinesDecoded = 0;
758 }
759
760 SkCodec::Result decode(int* rowsDecoded) override {
761 this->processData();
762
763 // Now apply Xforms on all the rows that were decoded.
764 if (!fLinesDecoded) {
765 return SkCodec::kIncompleteInput;
766 }
767 const int lastRow = fLinesDecoded + fFirstRow - 1;
768 SkASSERT(lastRow <= fLastRow);
769
770 // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it
771 // may be too tricky/expensive to handle that correctly.
772 png_bytep srcRow = fInterlaceBuffer.get();
773 const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1;
774 void* dst = fDst;
775 for (int rowNum = fFirstRow; rowNum <= lastRow; rowNum += sampleY) {
776 this->applyXformRow(dst, srcRow);
777 dst = SkTAddOffset<void>(dst, fRowBytes);
778 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY);
779 }
780
781 if (fInterlacedComplete) {
782 return SkCodec::kSuccess;
783 }
784
785 if (rowsDecoded) {
786 *rowsDecoded = fLinesDecoded;
787 }
788 return SkCodec::kIncompleteInput;
789 }
790
791 void setUpInterlaceBuffer(int height) {
792 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr());
793 fInterlaceBuffer.reset(fPng_rowbytes * height);
794 fInterlacedComplete = false;
795 }
796};
797
798#ifdef SK_GOOGLE3_PNG_HACK
799bool SkPngCodec::rereadHeaderIfNecessary() {
800 if (!fNeedsToRereadHeader) {
801 return true;
802 }
803
804 // On the first call, we'll need to rewind ourselves. Future calls will
805 // have already rewound in rewindIfNecessary.
806 if (this->stream()->getPosition() > 0) {
807 this->stream()->rewind();
808 }
809
810 this->destroyReadStruct();
811 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
812 sk_error_fn, sk_warning_fn);
813 if (!png_ptr) {
814 return false;
815 }
816
817 // Only use the AutoCleanPng to delete png_ptr as necessary.
818 // (i.e. not for reading bounds etc.)
819 AutoCleanPng autoClean(png_ptr, nullptr, nullptr, nullptr);
820
821 png_infop info_ptr = png_create_info_struct(png_ptr);
822 if (info_ptr == nullptr) {
823 return false;
824 }
825
826 autoClean.setInfoPtr(info_ptr);
827
828#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
829 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
830 // This needs to be installed before we read the png header. Android may store ninepatch
831 // chunks in the header.
832 if (fPngChunkReader.get()) {
833 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
834 png_set_read_user_chunk_fn(png_ptr, (png_voidp) fPngChunkReader.get(), sk_read_user_chunk);
835 }
836#endif
837
838 fPng_ptr = png_ptr;
839 fInfo_ptr = info_ptr;
840 autoClean.releasePngPtrs();
841 fNeedsToRereadHeader = false;
842 return true;
843}
844#endif // SK_GOOGLE3_PNG_HACK
845
msarettac6c7502016-04-25 09:30:24 -0700846// Reads the header and initializes the output fields, if not NULL.
847//
848// @param stream Input data. Will be read to get enough information to properly
849// setup the codec.
850// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
851// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
852// expected to continue to own it for the lifetime of the png_ptr.
853// @param outCodec Optional output variable. If non-NULL, will be set to a new
854// SkPngCodec on success.
855// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
856// png_structp on success.
857// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
858// png_infop on success;
859// @return true on success, in which case the caller is responsible for calling
860// png_destroy_read_struct(png_ptrp, info_ptrp).
861// If it returns false, the passed in fields (except stream) are unchanged.
862static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec,
863 png_structp* png_ptrp, png_infop* info_ptrp) {
864 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
865 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
866 sk_error_fn, sk_warning_fn);
867 if (!png_ptr) {
868 return false;
869 }
870
scroggo8e6c7ad2016-09-16 08:20:38 -0700871 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
msarettac6c7502016-04-25 09:30:24 -0700872
873 png_infop info_ptr = png_create_info_struct(png_ptr);
874 if (info_ptr == nullptr) {
875 return false;
876 }
877
878 autoClean.setInfoPtr(info_ptr);
879
880 // FIXME: Could we use the return value of setjmp to specify the type of
881 // error?
scroggo8e6c7ad2016-09-16 08:20:38 -0700882 if (setjmp(PNG_JMPBUF(png_ptr))) {
msarettac6c7502016-04-25 09:30:24 -0700883 return false;
884 }
885
msarettac6c7502016-04-25 09:30:24 -0700886#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
887 // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
888 // This needs to be installed before we read the png header. Android may store ninepatch
889 // chunks in the header.
890 if (chunkReader) {
891 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
892 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
893 }
894#endif
895
scroggo8e6c7ad2016-09-16 08:20:38 -0700896 const bool decodedBounds = autoClean.decodeBounds();
897
898 if (!decodedBounds) {
899 return false;
900 }
901
902 // On success, decodeBounds releases ownership of png_ptr and info_ptr.
903 if (png_ptrp) {
904 *png_ptrp = png_ptr;
905 }
906 if (info_ptrp) {
907 *info_ptrp = info_ptr;
908 }
909
910 // decodeBounds takes care of setting outCodec
911 if (outCodec) {
912 SkASSERT(*outCodec);
913 }
914 return true;
915}
916
917// FIXME (scroggo): Once SK_GOOGLE3_PNG_HACK is no more, this method can be inline in
918// AutoCleanPng::infoCallback
919static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
920 SkEncodedInfo::Color* outColor, SkEncodedInfo::Alpha* outAlpha) {
msarettac6c7502016-04-25 09:30:24 -0700921 png_uint_32 origWidth, origHeight;
922 int bitDepth, encodedColorType;
scroggod8d68552016-06-06 11:26:17 -0700923 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
msarettac6c7502016-04-25 09:30:24 -0700924 &encodedColorType, nullptr, nullptr, nullptr);
925
926 // Tell libpng to strip 16 bit/color files down to 8 bits/color.
927 // TODO: Should we handle this in SkSwizzler? Could this also benefit
928 // RAW decodes?
929 if (bitDepth == 16) {
930 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType);
scroggod8d68552016-06-06 11:26:17 -0700931 png_set_strip_16(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700932 }
933
934 // Now determine the default colorType and alphaType and set the required transforms.
935 // Often, we depend on SkSwizzler to perform any transforms that we need. However, we
936 // still depend on libpng for many of the rare and PNG-specific cases.
937 SkEncodedInfo::Color color;
938 SkEncodedInfo::Alpha alpha;
939 switch (encodedColorType) {
940 case PNG_COLOR_TYPE_PALETTE:
941 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
942 // byte into separate bytes (useful for paletted and grayscale images).
943 if (bitDepth < 8) {
944 // TODO: Should we use SkSwizzler here?
scroggod8d68552016-06-06 11:26:17 -0700945 png_set_packing(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700946 }
947
948 color = SkEncodedInfo::kPalette_Color;
949 // Set the alpha depending on if a transparency chunk exists.
scroggod8d68552016-06-06 11:26:17 -0700950 alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ?
msarettac6c7502016-04-25 09:30:24 -0700951 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
952 break;
953 case PNG_COLOR_TYPE_RGB:
scroggod8d68552016-06-06 11:26:17 -0700954 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
msarettac6c7502016-04-25 09:30:24 -0700955 // Convert to RGBA if transparency chunk exists.
scroggod8d68552016-06-06 11:26:17 -0700956 png_set_tRNS_to_alpha(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700957 color = SkEncodedInfo::kRGBA_Color;
958 alpha = SkEncodedInfo::kBinary_Alpha;
959 } else {
960 color = SkEncodedInfo::kRGB_Color;
961 alpha = SkEncodedInfo::kOpaque_Alpha;
962 }
963 break;
964 case PNG_COLOR_TYPE_GRAY:
965 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
966 if (bitDepth < 8) {
967 // TODO: Should we use SkSwizzler here?
scroggod8d68552016-06-06 11:26:17 -0700968 png_set_expand_gray_1_2_4_to_8(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700969 }
970
scroggod8d68552016-06-06 11:26:17 -0700971 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
972 png_set_tRNS_to_alpha(png_ptr);
msarettac6c7502016-04-25 09:30:24 -0700973 color = SkEncodedInfo::kGrayAlpha_Color;
974 alpha = SkEncodedInfo::kBinary_Alpha;
975 } else {
976 color = SkEncodedInfo::kGray_Color;
977 alpha = SkEncodedInfo::kOpaque_Alpha;
978 }
979 break;
980 case PNG_COLOR_TYPE_GRAY_ALPHA:
981 color = SkEncodedInfo::kGrayAlpha_Color;
982 alpha = SkEncodedInfo::kUnpremul_Alpha;
983 break;
984 case PNG_COLOR_TYPE_RGBA:
985 color = SkEncodedInfo::kRGBA_Color;
986 alpha = SkEncodedInfo::kUnpremul_Alpha;
987 break;
988 default:
989 // All the color types have been covered above.
990 SkASSERT(false);
991 color = SkEncodedInfo::kRGBA_Color;
992 alpha = SkEncodedInfo::kUnpremul_Alpha;
993 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700994 if (outColor) {
995 *outColor = color;
scroggo9a89a092016-05-31 13:52:47 -0700996 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700997 if (outAlpha) {
998 *outAlpha = alpha;
scroggod8d68552016-06-06 11:26:17 -0700999 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001000}
scroggod8d68552016-06-06 11:26:17 -07001001
scroggo8e6c7ad2016-09-16 08:20:38 -07001002#ifdef SK_GOOGLE3_PNG_HACK
1003void SkPngCodec::rereadInfoCallback() {
1004 general_info_callback(fPng_ptr, fInfo_ptr, nullptr, nullptr);
1005 png_set_interlace_handling(fPng_ptr);
1006 png_read_update_info(fPng_ptr, fInfo_ptr);
1007}
1008#endif
1009
1010void AutoCleanPng::infoCallback() {
1011 SkEncodedInfo::Color color;
1012 SkEncodedInfo::Alpha alpha;
1013 general_info_callback(fPng_ptr, fInfo_ptr, &color, &alpha);
1014
1015 const int numberPasses = png_set_interlace_handling(fPng_ptr);
1016
1017 fReadHeader = true;
1018 fDecodedBounds = true;
1019#ifndef SK_GOOGLE3_PNG_HACK
1020 // 1 tells libpng to save any extra data. We may be able to be more efficient by saving
1021 // it ourselves.
1022 png_process_data_pause(fPng_ptr, 1);
1023#else
1024 // Hack to make png_process_data stop.
1025 fPng_ptr->buffer_size = 0;
1026#endif
1027 if (fOutCodec) {
1028 SkASSERT(nullptr == *fOutCodec);
1029 sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr);
msarettf34cd632016-05-25 10:13:53 -07001030 if (!colorSpace) {
1031 // Treat unmarked pngs as sRGB.
1032 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
1033 }
scroggod8d68552016-06-06 11:26:17 -07001034
msarett549ca322016-08-17 08:54:08 -07001035 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, 8);
scroggo8e6c7ad2016-09-16 08:20:38 -07001036 // FIXME (scroggo): Once we get rid of SK_GOOGLE3_PNG_HACK, general_info_callback can
1037 // be inlined, so these values will already be set.
1038 png_uint_32 origWidth = png_get_image_width(fPng_ptr, fInfo_ptr);
1039 png_uint_32 origHeight = png_get_image_height(fPng_ptr, fInfo_ptr);
1040 png_byte bitDepth = png_get_bit_depth(fPng_ptr, fInfo_ptr);
msarett549ca322016-08-17 08:54:08 -07001041 SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace);
1042
1043 if (SkEncodedInfo::kOpaque_Alpha == alpha) {
1044 png_color_8p sigBits;
scroggo8e6c7ad2016-09-16 08:20:38 -07001045 if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
msarett549ca322016-08-17 08:54:08 -07001046 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
1047 // Recommend a decode to 565 if the sBIT indicates 565.
1048 imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
1049 }
1050 }
1051 }
scroggod8d68552016-06-06 11:26:17 -07001052
msarettac6c7502016-04-25 09:30:24 -07001053 if (1 == numberPasses) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001054 *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo, fStream,
1055 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
msarettac6c7502016-04-25 09:30:24 -07001056 } else {
scroggo8e6c7ad2016-09-16 08:20:38 -07001057 *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo, fStream,
1058 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, numberPasses);
msarettac6c7502016-04-25 09:30:24 -07001059 }
1060 }
1061
scroggo8e6c7ad2016-09-16 08:20:38 -07001062
1063 // Release the pointers, which are now owned by the codec or the caller is expected to
1064 // take ownership.
1065 this->releasePngPtrs();
msarettac6c7502016-04-25 09:30:24 -07001066}
1067
msarett549ca322016-08-17 08:54:08 -07001068SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
mtklein6dc5b9a2016-08-24 12:22:32 -07001069 SkStream* stream, SkPngChunkReader* chunkReader, void* png_ptr,
scroggo8e6c7ad2016-09-16 08:20:38 -07001070 void* info_ptr, int bitDepth)
msarett549ca322016-08-17 08:54:08 -07001071 : INHERITED(encodedInfo, imageInfo, stream)
msarettac6c7502016-04-25 09:30:24 -07001072 , fPngChunkReader(SkSafeRef(chunkReader))
1073 , fPng_ptr(png_ptr)
1074 , fInfo_ptr(info_ptr)
msarettd1ec89b2016-08-03 12:59:27 -07001075 , fColorXformSrcRow(nullptr)
msarettac6c7502016-04-25 09:30:24 -07001076 , fBitDepth(bitDepth)
scroggo8e6c7ad2016-09-16 08:20:38 -07001077#ifdef SK_GOOGLE3_PNG_HACK
1078 , fNeedsToRereadHeader(true)
1079#endif
msarettac6c7502016-04-25 09:30:24 -07001080{}
1081
1082SkPngCodec::~SkPngCodec() {
1083 this->destroyReadStruct();
1084}
1085
1086void SkPngCodec::destroyReadStruct() {
1087 if (fPng_ptr) {
1088 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
1089 SkASSERT(fInfo_ptr);
mtklein6dc5b9a2016-08-24 12:22:32 -07001090 png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr);
msarettac6c7502016-04-25 09:30:24 -07001091 fPng_ptr = nullptr;
1092 fInfo_ptr = nullptr;
1093 }
1094}
1095
1096///////////////////////////////////////////////////////////////////////////////
1097// Getting the pixels
1098///////////////////////////////////////////////////////////////////////////////
1099
msarettd1ec89b2016-08-03 12:59:27 -07001100bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options,
1101 SkPMColor ctable[], int* ctableCount) {
scroggo8e6c7ad2016-09-16 08:20:38 -07001102 if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
msarettd1ec89b2016-08-03 12:59:27 -07001103 SkCodecPrintf("Failed on png_read_update_info.\n");
1104 return false;
msarettac6c7502016-04-25 09:30:24 -07001105 }
1106 png_read_update_info(fPng_ptr, fInfo_ptr);
1107
msarett400a93b2016-09-01 18:32:52 -07001108 // Reset fSwizzler and fColorXform. We can't do this in onRewind() because the
1109 // interlaced scanline decoder may need to rewind.
1110 fSwizzler.reset(nullptr);
msarettd1ec89b2016-08-03 12:59:27 -07001111 fColorXform = nullptr;
msarett400a93b2016-09-01 18:32:52 -07001112
msarett2ecc35f2016-09-08 11:55:16 -07001113 if (needs_color_xform(dstInfo, this->getInfo())) {
msarettd1ec89b2016-08-03 12:59:27 -07001114 fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()),
1115 sk_ref_sp(dstInfo.colorSpace()));
msarett2ecc35f2016-09-08 11:55:16 -07001116 SkASSERT(fColorXform);
msarett400a93b2016-09-01 18:32:52 -07001117 }
msarettd3317422016-08-22 13:00:05 -07001118
msarett400a93b2016-09-01 18:32:52 -07001119 // If the image is RGBA and we have a color xform, we can skip the swizzler.
1120 // FIXME (msarett):
1121 // Support more input types to fColorXform (ex: RGB, Gray) and skip the swizzler more often.
1122 if (fColorXform && SkEncodedInfo::kRGBA_Color == this->getEncodedInfo().color() &&
1123 !options.fSubset)
1124 {
1125 fXformMode = kColorOnly_XformMode;
1126 return true;
msarettd1ec89b2016-08-03 12:59:27 -07001127 }
1128
msarettac6c7502016-04-25 09:30:24 -07001129 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
msarettdcd5e652016-08-22 08:48:40 -07001130 if (!this->createColorTable(dstInfo, ctableCount)) {
msarettd1ec89b2016-08-03 12:59:27 -07001131 return false;
msarettac6c7502016-04-25 09:30:24 -07001132 }
1133 }
1134
msarett400a93b2016-09-01 18:32:52 -07001135 // Copy the color table to the client if they request kIndex8 mode.
1136 copy_color_table(dstInfo, fColorTable, ctable, ctableCount);
msarettac6c7502016-04-25 09:30:24 -07001137
msarett400a93b2016-09-01 18:32:52 -07001138 this->initializeSwizzler(dstInfo, options);
1139 return true;
1140}
1141
msarettc0444612016-09-16 11:45:58 -07001142void SkPngCodec::initializeXformParams() {
1143 switch (fXformMode) {
1144 case kColorOnly_XformMode:
1145 fXformColorFormat = select_xform_format(this->dstInfo().colorType());
1146 fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
1147 this->getInfo().alphaType());
1148 fXformWidth = this->dstInfo().width();
1149 break;
1150 case kSwizzleColor_XformMode:
1151 fXformColorFormat = select_xform_format(this->dstInfo().colorType());
1152 fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
1153 this->getInfo().alphaType());
1154 fXformWidth = this->swizzler()->swizzleWidth();
1155 break;
1156 default:
1157 break;
1158 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001159}
1160
msarett400a93b2016-09-01 18:32:52 -07001161static inline bool apply_xform_on_decode(SkColorType dstColorType, SkEncodedInfo::Color srcColor) {
1162 // We will apply the color xform when reading the color table, unless F16 is requested.
1163 return SkEncodedInfo::kPalette_Color != srcColor || kRGBA_F16_SkColorType == dstColorType;
1164}
1165
1166void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
1167 SkImageInfo swizzlerInfo = dstInfo;
1168 Options swizzlerOptions = options;
1169 fXformMode = kSwizzleOnly_XformMode;
1170 if (fColorXform && apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color())) {
1171 swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType);
1172 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
1173 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
1174 }
1175
1176 fXformMode = kSwizzleColor_XformMode;
1177
1178 // Here, we swizzle into temporary memory, which is not zero initialized.
1179 // FIXME (msarett):
1180 // Is this a problem?
1181 swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
1182 }
1183
msarettac6c7502016-04-25 09:30:24 -07001184 const SkPMColor* colors = get_color_ptr(fColorTable.get());
msarettd1ec89b2016-08-03 12:59:27 -07001185 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo,
msarettd3317422016-08-22 13:00:05 -07001186 swizzlerOptions));
msarettac6c7502016-04-25 09:30:24 -07001187 SkASSERT(fSwizzler);
msarett400a93b2016-09-01 18:32:52 -07001188}
1189
1190SkSampler* SkPngCodec::getSampler(bool createIfNecessary) {
1191 if (fSwizzler || !createIfNecessary) {
1192 return fSwizzler;
1193 }
1194
1195 this->initializeSwizzler(this->dstInfo(), this->options());
1196 return fSwizzler;
msarettac6c7502016-04-25 09:30:24 -07001197}
1198
msarettac6c7502016-04-25 09:30:24 -07001199bool SkPngCodec::onRewind() {
scroggo8e6c7ad2016-09-16 08:20:38 -07001200#ifdef SK_GOOGLE3_PNG_HACK
1201 fNeedsToRereadHeader = true;
1202 return true;
1203#else
msarettac6c7502016-04-25 09:30:24 -07001204 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
1205 // succeeds, they will be repopulated, and if it fails, they will
1206 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
1207 // come through this function which will rewind and again attempt
1208 // to reinitialize them.
1209 this->destroyReadStruct();
1210
scroggo46c57472015-09-30 08:57:13 -07001211 png_structp png_ptr;
1212 png_infop info_ptr;
msarettac6c7502016-04-25 09:30:24 -07001213 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &info_ptr)) {
1214 return false;
scroggo05245902015-03-25 11:11:52 -07001215 }
1216
msarettac6c7502016-04-25 09:30:24 -07001217 fPng_ptr = png_ptr;
1218 fInfo_ptr = info_ptr;
1219 return true;
scroggo8e6c7ad2016-09-16 08:20:38 -07001220#endif
msarettac6c7502016-04-25 09:30:24 -07001221}
msarett6a738212016-03-04 13:27:35 -08001222
msarettd1ec89b2016-08-03 12:59:27 -07001223SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
1224 size_t rowBytes, const Options& options,
msarettac6c7502016-04-25 09:30:24 -07001225 SkPMColor ctable[], int* ctableCount,
1226 int* rowsDecoded) {
msarett2ecc35f2016-09-08 11:55:16 -07001227 if (!conversion_possible(dstInfo, this->getInfo()) ||
msarettd1ec89b2016-08-03 12:59:27 -07001228 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
1229 {
msarettac6c7502016-04-25 09:30:24 -07001230 return kInvalidConversion;
1231 }
scroggo8e6c7ad2016-09-16 08:20:38 -07001232#ifdef SK_GOOGLE3_PNG_HACK
1233 // Note that this is done after initializeXforms. Otherwise that method
1234 // would not have png_ptr to use.
1235 if (!this->rereadHeaderIfNecessary()) {
1236 return kCouldNotRewind;
1237 }
1238#endif
msarettd1ec89b2016-08-03 12:59:27 -07001239
msarettac6c7502016-04-25 09:30:24 -07001240 if (options.fSubset) {
msarettac6c7502016-04-25 09:30:24 -07001241 return kUnimplemented;
scroggo05245902015-03-25 11:11:52 -07001242 }
1243
msarett400a93b2016-09-01 18:32:52 -07001244 this->allocateStorage(dstInfo);
msarettc0444612016-09-16 11:45:58 -07001245 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001246 return this->decodeAllRows(dst, rowBytes, rowsDecoded);
1247}
msarettac6c7502016-04-25 09:30:24 -07001248
scroggo8e6c7ad2016-09-16 08:20:38 -07001249SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
1250 void* dst, size_t rowBytes, const SkCodec::Options& options,
1251 SkPMColor* ctable, int* ctableCount) {
1252 if (!conversion_possible(dstInfo, this->getInfo()) ||
1253 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
1254 {
1255 return kInvalidConversion;
1256 }
1257#ifdef SK_GOOGLE3_PNG_HACK
1258 // See note in onGetPixels.
1259 if (!this->rereadHeaderIfNecessary()) {
1260 return kCouldNotRewind;
1261 }
1262#endif
1263
1264 this->allocateStorage(dstInfo);
1265
1266 int firstRow, lastRow;
1267 if (options.fSubset) {
1268 firstRow = options.fSubset->top();
1269 lastRow = options.fSubset->bottom() - 1;
1270 } else {
1271 firstRow = 0;
1272 lastRow = dstInfo.height() - 1;
1273 }
1274 this->setRange(firstRow, lastRow, dst, rowBytes);
scroggod8d68552016-06-06 11:26:17 -07001275 return kSuccess;
scroggo6fb23912016-06-02 14:16:43 -07001276}
1277
scroggo8e6c7ad2016-09-16 08:20:38 -07001278SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) {
1279 // FIXME: Only necessary on the first call.
msarettc0444612016-09-16 11:45:58 -07001280 this->initializeXformParams();
scroggo8e6c7ad2016-09-16 08:20:38 -07001281
1282 return this->decode(rowsDecoded);
1283}
1284
msarettf7eb6fc2016-09-13 09:04:11 -07001285uint64_t SkPngCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
msarettac6c7502016-04-25 09:30:24 -07001286 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
1287 if (colorPtr) {
msarettc0444612016-09-16 11:45:58 -07001288 SkAlphaType alphaType = select_xform_alpha(dstInfo.alphaType(),
msarettf7eb6fc2016-09-13 09:04:11 -07001289 this->getInfo().alphaType());
1290 return get_color_table_fill_value(dstInfo.colorType(), alphaType, colorPtr, 0,
1291 fColorXform.get());
msarettac6c7502016-04-25 09:30:24 -07001292 }
msarettf7eb6fc2016-09-13 09:04:11 -07001293 return INHERITED::onGetFillValue(dstInfo);
msarettac6c7502016-04-25 09:30:24 -07001294}
1295
1296SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
1297 SkAutoTDelete<SkStream> streamDeleter(stream);
1298
scroggo8e6c7ad2016-09-16 08:20:38 -07001299 SkCodec* outCodec = nullptr;
1300 if (read_header(streamDeleter.get(), chunkReader, &outCodec, nullptr, nullptr)) {
msarettac6c7502016-04-25 09:30:24 -07001301 // Codec has taken ownership of the stream.
1302 SkASSERT(outCodec);
1303 streamDeleter.release();
1304 return outCodec;
1305 }
1306
1307 return nullptr;
scroggo05245902015-03-25 11:11:52 -07001308}