blob: a14a67c3497856a908a406b76700cc642bff2599 [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
103// call only if color_type is PALETTE. Returns true if the ctable has alpha
104static bool has_transparency_in_palette(png_structp png_ptr,
105 png_infop info_ptr) {
106 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
107 return false;
108 }
109
110 png_bytep trans;
111 int num_trans;
112 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
113 return num_trans > 0;
114}
115
116// Method for coverting to either an SkPMColor or a similarly packed
117// unpremultiplied color.
118typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
119
120// Note: SkColorTable claims to store SkPMColors, which is not necessarily
121// the case here.
msarett438b2ad2015-04-09 12:43:10 -0700122bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800123 int numPalette;
124 png_colorp palette;
125 png_bytep trans;
126
scroggo05245902015-03-25 11:11:52 -0700127 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
128 return false;
scroggof24f2242015-03-03 08:59:20 -0800129 }
130
msarett438b2ad2015-04-09 12:43:10 -0700131 // Note: These are not necessarily SkPMColors
scroggof24f2242015-03-03 08:59:20 -0800132 SkPMColor colorStorage[256]; // worst-case storage
133 SkPMColor* colorPtr = colorStorage;
134
135 int numTrans;
scroggo05245902015-03-25 11:11:52 -0700136 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
137 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
scroggof24f2242015-03-03 08:59:20 -0800138 } else {
139 numTrans = 0;
140 }
141
142 // check for bad images that might make us crash
143 if (numTrans > numPalette) {
144 numTrans = numPalette;
145 }
146
147 int index = 0;
148 int transLessThanFF = 0;
149
150 // Choose which function to use to create the color table. If the final destination's
151 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
152 PackColorProc proc;
153 if (premultiply) {
154 proc = &SkPreMultiplyARGB;
155 } else {
156 proc = &SkPackARGB32NoCheck;
157 }
158 for (; index < numTrans; index++) {
159 transLessThanFF |= (int)*trans - 0xFF;
160 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
161 palette++;
162 }
163
scroggo05245902015-03-25 11:11:52 -0700164 fReallyHasAlpha = transLessThanFF < 0;
scroggof24f2242015-03-03 08:59:20 -0800165
166 for (; index < numPalette; index++) {
167 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
168 palette++;
169 }
170
msarett438b2ad2015-04-09 12:43:10 -0700171 /* BUGGY IMAGE WORKAROUND
172 Invalid images could contain pixel values that are greater than the number of palette
173 entries. Since we use pixel values as indices into the palette this could result in reading
174 beyond the end of the palette which could leak the contents of uninitialized memory. To
175 ensure this doesn't happen, we grow the colortable to the maximum size that can be
176 addressed by the bitdepth of the image and fill it with the last palette color or black if
177 the palette is empty (really broken image).
178 */
179 int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
180 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
181 for (; index < colorCount; index++) {
182 *colorPtr++ = lastColor;
183 }
184
185 // Set the new color count
186 if (ctableCount != NULL) {
msarett438b2ad2015-04-09 12:43:10 -0700187 *ctableCount = colorCount;
scroggof24f2242015-03-03 08:59:20 -0800188 }
189
scroggo05245902015-03-25 11:11:52 -0700190 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
191 return true;
scroggof24f2242015-03-03 08:59:20 -0800192}
193
194///////////////////////////////////////////////////////////////////////////////
195// Creation
196///////////////////////////////////////////////////////////////////////////////
197
198#define PNG_BYTES_TO_CHECK 4
199
200bool SkPngCodec::IsPng(SkStream* stream) {
201 char buf[PNG_BYTES_TO_CHECK];
202 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
203 return false;
204 }
205 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
206 return false;
207 }
208 return true;
209}
210
scroggo3eada2a2015-04-01 09:33:23 -0700211// Reads the header, and initializes the passed in fields, if not NULL (except
212// stream, which is passed to the read function).
213// Returns true on success, in which case the caller is responsible for calling
214// png_destroy_read_struct. If it returns false, the passed in fields (except
215// stream) are unchanged.
216static bool read_header(SkStream* stream, png_structp* png_ptrp,
scroggo6f29a3c2015-07-07 06:09:08 -0700217 png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) {
scroggof24f2242015-03-03 08:59:20 -0800218 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
scroggof24f2242015-03-03 08:59:20 -0800219 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
scroggo0eed6df2015-03-26 10:07:56 -0700220 sk_error_fn, sk_warning_fn);
scroggof24f2242015-03-03 08:59:20 -0800221 if (!png_ptr) {
scroggo3eada2a2015-04-01 09:33:23 -0700222 return false;
scroggof24f2242015-03-03 08:59:20 -0800223 }
224
225 AutoCleanPng autoClean(png_ptr);
226
227 png_infop info_ptr = png_create_info_struct(png_ptr);
228 if (info_ptr == NULL) {
scroggo3eada2a2015-04-01 09:33:23 -0700229 return false;
scroggof24f2242015-03-03 08:59:20 -0800230 }
231
232 autoClean.setInfoPtr(info_ptr);
233
234 // FIXME: Could we use the return value of setjmp to specify the type of
235 // error?
236 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo3eada2a2015-04-01 09:33:23 -0700237 return false;
scroggof24f2242015-03-03 08:59:20 -0800238 }
239
240 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
241
242 // FIXME: This is where the old code hooks up the Peeker. Does it need to
243 // be set this early? (i.e. where are the user chunks? early in the stream,
244 // potentially?)
245 // If it does, we need to figure out a way to set it here.
246
247 // The call to png_read_info() gives us all of the information from the
248 // PNG file before the first IDAT (image data chunk).
249 png_read_info(png_ptr, info_ptr);
250 png_uint_32 origWidth, origHeight;
251 int bitDepth, colorType;
252 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
253 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
254
scroggo6f29a3c2015-07-07 06:09:08 -0700255 if (bitDepthPtr) {
256 *bitDepthPtr = bitDepth;
257 }
258
scroggof24f2242015-03-03 08:59:20 -0800259 // sanity check for size
260 {
261 int64_t size = sk_64_mul(origWidth, origHeight);
262 // now check that if we are 4-bytes per pixel, we also don't overflow
263 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
scroggo3eada2a2015-04-01 09:33:23 -0700264 return false;
scroggof24f2242015-03-03 08:59:20 -0800265 }
266 }
267
268 // Tell libpng to strip 16 bit/color files down to 8 bits/color
269 if (bitDepth == 16) {
270 png_set_strip_16(png_ptr);
271 }
272#ifdef PNG_READ_PACK_SUPPORTED
273 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
274 // byte into separate bytes (useful for paletted and grayscale images).
275 if (bitDepth < 8) {
276 png_set_packing(png_ptr);
277 }
278#endif
279 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
280 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
281 png_set_expand_gray_1_2_4_to_8(png_ptr);
282 }
283
scroggo6f29a3c2015-07-07 06:09:08 -0700284 // Now determine the default SkColorType and SkAlphaType and set required transforms
scroggof24f2242015-03-03 08:59:20 -0800285 SkColorType skColorType;
286 SkAlphaType skAlphaType;
287 switch (colorType) {
288 case PNG_COLOR_TYPE_PALETTE:
msarett438b2ad2015-04-09 12:43:10 -0700289 skColorType = kIndex_8_SkColorType;
scroggof24f2242015-03-03 08:59:20 -0800290 skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
291 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
292 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700293 case PNG_COLOR_TYPE_RGB:
294 if (has_transparency_in_palette(png_ptr, info_ptr)) {
295 //convert to RGBA with tranparency information in tRNS chunk if it exists
296 png_set_tRNS_to_alpha(png_ptr);
jvanverth6c90e092015-07-02 10:35:25 -0700297 skAlphaType = kUnpremul_SkAlphaType;
298 } else {
scroggo6f29a3c2015-07-07 06:09:08 -0700299 //convert to RGBA with Opaque Alpha
300 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
jvanverth6c90e092015-07-02 10:35:25 -0700301 skAlphaType = kOpaque_SkAlphaType;
302 }
303 skColorType = kN32_SkColorType;
304 break;
scroggo6f29a3c2015-07-07 06:09:08 -0700305 case PNG_COLOR_TYPE_GRAY:
306 if (has_transparency_in_palette(png_ptr, info_ptr)) {
307 //FIXME: support gray with alpha as a color type
308 //convert to RGBA if there is transparentcy info in the tRNS chunk
309 png_set_tRNS_to_alpha(png_ptr);
310 png_set_gray_to_rgb(png_ptr);
311 skColorType = kN32_SkColorType;
312 skAlphaType = kUnpremul_SkAlphaType;
313 } else {
314 skColorType = kGray_8_SkColorType;
315 skAlphaType = kOpaque_SkAlphaType;
316 }
317 break;
318 case PNG_COLOR_TYPE_GRAY_ALPHA:
319 //FIXME: support gray with alpha as a color type
320 //convert to RGBA
jvanverth6c90e092015-07-02 10:35:25 -0700321 png_set_gray_to_rgb(png_ptr);
scroggo6f29a3c2015-07-07 06:09:08 -0700322 skColorType = kN32_SkColorType;
323 skAlphaType = kUnpremul_SkAlphaType;
324 break;
325 case PNG_COLOR_TYPE_RGBA:
326 skColorType = kN32_SkColorType;
327 skAlphaType = kUnpremul_SkAlphaType;
328 break;
329 default:
330 //all the color types have been covered above
331 SkASSERT(false);
scroggof24f2242015-03-03 08:59:20 -0800332 }
333
334 // FIXME: Also need to check for sRGB (skbug.com/3471).
335
scroggo3eada2a2015-04-01 09:33:23 -0700336 if (imageInfo) {
scroggo6f29a3c2015-07-07 06:09:08 -0700337 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlphaType);
scroggo3eada2a2015-04-01 09:33:23 -0700338 }
scroggof24f2242015-03-03 08:59:20 -0800339 autoClean.detach();
scroggo3eada2a2015-04-01 09:33:23 -0700340 if (png_ptrp) {
341 *png_ptrp = png_ptr;
342 }
343 if (info_ptrp) {
344 *info_ptrp = info_ptr;
345 }
scroggo6f29a3c2015-07-07 06:09:08 -0700346
scroggo3eada2a2015-04-01 09:33:23 -0700347 return true;
348}
349
350SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
scroggo0a7e69c2015-04-03 07:22:22 -0700351 SkAutoTDelete<SkStream> streamDeleter(stream);
scroggo3eada2a2015-04-01 09:33:23 -0700352 png_structp png_ptr;
353 png_infop info_ptr;
354 SkImageInfo imageInfo;
scroggo6f29a3c2015-07-07 06:09:08 -0700355 int bitDepth;
356 if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) {
357 return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(),
358 png_ptr, info_ptr, bitDepth));
scroggo3eada2a2015-04-01 09:33:23 -0700359 }
360 return NULL;
scroggof24f2242015-03-03 08:59:20 -0800361}
362
scroggo05245902015-03-25 11:11:52 -0700363#define INVALID_NUMBER_PASSES -1
scroggof24f2242015-03-03 08:59:20 -0800364SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
scroggo6f29a3c2015-07-07 06:09:08 -0700365 png_structp png_ptr, png_infop info_ptr, int bitDepth)
scroggof24f2242015-03-03 08:59:20 -0800366 : INHERITED(info, stream)
367 , fPng_ptr(png_ptr)
scroggo05245902015-03-25 11:11:52 -0700368 , fInfo_ptr(info_ptr)
369 , fSrcConfig(SkSwizzler::kUnknown)
370 , fNumberPasses(INVALID_NUMBER_PASSES)
371 , fReallyHasAlpha(false)
scroggo6f29a3c2015-07-07 06:09:08 -0700372 , fBitDepth(bitDepth)
scroggo05245902015-03-25 11:11:52 -0700373{}
scroggof24f2242015-03-03 08:59:20 -0800374
375SkPngCodec::~SkPngCodec() {
msarettc0e80c12015-07-01 06:50:35 -0700376 // First, ensure that the scanline decoder is left in a finished state.
377 SkAutoTDelete<SkScanlineDecoder> decoder(this->detachScanlineDecoder());
378 if (NULL != decoder) {
379 this->finish();
380 }
381
scroggo3eada2a2015-04-01 09:33:23 -0700382 this->destroyReadStruct();
383}
384
385void SkPngCodec::destroyReadStruct() {
386 if (fPng_ptr) {
387 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
388 SkASSERT(fInfo_ptr);
389 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
390 fPng_ptr = NULL;
391 fInfo_ptr = NULL;
392 }
scroggof24f2242015-03-03 08:59:20 -0800393}
394
395///////////////////////////////////////////////////////////////////////////////
396// Getting the pixels
397///////////////////////////////////////////////////////////////////////////////
398
msaretteed039b2015-03-18 11:11:19 -0700399static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
scroggof24f2242015-03-03 08:59:20 -0800400 // TODO: Support other conversions
msaretteed039b2015-03-18 11:11:19 -0700401 if (dst.profileType() != src.profileType()) {
scroggof24f2242015-03-03 08:59:20 -0800402 return false;
403 }
msarett438b2ad2015-04-09 12:43:10 -0700404
405 // Check for supported alpha types
406 if (src.alphaType() != dst.alphaType()) {
407 if (kOpaque_SkAlphaType == src.alphaType()) {
408 // If the source is opaque, we must decode to opaque
409 return false;
410 }
411
412 // The source is not opaque
413 switch (dst.alphaType()) {
414 case kPremul_SkAlphaType:
415 case kUnpremul_SkAlphaType:
416 // The source is not opaque, so either of these is okay
417 break;
418 default:
419 // We cannot decode a non-opaque image to opaque (or unknown)
420 return false;
421 }
scroggof24f2242015-03-03 08:59:20 -0800422 }
msarett438b2ad2015-04-09 12:43:10 -0700423 // Check for supported color types
424 switch (dst.colorType()) {
msarett438b2ad2015-04-09 12:43:10 -0700425 case kN32_SkColorType:
426 return true;
427 default:
428 return dst.colorType() == src.colorType();
429 }
scroggof24f2242015-03-03 08:59:20 -0800430}
431
scroggo05245902015-03-25 11:11:52 -0700432SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
433 void* dst, size_t rowBytes,
msarett438b2ad2015-04-09 12:43:10 -0700434 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700435 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700436 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800437 // FIXME: Could we use the return value of setjmp to specify the type of
438 // error?
439 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700440 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800441 return kInvalidInput;
442 }
scroggo6f29a3c2015-07-07 06:09:08 -0700443 fNumberPasses = png_set_interlace_handling(fPng_ptr);
444 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800445
scroggo05245902015-03-25 11:11:52 -0700446 // Set to the default before calling decodePalette, which may change it.
447 fReallyHasAlpha = false;
scroggo6f29a3c2015-07-07 06:09:08 -0700448
449 //srcColorType was determined in readHeader() which determined png color type
450 const SkColorType srcColorType = this->getInfo().colorType();
451
452 switch (srcColorType) {
453 case kIndex_8_SkColorType:
454 //decode palette to Skia format
455 fSrcConfig = SkSwizzler::kIndex;
456 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), fBitDepth,
457 ctableCount)) {
458 return kInvalidInput;
459 }
460 break;
461 case kGray_8_SkColorType:
462 fSrcConfig = SkSwizzler::kGray;
463 break;
464 case kN32_SkColorType:
465 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
466 fSrcConfig = SkSwizzler::kRGBX;
467 } else {
468 fSrcConfig = SkSwizzler::kRGBA;
469 }
470 break;
471 default:
472 //would have exited before now if the colorType was supported by png
473 SkASSERT(false);
474 }
msarett9e43cab2015-04-29 07:38:43 -0700475
476 // Copy the color table to the client if they request kIndex8 mode
477 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
478
479 // Create the swizzler. SkPngCodec retains ownership of the color table.
scroggo05245902015-03-25 11:11:52 -0700480 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
481 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
482 dst, rowBytes, options.fZeroInitialized));
483 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800484 // FIXME: CreateSwizzler could fail for another reason.
485 return kUnimplemented;
486 }
scroggo05245902015-03-25 11:11:52 -0700487 return kSuccess;
488}
489
scroggo6f29a3c2015-07-07 06:09:08 -0700490
scroggo58421542015-04-01 11:25:20 -0700491bool SkPngCodec::handleRewind() {
492 switch (this->rewindIfNeeded()) {
493 case kNoRewindNecessary_RewindState:
494 return true;
495 case kCouldNotRewind_RewindState:
496 return false;
497 case kRewound_RewindState: {
498 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
499 // succeeds, they will be repopulated, and if it fails, they will
500 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
501 // come through this function which will rewind and again attempt
502 // to reinitialize them.
503 this->destroyReadStruct();
504 png_structp png_ptr;
505 png_infop info_ptr;
scroggo6f29a3c2015-07-07 06:09:08 -0700506 if (read_header(this->stream(), &png_ptr, &info_ptr, NULL, NULL)) {
scroggo58421542015-04-01 11:25:20 -0700507 fPng_ptr = png_ptr;
508 fInfo_ptr = info_ptr;
509 return true;
510 }
511 return false;
512 }
513 default:
514 SkASSERT(false);
515 return false;
516 }
517}
518
scroggo05245902015-03-25 11:11:52 -0700519SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
520 size_t rowBytes, const Options& options,
521 SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700522 if (!conversion_possible(requestedInfo, this->getInfo())) {
523 return kInvalidConversion;
524 }
msarettc0e80c12015-07-01 06:50:35 -0700525 // Do not allow a regular decode if the caller has asked for a scanline decoder
526 if (NULL != this->scanlineDecoder()) {
527 SkCodecPrintf("cannot getPixels() if a scanline decoder has been created\n");
528 return kInvalidParameters;
529 }
scroggo05245902015-03-25 11:11:52 -0700530 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
531 return kInvalidScale;
532 }
scroggo6f29a3c2015-07-07 06:09:08 -0700533 if (!this->handleRewind()) {
534 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700535 }
536
msarett9e43cab2015-04-29 07:38:43 -0700537 // Note that ctable and ctableCount may be modified if there is a color table
scroggo05245902015-03-25 11:11:52 -0700538 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
msarett9e43cab2015-04-29 07:38:43 -0700539 options, ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700540 if (result != kSuccess) {
541 return result;
542 }
scroggo05245902015-03-25 11:11:52 -0700543 // FIXME: Could we use the return value of setjmp to specify the type of
544 // error?
545 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700546 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700547 return kInvalidInput;
548 }
549
550 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
551 SkAutoMalloc storage;
552 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800553 const int width = requestedInfo.width();
554 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700555 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
scroggof24f2242015-03-03 08:59:20 -0800556 const size_t rowBytes = width * bpp;
557
558 storage.reset(width * height * bpp);
559 uint8_t* const base = static_cast<uint8_t*>(storage.get());
560
scroggo05245902015-03-25 11:11:52 -0700561 for (int i = 0; i < fNumberPasses; i++) {
scroggof24f2242015-03-03 08:59:20 -0800562 uint8_t* row = base;
563 for (int y = 0; y < height; y++) {
564 uint8_t* bmRow = row;
565 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
566 row += rowBytes;
567 }
568 }
569
570 // Now swizzle it.
571 uint8_t* row = base;
572 for (int y = 0; y < height; y++) {
scroggo05245902015-03-25 11:11:52 -0700573 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800574 row += rowBytes;
575 }
576 } else {
scroggo05245902015-03-25 11:11:52 -0700577 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800578 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
579 for (int y = 0; y < requestedInfo.height(); y++) {
580 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo05245902015-03-25 11:11:52 -0700581 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800582 }
583 }
584
scroggo05245902015-03-25 11:11:52 -0700585 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
586 // scanline decoding, but we could do it here. Alternatively, we could do
587 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800588
scroggo05245902015-03-25 11:11:52 -0700589 this->finish();
scroggof24f2242015-03-03 08:59:20 -0800590 return kSuccess;
591}
scroggo05245902015-03-25 11:11:52 -0700592
593void SkPngCodec::finish() {
594 if (setjmp(png_jmpbuf(fPng_ptr))) {
595 // We've already read all the scanlines. This is a success.
596 return;
597 }
598 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
599 png_read_end(fPng_ptr, fInfo_ptr);
600}
601
602class SkPngScanlineDecoder : public SkScanlineDecoder {
603public:
604 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
605 : INHERITED(dstInfo)
606 , fCodec(codec)
607 , fHasAlpha(false)
608 {
609 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
610 fSrcRow = static_cast<uint8_t*>(fStorage.get());
611 }
612
scroggoeb602a52015-07-09 08:16:03 -0700613 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700614 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700615 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700616 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700617 }
618
619 for (int i = 0; i < count; i++) {
620 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
621 fCodec->fSwizzler->setDstRow(dst);
622 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
623 dst = SkTAddOffset<void>(dst, rowBytes);
624 }
scroggoeb602a52015-07-09 08:16:03 -0700625 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700626 }
627
scroggoeb602a52015-07-09 08:16:03 -0700628 SkCodec::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700629 // FIXME: Could we use the return value of setjmp to specify the type of
630 // error?
631 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700632 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700633 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700634 }
emmaleer7dc91902015-05-27 08:49:04 -0700635 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
636 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
637 //as png_read_rows has it's own loop which calls png_read_row count times.
638 for (int i = 0; i < count; i++) {
639 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
640 }
scroggoeb602a52015-07-09 08:16:03 -0700641 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700642 }
643
mtklein36352bf2015-03-25 18:17:31 -0700644 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700645
646private:
647 SkPngCodec* fCodec; // Unowned.
648 bool fHasAlpha;
649 SkAutoMalloc fStorage;
650 uint8_t* fSrcRow;
651
652 typedef SkScanlineDecoder INHERITED;
653};
654
emmaleer0a4c3cb2015-06-22 10:40:21 -0700655
656class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
657public:
658 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
659 : INHERITED(dstInfo)
660 , fCodec(codec)
661 , fHasAlpha(false)
662 , fCurrentRow(0)
663 , fHeight(dstInfo.height())
emmaleer0a4c3cb2015-06-22 10:40:21 -0700664 , fRewindNeeded(false)
665 {
scroggo6f29a3c2015-07-07 06:09:08 -0700666 fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700667 fGarbageRow.reset(fSrcRowBytes);
668 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
669 }
670
scroggoeb602a52015-07-09 08:16:03 -0700671 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700672 //rewind stream if have previously called onGetScanlines,
673 //since we need entire progressive image to get scanlines
674 if (fRewindNeeded) {
675 if(false == fCodec->handleRewind()) {
scroggoeb602a52015-07-09 08:16:03 -0700676 return SkCodec::kCouldNotRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700677 }
678 } else {
679 fRewindNeeded = true;
680 }
681 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
682 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700683 return SkCodec::kInvalidInput;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700684 }
685 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
686 SkAutoMalloc storage(count * fSrcRowBytes);
687 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
688 uint8_t* srcRow;
689 for (int i = 0; i < number_passes; i++) {
690 //read rows we planned to skip into garbage row
691 for (int y = 0; y < fCurrentRow; y++){
692 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
693 }
694 //read rows we care about into buffer
695 srcRow = storagePtr;
696 for (int y = 0; y < count; y++) {
697 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
698 srcRow += fSrcRowBytes;
699 }
700 //read rows we don't want into garbage buffer
701 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
702 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
703 }
704 }
705 //swizzle the rows we care about
706 srcRow = storagePtr;
707 for (int y = 0; y < count; y++) {
708 fCodec->fSwizzler->setDstRow(dst);
709 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow));
710 dst = SkTAddOffset<void>(dst, dstRowBytes);
711 srcRow += fSrcRowBytes;
712 }
713 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700714 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700715 }
716
scroggoeb602a52015-07-09 08:16:03 -0700717 SkCodec::Result onSkipScanlines(int count) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700718 //when ongetScanlines is called it will skip to fCurrentRow
719 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700720 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700721 }
722
emmaleer0a4c3cb2015-06-22 10:40:21 -0700723 bool onReallyHasAlpha() const override { return fHasAlpha; }
724
725private:
726 SkPngCodec* fCodec; // Unowned.
727 bool fHasAlpha;
728 int fCurrentRow;
729 int fHeight;
730 size_t fSrcRowBytes;
731 bool fRewindNeeded;
732 SkAutoMalloc fGarbageRow;
733 uint8_t* fGarbageRowPtr;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700734
735 typedef SkScanlineDecoder INHERITED;
736};
737
738
msarett9e43cab2015-04-29 07:38:43 -0700739SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
740 const Options& options, SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700741 if (!conversion_possible(dstInfo, this->getInfo())) {
742 SkCodecPrintf("no conversion possible\n");
scroggo58421542015-04-01 11:25:20 -0700743 return NULL;
744 }
scroggo05245902015-03-25 11:11:52 -0700745 // Check to see if scaling was requested.
746 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
747 return NULL;
748 }
scroggo6f29a3c2015-07-07 06:09:08 -0700749 if (!this->handleRewind()) {
scroggo05245902015-03-25 11:11:52 -0700750 return NULL;
751 }
752
753 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
754 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
755 // be at least dstInfo.minRowBytes.
msarett9e43cab2015-04-29 07:38:43 -0700756 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
757 ctableCount) != kSuccess) {
scroggo230d4ac2015-03-26 07:15:55 -0700758 SkCodecPrintf("failed to initialize the swizzler.\n");
scroggo05245902015-03-25 11:11:52 -0700759 return NULL;
760 }
761
762 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
763 if (fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700764 // interlaced image
765 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this));
scroggo05245902015-03-25 11:11:52 -0700766 }
767
768 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
769}
770