blob: 82c952e97b3ffbad7a7210893264a3f0ee7d73ac [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
8#include "SkCodec_libpng.h"
msarett74114382015-03-16 11:55:18 -07009#include "SkCodecPriv.h"
scroggof24f2242015-03-03 08:59:20 -080010#include "SkColorPriv.h"
11#include "SkColorTable.h"
12#include "SkBitmap.h"
13#include "SkMath.h"
14#include "SkSize.h"
15#include "SkStream.h"
16#include "SkSwizzler.h"
17
18///////////////////////////////////////////////////////////////////////////////
19// Helper macros
20///////////////////////////////////////////////////////////////////////////////
21
22#ifndef png_jmpbuf
23# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
24#endif
25
26/* These were dropped in libpng >= 1.4 */
27#ifndef png_infopp_NULL
halcanary96fcdcc2015-08-27 07:41:13 -070028 #define png_infopp_NULL nullptr
scroggof24f2242015-03-03 08:59:20 -080029#endif
30
31#ifndef png_bytepp_NULL
halcanary96fcdcc2015-08-27 07:41:13 -070032 #define png_bytepp_NULL nullptr
scroggof24f2242015-03-03 08:59:20 -080033#endif
34
35#ifndef int_p_NULL
halcanary96fcdcc2015-08-27 07:41:13 -070036 #define int_p_NULL nullptr
scroggof24f2242015-03-03 08:59:20 -080037#endif
38
39#ifndef png_flush_ptr_NULL
halcanary96fcdcc2015-08-27 07:41:13 -070040 #define png_flush_ptr_NULL nullptr
scroggof24f2242015-03-03 08:59:20 -080041#endif
42
43///////////////////////////////////////////////////////////////////////////////
44// Callback functions
45///////////////////////////////////////////////////////////////////////////////
46
47static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070048 SkCodecPrintf("------ png error %s\n", msg);
scroggof24f2242015-03-03 08:59:20 -080049 longjmp(png_jmpbuf(png_ptr), 1);
50}
51
scroggo0eed6df2015-03-26 10:07:56 -070052void sk_warning_fn(png_structp, png_const_charp msg) {
53 SkCodecPrintf("----- png warning %s\n", msg);
54}
55
scroggof24f2242015-03-03 08:59:20 -080056static void sk_read_fn(png_structp png_ptr, png_bytep data,
57 png_size_t length) {
58 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
59 const size_t bytes = stream->read(data, length);
60 if (bytes != length) {
61 // FIXME: We want to report the fact that the stream was truncated.
62 // One way to do that might be to pass a enum to longjmp so setjmp can
63 // specify the failure.
64 png_error(png_ptr, "Read Error!");
65 }
66}
67
68///////////////////////////////////////////////////////////////////////////////
69// Helpers
70///////////////////////////////////////////////////////////////////////////////
71
72class AutoCleanPng : public SkNoncopyable {
73public:
74 AutoCleanPng(png_structp png_ptr)
75 : fPng_ptr(png_ptr)
halcanary96fcdcc2015-08-27 07:41:13 -070076 , fInfo_ptr(nullptr) {}
scroggof24f2242015-03-03 08:59:20 -080077
78 ~AutoCleanPng() {
halcanary96fcdcc2015-08-27 07:41:13 -070079 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
scroggof24f2242015-03-03 08:59:20 -080080 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070081 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
scroggof24f2242015-03-03 08:59:20 -080082 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
83 }
84 }
85
86 void setInfoPtr(png_infop info_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -070087 SkASSERT(nullptr == fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -080088 fInfo_ptr = info_ptr;
89 }
90
91 void detach() {
halcanary96fcdcc2015-08-27 07:41:13 -070092 fPng_ptr = nullptr;
93 fInfo_ptr = nullptr;
scroggof24f2242015-03-03 08:59:20 -080094 }
95
96private:
97 png_structp fPng_ptr;
98 png_infop fInfo_ptr;
99};
100#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
101
emmaleer21cea722015-07-10 07:48:03 -0700102//checks if there is transparency info in the tRNS chunk
103//image types which could have data in the tRNS chunk include: Index8, Gray8, RGB
104static bool has_transparency_in_tRNS(png_structp png_ptr,
scroggof24f2242015-03-03 08:59:20 -0800105 png_infop info_ptr) {
106 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
107 return false;
108 }
109
110 png_bytep trans;
111 int num_trans;
halcanary96fcdcc2015-08-27 07:41:13 -0700112 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr);
scroggof24f2242015-03-03 08:59:20 -0800113 return num_trans > 0;
114}
115
116// Method for coverting to either an SkPMColor or a similarly packed
117// unpremultiplied color.
118typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
119
120// Note: SkColorTable claims to store SkPMColors, which is not necessarily
121// the case here.
scroggo9b2cdbf42015-07-10 12:07:02 -0700122bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800123 int numPalette;
124 png_colorp palette;
125 png_bytep trans;
126
scroggo05245902015-03-25 11:11:52 -0700127 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
128 return false;
scroggof24f2242015-03-03 08:59:20 -0800129 }
130
msarett438b2ad2015-04-09 12:43:10 -0700131 // Note: These are not necessarily SkPMColors
scroggof24f2242015-03-03 08:59:20 -0800132 SkPMColor colorStorage[256]; // worst-case storage
133 SkPMColor* colorPtr = colorStorage;
134
135 int numTrans;
scroggo05245902015-03-25 11:11:52 -0700136 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700137 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, nullptr);
scroggof24f2242015-03-03 08:59:20 -0800138 } else {
139 numTrans = 0;
140 }
141
142 // check for bad images that might make us crash
143 if (numTrans > numPalette) {
144 numTrans = numPalette;
145 }
146
147 int index = 0;
148 int transLessThanFF = 0;
149
150 // Choose which function to use to create the color table. If the final destination's
151 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
152 PackColorProc proc;
153 if (premultiply) {
154 proc = &SkPreMultiplyARGB;
155 } else {
156 proc = &SkPackARGB32NoCheck;
157 }
158 for (; index < numTrans; index++) {
159 transLessThanFF |= (int)*trans - 0xFF;
160 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
161 palette++;
162 }
163
scroggo46c57472015-09-30 08:57:13 -0700164 if (transLessThanFF >= 0) {
165 // No transparent colors were found.
166 fAlphaState = kOpaque_AlphaState;
167 }
scroggof24f2242015-03-03 08:59:20 -0800168
169 for (; index < numPalette; index++) {
170 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
171 palette++;
172 }
173
msarett438b2ad2015-04-09 12:43:10 -0700174 /* BUGGY IMAGE WORKAROUND
175 Invalid images could contain pixel values that are greater than the number of palette
176 entries. Since we use pixel values as indices into the palette this could result in reading
177 beyond the end of the palette which could leak the contents of uninitialized memory. To
178 ensure this doesn't happen, we grow the colortable to the maximum size that can be
179 addressed by the bitdepth of the image and fill it with the last palette color or black if
180 the palette is empty (really broken image).
181 */
scroggo9b2cdbf42015-07-10 12:07:02 -0700182 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8));
msarett438b2ad2015-04-09 12:43:10 -0700183 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
184 for (; index < colorCount; index++) {
185 *colorPtr++ = lastColor;
186 }
187
188 // Set the new color count
halcanary96fcdcc2015-08-27 07:41:13 -0700189 if (ctableCount != nullptr) {
msarett438b2ad2015-04-09 12:43:10 -0700190 *ctableCount = colorCount;
scroggof24f2242015-03-03 08:59:20 -0800191 }
192
halcanary385fe4d2015-08-26 13:07:48 -0700193 fColorTable.reset(new SkColorTable(colorStorage, colorCount));
scroggo05245902015-03-25 11:11:52 -0700194 return true;
scroggof24f2242015-03-03 08:59:20 -0800195}
196
197///////////////////////////////////////////////////////////////////////////////
198// Creation
199///////////////////////////////////////////////////////////////////////////////
200
201#define PNG_BYTES_TO_CHECK 4
202
203bool SkPngCodec::IsPng(SkStream* stream) {
204 char buf[PNG_BYTES_TO_CHECK];
205 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
206 return false;
207 }
208 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
209 return false;
210 }
211 return true;
212}
213
halcanary96fcdcc2015-08-27 07:41:13 -0700214// Reads the header, and initializes the passed in fields, if not nullptr (except
scroggo3eada2a2015-04-01 09:33:23 -0700215// stream, which is passed to the read function).
216// Returns true on success, in which case the caller is responsible for calling
217// png_destroy_read_struct. If it returns false, the passed in fields (except
218// stream) are unchanged.
219static bool read_header(SkStream* stream, png_structp* png_ptrp,
scroggo46c57472015-09-30 08:57:13 -0700220 png_infop* info_ptrp, SkImageInfo* imageInfo,
221 int* bitDepthPtr, int* numberPassesPtr) {
scroggof24f2242015-03-03 08:59:20 -0800222 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
halcanary96fcdcc2015-08-27 07:41:13 -0700223 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
scroggo0eed6df2015-03-26 10:07:56 -0700224 sk_error_fn, sk_warning_fn);
scroggof24f2242015-03-03 08:59:20 -0800225 if (!png_ptr) {
scroggo3eada2a2015-04-01 09:33:23 -0700226 return false;
scroggof24f2242015-03-03 08:59:20 -0800227 }
228
229 AutoCleanPng autoClean(png_ptr);
230
231 png_infop info_ptr = png_create_info_struct(png_ptr);
halcanary96fcdcc2015-08-27 07:41:13 -0700232 if (info_ptr == nullptr) {
scroggo3eada2a2015-04-01 09:33:23 -0700233 return false;
scroggof24f2242015-03-03 08:59:20 -0800234 }
235
236 autoClean.setInfoPtr(info_ptr);
237
238 // FIXME: Could we use the return value of setjmp to specify the type of
239 // error?
240 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo3eada2a2015-04-01 09:33:23 -0700241 return false;
scroggof24f2242015-03-03 08:59:20 -0800242 }
243
244 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
245
246 // FIXME: This is where the old code hooks up the Peeker. Does it need to
247 // be set this early? (i.e. where are the user chunks? early in the stream,
248 // potentially?)
249 // If it does, we need to figure out a way to set it here.
250
251 // The call to png_read_info() gives us all of the information from the
252 // PNG file before the first IDAT (image data chunk).
253 png_read_info(png_ptr, info_ptr);
254 png_uint_32 origWidth, origHeight;
255 int bitDepth, colorType;
256 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
257 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
258
scroggo6f29a3c2015-07-07 06:09:08 -0700259 if (bitDepthPtr) {
260 *bitDepthPtr = bitDepth;
261 }
262
scroggof24f2242015-03-03 08:59:20 -0800263 // sanity check for size
264 {
265 int64_t size = sk_64_mul(origWidth, origHeight);
266 // now check that if we are 4-bytes per pixel, we also don't overflow
267 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
scroggo3eada2a2015-04-01 09:33:23 -0700268 return false;
scroggof24f2242015-03-03 08:59:20 -0800269 }
270 }
271
272 // Tell libpng to strip 16 bit/color files down to 8 bits/color
273 if (bitDepth == 16) {
274 png_set_strip_16(png_ptr);
275 }
276#ifdef PNG_READ_PACK_SUPPORTED
277 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
278 // byte into separate bytes (useful for paletted and grayscale images).
279 if (bitDepth < 8) {
280 png_set_packing(png_ptr);
281 }
282#endif
283 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
284 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
285 png_set_expand_gray_1_2_4_to_8(png_ptr);
286 }
287
scroggo6f29a3c2015-07-07 06:09:08 -0700288 // Now determine the default SkColorType and SkAlphaType and set required transforms
msarettf724b992015-10-15 06:41:06 -0700289 SkColorType skColorType = kUnknown_SkColorType;
290 SkAlphaType skAlphaType = kUnknown_SkAlphaType;
scroggof24f2242015-03-03 08:59:20 -0800291 switch (colorType) {
292 case PNG_COLOR_TYPE_PALETTE:
msarett438b2ad2015-04-09 12:43:10 -0700293 skColorType = kIndex_8_SkColorType;
emmaleer21cea722015-07-10 07:48:03 -0700294 skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ?
scroggof24f2242015-03-03 08:59:20 -0800295 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
296 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700297 case PNG_COLOR_TYPE_RGB:
emmaleer21cea722015-07-10 07:48:03 -0700298 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700299 //convert to RGBA with tranparency information in tRNS chunk if it exists
300 png_set_tRNS_to_alpha(png_ptr);
jvanverth6c90e092015-07-02 10:35:25 -0700301 skAlphaType = kUnpremul_SkAlphaType;
302 } else {
scroggo6f29a3c2015-07-07 06:09:08 -0700303 //convert to RGBA with Opaque Alpha
304 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
jvanverth6c90e092015-07-02 10:35:25 -0700305 skAlphaType = kOpaque_SkAlphaType;
306 }
307 skColorType = kN32_SkColorType;
308 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700309 case PNG_COLOR_TYPE_GRAY:
emmaleer21cea722015-07-10 07:48:03 -0700310 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700311 //FIXME: support gray with alpha as a color type
312 //convert to RGBA if there is transparentcy info in the tRNS chunk
313 png_set_tRNS_to_alpha(png_ptr);
314 png_set_gray_to_rgb(png_ptr);
315 skColorType = kN32_SkColorType;
316 skAlphaType = kUnpremul_SkAlphaType;
317 } else {
318 skColorType = kGray_8_SkColorType;
319 skAlphaType = kOpaque_SkAlphaType;
320 }
321 break;
322 case PNG_COLOR_TYPE_GRAY_ALPHA:
323 //FIXME: support gray with alpha as a color type
324 //convert to RGBA
jvanverth6c90e092015-07-02 10:35:25 -0700325 png_set_gray_to_rgb(png_ptr);
scroggo6f29a3c2015-07-07 06:09:08 -0700326 skColorType = kN32_SkColorType;
327 skAlphaType = kUnpremul_SkAlphaType;
328 break;
329 case PNG_COLOR_TYPE_RGBA:
330 skColorType = kN32_SkColorType;
331 skAlphaType = kUnpremul_SkAlphaType;
332 break;
333 default:
334 //all the color types have been covered above
335 SkASSERT(false);
scroggof24f2242015-03-03 08:59:20 -0800336 }
337
scroggo46c57472015-09-30 08:57:13 -0700338 int numberPasses = png_set_interlace_handling(png_ptr);
339 if (numberPassesPtr) {
340 *numberPassesPtr = numberPasses;
341 }
342
halcanary6950de62015-11-07 05:29:00 -0800343 // FIXME: Also need to check for sRGB ( https://bug.skia.org/3471 ).
scroggof24f2242015-03-03 08:59:20 -0800344
scroggo3eada2a2015-04-01 09:33:23 -0700345 if (imageInfo) {
scroggo6f29a3c2015-07-07 06:09:08 -0700346 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlphaType);
scroggo3eada2a2015-04-01 09:33:23 -0700347 }
scroggof24f2242015-03-03 08:59:20 -0800348 autoClean.detach();
scroggo3eada2a2015-04-01 09:33:23 -0700349 if (png_ptrp) {
350 *png_ptrp = png_ptr;
351 }
352 if (info_ptrp) {
353 *info_ptrp = info_ptr;
354 }
scroggo6f29a3c2015-07-07 06:09:08 -0700355
scroggo3eada2a2015-04-01 09:33:23 -0700356 return true;
357}
358
scroggof24f2242015-03-03 08:59:20 -0800359SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
scroggo46c57472015-09-30 08:57:13 -0700360 png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
scroggof24f2242015-03-03 08:59:20 -0800361 : INHERITED(info, stream)
362 , fPng_ptr(png_ptr)
scroggo05245902015-03-25 11:11:52 -0700363 , fInfo_ptr(info_ptr)
364 , fSrcConfig(SkSwizzler::kUnknown)
scroggo46c57472015-09-30 08:57:13 -0700365 , fNumberPasses(numberPasses)
scroggo6f29a3c2015-07-07 06:09:08 -0700366 , fBitDepth(bitDepth)
scroggo46c57472015-09-30 08:57:13 -0700367{
368 if (info.alphaType() == kOpaque_SkAlphaType) {
369 fAlphaState = kOpaque_AlphaState;
370 } else {
371 fAlphaState = kUnknown_AlphaState;
372 }
373}
scroggof24f2242015-03-03 08:59:20 -0800374
375SkPngCodec::~SkPngCodec() {
scroggo3eada2a2015-04-01 09:33:23 -0700376 this->destroyReadStruct();
377}
378
379void SkPngCodec::destroyReadStruct() {
380 if (fPng_ptr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700381 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
scroggo3eada2a2015-04-01 09:33:23 -0700382 SkASSERT(fInfo_ptr);
383 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
halcanary96fcdcc2015-08-27 07:41:13 -0700384 fPng_ptr = nullptr;
385 fInfo_ptr = nullptr;
scroggo3eada2a2015-04-01 09:33:23 -0700386 }
scroggof24f2242015-03-03 08:59:20 -0800387}
388
389///////////////////////////////////////////////////////////////////////////////
390// Getting the pixels
391///////////////////////////////////////////////////////////////////////////////
392
scroggo05245902015-03-25 11:11:52 -0700393SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
msarett438b2ad2015-04-09 12:43:10 -0700394 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700395 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700396 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800397 // FIXME: Could we use the return value of setjmp to specify the type of
398 // error?
399 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700400 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800401 return kInvalidInput;
402 }
scroggo6f29a3c2015-07-07 06:09:08 -0700403 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800404
scroggo46c57472015-09-30 08:57:13 -0700405 //srcColorType was determined in read_header() which determined png color type
scroggo6f29a3c2015-07-07 06:09:08 -0700406 const SkColorType srcColorType = this->getInfo().colorType();
407
408 switch (srcColorType) {
409 case kIndex_8_SkColorType:
410 //decode palette to Skia format
411 fSrcConfig = SkSwizzler::kIndex;
scroggo9b2cdbf42015-07-10 12:07:02 -0700412 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(),
scroggo6f29a3c2015-07-07 06:09:08 -0700413 ctableCount)) {
414 return kInvalidInput;
415 }
416 break;
417 case kGray_8_SkColorType:
418 fSrcConfig = SkSwizzler::kGray;
419 break;
420 case kN32_SkColorType:
421 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
422 fSrcConfig = SkSwizzler::kRGBX;
423 } else {
424 fSrcConfig = SkSwizzler::kRGBA;
425 }
426 break;
427 default:
428 //would have exited before now if the colorType was supported by png
429 SkASSERT(false);
430 }
msarett9e43cab2015-04-29 07:38:43 -0700431
432 // Copy the color table to the client if they request kIndex8 mode
433 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
434
435 // Create the swizzler. SkPngCodec retains ownership of the color table.
msarett99f567e2015-08-05 12:58:26 -0700436 const SkPMColor* colors = get_color_ptr(fColorTable.get());
msarettfdb47572015-10-13 12:50:14 -0700437 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, options));
scroggo05245902015-03-25 11:11:52 -0700438 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800439 // FIXME: CreateSwizzler could fail for another reason.
440 return kUnimplemented;
441 }
scroggo05245902015-03-25 11:11:52 -0700442 return kSuccess;
443}
444
scroggo6f29a3c2015-07-07 06:09:08 -0700445
scroggob427db12015-08-12 07:24:13 -0700446bool SkPngCodec::onRewind() {
halcanary96fcdcc2015-08-27 07:41:13 -0700447 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
scroggob427db12015-08-12 07:24:13 -0700448 // succeeds, they will be repopulated, and if it fails, they will
halcanary96fcdcc2015-08-27 07:41:13 -0700449 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
scroggob427db12015-08-12 07:24:13 -0700450 // come through this function which will rewind and again attempt
451 // to reinitialize them.
452 this->destroyReadStruct();
453
454 png_structp png_ptr;
455 png_infop info_ptr;
scroggo46c57472015-09-30 08:57:13 -0700456 if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) {
scroggob427db12015-08-12 07:24:13 -0700457 return false;
scroggo58421542015-04-01 11:25:20 -0700458 }
scroggob427db12015-08-12 07:24:13 -0700459
460 fPng_ptr = png_ptr;
461 fInfo_ptr = info_ptr;
462 return true;
scroggo58421542015-04-01 11:25:20 -0700463}
464
scroggo05245902015-03-25 11:11:52 -0700465SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
msarett614aa072015-07-27 15:13:17 -0700466 size_t dstRowBytes, const Options& options,
msarette6dd0042015-10-09 11:07:34 -0700467 SkPMColor ctable[], int* ctableCount,
468 int* rowsDecoded) {
scroggo6f29a3c2015-07-07 06:09:08 -0700469 if (!conversion_possible(requestedInfo, this->getInfo())) {
470 return kInvalidConversion;
471 }
scroggob636b452015-07-22 07:16:20 -0700472 if (options.fSubset) {
473 // Subsets are not supported.
474 return kUnimplemented;
475 }
scroggo05245902015-03-25 11:11:52 -0700476
msarett9e43cab2015-04-29 07:38:43 -0700477 // Note that ctable and ctableCount may be modified if there is a color table
msarettfdb47572015-10-13 12:50:14 -0700478 const Result result = this->initializeSwizzler(requestedInfo, options, ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700479 if (result != kSuccess) {
480 return result;
481 }
scroggo05245902015-03-25 11:11:52 -0700482 // FIXME: Could we use the return value of setjmp to specify the type of
483 // error?
msarette6dd0042015-10-09 11:07:34 -0700484 int row = 0;
485 // This must be declared above the call to setjmp to avoid memory leaks on incomplete images.
486 SkAutoMalloc storage;
scroggo05245902015-03-25 11:11:52 -0700487 if (setjmp(png_jmpbuf(fPng_ptr))) {
msarette6dd0042015-10-09 11:07:34 -0700488 // Assume that any error that occurs while reading rows is caused by an incomplete input.
489 if (fNumberPasses > 1) {
490 // FIXME (msarett): Handle incomplete interlaced pngs.
491 return kInvalidInput;
492 }
493 // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium,
494 // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192
495 // bytes), and if we can't fill the buffer, we immediately fail.
496 // For example, if we try to read 8192 bytes, and the image (incorrectly) only contains
497 // half that, which may have been enough to contain a non-zero number of lines, we fail
498 // when we could have decoded a few more lines and then failed.
499 // The read function that we provide for libpng has no way of indicating that we have
500 // made a partial read.
501 // Making our buffer size smaller improves our incomplete decodes, but what impact does
502 // it have on regular decode performance? Should we investigate using a different API
503 // instead of png_read_row(s)? Chromium uses png_process_data.
504 *rowsDecoded = row;
505 return kIncompleteInput;
scroggo05245902015-03-25 11:11:52 -0700506 }
507
scroggo46c57472015-09-30 08:57:13 -0700508 bool hasAlpha = false;
509 // FIXME: We could split these out based on subclass.
msarett614aa072015-07-27 15:13:17 -0700510 void* dstRow = dst;
scroggo05245902015-03-25 11:11:52 -0700511 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800512 const int width = requestedInfo.width();
513 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700514 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
msarett614aa072015-07-27 15:13:17 -0700515 const size_t srcRowBytes = width * bpp;
scroggof24f2242015-03-03 08:59:20 -0800516
517 storage.reset(width * height * bpp);
518 uint8_t* const base = static_cast<uint8_t*>(storage.get());
519
scroggo05245902015-03-25 11:11:52 -0700520 for (int i = 0; i < fNumberPasses; i++) {
msarett614aa072015-07-27 15:13:17 -0700521 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800522 for (int y = 0; y < height; y++) {
msarett614aa072015-07-27 15:13:17 -0700523 uint8_t* bmRow = srcRow;
scroggof24f2242015-03-03 08:59:20 -0800524 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700525 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800526 }
527 }
528
529 // Now swizzle it.
msarett614aa072015-07-27 15:13:17 -0700530 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800531 for (int y = 0; y < height; y++) {
scroggo46c57472015-09-30 08:57:13 -0700532 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
msarett614aa072015-07-27 15:13:17 -0700533 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
534 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800535 }
536 } else {
scroggo05245902015-03-25 11:11:52 -0700537 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800538 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
msarette6dd0042015-10-09 11:07:34 -0700539 for (; row < requestedInfo.height(); row++) {
scroggof24f2242015-03-03 08:59:20 -0800540 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo46c57472015-09-30 08:57:13 -0700541 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines.
542 hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
msarett614aa072015-07-27 15:13:17 -0700543 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
scroggof24f2242015-03-03 08:59:20 -0800544 }
545 }
546
scroggo46c57472015-09-30 08:57:13 -0700547 if (hasAlpha) {
548 fAlphaState = kHasAlpha_AlphaState;
549 } else {
550 fAlphaState = kOpaque_AlphaState;
551 }
552
scroggo05245902015-03-25 11:11:52 -0700553 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
554 // scanline decoding, but we could do it here. Alternatively, we could do
555 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800556
scroggo05245902015-03-25 11:11:52 -0700557 if (setjmp(png_jmpbuf(fPng_ptr))) {
558 // We've already read all the scanlines. This is a success.
emmaleer973ae862015-07-20 13:38:44 -0700559 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700560 }
emmaleer973ae862015-07-20 13:38:44 -0700561
562 // read rest of file, and get additional comment and time chunks in info_ptr
scroggo05245902015-03-25 11:11:52 -0700563 png_read_end(fPng_ptr, fInfo_ptr);
scroggo46c57472015-09-30 08:57:13 -0700564
emmaleer973ae862015-07-20 13:38:44 -0700565 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700566}
567
msarette6dd0042015-10-09 11:07:34 -0700568uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
569 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
570 if (colorPtr) {
571 return get_color_table_fill_value(colorType, colorPtr, 0);
572 }
573 return INHERITED::onGetFillValue(colorType, alphaType);
574}
575
scroggo46c57472015-09-30 08:57:13 -0700576bool SkPngCodec::onReallyHasAlpha() const {
577 switch (fAlphaState) {
578 case kOpaque_AlphaState:
579 return false;
580 case kUnknown_AlphaState:
581 // Maybe the subclass knows?
582 return this->alphaInScanlineDecode() == kHasAlpha_AlphaState;
583 case kHasAlpha_AlphaState:
584 switch (this->alphaInScanlineDecode()) {
585 case kUnknown_AlphaState:
586 // Scanline decoder must not have been used. Return our knowledge.
587 return true;
588 case kOpaque_AlphaState:
589 // Scanline decoder was used, and did not find alpha in its subset.
590 return false;
591 case kHasAlpha_AlphaState:
592 return true;
593 }
594 }
595
596 // All valid AlphaStates have been covered, so this should not be reached.
597 SkASSERT(false);
598 return true;
599}
600
601// Subclass of SkPngCodec which supports scanline decoding
602class SkPngScanlineDecoder : public SkPngCodec {
scroggo05245902015-03-25 11:11:52 -0700603public:
scroggo46c57472015-09-30 08:57:13 -0700604 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
605 png_structp png_ptr, png_infop info_ptr, int bitDepth)
606 : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1)
scroggo46c57472015-09-30 08:57:13 -0700607 , fAlphaState(kUnknown_AlphaState)
msarettf724b992015-10-15 06:41:06 -0700608 , fSrcRow(nullptr)
scroggo1c005e42015-08-04 09:24:45 -0700609 {}
610
scroggo46c57472015-09-30 08:57:13 -0700611 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
612 SkPMColor ctable[], int* ctableCount) override {
scroggo1c005e42015-08-04 09:24:45 -0700613 if (!conversion_possible(dstInfo, this->getInfo())) {
scroggo46c57472015-09-30 08:57:13 -0700614 return kInvalidConversion;
scroggo1c005e42015-08-04 09:24:45 -0700615 }
616
scroggo46c57472015-09-30 08:57:13 -0700617 const Result result = this->initializeSwizzler(dstInfo, options, ctable,
618 ctableCount);
619 if (result != kSuccess) {
scroggo1c005e42015-08-04 09:24:45 -0700620 return result;
621 }
622
scroggo46c57472015-09-30 08:57:13 -0700623 fAlphaState = kUnknown_AlphaState;
624 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()));
scroggo05245902015-03-25 11:11:52 -0700625 fSrcRow = static_cast<uint8_t*>(fStorage.get());
scroggo1c005e42015-08-04 09:24:45 -0700626
scroggo46c57472015-09-30 08:57:13 -0700627 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700628 }
629
msarette6dd0042015-10-09 11:07:34 -0700630 int onGetScanlines(void* dst, int count, size_t rowBytes) override {
631 // Assume that an error in libpng indicates an incomplete input.
632 int row = 0;
scroggo46c57472015-09-30 08:57:13 -0700633 if (setjmp(png_jmpbuf(this->png_ptr()))) {
scroggo230d4ac2015-03-26 07:15:55 -0700634 SkCodecPrintf("setjmp long jump!\n");
msarette6dd0042015-10-09 11:07:34 -0700635 return row;
scroggo05245902015-03-25 11:11:52 -0700636 }
637
msarett614aa072015-07-27 15:13:17 -0700638 void* dstRow = dst;
scroggo46c57472015-09-30 08:57:13 -0700639 bool hasAlpha = false;
msarette6dd0042015-10-09 11:07:34 -0700640 for (; row < count; row++) {
scroggo46c57472015-09-30 08:57:13 -0700641 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
642 hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow));
msarett614aa072015-07-27 15:13:17 -0700643 dstRow = SkTAddOffset<void>(dstRow, rowBytes);
scroggo05245902015-03-25 11:11:52 -0700644 }
scroggo46c57472015-09-30 08:57:13 -0700645
646 if (hasAlpha) {
647 fAlphaState = kHasAlpha_AlphaState;
648 } else {
649 if (kUnknown_AlphaState == fAlphaState) {
650 fAlphaState = kOpaque_AlphaState;
651 }
652 // Otherwise, the AlphaState is unchanged.
653 }
654
msarette6dd0042015-10-09 11:07:34 -0700655 return row;
scroggo05245902015-03-25 11:11:52 -0700656 }
657
msarette6dd0042015-10-09 11:07:34 -0700658 bool onSkipScanlines(int count) override {
659 // Assume that an error in libpng indicates an incomplete input.
scroggo46c57472015-09-30 08:57:13 -0700660 if (setjmp(png_jmpbuf(this->png_ptr()))) {
scroggo230d4ac2015-03-26 07:15:55 -0700661 SkCodecPrintf("setjmp long jump!\n");
msarette6dd0042015-10-09 11:07:34 -0700662 return false;
scroggo05245902015-03-25 11:11:52 -0700663 }
emmaleer7dc91902015-05-27 08:49:04 -0700664 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
665 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
666 //as png_read_rows has it's own loop which calls png_read_row count times.
msarette6dd0042015-10-09 11:07:34 -0700667 for (int row = 0; row < count; row++) {
scroggo46c57472015-09-30 08:57:13 -0700668 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
emmaleer7dc91902015-05-27 08:49:04 -0700669 }
msarette6dd0042015-10-09 11:07:34 -0700670 return true;
scroggo05245902015-03-25 11:11:52 -0700671 }
672
scroggo46c57472015-09-30 08:57:13 -0700673 AlphaState alphaInScanlineDecode() const override {
674 return fAlphaState;
emmaleer8f4ba762015-08-14 07:44:46 -0700675 }
676
scroggo05245902015-03-25 11:11:52 -0700677private:
scroggo46c57472015-09-30 08:57:13 -0700678 AlphaState fAlphaState;
scroggo9b2cdbf42015-07-10 12:07:02 -0700679 SkAutoMalloc fStorage;
680 uint8_t* fSrcRow;
scroggo05245902015-03-25 11:11:52 -0700681
scroggo46c57472015-09-30 08:57:13 -0700682 typedef SkPngCodec INHERITED;
scroggo05245902015-03-25 11:11:52 -0700683};
684
emmaleer0a4c3cb2015-06-22 10:40:21 -0700685
scroggo46c57472015-09-30 08:57:13 -0700686class SkPngInterlacedScanlineDecoder : public SkPngCodec {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700687public:
scroggo46c57472015-09-30 08:57:13 -0700688 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
689 png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
690 : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses)
691 , fAlphaState(kUnknown_AlphaState)
692 , fHeight(-1)
scroggo1c005e42015-08-04 09:24:45 -0700693 , fCanSkipRewind(false)
scroggo1c005e42015-08-04 09:24:45 -0700694 {
scroggo46c57472015-09-30 08:57:13 -0700695 SkASSERT(numberPasses != 1);
696 }
697
698 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
msarettfdb47572015-10-13 12:50:14 -0700699 SkPMColor ctable[], int* ctableCount) override {
scroggo1c005e42015-08-04 09:24:45 -0700700 if (!conversion_possible(dstInfo, this->getInfo())) {
scroggo46c57472015-09-30 08:57:13 -0700701 return kInvalidConversion;
scroggo1c005e42015-08-04 09:24:45 -0700702 }
703
msarettfdb47572015-10-13 12:50:14 -0700704 const Result result = this->initializeSwizzler(dstInfo, options, ctable,
705 ctableCount);
706 if (result != kSuccess) {
scroggo1c005e42015-08-04 09:24:45 -0700707 return result;
708 }
709
scroggo46c57472015-09-30 08:57:13 -0700710 fAlphaState = kUnknown_AlphaState;
scroggo1c005e42015-08-04 09:24:45 -0700711 fHeight = dstInfo.height();
scroggo46c57472015-09-30 08:57:13 -0700712 // FIXME: This need not be called on a second call to onStartScanlineDecode.
713 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig());
scroggo1c005e42015-08-04 09:24:45 -0700714 fGarbageRow.reset(fSrcRowBytes);
715 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
716 fCanSkipRewind = true;
717
718 return SkCodec::kSuccess;
719 }
720
msarette6dd0042015-10-09 11:07:34 -0700721 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
scroggo1c005e42015-08-04 09:24:45 -0700722 // rewind stream if have previously called onGetScanlines,
723 // since we need entire progressive image to get scanlines
724 if (fCanSkipRewind) {
scroggo46c57472015-09-30 08:57:13 -0700725 // We already rewound in onStartScanlineDecode, so there is no reason to rewind.
scroggo1c005e42015-08-04 09:24:45 -0700726 // Next time onGetScanlines is called, we will need to rewind.
727 fCanSkipRewind = false;
scroggo46c57472015-09-30 08:57:13 -0700728 } else {
729 // rewindIfNeeded resets fCurrScanline, since it assumes that start
730 // needs to be called again before scanline decoding. PNG scanline
731 // decoding is the exception, since it needs to rewind between
732 // calls to getScanlines. Keep track of fCurrScanline, to undo the
733 // reset.
msarette6dd0042015-10-09 11:07:34 -0700734 const int currScanline = this->nextScanline();
scroggo46c57472015-09-30 08:57:13 -0700735 // This method would never be called if currScanline is -1
736 SkASSERT(currScanline != -1);
737
738 if (!this->rewindIfNeeded()) {
739 return kCouldNotRewind;
740 }
741 this->updateNextScanline(currScanline);
scroggo1c005e42015-08-04 09:24:45 -0700742 }
743
scroggo46c57472015-09-30 08:57:13 -0700744 if (setjmp(png_jmpbuf(this->png_ptr()))) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700745 SkCodecPrintf("setjmp long jump!\n");
msarette6dd0042015-10-09 11:07:34 -0700746 // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
747 // we may be able to report that all of the memory has been initialized. Even if we
748 // fail on the first pass, we can still report than some scanlines are initialized.
749 return 0;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700750 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700751 SkAutoMalloc storage(count * fSrcRowBytes);
752 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
753 uint8_t* srcRow;
msarette6dd0042015-10-09 11:07:34 -0700754 const int startRow = this->nextScanline();
scroggo46c57472015-09-30 08:57:13 -0700755 for (int i = 0; i < this->numberPasses(); i++) {
756 // read rows we planned to skip into garbage row
757 for (int y = 0; y < startRow; y++){
758 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700759 }
scroggo46c57472015-09-30 08:57:13 -0700760 // read rows we care about into buffer
emmaleer0a4c3cb2015-06-22 10:40:21 -0700761 srcRow = storagePtr;
762 for (int y = 0; y < count; y++) {
scroggo46c57472015-09-30 08:57:13 -0700763 png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700764 srcRow += fSrcRowBytes;
765 }
scroggo46c57472015-09-30 08:57:13 -0700766 // read rows we don't want into garbage buffer
767 for (int y = 0; y < fHeight - startRow - count; y++) {
768 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700769 }
770 }
771 //swizzle the rows we care about
772 srcRow = storagePtr;
msarett614aa072015-07-27 15:13:17 -0700773 void* dstRow = dst;
scroggo46c57472015-09-30 08:57:13 -0700774 bool hasAlpha = false;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700775 for (int y = 0; y < count; y++) {
scroggo46c57472015-09-30 08:57:13 -0700776 hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow));
msarett614aa072015-07-27 15:13:17 -0700777 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700778 srcRow += fSrcRowBytes;
779 }
scroggo46c57472015-09-30 08:57:13 -0700780
781 if (hasAlpha) {
782 fAlphaState = kHasAlpha_AlphaState;
783 } else {
784 if (kUnknown_AlphaState == fAlphaState) {
785 fAlphaState = kOpaque_AlphaState;
786 }
787 // Otherwise, the AlphaState is unchanged.
788 }
789
msarette6dd0042015-10-09 11:07:34 -0700790 return count;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700791 }
792
msarette6dd0042015-10-09 11:07:34 -0700793 bool onSkipScanlines(int count) override {
scroggo46c57472015-09-30 08:57:13 -0700794 // The non-virtual version will update fCurrScanline.
msarette6dd0042015-10-09 11:07:34 -0700795 return true;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700796 }
797
scroggo46c57472015-09-30 08:57:13 -0700798 AlphaState alphaInScanlineDecode() const override {
799 return fAlphaState;
800 }
emmaleer0a4c3cb2015-06-22 10:40:21 -0700801
msarett5406d6f2015-08-31 06:55:13 -0700802 SkScanlineOrder onGetScanlineOrder() const override {
803 return kNone_SkScanlineOrder;
emmaleer8f4ba762015-08-14 07:44:46 -0700804 }
805
emmaleer0a4c3cb2015-06-22 10:40:21 -0700806private:
scroggo46c57472015-09-30 08:57:13 -0700807 AlphaState fAlphaState;
scroggo9b2cdbf42015-07-10 12:07:02 -0700808 int fHeight;
809 size_t fSrcRowBytes;
810 SkAutoMalloc fGarbageRow;
811 uint8_t* fGarbageRowPtr;
scroggo1c005e42015-08-04 09:24:45 -0700812 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
813 // is called whenever some action is taken that reads the stream and
814 // therefore the next call will require a rewind. So it modifies a boolean
815 // to note that the *next* time it is called a rewind is needed.
scroggo46c57472015-09-30 08:57:13 -0700816 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling
817 // onStartScanlineDecode followed by onGetScanlines does *not* require a
818 // rewind. Since rewindIfNeeded does not have this flexibility, we need to
819 // add another layer.
scroggo1c005e42015-08-04 09:24:45 -0700820 bool fCanSkipRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700821
scroggo46c57472015-09-30 08:57:13 -0700822 typedef SkPngCodec INHERITED;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700823};
824
scroggo46c57472015-09-30 08:57:13 -0700825SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
826 SkAutoTDelete<SkStream> streamDeleter(stream);
827 png_structp png_ptr;
828 png_infop info_ptr;
829 SkImageInfo imageInfo;
830 int bitDepth;
831 int numberPasses;
832
833 if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700834 return nullptr;
scroggo05245902015-03-25 11:11:52 -0700835 }
836
scroggo46c57472015-09-30 08:57:13 -0700837 if (1 == numberPasses) {
838 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr,
839 bitDepth);
scroggo05245902015-03-25 11:11:52 -0700840 }
841
scroggo46c57472015-09-30 08:57:13 -0700842 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr,
843 info_ptr, bitDepth, numberPasses);
scroggo05245902015-03-25 11:11:52 -0700844}