blob: 553233de12117ea8ccebcdfd261a720b8e721412 [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"
scroggo05245902015-03-25 11:11:52 -070014#include "SkScanlineDecoder.h"
scroggof24f2242015-03-03 08:59:20 -080015#include "SkSize.h"
16#include "SkStream.h"
17#include "SkSwizzler.h"
18
19///////////////////////////////////////////////////////////////////////////////
20// Helper macros
21///////////////////////////////////////////////////////////////////////////////
22
23#ifndef png_jmpbuf
24# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
25#endif
26
27/* These were dropped in libpng >= 1.4 */
28#ifndef png_infopp_NULL
29 #define png_infopp_NULL NULL
30#endif
31
32#ifndef png_bytepp_NULL
33 #define png_bytepp_NULL NULL
34#endif
35
36#ifndef int_p_NULL
37 #define int_p_NULL NULL
38#endif
39
40#ifndef png_flush_ptr_NULL
41 #define png_flush_ptr_NULL NULL
42#endif
43
44///////////////////////////////////////////////////////////////////////////////
45// Callback functions
46///////////////////////////////////////////////////////////////////////////////
47
48static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo230d4ac2015-03-26 07:15:55 -070049 SkCodecPrintf("------ png error %s\n", msg);
scroggof24f2242015-03-03 08:59:20 -080050 longjmp(png_jmpbuf(png_ptr), 1);
51}
52
scroggo0eed6df2015-03-26 10:07:56 -070053void sk_warning_fn(png_structp, png_const_charp msg) {
54 SkCodecPrintf("----- png warning %s\n", msg);
55}
56
scroggof24f2242015-03-03 08:59:20 -080057static void sk_read_fn(png_structp png_ptr, png_bytep data,
58 png_size_t length) {
59 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
60 const size_t bytes = stream->read(data, length);
61 if (bytes != length) {
62 // FIXME: We want to report the fact that the stream was truncated.
63 // One way to do that might be to pass a enum to longjmp so setjmp can
64 // specify the failure.
65 png_error(png_ptr, "Read Error!");
66 }
67}
68
69///////////////////////////////////////////////////////////////////////////////
70// Helpers
71///////////////////////////////////////////////////////////////////////////////
72
73class AutoCleanPng : public SkNoncopyable {
74public:
75 AutoCleanPng(png_structp png_ptr)
76 : fPng_ptr(png_ptr)
77 , fInfo_ptr(NULL) {}
78
79 ~AutoCleanPng() {
80 // fInfo_ptr will never be non-NULL unless fPng_ptr is.
81 if (fPng_ptr) {
82 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
83 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
84 }
85 }
86
87 void setInfoPtr(png_infop info_ptr) {
88 SkASSERT(NULL == fInfo_ptr);
89 fInfo_ptr = info_ptr;
90 }
91
92 void detach() {
93 fPng_ptr = NULL;
94 fInfo_ptr = NULL;
95 }
96
97private:
98 png_structp fPng_ptr;
99 png_infop fInfo_ptr;
100};
101#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
102
emmaleer21cea722015-07-10 07:48:03 -0700103//checks if there is transparency info in the tRNS chunk
104//image types which could have data in the tRNS chunk include: Index8, Gray8, RGB
105static bool has_transparency_in_tRNS(png_structp png_ptr,
scroggof24f2242015-03-03 08:59:20 -0800106 png_infop info_ptr) {
107 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
108 return false;
109 }
110
111 png_bytep trans;
112 int num_trans;
113 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
114 return num_trans > 0;
115}
116
117// Method for coverting to either an SkPMColor or a similarly packed
118// unpremultiplied color.
119typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
120
121// Note: SkColorTable claims to store SkPMColors, which is not necessarily
122// the case here.
scroggo9b2cdbf2015-07-10 12:07:02 -0700123bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800124 int numPalette;
125 png_colorp palette;
126 png_bytep trans;
127
scroggo05245902015-03-25 11:11:52 -0700128 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
129 return false;
scroggof24f2242015-03-03 08:59:20 -0800130 }
131
msarett438b2ad2015-04-09 12:43:10 -0700132 // Note: These are not necessarily SkPMColors
scroggof24f2242015-03-03 08:59:20 -0800133 SkPMColor colorStorage[256]; // worst-case storage
134 SkPMColor* colorPtr = colorStorage;
135
136 int numTrans;
scroggo05245902015-03-25 11:11:52 -0700137 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
138 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
scroggof24f2242015-03-03 08:59:20 -0800139 } else {
140 numTrans = 0;
141 }
142
143 // check for bad images that might make us crash
144 if (numTrans > numPalette) {
145 numTrans = numPalette;
146 }
147
148 int index = 0;
149 int transLessThanFF = 0;
150
151 // Choose which function to use to create the color table. If the final destination's
152 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
153 PackColorProc proc;
154 if (premultiply) {
155 proc = &SkPreMultiplyARGB;
156 } else {
157 proc = &SkPackARGB32NoCheck;
158 }
159 for (; index < numTrans; index++) {
160 transLessThanFF |= (int)*trans - 0xFF;
161 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
162 palette++;
163 }
164
scroggo05245902015-03-25 11:11:52 -0700165 fReallyHasAlpha = transLessThanFF < 0;
scroggof24f2242015-03-03 08:59:20 -0800166
167 for (; index < numPalette; index++) {
168 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
169 palette++;
170 }
171
msarett438b2ad2015-04-09 12:43:10 -0700172 /* BUGGY IMAGE WORKAROUND
173 Invalid images could contain pixel values that are greater than the number of palette
174 entries. Since we use pixel values as indices into the palette this could result in reading
175 beyond the end of the palette which could leak the contents of uninitialized memory. To
176 ensure this doesn't happen, we grow the colortable to the maximum size that can be
177 addressed by the bitdepth of the image and fill it with the last palette color or black if
178 the palette is empty (really broken image).
179 */
scroggo9b2cdbf2015-07-10 12:07:02 -0700180 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8));
msarett438b2ad2015-04-09 12:43:10 -0700181 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
182 for (; index < colorCount; index++) {
183 *colorPtr++ = lastColor;
184 }
185
186 // Set the new color count
187 if (ctableCount != NULL) {
msarett438b2ad2015-04-09 12:43:10 -0700188 *ctableCount = colorCount;
scroggof24f2242015-03-03 08:59:20 -0800189 }
190
scroggo05245902015-03-25 11:11:52 -0700191 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
192 return true;
scroggof24f2242015-03-03 08:59:20 -0800193}
194
195///////////////////////////////////////////////////////////////////////////////
196// Creation
197///////////////////////////////////////////////////////////////////////////////
198
199#define PNG_BYTES_TO_CHECK 4
200
201bool SkPngCodec::IsPng(SkStream* stream) {
202 char buf[PNG_BYTES_TO_CHECK];
203 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
204 return false;
205 }
206 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
207 return false;
208 }
209 return true;
210}
211
scroggo3eada2a2015-04-01 09:33:23 -0700212// Reads the header, and initializes the passed in fields, if not NULL (except
213// stream, which is passed to the read function).
214// Returns true on success, in which case the caller is responsible for calling
215// png_destroy_read_struct. If it returns false, the passed in fields (except
216// stream) are unchanged.
217static bool read_header(SkStream* stream, png_structp* png_ptrp,
scroggo6f29a3c2015-07-07 06:09:08 -0700218 png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) {
scroggof24f2242015-03-03 08:59:20 -0800219 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
scroggof24f2242015-03-03 08:59:20 -0800220 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
scroggo0eed6df2015-03-26 10:07:56 -0700221 sk_error_fn, sk_warning_fn);
scroggof24f2242015-03-03 08:59:20 -0800222 if (!png_ptr) {
scroggo3eada2a2015-04-01 09:33:23 -0700223 return false;
scroggof24f2242015-03-03 08:59:20 -0800224 }
225
226 AutoCleanPng autoClean(png_ptr);
227
228 png_infop info_ptr = png_create_info_struct(png_ptr);
229 if (info_ptr == NULL) {
scroggo3eada2a2015-04-01 09:33:23 -0700230 return false;
scroggof24f2242015-03-03 08:59:20 -0800231 }
232
233 autoClean.setInfoPtr(info_ptr);
234
235 // FIXME: Could we use the return value of setjmp to specify the type of
236 // error?
237 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo3eada2a2015-04-01 09:33:23 -0700238 return false;
scroggof24f2242015-03-03 08:59:20 -0800239 }
240
241 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
242
243 // FIXME: This is where the old code hooks up the Peeker. Does it need to
244 // be set this early? (i.e. where are the user chunks? early in the stream,
245 // potentially?)
246 // If it does, we need to figure out a way to set it here.
247
248 // The call to png_read_info() gives us all of the information from the
249 // PNG file before the first IDAT (image data chunk).
250 png_read_info(png_ptr, info_ptr);
251 png_uint_32 origWidth, origHeight;
252 int bitDepth, colorType;
253 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
254 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
255
scroggo6f29a3c2015-07-07 06:09:08 -0700256 if (bitDepthPtr) {
257 *bitDepthPtr = bitDepth;
258 }
259
scroggof24f2242015-03-03 08:59:20 -0800260 // sanity check for size
261 {
262 int64_t size = sk_64_mul(origWidth, origHeight);
263 // now check that if we are 4-bytes per pixel, we also don't overflow
264 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
scroggo3eada2a2015-04-01 09:33:23 -0700265 return false;
scroggof24f2242015-03-03 08:59:20 -0800266 }
267 }
268
269 // Tell libpng to strip 16 bit/color files down to 8 bits/color
270 if (bitDepth == 16) {
271 png_set_strip_16(png_ptr);
272 }
273#ifdef PNG_READ_PACK_SUPPORTED
274 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
275 // byte into separate bytes (useful for paletted and grayscale images).
276 if (bitDepth < 8) {
277 png_set_packing(png_ptr);
278 }
279#endif
280 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
281 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
282 png_set_expand_gray_1_2_4_to_8(png_ptr);
283 }
284
scroggo6f29a3c2015-07-07 06:09:08 -0700285 // Now determine the default SkColorType and SkAlphaType and set required transforms
scroggof24f2242015-03-03 08:59:20 -0800286 SkColorType skColorType;
287 SkAlphaType skAlphaType;
288 switch (colorType) {
289 case PNG_COLOR_TYPE_PALETTE:
msarett438b2ad2015-04-09 12:43:10 -0700290 skColorType = kIndex_8_SkColorType;
emmaleer21cea722015-07-10 07:48:03 -0700291 skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ?
scroggof24f2242015-03-03 08:59:20 -0800292 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
293 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700294 case PNG_COLOR_TYPE_RGB:
emmaleer21cea722015-07-10 07:48:03 -0700295 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700296 //convert to RGBA with tranparency information in tRNS chunk if it exists
297 png_set_tRNS_to_alpha(png_ptr);
jvanverth6c90e092015-07-02 10:35:25 -0700298 skAlphaType = kUnpremul_SkAlphaType;
299 } else {
scroggo6f29a3c2015-07-07 06:09:08 -0700300 //convert to RGBA with Opaque Alpha
301 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
jvanverth6c90e092015-07-02 10:35:25 -0700302 skAlphaType = kOpaque_SkAlphaType;
303 }
304 skColorType = kN32_SkColorType;
305 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700306 case PNG_COLOR_TYPE_GRAY:
emmaleer21cea722015-07-10 07:48:03 -0700307 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
scroggo6f29a3c2015-07-07 06:09:08 -0700308 //FIXME: support gray with alpha as a color type
309 //convert to RGBA if there is transparentcy info in the tRNS chunk
310 png_set_tRNS_to_alpha(png_ptr);
311 png_set_gray_to_rgb(png_ptr);
312 skColorType = kN32_SkColorType;
313 skAlphaType = kUnpremul_SkAlphaType;
314 } else {
315 skColorType = kGray_8_SkColorType;
316 skAlphaType = kOpaque_SkAlphaType;
317 }
318 break;
319 case PNG_COLOR_TYPE_GRAY_ALPHA:
320 //FIXME: support gray with alpha as a color type
321 //convert to RGBA
jvanverth6c90e092015-07-02 10:35:25 -0700322 png_set_gray_to_rgb(png_ptr);
scroggo6f29a3c2015-07-07 06:09:08 -0700323 skColorType = kN32_SkColorType;
324 skAlphaType = kUnpremul_SkAlphaType;
325 break;
326 case PNG_COLOR_TYPE_RGBA:
327 skColorType = kN32_SkColorType;
328 skAlphaType = kUnpremul_SkAlphaType;
329 break;
330 default:
331 //all the color types have been covered above
332 SkASSERT(false);
scroggof24f2242015-03-03 08:59:20 -0800333 }
334
335 // FIXME: Also need to check for sRGB (skbug.com/3471).
336
scroggo3eada2a2015-04-01 09:33:23 -0700337 if (imageInfo) {
scroggo6f29a3c2015-07-07 06:09:08 -0700338 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlphaType);
scroggo3eada2a2015-04-01 09:33:23 -0700339 }
scroggof24f2242015-03-03 08:59:20 -0800340 autoClean.detach();
scroggo3eada2a2015-04-01 09:33:23 -0700341 if (png_ptrp) {
342 *png_ptrp = png_ptr;
343 }
344 if (info_ptrp) {
345 *info_ptrp = info_ptr;
346 }
scroggo6f29a3c2015-07-07 06:09:08 -0700347
scroggo3eada2a2015-04-01 09:33:23 -0700348 return true;
349}
350
351SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
scroggo0a7e69c2015-04-03 07:22:22 -0700352 SkAutoTDelete<SkStream> streamDeleter(stream);
scroggo3eada2a2015-04-01 09:33:23 -0700353 png_structp png_ptr;
354 png_infop info_ptr;
355 SkImageInfo imageInfo;
scroggo6f29a3c2015-07-07 06:09:08 -0700356 int bitDepth;
357 if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) {
358 return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(),
359 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
msaretteed039b2015-03-18 11:11:19 -0700394static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
scroggof24f2242015-03-03 08:59:20 -0800395 // TODO: Support other conversions
msaretteed039b2015-03-18 11:11:19 -0700396 if (dst.profileType() != src.profileType()) {
scroggof24f2242015-03-03 08:59:20 -0800397 return false;
398 }
msarett438b2ad2015-04-09 12:43:10 -0700399
400 // Check for supported alpha types
401 if (src.alphaType() != dst.alphaType()) {
402 if (kOpaque_SkAlphaType == src.alphaType()) {
403 // If the source is opaque, we must decode to opaque
404 return false;
405 }
406
407 // The source is not opaque
408 switch (dst.alphaType()) {
409 case kPremul_SkAlphaType:
410 case kUnpremul_SkAlphaType:
411 // The source is not opaque, so either of these is okay
412 break;
413 default:
414 // We cannot decode a non-opaque image to opaque (or unknown)
415 return false;
416 }
scroggof24f2242015-03-03 08:59:20 -0800417 }
msarett438b2ad2015-04-09 12:43:10 -0700418 // Check for supported color types
419 switch (dst.colorType()) {
msarett438b2ad2015-04-09 12:43:10 -0700420 case kN32_SkColorType:
421 return true;
422 default:
423 return dst.colorType() == src.colorType();
424 }
scroggof24f2242015-03-03 08:59:20 -0800425}
426
scroggo05245902015-03-25 11:11:52 -0700427SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
428 void* dst, size_t rowBytes,
msarett438b2ad2015-04-09 12:43:10 -0700429 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700430 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700431 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800432 // FIXME: Could we use the return value of setjmp to specify the type of
433 // error?
434 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700435 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800436 return kInvalidInput;
437 }
scroggo6f29a3c2015-07-07 06:09:08 -0700438 fNumberPasses = png_set_interlace_handling(fPng_ptr);
439 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800440
scroggo05245902015-03-25 11:11:52 -0700441 // Set to the default before calling decodePalette, which may change it.
442 fReallyHasAlpha = false;
scroggo6f29a3c2015-07-07 06:09:08 -0700443
444 //srcColorType was determined in readHeader() which determined png color type
445 const SkColorType srcColorType = this->getInfo().colorType();
446
447 switch (srcColorType) {
448 case kIndex_8_SkColorType:
449 //decode palette to Skia format
450 fSrcConfig = SkSwizzler::kIndex;
scroggo9b2cdbf2015-07-10 12:07:02 -0700451 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(),
scroggo6f29a3c2015-07-07 06:09:08 -0700452 ctableCount)) {
453 return kInvalidInput;
454 }
455 break;
456 case kGray_8_SkColorType:
457 fSrcConfig = SkSwizzler::kGray;
458 break;
459 case kN32_SkColorType:
460 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
461 fSrcConfig = SkSwizzler::kRGBX;
462 } else {
463 fSrcConfig = SkSwizzler::kRGBA;
464 }
465 break;
466 default:
467 //would have exited before now if the colorType was supported by png
468 SkASSERT(false);
469 }
msarett9e43cab2015-04-29 07:38:43 -0700470
471 // Copy the color table to the client if they request kIndex8 mode
472 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
473
474 // Create the swizzler. SkPngCodec retains ownership of the color table.
scroggo05245902015-03-25 11:11:52 -0700475 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
476 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
477 dst, rowBytes, options.fZeroInitialized));
478 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800479 // FIXME: CreateSwizzler could fail for another reason.
480 return kUnimplemented;
481 }
scroggo05245902015-03-25 11:11:52 -0700482 return kSuccess;
483}
484
scroggo6f29a3c2015-07-07 06:09:08 -0700485
scroggo58421542015-04-01 11:25:20 -0700486bool SkPngCodec::handleRewind() {
487 switch (this->rewindIfNeeded()) {
488 case kNoRewindNecessary_RewindState:
489 return true;
490 case kCouldNotRewind_RewindState:
491 return false;
492 case kRewound_RewindState: {
493 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
494 // succeeds, they will be repopulated, and if it fails, they will
495 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
496 // come through this function which will rewind and again attempt
497 // to reinitialize them.
498 this->destroyReadStruct();
499 png_structp png_ptr;
500 png_infop info_ptr;
scroggo6f29a3c2015-07-07 06:09:08 -0700501 if (read_header(this->stream(), &png_ptr, &info_ptr, NULL, NULL)) {
scroggo58421542015-04-01 11:25:20 -0700502 fPng_ptr = png_ptr;
503 fInfo_ptr = info_ptr;
504 return true;
505 }
506 return false;
507 }
508 default:
509 SkASSERT(false);
510 return false;
511 }
512}
513
scroggo05245902015-03-25 11:11:52 -0700514SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
515 size_t rowBytes, const Options& options,
516 SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700517 if (!conversion_possible(requestedInfo, this->getInfo())) {
518 return kInvalidConversion;
519 }
scroggob636b452015-07-22 07:16:20 -0700520 if (options.fSubset) {
521 // Subsets are not supported.
522 return kUnimplemented;
523 }
scroggo05245902015-03-25 11:11:52 -0700524 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
525 return kInvalidScale;
526 }
scroggo6f29a3c2015-07-07 06:09:08 -0700527 if (!this->handleRewind()) {
528 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700529 }
530
msarett9e43cab2015-04-29 07:38:43 -0700531 // Note that ctable and ctableCount may be modified if there is a color table
scroggo05245902015-03-25 11:11:52 -0700532 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
msarett9e43cab2015-04-29 07:38:43 -0700533 options, ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700534 if (result != kSuccess) {
535 return result;
536 }
scroggo05245902015-03-25 11:11:52 -0700537 // FIXME: Could we use the return value of setjmp to specify the type of
538 // error?
539 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700540 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700541 return kInvalidInput;
542 }
543
544 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
545 SkAutoMalloc storage;
546 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800547 const int width = requestedInfo.width();
548 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700549 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
scroggof24f2242015-03-03 08:59:20 -0800550 const size_t rowBytes = width * bpp;
551
552 storage.reset(width * height * bpp);
553 uint8_t* const base = static_cast<uint8_t*>(storage.get());
554
scroggo05245902015-03-25 11:11:52 -0700555 for (int i = 0; i < fNumberPasses; i++) {
scroggof24f2242015-03-03 08:59:20 -0800556 uint8_t* row = base;
557 for (int y = 0; y < height; y++) {
558 uint8_t* bmRow = row;
559 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
560 row += rowBytes;
561 }
562 }
563
564 // Now swizzle it.
565 uint8_t* row = base;
566 for (int y = 0; y < height; y++) {
scroggo05245902015-03-25 11:11:52 -0700567 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800568 row += rowBytes;
569 }
570 } else {
scroggo05245902015-03-25 11:11:52 -0700571 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800572 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
573 for (int y = 0; y < requestedInfo.height(); y++) {
574 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo05245902015-03-25 11:11:52 -0700575 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800576 }
577 }
578
scroggo05245902015-03-25 11:11:52 -0700579 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
580 // scanline decoding, but we could do it here. Alternatively, we could do
581 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800582
scroggo05245902015-03-25 11:11:52 -0700583 if (setjmp(png_jmpbuf(fPng_ptr))) {
584 // We've already read all the scanlines. This is a success.
emmaleer973ae862015-07-20 13:38:44 -0700585 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700586 }
emmaleer973ae862015-07-20 13:38:44 -0700587
588 // read rest of file, and get additional comment and time chunks in info_ptr
scroggo05245902015-03-25 11:11:52 -0700589 png_read_end(fPng_ptr, fInfo_ptr);
emmaleer973ae862015-07-20 13:38:44 -0700590 return kSuccess;
scroggo05245902015-03-25 11:11:52 -0700591}
592
593class SkPngScanlineDecoder : public SkScanlineDecoder {
594public:
595 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
596 : INHERITED(dstInfo)
597 , fCodec(codec)
598 , fHasAlpha(false)
599 {
600 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
601 fSrcRow = static_cast<uint8_t*>(fStorage.get());
602 }
603
scroggoeb602a52015-07-09 08:16:03 -0700604 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700605 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700606 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700607 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700608 }
609
610 for (int i = 0; i < count; i++) {
611 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
612 fCodec->fSwizzler->setDstRow(dst);
613 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
614 dst = SkTAddOffset<void>(dst, rowBytes);
615 }
scroggoeb602a52015-07-09 08:16:03 -0700616 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700617 }
618
scroggoeb602a52015-07-09 08:16:03 -0700619 SkCodec::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700620 // FIXME: Could we use the return value of setjmp to specify the type of
621 // error?
622 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700623 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700624 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700625 }
emmaleer7dc91902015-05-27 08:49:04 -0700626 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
627 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
628 //as png_read_rows has it's own loop which calls png_read_row count times.
629 for (int i = 0; i < count; i++) {
630 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
631 }
scroggoeb602a52015-07-09 08:16:03 -0700632 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700633 }
634
mtklein36352bf2015-03-25 18:17:31 -0700635 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700636
637private:
scroggo9b2cdbf2015-07-10 12:07:02 -0700638 SkAutoTDelete<SkPngCodec> fCodec;
639 bool fHasAlpha;
640 SkAutoMalloc fStorage;
641 uint8_t* fSrcRow;
scroggo05245902015-03-25 11:11:52 -0700642
643 typedef SkScanlineDecoder INHERITED;
644};
645
emmaleer0a4c3cb2015-06-22 10:40:21 -0700646
647class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
648public:
649 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
650 : INHERITED(dstInfo)
651 , fCodec(codec)
652 , fHasAlpha(false)
653 , fCurrentRow(0)
654 , fHeight(dstInfo.height())
emmaleer0a4c3cb2015-06-22 10:40:21 -0700655 {
scroggo6f29a3c2015-07-07 06:09:08 -0700656 fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700657 fGarbageRow.reset(fSrcRowBytes);
658 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
659 }
660
scroggoeb602a52015-07-09 08:16:03 -0700661 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700662 //rewind stream if have previously called onGetScanlines,
663 //since we need entire progressive image to get scanlines
scroggo9b2cdbf2015-07-10 12:07:02 -0700664 if (!fCodec->handleRewind()) {
665 return SkCodec::kCouldNotRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700666 }
667 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
668 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700669 return SkCodec::kInvalidInput;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700670 }
671 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
672 SkAutoMalloc storage(count * fSrcRowBytes);
673 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
674 uint8_t* srcRow;
675 for (int i = 0; i < number_passes; i++) {
676 //read rows we planned to skip into garbage row
677 for (int y = 0; y < fCurrentRow; y++){
678 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
679 }
680 //read rows we care about into buffer
681 srcRow = storagePtr;
682 for (int y = 0; y < count; y++) {
683 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
684 srcRow += fSrcRowBytes;
685 }
686 //read rows we don't want into garbage buffer
687 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
688 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
689 }
690 }
691 //swizzle the rows we care about
692 srcRow = storagePtr;
693 for (int y = 0; y < count; y++) {
694 fCodec->fSwizzler->setDstRow(dst);
695 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow));
696 dst = SkTAddOffset<void>(dst, dstRowBytes);
697 srcRow += fSrcRowBytes;
698 }
699 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700700 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700701 }
702
scroggoeb602a52015-07-09 08:16:03 -0700703 SkCodec::Result onSkipScanlines(int count) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700704 //when ongetScanlines is called it will skip to fCurrentRow
705 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700706 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700707 }
708
emmaleer0a4c3cb2015-06-22 10:40:21 -0700709 bool onReallyHasAlpha() const override { return fHasAlpha; }
710
711private:
scroggo9b2cdbf2015-07-10 12:07:02 -0700712 SkAutoTDelete<SkPngCodec> fCodec;
713 bool fHasAlpha;
714 int fCurrentRow;
715 int fHeight;
716 size_t fSrcRowBytes;
717 SkAutoMalloc fGarbageRow;
718 uint8_t* fGarbageRowPtr;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700719
720 typedef SkScanlineDecoder INHERITED;
721};
722
723
msarett9e43cab2015-04-29 07:38:43 -0700724SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
725 const Options& options, SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700726 if (!conversion_possible(dstInfo, this->getInfo())) {
727 SkCodecPrintf("no conversion possible\n");
scroggo58421542015-04-01 11:25:20 -0700728 return NULL;
729 }
scroggo05245902015-03-25 11:11:52 -0700730 // Check to see if scaling was requested.
731 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
732 return NULL;
733 }
scroggo9b2cdbf2015-07-10 12:07:02 -0700734 // Create a new SkPngCodec, to be owned by the scanline decoder.
735 SkStream* stream = this->stream()->duplicate();
736 if (!stream) {
737 return NULL;
738 }
739 SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream)));
740 if (!codec) {
scroggo05245902015-03-25 11:11:52 -0700741 return NULL;
742 }
743
744 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
745 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
746 // be at least dstInfo.minRowBytes.
scroggo9b2cdbf2015-07-10 12:07:02 -0700747 if (codec->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
msarett9e43cab2015-04-29 07:38:43 -0700748 ctableCount) != kSuccess) {
scroggo230d4ac2015-03-26 07:15:55 -0700749 SkCodecPrintf("failed to initialize the swizzler.\n");
scroggo05245902015-03-25 11:11:52 -0700750 return NULL;
751 }
752
scroggo9b2cdbf2015-07-10 12:07:02 -0700753 SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES);
754 if (codec->fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700755 // interlaced image
scroggo9b2cdbf2015-07-10 12:07:02 -0700756 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, codec.detach()));
scroggo05245902015-03-25 11:11:52 -0700757 }
758
scroggo9b2cdbf2015-07-10 12:07:02 -0700759 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, codec.detach()));
scroggo05245902015-03-25 11:11:52 -0700760}
761