blob: 32004366f839672ad8d932c3a81300834fc2305c [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,
217 png_infop* info_ptrp, SkImageInfo* imageInfo) {
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
255 // sanity check for size
256 {
257 int64_t size = sk_64_mul(origWidth, origHeight);
258 // now check that if we are 4-bytes per pixel, we also don't overflow
259 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
scroggo3eada2a2015-04-01 09:33:23 -0700260 return false;
scroggof24f2242015-03-03 08:59:20 -0800261 }
262 }
263
264 // Tell libpng to strip 16 bit/color files down to 8 bits/color
265 if (bitDepth == 16) {
266 png_set_strip_16(png_ptr);
267 }
268#ifdef PNG_READ_PACK_SUPPORTED
269 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
270 // byte into separate bytes (useful for paletted and grayscale images).
271 if (bitDepth < 8) {
272 png_set_packing(png_ptr);
273 }
274#endif
275 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
276 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
277 png_set_expand_gray_1_2_4_to_8(png_ptr);
278 }
279
280
281 // Now determine the default SkColorType and SkAlphaType.
282 SkColorType skColorType;
283 SkAlphaType skAlphaType;
284 switch (colorType) {
285 case PNG_COLOR_TYPE_PALETTE:
msarett438b2ad2015-04-09 12:43:10 -0700286 skColorType = kIndex_8_SkColorType;
scroggof24f2242015-03-03 08:59:20 -0800287 skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
288 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
289 break;
290 case PNG_COLOR_TYPE_GRAY:
291 if (false) {
292 // FIXME: Is this the wrong default behavior? This means if the
293 // caller supplies the info we gave them, they'll get Alpha 8.
294 skColorType = kAlpha_8_SkColorType;
295 // FIXME: Strangely, the canonical type for Alpha 8 is Premul.
296 skAlphaType = kPremul_SkAlphaType;
297 } else {
298 skColorType = kN32_SkColorType;
299 skAlphaType = kOpaque_SkAlphaType;
300 }
301 break;
302 default:
303 // Note: This *almost* mimics the code in SkImageDecoder_libpng.
304 // has_transparency_in_palette makes an additional check - whether
305 // numTrans is greater than 0. Why does the other code not make that
306 // check?
307 if (has_transparency_in_palette(png_ptr, info_ptr)
308 || PNG_COLOR_TYPE_RGB_ALPHA == colorType
309 || PNG_COLOR_TYPE_GRAY_ALPHA == colorType)
310 {
311 skAlphaType = kUnpremul_SkAlphaType;
312 } else {
313 skAlphaType = kOpaque_SkAlphaType;
314 }
315 skColorType = kN32_SkColorType;
316 break;
317 }
318
319 {
320 // FIXME: Again, this block needs to go into onGetPixels.
321 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType;
322
323 // Unless the user is requesting A8, convert a grayscale image into RGB.
324 // GRAY_ALPHA will always be converted to RGB
325 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
326 png_set_gray_to_rgb(png_ptr);
327 }
328
329 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
330 // FIXME: It seems like we could just use RGB as the SrcConfig here.
331 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
332 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
333 }
334 }
335
336 // FIXME: Also need to check for sRGB (skbug.com/3471).
337
scroggo3eada2a2015-04-01 09:33:23 -0700338 if (imageInfo) {
339 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType,
340 skAlphaType);
341 }
scroggof24f2242015-03-03 08:59:20 -0800342 autoClean.detach();
scroggo3eada2a2015-04-01 09:33:23 -0700343 if (png_ptrp) {
344 *png_ptrp = png_ptr;
345 }
346 if (info_ptrp) {
347 *info_ptrp = info_ptr;
348 }
349 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;
357 if (read_header(stream, &png_ptr, &info_ptr, &imageInfo)) {
scroggo0a7e69c2015-04-03 07:22:22 -0700358 return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(), png_ptr, info_ptr));
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,
365 png_structp png_ptr, png_infop info_ptr)
366 : 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)
372{}
scroggof24f2242015-03-03 08:59:20 -0800373
374SkPngCodec::~SkPngCodec() {
msarettc0e80c12015-07-01 06:50:35 -0700375 // First, ensure that the scanline decoder is left in a finished state.
376 SkAutoTDelete<SkScanlineDecoder> decoder(this->detachScanlineDecoder());
377 if (NULL != decoder) {
378 this->finish();
379 }
380
scroggo3eada2a2015-04-01 09:33:23 -0700381 this->destroyReadStruct();
382}
383
384void SkPngCodec::destroyReadStruct() {
385 if (fPng_ptr) {
386 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
387 SkASSERT(fInfo_ptr);
388 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
389 fPng_ptr = NULL;
390 fInfo_ptr = NULL;
391 }
scroggof24f2242015-03-03 08:59:20 -0800392}
393
394///////////////////////////////////////////////////////////////////////////////
395// Getting the pixels
396///////////////////////////////////////////////////////////////////////////////
397
msaretteed039b2015-03-18 11:11:19 -0700398static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
scroggof24f2242015-03-03 08:59:20 -0800399 // TODO: Support other conversions
msaretteed039b2015-03-18 11:11:19 -0700400 if (dst.profileType() != src.profileType()) {
scroggof24f2242015-03-03 08:59:20 -0800401 return false;
402 }
msarett438b2ad2015-04-09 12:43:10 -0700403
404 // Check for supported alpha types
405 if (src.alphaType() != dst.alphaType()) {
406 if (kOpaque_SkAlphaType == src.alphaType()) {
407 // If the source is opaque, we must decode to opaque
408 return false;
409 }
410
411 // The source is not opaque
412 switch (dst.alphaType()) {
413 case kPremul_SkAlphaType:
414 case kUnpremul_SkAlphaType:
415 // The source is not opaque, so either of these is okay
416 break;
417 default:
418 // We cannot decode a non-opaque image to opaque (or unknown)
419 return false;
420 }
scroggof24f2242015-03-03 08:59:20 -0800421 }
msarett438b2ad2015-04-09 12:43:10 -0700422
423 // Check for supported color types
424 switch (dst.colorType()) {
425 // Allow output to kN32 from any type of input
426 case kN32_SkColorType:
427 return true;
428 default:
429 return dst.colorType() == src.colorType();
430 }
scroggof24f2242015-03-03 08:59:20 -0800431}
432
scroggo05245902015-03-25 11:11:52 -0700433SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
434 void* dst, size_t rowBytes,
msarett438b2ad2015-04-09 12:43:10 -0700435 const Options& options,
msarett9e43cab2015-04-29 07:38:43 -0700436 SkPMColor ctable[],
msarett438b2ad2015-04-09 12:43:10 -0700437 int* ctableCount) {
scroggof24f2242015-03-03 08:59:20 -0800438 // FIXME: Could we use the return value of setjmp to specify the type of
439 // error?
440 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700441 SkCodecPrintf("setjmp long jump!\n");
scroggof24f2242015-03-03 08:59:20 -0800442 return kInvalidInput;
443 }
444
445 // FIXME: We already retrieved this information. Store it in SkPngCodec?
446 png_uint_32 origWidth, origHeight;
447 int bitDepth, pngColorType, interlaceType;
448 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
449 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
450
scroggo05245902015-03-25 11:11:52 -0700451 fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
scroggof24f2242015-03-03 08:59:20 -0800452 png_set_interlace_handling(fPng_ptr) : 1;
453
scroggo05245902015-03-25 11:11:52 -0700454 // Set to the default before calling decodePalette, which may change it.
455 fReallyHasAlpha = false;
scroggof24f2242015-03-03 08:59:20 -0800456 if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
scroggo05245902015-03-25 11:11:52 -0700457 fSrcConfig = SkSwizzler::kIndex;
msarett438b2ad2015-04-09 12:43:10 -0700458 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), bitDepth,
459 ctableCount)) {
scroggof24f2242015-03-03 08:59:20 -0800460 return kInvalidInput;
461 }
scroggof24f2242015-03-03 08:59:20 -0800462 } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
463 // Note: we check the destination, since otherwise we would have
464 // told png to upscale.
465 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo05245902015-03-25 11:11:52 -0700466 fSrcConfig = SkSwizzler::kGray;
msarett9bde9182015-03-25 05:27:48 -0700467 } else if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
scroggo05245902015-03-25 11:11:52 -0700468 fSrcConfig = SkSwizzler::kRGBX;
scroggof24f2242015-03-03 08:59:20 -0800469 } else {
scroggo05245902015-03-25 11:11:52 -0700470 fSrcConfig = SkSwizzler::kRGBA;
scroggof24f2242015-03-03 08:59:20 -0800471 }
msarett9e43cab2015-04-29 07:38:43 -0700472
473 // Copy the color table to the client if they request kIndex8 mode
474 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
475
476 // Create the swizzler. SkPngCodec retains ownership of the color table.
scroggo05245902015-03-25 11:11:52 -0700477 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
478 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
479 dst, rowBytes, options.fZeroInitialized));
480 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800481 // FIXME: CreateSwizzler could fail for another reason.
482 return kUnimplemented;
483 }
484
485 // FIXME: Here is where we should likely insert some of the modifications
486 // made in the factory.
487 png_read_update_info(fPng_ptr, fInfo_ptr);
488
scroggo05245902015-03-25 11:11:52 -0700489 return kSuccess;
490}
491
scroggo58421542015-04-01 11:25:20 -0700492bool SkPngCodec::handleRewind() {
493 switch (this->rewindIfNeeded()) {
494 case kNoRewindNecessary_RewindState:
495 return true;
496 case kCouldNotRewind_RewindState:
497 return false;
498 case kRewound_RewindState: {
499 // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
500 // succeeds, they will be repopulated, and if it fails, they will
501 // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
502 // come through this function which will rewind and again attempt
503 // to reinitialize them.
504 this->destroyReadStruct();
505 png_structp png_ptr;
506 png_infop info_ptr;
507 if (read_header(this->stream(), &png_ptr, &info_ptr, NULL)) {
508 fPng_ptr = png_ptr;
509 fInfo_ptr = info_ptr;
510 return true;
511 }
512 return false;
513 }
514 default:
515 SkASSERT(false);
516 return false;
517 }
518}
519
scroggo05245902015-03-25 11:11:52 -0700520SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
521 size_t rowBytes, const Options& options,
522 SkPMColor ctable[], int* ctableCount) {
msarettc0e80c12015-07-01 06:50:35 -0700523 // Do not allow a regular decode if the caller has asked for a scanline decoder
524 if (NULL != this->scanlineDecoder()) {
525 SkCodecPrintf("cannot getPixels() if a scanline decoder has been created\n");
526 return kInvalidParameters;
527 }
528
scroggo58421542015-04-01 11:25:20 -0700529 if (!this->handleRewind()) {
halcanarya096d7a2015-03-27 12:16:53 -0700530 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700531 }
532 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
533 return kInvalidScale;
534 }
535 if (!conversion_possible(requestedInfo, this->getInfo())) {
536 return kInvalidConversion;
537 }
538
msarett9e43cab2015-04-29 07:38:43 -0700539 // Note that ctable and ctableCount may be modified if there is a color table
scroggo05245902015-03-25 11:11:52 -0700540 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
msarett9e43cab2015-04-29 07:38:43 -0700541 options, ctable, ctableCount);
msarett438b2ad2015-04-09 12:43:10 -0700542
scroggo05245902015-03-25 11:11:52 -0700543 if (result != kSuccess) {
544 return result;
545 }
546
547 // FIXME: Could we use the return value of setjmp to specify the type of
548 // error?
549 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700550 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700551 return kInvalidInput;
552 }
553
554 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
555 SkAutoMalloc storage;
556 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800557 const int width = requestedInfo.width();
558 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700559 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
scroggof24f2242015-03-03 08:59:20 -0800560 const size_t rowBytes = width * bpp;
561
562 storage.reset(width * height * bpp);
563 uint8_t* const base = static_cast<uint8_t*>(storage.get());
564
scroggo05245902015-03-25 11:11:52 -0700565 for (int i = 0; i < fNumberPasses; i++) {
scroggof24f2242015-03-03 08:59:20 -0800566 uint8_t* row = base;
567 for (int y = 0; y < height; y++) {
568 uint8_t* bmRow = row;
569 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
570 row += rowBytes;
571 }
572 }
573
574 // Now swizzle it.
575 uint8_t* row = base;
576 for (int y = 0; y < height; y++) {
scroggo05245902015-03-25 11:11:52 -0700577 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800578 row += rowBytes;
579 }
580 } else {
scroggo05245902015-03-25 11:11:52 -0700581 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800582 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
583 for (int y = 0; y < requestedInfo.height(); y++) {
584 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo05245902015-03-25 11:11:52 -0700585 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800586 }
587 }
588
scroggo05245902015-03-25 11:11:52 -0700589 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
590 // scanline decoding, but we could do it here. Alternatively, we could do
591 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800592
scroggo05245902015-03-25 11:11:52 -0700593 this->finish();
scroggof24f2242015-03-03 08:59:20 -0800594 return kSuccess;
595}
scroggo05245902015-03-25 11:11:52 -0700596
597void SkPngCodec::finish() {
598 if (setjmp(png_jmpbuf(fPng_ptr))) {
599 // We've already read all the scanlines. This is a success.
600 return;
601 }
602 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
603 png_read_end(fPng_ptr, fInfo_ptr);
604}
605
606class SkPngScanlineDecoder : public SkScanlineDecoder {
607public:
608 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
609 : INHERITED(dstInfo)
610 , fCodec(codec)
611 , fHasAlpha(false)
612 {
613 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
614 fSrcRow = static_cast<uint8_t*>(fStorage.get());
615 }
616
mtklein36352bf2015-03-25 18:17:31 -0700617 SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700618 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700619 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700620 return SkImageGenerator::kInvalidInput;
621 }
622
623 for (int i = 0; i < count; i++) {
624 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
625 fCodec->fSwizzler->setDstRow(dst);
626 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
627 dst = SkTAddOffset<void>(dst, rowBytes);
628 }
629 return SkImageGenerator::kSuccess;
630 }
631
mtklein36352bf2015-03-25 18:17:31 -0700632 SkImageGenerator::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700633 // FIXME: Could we use the return value of setjmp to specify the type of
634 // error?
635 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700636 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700637 return SkImageGenerator::kInvalidInput;
638 }
emmaleer7dc91902015-05-27 08:49:04 -0700639 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
640 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
641 //as png_read_rows has it's own loop which calls png_read_row count times.
642 for (int i = 0; i < count; i++) {
643 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
644 }
scroggo05245902015-03-25 11:11:52 -0700645 return SkImageGenerator::kSuccess;
646 }
647
mtklein36352bf2015-03-25 18:17:31 -0700648 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700649
650private:
651 SkPngCodec* fCodec; // Unowned.
652 bool fHasAlpha;
653 SkAutoMalloc fStorage;
654 uint8_t* fSrcRow;
655
656 typedef SkScanlineDecoder INHERITED;
657};
658
emmaleer0a4c3cb2015-06-22 10:40:21 -0700659
660class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
661public:
662 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
663 : INHERITED(dstInfo)
664 , fCodec(codec)
665 , fHasAlpha(false)
666 , fCurrentRow(0)
667 , fHeight(dstInfo.height())
668 , fSrcRowBytes(dstInfo.minRowBytes())
669 , fRewindNeeded(false)
670 {
671 fGarbageRow.reset(fSrcRowBytes);
672 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
673 }
674
675 SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
676 //rewind stream if have previously called onGetScanlines,
677 //since we need entire progressive image to get scanlines
678 if (fRewindNeeded) {
679 if(false == fCodec->handleRewind()) {
680 return SkImageGenerator::kCouldNotRewind;
681 }
682 } else {
683 fRewindNeeded = true;
684 }
685 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
686 SkCodecPrintf("setjmp long jump!\n");
687 return SkImageGenerator::kInvalidInput;
688 }
689 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
690 SkAutoMalloc storage(count * fSrcRowBytes);
691 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
692 uint8_t* srcRow;
693 for (int i = 0; i < number_passes; i++) {
694 //read rows we planned to skip into garbage row
695 for (int y = 0; y < fCurrentRow; y++){
696 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
697 }
698 //read rows we care about into buffer
699 srcRow = storagePtr;
700 for (int y = 0; y < count; y++) {
701 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
702 srcRow += fSrcRowBytes;
703 }
704 //read rows we don't want into garbage buffer
705 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
706 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
707 }
708 }
709 //swizzle the rows we care about
710 srcRow = storagePtr;
711 for (int y = 0; y < count; y++) {
712 fCodec->fSwizzler->setDstRow(dst);
713 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow));
714 dst = SkTAddOffset<void>(dst, dstRowBytes);
715 srcRow += fSrcRowBytes;
716 }
717 fCurrentRow += count;
718 return SkImageGenerator::kSuccess;
719 }
720
721 SkImageGenerator::Result onSkipScanlines(int count) override {
722 //when ongetScanlines is called it will skip to fCurrentRow
723 fCurrentRow += count;
724 return SkImageGenerator::kSuccess;
725 }
726
emmaleer0a4c3cb2015-06-22 10:40:21 -0700727 bool onReallyHasAlpha() const override { return fHasAlpha; }
728
729private:
730 SkPngCodec* fCodec; // Unowned.
731 bool fHasAlpha;
732 int fCurrentRow;
733 int fHeight;
734 size_t fSrcRowBytes;
735 bool fRewindNeeded;
736 SkAutoMalloc fGarbageRow;
737 uint8_t* fGarbageRowPtr;
738
739
740
msarettc0e80c12015-07-01 06:50:35 -0700741
emmaleer0a4c3cb2015-06-22 10:40:21 -0700742
743 typedef SkScanlineDecoder INHERITED;
744};
745
746
msarett9e43cab2015-04-29 07:38:43 -0700747SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
748 const Options& options, SkPMColor ctable[], int* ctableCount) {
scroggo58421542015-04-01 11:25:20 -0700749 if (!this->handleRewind()) {
750 return NULL;
751 }
752
scroggo05245902015-03-25 11:11:52 -0700753 // Check to see if scaling was requested.
754 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
755 return NULL;
756 }
757
758 if (!conversion_possible(dstInfo, this->getInfo())) {
scroggo230d4ac2015-03-26 07:15:55 -0700759 SkCodecPrintf("no conversion possible\n");
scroggo05245902015-03-25 11:11:52 -0700760 return NULL;
761 }
762
763 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
764 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
765 // be at least dstInfo.minRowBytes.
msarett9e43cab2015-04-29 07:38:43 -0700766 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
767 ctableCount) != kSuccess) {
scroggo230d4ac2015-03-26 07:15:55 -0700768 SkCodecPrintf("failed to initialize the swizzler.\n");
scroggo05245902015-03-25 11:11:52 -0700769 return NULL;
770 }
771
772 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
773 if (fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700774 // interlaced image
775 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this));
scroggo05245902015-03-25 11:11:52 -0700776 }
777
778 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
779}
780