blob: 31a4d2e711588f5d9214da37aa3cc9c26c89ff77 [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"
emmaleer8f4ba762015-08-14 07:44:46 -070014#include "SkScaledCodec.h"
scroggo05245902015-03-25 11:11:52 -070015#include "SkScanlineDecoder.h"
scroggof24f2242015-03-03 08:59:20 -080016#include "SkSize.h"
17#include "SkStream.h"
18#include "SkSwizzler.h"
19
20///////////////////////////////////////////////////////////////////////////////
21// Helper macros
22///////////////////////////////////////////////////////////////////////////////
23
24#ifndef png_jmpbuf
25# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
26#endif
27
28/* These were dropped in libpng >= 1.4 */
29#ifndef png_infopp_NULL
30 #define png_infopp_NULL NULL
31#endif
32
33#ifndef png_bytepp_NULL
34 #define png_bytepp_NULL NULL
35#endif
36
37#ifndef int_p_NULL
38 #define int_p_NULL NULL
39#endif
40
41#ifndef png_flush_ptr_NULL
42 #define png_flush_ptr_NULL NULL
43#endif
44
45///////////////////////////////////////////////////////////////////////////////
46// Callback functions
47///////////////////////////////////////////////////////////////////////////////
48
49static 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);
scroggof24f2242015-03-03 08:59:20 -080051 longjmp(png_jmpbuf(png_ptr), 1);
52}
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
scroggof24f2242015-03-03 08:59:20 -080058static void sk_read_fn(png_structp png_ptr, png_bytep data,
59 png_size_t length) {
60 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
61 const size_t bytes = stream->read(data, length);
62 if (bytes != length) {
63 // FIXME: We want to report the fact that the stream was truncated.
64 // One way to do that might be to pass a enum to longjmp so setjmp can
65 // specify the failure.
66 png_error(png_ptr, "Read Error!");
67 }
68}
69
70///////////////////////////////////////////////////////////////////////////////
71// Helpers
72///////////////////////////////////////////////////////////////////////////////
73
74class AutoCleanPng : public SkNoncopyable {
75public:
76 AutoCleanPng(png_structp png_ptr)
77 : fPng_ptr(png_ptr)
78 , fInfo_ptr(NULL) {}
79
80 ~AutoCleanPng() {
81 // fInfo_ptr will never be non-NULL unless fPng_ptr is.
82 if (fPng_ptr) {
83 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
84 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
85 }
86 }
87
88 void setInfoPtr(png_infop info_ptr) {
89 SkASSERT(NULL == fInfo_ptr);
90 fInfo_ptr = info_ptr;
91 }
92
93 void detach() {
94 fPng_ptr = NULL;
95 fInfo_ptr = NULL;
96 }
97
98private:
99 png_structp fPng_ptr;
100 png_infop fInfo_ptr;
101};
102#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
103
emmaleer21cea722015-07-10 07:48:03 -0700104//checks if there is transparency info in the tRNS chunk
105//image types which could have data in the tRNS chunk include: Index8, Gray8, RGB
106static bool has_transparency_in_tRNS(png_structp png_ptr,
scroggof24f2242015-03-03 08:59:20 -0800107 png_infop info_ptr) {
108 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
109 return false;
110 }
111
112 png_bytep trans;
113 int num_trans;
114 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
115 return num_trans > 0;
116}
117
118// Method for coverting to either an SkPMColor or a similarly packed
119// unpremultiplied color.
120typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
121
122// Note: SkColorTable claims to store SkPMColors, which is not necessarily
123// the case here.
scroggo9b2cdbf42015-07-10 12:07:02 -0700124bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800125 int numPalette;
126 png_colorp palette;
127 png_bytep trans;
128
scroggo05245902015-03-25 11:11:52 -0700129 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
130 return false;
scroggof24f2242015-03-03 08:59:20 -0800131 }
132
msarett438b2ad2015-04-09 12:43:10 -0700133 // Note: These are not necessarily SkPMColors
scroggof24f2242015-03-03 08:59:20 -0800134 SkPMColor colorStorage[256]; // worst-case storage
135 SkPMColor* colorPtr = colorStorage;
136
137 int numTrans;
scroggo05245902015-03-25 11:11:52 -0700138 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
139 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
scroggof24f2242015-03-03 08:59:20 -0800140 } else {
141 numTrans = 0;
142 }
143
144 // check for bad images that might make us crash
145 if (numTrans > numPalette) {
146 numTrans = numPalette;
147 }
148
149 int index = 0;
150 int transLessThanFF = 0;
151
152 // Choose which function to use to create the color table. If the final destination's
153 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
154 PackColorProc proc;
155 if (premultiply) {
156 proc = &SkPreMultiplyARGB;
157 } else {
158 proc = &SkPackARGB32NoCheck;
159 }
160 for (; index < numTrans; index++) {
161 transLessThanFF |= (int)*trans - 0xFF;
162 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
163 palette++;
164 }
165
scroggo05245902015-03-25 11:11:52 -0700166 fReallyHasAlpha = transLessThanFF < 0;
scroggof24f2242015-03-03 08:59:20 -0800167
168 for (; index < numPalette; index++) {
169 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
170 palette++;
171 }
172
msarett438b2ad2015-04-09 12:43:10 -0700173 /* BUGGY IMAGE WORKAROUND
174 Invalid images could contain pixel values that are greater than the number of palette
175 entries. Since we use pixel values as indices into the palette this could result in reading
176 beyond the end of the palette which could leak the contents of uninitialized memory. To
177 ensure this doesn't happen, we grow the colortable to the maximum size that can be
178 addressed by the bitdepth of the image and fill it with the last palette color or black if
179 the palette is empty (really broken image).
180 */
scroggo9b2cdbf42015-07-10 12:07:02 -0700181 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8));
msarett438b2ad2015-04-09 12:43:10 -0700182 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
183 for (; index < colorCount; index++) {
184 *colorPtr++ = lastColor;
185 }
186
187 // Set the new color count
188 if (ctableCount != NULL) {
msarett438b2ad2015-04-09 12:43:10 -0700189 *ctableCount = colorCount;
scroggof24f2242015-03-03 08:59:20 -0800190 }
191
halcanary385fe4d2015-08-26 13:07:48 -0700192 fColorTable.reset(new SkColorTable(colorStorage, colorCount));
scroggo05245902015-03-25 11:11:52 -0700193 return true;
scroggof24f2242015-03-03 08:59:20 -0800194}
195
196///////////////////////////////////////////////////////////////////////////////
197// Creation
198///////////////////////////////////////////////////////////////////////////////
199
200#define PNG_BYTES_TO_CHECK 4
201
202bool SkPngCodec::IsPng(SkStream* stream) {
203 char buf[PNG_BYTES_TO_CHECK];
204 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
205 return false;
206 }
207 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
208 return false;
209 }
210 return true;
211}
212
scroggo3eada2a2015-04-01 09:33:23 -0700213// Reads the header, and initializes the passed in fields, if not NULL (except
214// stream, which is passed to the read function).
215// Returns true on success, in which case the caller is responsible for calling
216// png_destroy_read_struct. If it returns false, the passed in fields (except
217// stream) are unchanged.
218static bool read_header(SkStream* stream, png_structp* png_ptrp,
scroggo6f29a3c2015-07-07 06:09:08 -0700219 png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) {
scroggof24f2242015-03-03 08:59:20 -0800220 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
scroggof24f2242015-03-03 08:59:20 -0800221 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
scroggo0eed6df2015-03-26 10:07:56 -0700222 sk_error_fn, sk_warning_fn);
scroggof24f2242015-03-03 08:59:20 -0800223 if (!png_ptr) {
scroggo3eada2a2015-04-01 09:33:23 -0700224 return false;
scroggof24f2242015-03-03 08:59:20 -0800225 }
226
227 AutoCleanPng autoClean(png_ptr);
228
229 png_infop info_ptr = png_create_info_struct(png_ptr);
230 if (info_ptr == NULL) {
scroggo3eada2a2015-04-01 09:33:23 -0700231 return false;
scroggof24f2242015-03-03 08:59:20 -0800232 }
233
234 autoClean.setInfoPtr(info_ptr);
235
236 // FIXME: Could we use the return value of setjmp to specify the type of
237 // error?
238 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo3eada2a2015-04-01 09:33:23 -0700239 return false;
scroggof24f2242015-03-03 08:59:20 -0800240 }
241
242 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
243
244 // FIXME: This is where the old code hooks up the Peeker. Does it need to
245 // be set this early? (i.e. where are the user chunks? early in the stream,
246 // potentially?)
247 // If it does, we need to figure out a way to set it here.
248
249 // The call to png_read_info() gives us all of the information from the
250 // PNG file before the first IDAT (image data chunk).
251 png_read_info(png_ptr, info_ptr);
252 png_uint_32 origWidth, origHeight;
253 int bitDepth, colorType;
254 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
255 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
256
scroggo6f29a3c2015-07-07 06:09:08 -0700257 if (bitDepthPtr) {
258 *bitDepthPtr = bitDepth;
259 }
260
scroggof24f2242015-03-03 08:59:20 -0800261 // sanity check for size
262 {
263 int64_t size = sk_64_mul(origWidth, origHeight);
264 // now check that if we are 4-bytes per pixel, we also don't overflow
265 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
scroggo3eada2a2015-04-01 09:33:23 -0700266 return false;
scroggof24f2242015-03-03 08:59:20 -0800267 }
268 }
269
270 // Tell libpng to strip 16 bit/color files down to 8 bits/color
271 if (bitDepth == 16) {
272 png_set_strip_16(png_ptr);
273 }
274#ifdef PNG_READ_PACK_SUPPORTED
275 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
276 // byte into separate bytes (useful for paletted and grayscale images).
277 if (bitDepth < 8) {
278 png_set_packing(png_ptr);
279 }
280#endif
281 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
282 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
283 png_set_expand_gray_1_2_4_to_8(png_ptr);
284 }
285
scroggo6f29a3c2015-07-07 06:09:08 -0700286 // Now determine the default SkColorType and SkAlphaType and set required transforms
scroggof24f2242015-03-03 08:59:20 -0800287 SkColorType skColorType;
288 SkAlphaType skAlphaType;
289 switch (colorType) {
290 case PNG_COLOR_TYPE_PALETTE:
msarett438b2ad2015-04-09 12:43:10 -0700291 skColorType = kIndex_8_SkColorType;
emmaleer21cea722015-07-10 07:48:03 -0700292 skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ?
scroggof24f2242015-03-03 08:59:20 -0800293 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
294 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700295 case PNG_COLOR_TYPE_RGB:
emmaleer21cea722015-07-10 07:48:03 -0700296 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700297 //convert to RGBA with tranparency information in tRNS chunk if it exists
298 png_set_tRNS_to_alpha(png_ptr);
jvanverth6c90e092015-07-02 10:35:25 -0700299 skAlphaType = kUnpremul_SkAlphaType;
300 } else {
scroggo6f29a3c2015-07-07 06:09:08 -0700301 //convert to RGBA with Opaque Alpha
302 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
jvanverth6c90e092015-07-02 10:35:25 -0700303 skAlphaType = kOpaque_SkAlphaType;
304 }
305 skColorType = kN32_SkColorType;
306 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700307 case PNG_COLOR_TYPE_GRAY:
emmaleer21cea722015-07-10 07:48:03 -0700308 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700309 //FIXME: support gray with alpha as a color type
310 //convert to RGBA if there is transparentcy info in the tRNS chunk
311 png_set_tRNS_to_alpha(png_ptr);
312 png_set_gray_to_rgb(png_ptr);
313 skColorType = kN32_SkColorType;
314 skAlphaType = kUnpremul_SkAlphaType;
315 } else {
316 skColorType = kGray_8_SkColorType;
317 skAlphaType = kOpaque_SkAlphaType;
318 }
319 break;
320 case PNG_COLOR_TYPE_GRAY_ALPHA:
321 //FIXME: support gray with alpha as a color type
322 //convert to RGBA
jvanverth6c90e092015-07-02 10:35:25 -0700323 png_set_gray_to_rgb(png_ptr);
scroggo6f29a3c2015-07-07 06:09:08 -0700324 skColorType = kN32_SkColorType;
325 skAlphaType = kUnpremul_SkAlphaType;
326 break;
327 case PNG_COLOR_TYPE_RGBA:
328 skColorType = kN32_SkColorType;
329 skAlphaType = kUnpremul_SkAlphaType;
330 break;
331 default:
332 //all the color types have been covered above
333 SkASSERT(false);
scroggof24f2242015-03-03 08:59:20 -0800334 }
335
336 // FIXME: Also need to check for sRGB (skbug.com/3471).
337
scroggo3eada2a2015-04-01 09:33:23 -0700338 if (imageInfo) {
scroggo6f29a3c2015-07-07 06:09:08 -0700339 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlphaType);
scroggo3eada2a2015-04-01 09:33:23 -0700340 }
scroggof24f2242015-03-03 08:59:20 -0800341 autoClean.detach();
scroggo3eada2a2015-04-01 09:33:23 -0700342 if (png_ptrp) {
343 *png_ptrp = png_ptr;
344 }
345 if (info_ptrp) {
346 *info_ptrp = info_ptr;
347 }
scroggo6f29a3c2015-07-07 06:09:08 -0700348
scroggo3eada2a2015-04-01 09:33:23 -0700349 return true;
350}
351
352SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
scroggo0a7e69c2015-04-03 07:22:22 -0700353 SkAutoTDelete<SkStream> streamDeleter(stream);
scroggo3eada2a2015-04-01 09:33:23 -0700354 png_structp png_ptr;
355 png_infop info_ptr;
356 SkImageInfo imageInfo;
scroggo6f29a3c2015-07-07 06:09:08 -0700357 int bitDepth;
358 if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) {
halcanary385fe4d2015-08-26 13:07:48 -0700359 return new SkPngCodec(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, bitDepth);
scroggo3eada2a2015-04-01 09:33:23 -0700360 }
361 return NULL;
scroggof24f2242015-03-03 08:59:20 -0800362}
363
scroggo05245902015-03-25 11:11:52 -0700364#define INVALID_NUMBER_PASSES -1
scroggof24f2242015-03-03 08:59:20 -0800365SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
scroggo6f29a3c2015-07-07 06:09:08 -0700366 png_structp png_ptr, png_infop info_ptr, int bitDepth)
scroggof24f2242015-03-03 08:59:20 -0800367 : INHERITED(info, stream)
368 , fPng_ptr(png_ptr)
scroggo05245902015-03-25 11:11:52 -0700369 , fInfo_ptr(info_ptr)
370 , fSrcConfig(SkSwizzler::kUnknown)
371 , fNumberPasses(INVALID_NUMBER_PASSES)
372 , fReallyHasAlpha(false)
scroggo6f29a3c2015-07-07 06:09:08 -0700373 , fBitDepth(bitDepth)
scroggo05245902015-03-25 11:11:52 -0700374{}
scroggof24f2242015-03-03 08:59:20 -0800375
376SkPngCodec::~SkPngCodec() {
scroggo3eada2a2015-04-01 09:33:23 -0700377 this->destroyReadStruct();
378}
379
380void SkPngCodec::destroyReadStruct() {
381 if (fPng_ptr) {
382 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
383 SkASSERT(fInfo_ptr);
384 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
385 fPng_ptr = NULL;
386 fInfo_ptr = NULL;
387 }
scroggof24f2242015-03-03 08:59:20 -0800388}
389
390///////////////////////////////////////////////////////////////////////////////
391// Getting the pixels
392///////////////////////////////////////////////////////////////////////////////
393
scroggo05245902015-03-25 11:11:52 -0700394SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
msarett438b2ad2015-04-09 12:43:10 -0700395 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700396 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700397 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800398 // FIXME: Could we use the return value of setjmp to specify the type of
399 // error?
400 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700401 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800402 return kInvalidInput;
403 }
scroggo6f29a3c2015-07-07 06:09:08 -0700404 fNumberPasses = png_set_interlace_handling(fPng_ptr);
405 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800406
scroggo05245902015-03-25 11:11:52 -0700407 // Set to the default before calling decodePalette, which may change it.
408 fReallyHasAlpha = false;
scroggo6f29a3c2015-07-07 06:09:08 -0700409
410 //srcColorType was determined in readHeader() which determined png color type
411 const SkColorType srcColorType = this->getInfo().colorType();
412
413 switch (srcColorType) {
414 case kIndex_8_SkColorType:
415 //decode palette to Skia format
416 fSrcConfig = SkSwizzler::kIndex;
scroggo9b2cdbf42015-07-10 12:07:02 -0700417 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(),
scroggo6f29a3c2015-07-07 06:09:08 -0700418 ctableCount)) {
419 return kInvalidInput;
420 }
421 break;
422 case kGray_8_SkColorType:
423 fSrcConfig = SkSwizzler::kGray;
424 break;
425 case kN32_SkColorType:
426 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
427 fSrcConfig = SkSwizzler::kRGBX;
428 } else {
429 fSrcConfig = SkSwizzler::kRGBA;
430 }
431 break;
432 default:
433 //would have exited before now if the colorType was supported by png
434 SkASSERT(false);
435 }
msarett9e43cab2015-04-29 07:38:43 -0700436
437 // Copy the color table to the client if they request kIndex8 mode
438 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
439
440 // Create the swizzler. SkPngCodec retains ownership of the color table.
msarett99f567e2015-08-05 12:58:26 -0700441 const SkPMColor* colors = get_color_ptr(fColorTable.get());
scroggo05245902015-03-25 11:11:52 -0700442 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
emmaleer8f4ba762015-08-14 07:44:46 -0700443 options.fZeroInitialized, this->getInfo()));
scroggo05245902015-03-25 11:11:52 -0700444 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800445 // FIXME: CreateSwizzler could fail for another reason.
446 return kUnimplemented;
447 }
scroggo05245902015-03-25 11:11:52 -0700448 return kSuccess;
449}
450
scroggo6f29a3c2015-07-07 06:09:08 -0700451
scroggob427db12015-08-12 07:24:13 -0700452bool SkPngCodec::onRewind() {
453 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
454 // succeeds, they will be repopulated, and if it fails, they will
455 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
456 // come through this function which will rewind and again attempt
457 // to reinitialize them.
458 this->destroyReadStruct();
459
460 png_structp png_ptr;
461 png_infop info_ptr;
462 if (!read_header(this->stream(), &png_ptr, &info_ptr, NULL, NULL)) {
463 return false;
scroggo58421542015-04-01 11:25:20 -0700464 }
scroggob427db12015-08-12 07:24:13 -0700465
466 fPng_ptr = png_ptr;
467 fInfo_ptr = info_ptr;
468 return true;
scroggo58421542015-04-01 11:25:20 -0700469}
470
scroggo05245902015-03-25 11:11:52 -0700471SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
msarett614aa072015-07-27 15:13:17 -0700472 size_t dstRowBytes, const Options& options,
scroggo05245902015-03-25 11:11:52 -0700473 SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700474 if (!conversion_possible(requestedInfo, this->getInfo())) {
475 return kInvalidConversion;
476 }
scroggob636b452015-07-22 07:16:20 -0700477 if (options.fSubset) {
478 // Subsets are not supported.
479 return kUnimplemented;
480 }
scroggo05245902015-03-25 11:11:52 -0700481 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
482 return kInvalidScale;
483 }
scroggob427db12015-08-12 07:24:13 -0700484 if (!this->rewindIfNeeded()) {
scroggo6f29a3c2015-07-07 06:09:08 -0700485 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700486 }
487
msarett9e43cab2015-04-29 07:38:43 -0700488 // Note that ctable and ctableCount may be modified if there is a color table
msarett614aa072015-07-27 15:13:17 -0700489 const Result result = this->initializeSwizzler(requestedInfo, options,
490 ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700491 if (result != kSuccess) {
492 return result;
493 }
scroggo05245902015-03-25 11:11:52 -0700494 // FIXME: Could we use the return value of setjmp to specify the type of
495 // error?
496 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700497 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700498 return kInvalidInput;
499 }
500
501 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
502 SkAutoMalloc storage;
msarett614aa072015-07-27 15:13:17 -0700503 void* dstRow = dst;
scroggo05245902015-03-25 11:11:52 -0700504 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800505 const int width = requestedInfo.width();
506 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700507 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
msarett614aa072015-07-27 15:13:17 -0700508 const size_t srcRowBytes = width * bpp;
scroggof24f2242015-03-03 08:59:20 -0800509
510 storage.reset(width * height * bpp);
511 uint8_t* const base = static_cast<uint8_t*>(storage.get());
512
scroggo05245902015-03-25 11:11:52 -0700513 for (int i = 0; i < fNumberPasses; i++) {
msarett614aa072015-07-27 15:13:17 -0700514 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800515 for (int y = 0; y < height; y++) {
msarett614aa072015-07-27 15:13:17 -0700516 uint8_t* bmRow = srcRow;
scroggof24f2242015-03-03 08:59:20 -0800517 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700518 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800519 }
520 }
521
522 // Now swizzle it.
msarett614aa072015-07-27 15:13:17 -0700523 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800524 for (int y = 0; y < height; y++) {
msarett614aa072015-07-27 15:13:17 -0700525 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
526 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
527 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800528 }
529 } else {
scroggo05245902015-03-25 11:11:52 -0700530 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800531 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
532 for (int y = 0; y < requestedInfo.height(); y++) {
533 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700534 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
535 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
scroggof24f2242015-03-03 08:59:20 -0800536 }
537 }
538
scroggo05245902015-03-25 11:11:52 -0700539 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
540 // scanline decoding, but we could do it here. Alternatively, we could do
541 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800542
scroggo05245902015-03-25 11:11:52 -0700543 if (setjmp(png_jmpbuf(fPng_ptr))) {
544 // We've already read all the scanlines. This is a success.
emmaleer973ae862015-07-20 13:38:44 -0700545 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700546 }
emmaleer973ae862015-07-20 13:38:44 -0700547
548 // read rest of file, and get additional comment and time chunks in info_ptr
scroggo05245902015-03-25 11:11:52 -0700549 png_read_end(fPng_ptr, fInfo_ptr);
emmaleer973ae862015-07-20 13:38:44 -0700550 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700551}
552
553class SkPngScanlineDecoder : public SkScanlineDecoder {
554public:
scroggo1c005e42015-08-04 09:24:45 -0700555 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
556 : INHERITED(srcInfo)
scroggo05245902015-03-25 11:11:52 -0700557 , fCodec(codec)
558 , fHasAlpha(false)
scroggo1c005e42015-08-04 09:24:45 -0700559 {}
560
561 SkCodec::Result onStart(const SkImageInfo& dstInfo,
562 const SkCodec::Options& options,
emmaleer8f4ba762015-08-14 07:44:46 -0700563 SkPMColor ctable[], int* ctableCount) override {
scroggob427db12015-08-12 07:24:13 -0700564 if (!fCodec->rewindIfNeeded()) {
scroggo1c005e42015-08-04 09:24:45 -0700565 return SkCodec::kCouldNotRewind;
566 }
567
568 if (!conversion_possible(dstInfo, this->getInfo())) {
569 return SkCodec::kInvalidConversion;
570 }
571
572 // Check to see if scaling was requested.
573 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
emmaleer8f4ba762015-08-14 07:44:46 -0700574 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
575 return SkCodec::kInvalidScale;
576 }
scroggo1c005e42015-08-04 09:24:45 -0700577 }
578
579 const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
580 ctableCount);
581 if (result != SkCodec::kSuccess) {
582 return result;
583 }
584
585 fHasAlpha = false;
emmaleer8f4ba762015-08-14 07:44:46 -0700586 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
scroggo05245902015-03-25 11:11:52 -0700587 fSrcRow = static_cast<uint8_t*>(fStorage.get());
scroggo1c005e42015-08-04 09:24:45 -0700588
589 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700590 }
591
scroggoeb602a52015-07-09 08:16:03 -0700592 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700593 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700594 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700595 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700596 }
597
msarett614aa072015-07-27 15:13:17 -0700598 void* dstRow = dst;
scroggo05245902015-03-25 11:11:52 -0700599 for (int i = 0; i < count; i++) {
600 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700601 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow));
602 dstRow = SkTAddOffset<void>(dstRow, rowBytes);
scroggo05245902015-03-25 11:11:52 -0700603 }
scroggoeb602a52015-07-09 08:16:03 -0700604 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700605 }
606
scroggoeb602a52015-07-09 08:16:03 -0700607 SkCodec::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700608 // FIXME: Could we use the return value of setjmp to specify the type of
609 // error?
610 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700611 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700612 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700613 }
emmaleer7dc91902015-05-27 08:49:04 -0700614 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
615 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
616 //as png_read_rows has it's own loop which calls png_read_row count times.
617 for (int i = 0; i < count; i++) {
618 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
619 }
scroggoeb602a52015-07-09 08:16:03 -0700620 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700621 }
622
mtklein36352bf2015-03-25 18:17:31 -0700623 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700624
emmaleer8f4ba762015-08-14 07:44:46 -0700625 SkEncodedFormat onGetEncodedFormat() const override {
626 return kPNG_SkEncodedFormat;
627 }
628
629
scroggo05245902015-03-25 11:11:52 -0700630private:
scroggo9b2cdbf42015-07-10 12:07:02 -0700631 SkAutoTDelete<SkPngCodec> fCodec;
632 bool fHasAlpha;
633 SkAutoMalloc fStorage;
634 uint8_t* fSrcRow;
scroggo05245902015-03-25 11:11:52 -0700635
636 typedef SkScanlineDecoder INHERITED;
637};
638
emmaleer0a4c3cb2015-06-22 10:40:21 -0700639
640class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
641public:
scroggo1c005e42015-08-04 09:24:45 -0700642 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
643 : INHERITED(srcInfo)
emmaleer0a4c3cb2015-06-22 10:40:21 -0700644 , fCodec(codec)
645 , fHasAlpha(false)
646 , fCurrentRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700647 , fHeight(srcInfo.height())
648 , fCanSkipRewind(false)
649 {}
emmaleer0a4c3cb2015-06-22 10:40:21 -0700650
scroggo1c005e42015-08-04 09:24:45 -0700651 SkCodec::Result onStart(const SkImageInfo& dstInfo,
652 const SkCodec::Options& options,
653 SkPMColor ctable[], int* ctableCount) override
654 {
scroggob427db12015-08-12 07:24:13 -0700655 if (!fCodec->rewindIfNeeded()) {
scroggo9b2cdbf42015-07-10 12:07:02 -0700656 return SkCodec::kCouldNotRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700657 }
scroggo1c005e42015-08-04 09:24:45 -0700658
659 if (!conversion_possible(dstInfo, this->getInfo())) {
emmaleer8f4ba762015-08-14 07:44:46 -0700660 return SkCodec::kInvalidConversion;
scroggo1c005e42015-08-04 09:24:45 -0700661 }
662
663 // Check to see if scaling was requested.
664 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
emmaleer8f4ba762015-08-14 07:44:46 -0700665 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
666 return SkCodec::kInvalidScale;
667 }
scroggo1c005e42015-08-04 09:24:45 -0700668 }
669
670 const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
671 ctableCount);
672 if (result != SkCodec::kSuccess) {
673 return result;
674 }
675
676 fHasAlpha = false;
677 fCurrentRow = 0;
678 fHeight = dstInfo.height();
emmaleer8f4ba762015-08-14 07:44:46 -0700679 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
scroggo1c005e42015-08-04 09:24:45 -0700680 fGarbageRow.reset(fSrcRowBytes);
681 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
682 fCanSkipRewind = true;
683
684 return SkCodec::kSuccess;
685 }
686
687 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
688 // rewind stream if have previously called onGetScanlines,
689 // since we need entire progressive image to get scanlines
690 if (fCanSkipRewind) {
691 // We already rewound in onStart, so there is no reason to rewind.
692 // Next time onGetScanlines is called, we will need to rewind.
693 fCanSkipRewind = false;
scroggob427db12015-08-12 07:24:13 -0700694 } else if (!fCodec->rewindIfNeeded()) {
scroggo1c005e42015-08-04 09:24:45 -0700695 return SkCodec::kCouldNotRewind;
696 }
697
emmaleer0a4c3cb2015-06-22 10:40:21 -0700698 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
699 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700700 return SkCodec::kInvalidInput;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700701 }
702 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
703 SkAutoMalloc storage(count * fSrcRowBytes);
704 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
705 uint8_t* srcRow;
706 for (int i = 0; i < number_passes; i++) {
707 //read rows we planned to skip into garbage row
708 for (int y = 0; y < fCurrentRow; y++){
709 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
710 }
711 //read rows we care about into buffer
712 srcRow = storagePtr;
713 for (int y = 0; y < count; y++) {
714 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
715 srcRow += fSrcRowBytes;
716 }
717 //read rows we don't want into garbage buffer
718 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
719 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
720 }
721 }
722 //swizzle the rows we care about
723 srcRow = storagePtr;
msarett614aa072015-07-27 15:13:17 -0700724 void* dstRow = dst;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700725 for (int y = 0; y < count; y++) {
msarett614aa072015-07-27 15:13:17 -0700726 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow));
727 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700728 srcRow += fSrcRowBytes;
729 }
730 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700731 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700732 }
733
scroggoeb602a52015-07-09 08:16:03 -0700734 SkCodec::Result onSkipScanlines(int count) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700735 //when ongetScanlines is called it will skip to fCurrentRow
736 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700737 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700738 }
739
emmaleer0a4c3cb2015-06-22 10:40:21 -0700740 bool onReallyHasAlpha() const override { return fHasAlpha; }
741
emmaleer8f4ba762015-08-14 07:44:46 -0700742 bool onRequiresPostYSampling() override {
743 return true;
744 }
745
746 SkEncodedFormat onGetEncodedFormat() const override {
747 return kPNG_SkEncodedFormat;
748 }
749
emmaleer0a4c3cb2015-06-22 10:40:21 -0700750private:
scroggo9b2cdbf42015-07-10 12:07:02 -0700751 SkAutoTDelete<SkPngCodec> fCodec;
752 bool fHasAlpha;
753 int fCurrentRow;
754 int fHeight;
755 size_t fSrcRowBytes;
756 SkAutoMalloc fGarbageRow;
757 uint8_t* fGarbageRowPtr;
scroggo1c005e42015-08-04 09:24:45 -0700758 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
759 // is called whenever some action is taken that reads the stream and
760 // therefore the next call will require a rewind. So it modifies a boolean
761 // to note that the *next* time it is called a rewind is needed.
762 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart
763 // followed by onGetScanlines does *not* require a rewind. Since
764 // rewindIfNeeded does not have this flexibility, we need to add another
765 // layer.
766 bool fCanSkipRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700767
768 typedef SkScanlineDecoder INHERITED;
769};
770
scroggo1c005e42015-08-04 09:24:45 -0700771SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) {
scroggo9b2cdbf42015-07-10 12:07:02 -0700772 SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream)));
773 if (!codec) {
scroggo05245902015-03-25 11:11:52 -0700774 return NULL;
775 }
776
scroggo1c005e42015-08-04 09:24:45 -0700777 codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr);
scroggo9b2cdbf42015-07-10 12:07:02 -0700778 SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES);
scroggo1c005e42015-08-04 09:24:45 -0700779
780 const SkImageInfo& srcInfo = codec->getInfo();
scroggo9b2cdbf42015-07-10 12:07:02 -0700781 if (codec->fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700782 // interlaced image
halcanary385fe4d2015-08-26 13:07:48 -0700783 return new SkPngInterlacedScanlineDecoder(srcInfo, codec.detach());
scroggo05245902015-03-25 11:11:52 -0700784 }
785
halcanary385fe4d2015-08-26 13:07:48 -0700786 return new SkPngScanlineDecoder(srcInfo, codec.detach());
scroggo05245902015-03-25 11:11:52 -0700787}
788