blob: 2fa3e686beeef1af2496bdbca80d4219394190cc [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
scroggo05245902015-03-25 11:11:52 -0700192 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
193 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)) {
359 return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(),
360 png_ptr, info_ptr, bitDepth));
scroggo3eada2a2015-04-01 09:33:23 -0700361 }
362 return NULL;
scroggof24f2242015-03-03 08:59:20 -0800363}
364
scroggo05245902015-03-25 11:11:52 -0700365#define INVALID_NUMBER_PASSES -1
scroggof24f2242015-03-03 08:59:20 -0800366SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
scroggo6f29a3c2015-07-07 06:09:08 -0700367 png_structp png_ptr, png_infop info_ptr, int bitDepth)
scroggof24f2242015-03-03 08:59:20 -0800368 : INHERITED(info, stream)
369 , fPng_ptr(png_ptr)
scroggo05245902015-03-25 11:11:52 -0700370 , fInfo_ptr(info_ptr)
371 , fSrcConfig(SkSwizzler::kUnknown)
372 , fNumberPasses(INVALID_NUMBER_PASSES)
373 , fReallyHasAlpha(false)
scroggo6f29a3c2015-07-07 06:09:08 -0700374 , fBitDepth(bitDepth)
scroggo05245902015-03-25 11:11:52 -0700375{}
scroggof24f2242015-03-03 08:59:20 -0800376
377SkPngCodec::~SkPngCodec() {
scroggo3eada2a2015-04-01 09:33:23 -0700378 this->destroyReadStruct();
379}
380
381void SkPngCodec::destroyReadStruct() {
382 if (fPng_ptr) {
383 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
384 SkASSERT(fInfo_ptr);
385 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
386 fPng_ptr = NULL;
387 fInfo_ptr = NULL;
388 }
scroggof24f2242015-03-03 08:59:20 -0800389}
390
391///////////////////////////////////////////////////////////////////////////////
392// Getting the pixels
393///////////////////////////////////////////////////////////////////////////////
394
scroggo05245902015-03-25 11:11:52 -0700395SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
msarett438b2ad2015-04-09 12:43:10 -0700396 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700397 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700398 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800399 // FIXME: Could we use the return value of setjmp to specify the type of
400 // error?
401 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700402 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800403 return kInvalidInput;
404 }
scroggo6f29a3c2015-07-07 06:09:08 -0700405 fNumberPasses = png_set_interlace_handling(fPng_ptr);
406 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800407
scroggo05245902015-03-25 11:11:52 -0700408 // Set to the default before calling decodePalette, which may change it.
409 fReallyHasAlpha = false;
scroggo6f29a3c2015-07-07 06:09:08 -0700410
411 //srcColorType was determined in readHeader() which determined png color type
412 const SkColorType srcColorType = this->getInfo().colorType();
413
414 switch (srcColorType) {
415 case kIndex_8_SkColorType:
416 //decode palette to Skia format
417 fSrcConfig = SkSwizzler::kIndex;
scroggo9b2cdbf42015-07-10 12:07:02 -0700418 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(),
scroggo6f29a3c2015-07-07 06:09:08 -0700419 ctableCount)) {
420 return kInvalidInput;
421 }
422 break;
423 case kGray_8_SkColorType:
424 fSrcConfig = SkSwizzler::kGray;
425 break;
426 case kN32_SkColorType:
427 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
428 fSrcConfig = SkSwizzler::kRGBX;
429 } else {
430 fSrcConfig = SkSwizzler::kRGBA;
431 }
432 break;
433 default:
434 //would have exited before now if the colorType was supported by png
435 SkASSERT(false);
436 }
msarett9e43cab2015-04-29 07:38:43 -0700437
438 // Copy the color table to the client if they request kIndex8 mode
439 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
440
441 // Create the swizzler. SkPngCodec retains ownership of the color table.
msarett99f567e2015-08-05 12:58:26 -0700442 const SkPMColor* colors = get_color_ptr(fColorTable.get());
scroggo05245902015-03-25 11:11:52 -0700443 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
emmaleer8f4ba762015-08-14 07:44:46 -0700444 options.fZeroInitialized, this->getInfo()));
scroggo05245902015-03-25 11:11:52 -0700445 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800446 // FIXME: CreateSwizzler could fail for another reason.
447 return kUnimplemented;
448 }
scroggo05245902015-03-25 11:11:52 -0700449 return kSuccess;
450}
451
scroggo6f29a3c2015-07-07 06:09:08 -0700452
scroggob427db12015-08-12 07:24:13 -0700453bool SkPngCodec::onRewind() {
454 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
455 // succeeds, they will be repopulated, and if it fails, they will
456 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
457 // come through this function which will rewind and again attempt
458 // to reinitialize them.
459 this->destroyReadStruct();
460
461 png_structp png_ptr;
462 png_infop info_ptr;
463 if (!read_header(this->stream(), &png_ptr, &info_ptr, NULL, NULL)) {
464 return false;
scroggo58421542015-04-01 11:25:20 -0700465 }
scroggob427db12015-08-12 07:24:13 -0700466
467 fPng_ptr = png_ptr;
468 fInfo_ptr = info_ptr;
469 return true;
scroggo58421542015-04-01 11:25:20 -0700470}
471
scroggo05245902015-03-25 11:11:52 -0700472SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
msarett614aa072015-07-27 15:13:17 -0700473 size_t dstRowBytes, const Options& options,
scroggo05245902015-03-25 11:11:52 -0700474 SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700475 if (!conversion_possible(requestedInfo, this->getInfo())) {
476 return kInvalidConversion;
477 }
scroggob636b452015-07-22 07:16:20 -0700478 if (options.fSubset) {
479 // Subsets are not supported.
480 return kUnimplemented;
481 }
scroggo05245902015-03-25 11:11:52 -0700482 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
483 return kInvalidScale;
484 }
scroggob427db12015-08-12 07:24:13 -0700485 if (!this->rewindIfNeeded()) {
scroggo6f29a3c2015-07-07 06:09:08 -0700486 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700487 }
488
msarett9e43cab2015-04-29 07:38:43 -0700489 // Note that ctable and ctableCount may be modified if there is a color table
msarett614aa072015-07-27 15:13:17 -0700490 const Result result = this->initializeSwizzler(requestedInfo, options,
491 ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700492 if (result != kSuccess) {
493 return result;
494 }
scroggo05245902015-03-25 11:11:52 -0700495 // FIXME: Could we use the return value of setjmp to specify the type of
496 // error?
497 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700498 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700499 return kInvalidInput;
500 }
501
502 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
503 SkAutoMalloc storage;
msarett614aa072015-07-27 15:13:17 -0700504 void* dstRow = dst;
scroggo05245902015-03-25 11:11:52 -0700505 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800506 const int width = requestedInfo.width();
507 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700508 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
msarett614aa072015-07-27 15:13:17 -0700509 const size_t srcRowBytes = width * bpp;
scroggof24f2242015-03-03 08:59:20 -0800510
511 storage.reset(width * height * bpp);
512 uint8_t* const base = static_cast<uint8_t*>(storage.get());
513
scroggo05245902015-03-25 11:11:52 -0700514 for (int i = 0; i < fNumberPasses; i++) {
msarett614aa072015-07-27 15:13:17 -0700515 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800516 for (int y = 0; y < height; y++) {
msarett614aa072015-07-27 15:13:17 -0700517 uint8_t* bmRow = srcRow;
scroggof24f2242015-03-03 08:59:20 -0800518 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700519 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800520 }
521 }
522
523 // Now swizzle it.
msarett614aa072015-07-27 15:13:17 -0700524 uint8_t* srcRow = base;
scroggof24f2242015-03-03 08:59:20 -0800525 for (int y = 0; y < height; y++) {
msarett614aa072015-07-27 15:13:17 -0700526 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
527 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
528 srcRow += srcRowBytes;
scroggof24f2242015-03-03 08:59:20 -0800529 }
530 } else {
scroggo05245902015-03-25 11:11:52 -0700531 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800532 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
533 for (int y = 0; y < requestedInfo.height(); y++) {
534 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700535 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
536 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
scroggof24f2242015-03-03 08:59:20 -0800537 }
538 }
539
scroggo05245902015-03-25 11:11:52 -0700540 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
541 // scanline decoding, but we could do it here. Alternatively, we could do
542 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800543
scroggo05245902015-03-25 11:11:52 -0700544 if (setjmp(png_jmpbuf(fPng_ptr))) {
545 // We've already read all the scanlines. This is a success.
emmaleer973ae862015-07-20 13:38:44 -0700546 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700547 }
emmaleer973ae862015-07-20 13:38:44 -0700548
549 // read rest of file, and get additional comment and time chunks in info_ptr
scroggo05245902015-03-25 11:11:52 -0700550 png_read_end(fPng_ptr, fInfo_ptr);
emmaleer973ae862015-07-20 13:38:44 -0700551 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700552}
553
554class SkPngScanlineDecoder : public SkScanlineDecoder {
555public:
scroggo1c005e42015-08-04 09:24:45 -0700556 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
557 : INHERITED(srcInfo)
scroggo05245902015-03-25 11:11:52 -0700558 , fCodec(codec)
559 , fHasAlpha(false)
scroggo1c005e42015-08-04 09:24:45 -0700560 {}
561
562 SkCodec::Result onStart(const SkImageInfo& dstInfo,
563 const SkCodec::Options& options,
emmaleer8f4ba762015-08-14 07:44:46 -0700564 SkPMColor ctable[], int* ctableCount) override {
scroggob427db12015-08-12 07:24:13 -0700565 if (!fCodec->rewindIfNeeded()) {
scroggo1c005e42015-08-04 09:24:45 -0700566 return SkCodec::kCouldNotRewind;
567 }
568
569 if (!conversion_possible(dstInfo, this->getInfo())) {
570 return SkCodec::kInvalidConversion;
571 }
572
573 // Check to see if scaling was requested.
574 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
emmaleer8f4ba762015-08-14 07:44:46 -0700575 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
576 return SkCodec::kInvalidScale;
577 }
scroggo1c005e42015-08-04 09:24:45 -0700578 }
579
580 const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
581 ctableCount);
582 if (result != SkCodec::kSuccess) {
583 return result;
584 }
585
586 fHasAlpha = false;
emmaleer8f4ba762015-08-14 07:44:46 -0700587 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
scroggo05245902015-03-25 11:11:52 -0700588 fSrcRow = static_cast<uint8_t*>(fStorage.get());
scroggo1c005e42015-08-04 09:24:45 -0700589
590 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700591 }
592
scroggoeb602a52015-07-09 08:16:03 -0700593 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700594 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700595 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700596 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700597 }
598
msarett614aa072015-07-27 15:13:17 -0700599 void* dstRow = dst;
scroggo05245902015-03-25 11:11:52 -0700600 for (int i = 0; i < count; i++) {
601 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
msarett614aa072015-07-27 15:13:17 -0700602 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow));
603 dstRow = SkTAddOffset<void>(dstRow, rowBytes);
scroggo05245902015-03-25 11:11:52 -0700604 }
scroggoeb602a52015-07-09 08:16:03 -0700605 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700606 }
607
scroggoeb602a52015-07-09 08:16:03 -0700608 SkCodec::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700609 // FIXME: Could we use the return value of setjmp to specify the type of
610 // error?
611 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700612 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700613 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700614 }
emmaleer7dc91902015-05-27 08:49:04 -0700615 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
616 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
617 //as png_read_rows has it's own loop which calls png_read_row count times.
618 for (int i = 0; i < count; i++) {
619 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
620 }
scroggoeb602a52015-07-09 08:16:03 -0700621 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700622 }
623
mtklein36352bf2015-03-25 18:17:31 -0700624 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700625
emmaleer8f4ba762015-08-14 07:44:46 -0700626 SkEncodedFormat onGetEncodedFormat() const override {
627 return kPNG_SkEncodedFormat;
628 }
629
630
scroggo05245902015-03-25 11:11:52 -0700631private:
scroggo9b2cdbf42015-07-10 12:07:02 -0700632 SkAutoTDelete<SkPngCodec> fCodec;
633 bool fHasAlpha;
634 SkAutoMalloc fStorage;
635 uint8_t* fSrcRow;
scroggo05245902015-03-25 11:11:52 -0700636
637 typedef SkScanlineDecoder INHERITED;
638};
639
emmaleer0a4c3cb2015-06-22 10:40:21 -0700640
641class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
642public:
scroggo1c005e42015-08-04 09:24:45 -0700643 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
644 : INHERITED(srcInfo)
emmaleer0a4c3cb2015-06-22 10:40:21 -0700645 , fCodec(codec)
646 , fHasAlpha(false)
647 , fCurrentRow(0)
scroggo1c005e42015-08-04 09:24:45 -0700648 , fHeight(srcInfo.height())
649 , fCanSkipRewind(false)
650 {}
emmaleer0a4c3cb2015-06-22 10:40:21 -0700651
scroggo1c005e42015-08-04 09:24:45 -0700652 SkCodec::Result onStart(const SkImageInfo& dstInfo,
653 const SkCodec::Options& options,
654 SkPMColor ctable[], int* ctableCount) override
655 {
scroggob427db12015-08-12 07:24:13 -0700656 if (!fCodec->rewindIfNeeded()) {
scroggo9b2cdbf42015-07-10 12:07:02 -0700657 return SkCodec::kCouldNotRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700658 }
scroggo1c005e42015-08-04 09:24:45 -0700659
660 if (!conversion_possible(dstInfo, this->getInfo())) {
emmaleer8f4ba762015-08-14 07:44:46 -0700661 return SkCodec::kInvalidConversion;
scroggo1c005e42015-08-04 09:24:45 -0700662 }
663
664 // Check to see if scaling was requested.
665 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
emmaleer8f4ba762015-08-14 07:44:46 -0700666 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
667 return SkCodec::kInvalidScale;
668 }
scroggo1c005e42015-08-04 09:24:45 -0700669 }
670
671 const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
672 ctableCount);
673 if (result != SkCodec::kSuccess) {
674 return result;
675 }
676
677 fHasAlpha = false;
678 fCurrentRow = 0;
679 fHeight = dstInfo.height();
emmaleer8f4ba762015-08-14 07:44:46 -0700680 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
scroggo1c005e42015-08-04 09:24:45 -0700681 fGarbageRow.reset(fSrcRowBytes);
682 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
683 fCanSkipRewind = true;
684
685 return SkCodec::kSuccess;
686 }
687
688 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
689 // rewind stream if have previously called onGetScanlines,
690 // since we need entire progressive image to get scanlines
691 if (fCanSkipRewind) {
692 // We already rewound in onStart, so there is no reason to rewind.
693 // Next time onGetScanlines is called, we will need to rewind.
694 fCanSkipRewind = false;
scroggob427db12015-08-12 07:24:13 -0700695 } else if (!fCodec->rewindIfNeeded()) {
scroggo1c005e42015-08-04 09:24:45 -0700696 return SkCodec::kCouldNotRewind;
697 }
698
emmaleer0a4c3cb2015-06-22 10:40:21 -0700699 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
700 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700701 return SkCodec::kInvalidInput;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700702 }
703 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
704 SkAutoMalloc storage(count * fSrcRowBytes);
705 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
706 uint8_t* srcRow;
707 for (int i = 0; i < number_passes; i++) {
708 //read rows we planned to skip into garbage row
709 for (int y = 0; y < fCurrentRow; y++){
710 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
711 }
712 //read rows we care about into buffer
713 srcRow = storagePtr;
714 for (int y = 0; y < count; y++) {
715 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
716 srcRow += fSrcRowBytes;
717 }
718 //read rows we don't want into garbage buffer
719 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
720 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
721 }
722 }
723 //swizzle the rows we care about
724 srcRow = storagePtr;
msarett614aa072015-07-27 15:13:17 -0700725 void* dstRow = dst;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700726 for (int y = 0; y < count; y++) {
msarett614aa072015-07-27 15:13:17 -0700727 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow));
728 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700729 srcRow += fSrcRowBytes;
730 }
731 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700732 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700733 }
734
scroggoeb602a52015-07-09 08:16:03 -0700735 SkCodec::Result onSkipScanlines(int count) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700736 //when ongetScanlines is called it will skip to fCurrentRow
737 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700738 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700739 }
740
emmaleer0a4c3cb2015-06-22 10:40:21 -0700741 bool onReallyHasAlpha() const override { return fHasAlpha; }
742
emmaleer8f4ba762015-08-14 07:44:46 -0700743 bool onRequiresPostYSampling() override {
744 return true;
745 }
746
747 SkEncodedFormat onGetEncodedFormat() const override {
748 return kPNG_SkEncodedFormat;
749 }
750
emmaleer0a4c3cb2015-06-22 10:40:21 -0700751private:
scroggo9b2cdbf42015-07-10 12:07:02 -0700752 SkAutoTDelete<SkPngCodec> fCodec;
753 bool fHasAlpha;
754 int fCurrentRow;
755 int fHeight;
756 size_t fSrcRowBytes;
757 SkAutoMalloc fGarbageRow;
758 uint8_t* fGarbageRowPtr;
scroggo1c005e42015-08-04 09:24:45 -0700759 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
760 // is called whenever some action is taken that reads the stream and
761 // therefore the next call will require a rewind. So it modifies a boolean
762 // to note that the *next* time it is called a rewind is needed.
763 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart
764 // followed by onGetScanlines does *not* require a rewind. Since
765 // rewindIfNeeded does not have this flexibility, we need to add another
766 // layer.
767 bool fCanSkipRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700768
769 typedef SkScanlineDecoder INHERITED;
770};
771
scroggo1c005e42015-08-04 09:24:45 -0700772SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) {
scroggo9b2cdbf42015-07-10 12:07:02 -0700773 SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream)));
774 if (!codec) {
scroggo05245902015-03-25 11:11:52 -0700775 return NULL;
776 }
777
scroggo1c005e42015-08-04 09:24:45 -0700778 codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr);
scroggo9b2cdbf42015-07-10 12:07:02 -0700779 SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES);
scroggo1c005e42015-08-04 09:24:45 -0700780
781 const SkImageInfo& srcInfo = codec->getInfo();
scroggo9b2cdbf42015-07-10 12:07:02 -0700782 if (codec->fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700783 // interlaced image
scroggo1c005e42015-08-04 09:24:45 -0700784 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (srcInfo, codec.detach()));
scroggo05245902015-03-25 11:11:52 -0700785 }
786
scroggo1c005e42015-08-04 09:24:45 -0700787 return SkNEW_ARGS(SkPngScanlineDecoder, (srcInfo, codec.detach()));
scroggo05245902015-03-25 11:11:52 -0700788}
789