blob: a5d38343138acf1718bb4fc56320c71f760f9a52 [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.
msarett438b2ad2015-04-09 12:43:10 -0700123bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, 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 */
180 int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
181 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() {
msarettc0e80c12015-07-01 06:50:35 -0700377 // First, ensure that the scanline decoder is left in a finished state.
378 SkAutoTDelete<SkScanlineDecoder> decoder(this->detachScanlineDecoder());
379 if (NULL != decoder) {
380 this->finish();
381 }
382
scroggo3eada2a2015-04-01 09:33:23 -0700383 this->destroyReadStruct();
384}
385
386void SkPngCodec::destroyReadStruct() {
387 if (fPng_ptr) {
388 // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
389 SkASSERT(fInfo_ptr);
390 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
391 fPng_ptr = NULL;
392 fInfo_ptr = NULL;
393 }
scroggof24f2242015-03-03 08:59:20 -0800394}
395
396///////////////////////////////////////////////////////////////////////////////
397// Getting the pixels
398///////////////////////////////////////////////////////////////////////////////
399
msaretteed039b2015-03-18 11:11:19 -0700400static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
scroggof24f2242015-03-03 08:59:20 -0800401 // TODO: Support other conversions
msaretteed039b2015-03-18 11:11:19 -0700402 if (dst.profileType() != src.profileType()) {
scroggof24f2242015-03-03 08:59:20 -0800403 return false;
404 }
msarett438b2ad2015-04-09 12:43:10 -0700405
406 // Check for supported alpha types
407 if (src.alphaType() != dst.alphaType()) {
408 if (kOpaque_SkAlphaType == src.alphaType()) {
409 // If the source is opaque, we must decode to opaque
410 return false;
411 }
412
413 // The source is not opaque
414 switch (dst.alphaType()) {
415 case kPremul_SkAlphaType:
416 case kUnpremul_SkAlphaType:
417 // The source is not opaque, so either of these is okay
418 break;
419 default:
420 // We cannot decode a non-opaque image to opaque (or unknown)
421 return false;
422 }
scroggof24f2242015-03-03 08:59:20 -0800423 }
msarett438b2ad2015-04-09 12:43:10 -0700424 // Check for supported color types
425 switch (dst.colorType()) {
msarett438b2ad2015-04-09 12:43:10 -0700426 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 }
scroggo6f29a3c2015-07-07 06:09:08 -0700444 fNumberPasses = png_set_interlace_handling(fPng_ptr);
445 png_read_update_info(fPng_ptr, fInfo_ptr);
scroggof24f2242015-03-03 08:59:20 -0800446
scroggo05245902015-03-25 11:11:52 -0700447 // Set to the default before calling decodePalette, which may change it.
448 fReallyHasAlpha = false;
scroggo6f29a3c2015-07-07 06:09:08 -0700449
450 //srcColorType was determined in readHeader() which determined png color type
451 const SkColorType srcColorType = this->getInfo().colorType();
452
453 switch (srcColorType) {
454 case kIndex_8_SkColorType:
455 //decode palette to Skia format
456 fSrcConfig = SkSwizzler::kIndex;
457 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), fBitDepth,
458 ctableCount)) {
459 return kInvalidInput;
460 }
461 break;
462 case kGray_8_SkColorType:
463 fSrcConfig = SkSwizzler::kGray;
464 break;
465 case kN32_SkColorType:
466 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
467 fSrcConfig = SkSwizzler::kRGBX;
468 } else {
469 fSrcConfig = SkSwizzler::kRGBA;
470 }
471 break;
472 default:
473 //would have exited before now if the colorType was supported by png
474 SkASSERT(false);
475 }
msarett9e43cab2015-04-29 07:38:43 -0700476
477 // Copy the color table to the client if they request kIndex8 mode
478 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
479
480 // Create the swizzler. SkPngCodec retains ownership of the color table.
scroggo05245902015-03-25 11:11:52 -0700481 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
482 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
483 dst, rowBytes, options.fZeroInitialized));
484 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800485 // FIXME: CreateSwizzler could fail for another reason.
486 return kUnimplemented;
487 }
scroggo05245902015-03-25 11:11:52 -0700488 return kSuccess;
489}
490
scroggo6f29a3c2015-07-07 06:09:08 -0700491
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;
scroggo6f29a3c2015-07-07 06:09:08 -0700507 if (read_header(this->stream(), &png_ptr, &info_ptr, NULL, NULL)) {
scroggo58421542015-04-01 11:25:20 -0700508 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) {
scroggo6f29a3c2015-07-07 06:09:08 -0700523 if (!conversion_possible(requestedInfo, this->getInfo())) {
524 return kInvalidConversion;
525 }
msarettc0e80c12015-07-01 06:50:35 -0700526 // Do not allow a regular decode if the caller has asked for a scanline decoder
527 if (NULL != this->scanlineDecoder()) {
528 SkCodecPrintf("cannot getPixels() if a scanline decoder has been created\n");
529 return kInvalidParameters;
530 }
scroggo05245902015-03-25 11:11:52 -0700531 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
532 return kInvalidScale;
533 }
scroggo6f29a3c2015-07-07 06:09:08 -0700534 if (!this->handleRewind()) {
535 return kCouldNotRewind;
scroggo05245902015-03-25 11:11:52 -0700536 }
537
msarett9e43cab2015-04-29 07:38:43 -0700538 // Note that ctable and ctableCount may be modified if there is a color table
scroggo05245902015-03-25 11:11:52 -0700539 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
msarett9e43cab2015-04-29 07:38:43 -0700540 options, ctable, ctableCount);
scroggo05245902015-03-25 11:11:52 -0700541 if (result != kSuccess) {
542 return result;
543 }
scroggo05245902015-03-25 11:11:52 -0700544 // FIXME: Could we use the return value of setjmp to specify the type of
545 // error?
546 if (setjmp(png_jmpbuf(fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700547 SkCodecPrintf("setjmp long jump!\n");
scroggo05245902015-03-25 11:11:52 -0700548 return kInvalidInput;
549 }
550
551 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
552 SkAutoMalloc storage;
553 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800554 const int width = requestedInfo.width();
555 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700556 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
scroggof24f2242015-03-03 08:59:20 -0800557 const size_t rowBytes = width * bpp;
558
559 storage.reset(width * height * bpp);
560 uint8_t* const base = static_cast<uint8_t*>(storage.get());
561
scroggo05245902015-03-25 11:11:52 -0700562 for (int i = 0; i < fNumberPasses; i++) {
scroggof24f2242015-03-03 08:59:20 -0800563 uint8_t* row = base;
564 for (int y = 0; y < height; y++) {
565 uint8_t* bmRow = row;
566 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
567 row += rowBytes;
568 }
569 }
570
571 // Now swizzle it.
572 uint8_t* row = base;
573 for (int y = 0; y < height; y++) {
scroggo05245902015-03-25 11:11:52 -0700574 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800575 row += rowBytes;
576 }
577 } else {
scroggo05245902015-03-25 11:11:52 -0700578 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800579 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
580 for (int y = 0; y < requestedInfo.height(); y++) {
581 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo05245902015-03-25 11:11:52 -0700582 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800583 }
584 }
585
scroggo05245902015-03-25 11:11:52 -0700586 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
587 // scanline decoding, but we could do it here. Alternatively, we could do
588 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800589
scroggo05245902015-03-25 11:11:52 -0700590 this->finish();
scroggof24f2242015-03-03 08:59:20 -0800591 return kSuccess;
592}
scroggo05245902015-03-25 11:11:52 -0700593
594void SkPngCodec::finish() {
595 if (setjmp(png_jmpbuf(fPng_ptr))) {
596 // We've already read all the scanlines. This is a success.
597 return;
598 }
599 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
600 png_read_end(fPng_ptr, fInfo_ptr);
601}
602
603class SkPngScanlineDecoder : public SkScanlineDecoder {
604public:
605 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
606 : INHERITED(dstInfo)
607 , fCodec(codec)
608 , fHasAlpha(false)
609 {
610 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
611 fSrcRow = static_cast<uint8_t*>(fStorage.get());
612 }
613
scroggoeb602a52015-07-09 08:16:03 -0700614 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
scroggo05245902015-03-25 11:11:52 -0700615 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700616 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700617 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700618 }
619
620 for (int i = 0; i < count; i++) {
621 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
622 fCodec->fSwizzler->setDstRow(dst);
623 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
624 dst = SkTAddOffset<void>(dst, rowBytes);
625 }
scroggoeb602a52015-07-09 08:16:03 -0700626 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700627 }
628
scroggoeb602a52015-07-09 08:16:03 -0700629 SkCodec::Result onSkipScanlines(int count) override {
scroggo05245902015-03-25 11:11:52 -0700630 // FIXME: Could we use the return value of setjmp to specify the type of
631 // error?
632 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
scroggo230d4ac2015-03-26 07:15:55 -0700633 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700634 return SkCodec::kInvalidInput;
scroggo05245902015-03-25 11:11:52 -0700635 }
emmaleer7dc91902015-05-27 08:49:04 -0700636 //there is a potential tradeoff of memory vs speed created by putting this in a loop.
637 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
638 //as png_read_rows has it's own loop which calls png_read_row count times.
639 for (int i = 0; i < count; i++) {
640 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
641 }
scroggoeb602a52015-07-09 08:16:03 -0700642 return SkCodec::kSuccess;
scroggo05245902015-03-25 11:11:52 -0700643 }
644
mtklein36352bf2015-03-25 18:17:31 -0700645 bool onReallyHasAlpha() const override { return fHasAlpha; }
scroggo05245902015-03-25 11:11:52 -0700646
647private:
648 SkPngCodec* fCodec; // Unowned.
649 bool fHasAlpha;
650 SkAutoMalloc fStorage;
651 uint8_t* fSrcRow;
652
653 typedef SkScanlineDecoder INHERITED;
654};
655
emmaleer0a4c3cb2015-06-22 10:40:21 -0700656
657class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
658public:
659 SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
660 : INHERITED(dstInfo)
661 , fCodec(codec)
662 , fHasAlpha(false)
663 , fCurrentRow(0)
664 , fHeight(dstInfo.height())
emmaleer0a4c3cb2015-06-22 10:40:21 -0700665 , fRewindNeeded(false)
666 {
scroggo6f29a3c2015-07-07 06:09:08 -0700667 fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700668 fGarbageRow.reset(fSrcRowBytes);
669 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
670 }
671
scroggoeb602a52015-07-09 08:16:03 -0700672 SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700673 //rewind stream if have previously called onGetScanlines,
674 //since we need entire progressive image to get scanlines
675 if (fRewindNeeded) {
676 if(false == fCodec->handleRewind()) {
scroggoeb602a52015-07-09 08:16:03 -0700677 return SkCodec::kCouldNotRewind;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700678 }
679 } else {
680 fRewindNeeded = true;
681 }
682 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
683 SkCodecPrintf("setjmp long jump!\n");
scroggoeb602a52015-07-09 08:16:03 -0700684 return SkCodec::kInvalidInput;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700685 }
686 const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
687 SkAutoMalloc storage(count * fSrcRowBytes);
688 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
689 uint8_t* srcRow;
690 for (int i = 0; i < number_passes; i++) {
691 //read rows we planned to skip into garbage row
692 for (int y = 0; y < fCurrentRow; y++){
693 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
694 }
695 //read rows we care about into buffer
696 srcRow = storagePtr;
697 for (int y = 0; y < count; y++) {
698 png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
699 srcRow += fSrcRowBytes;
700 }
701 //read rows we don't want into garbage buffer
702 for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
703 png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
704 }
705 }
706 //swizzle the rows we care about
707 srcRow = storagePtr;
708 for (int y = 0; y < count; y++) {
709 fCodec->fSwizzler->setDstRow(dst);
710 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow));
711 dst = SkTAddOffset<void>(dst, dstRowBytes);
712 srcRow += fSrcRowBytes;
713 }
714 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700715 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700716 }
717
scroggoeb602a52015-07-09 08:16:03 -0700718 SkCodec::Result onSkipScanlines(int count) override {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700719 //when ongetScanlines is called it will skip to fCurrentRow
720 fCurrentRow += count;
scroggoeb602a52015-07-09 08:16:03 -0700721 return SkCodec::kSuccess;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700722 }
723
emmaleer0a4c3cb2015-06-22 10:40:21 -0700724 bool onReallyHasAlpha() const override { return fHasAlpha; }
725
726private:
727 SkPngCodec* fCodec; // Unowned.
728 bool fHasAlpha;
729 int fCurrentRow;
730 int fHeight;
731 size_t fSrcRowBytes;
732 bool fRewindNeeded;
733 SkAutoMalloc fGarbageRow;
734 uint8_t* fGarbageRowPtr;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700735
736 typedef SkScanlineDecoder INHERITED;
737};
738
739
msarett9e43cab2015-04-29 07:38:43 -0700740SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
741 const Options& options, SkPMColor ctable[], int* ctableCount) {
scroggo6f29a3c2015-07-07 06:09:08 -0700742 if (!conversion_possible(dstInfo, this->getInfo())) {
743 SkCodecPrintf("no conversion possible\n");
scroggo58421542015-04-01 11:25:20 -0700744 return NULL;
745 }
scroggo05245902015-03-25 11:11:52 -0700746 // Check to see if scaling was requested.
747 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
748 return NULL;
749 }
scroggo6f29a3c2015-07-07 06:09:08 -0700750 if (!this->handleRewind()) {
scroggo05245902015-03-25 11:11:52 -0700751 return NULL;
752 }
753
754 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
755 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
756 // be at least dstInfo.minRowBytes.
msarett9e43cab2015-04-29 07:38:43 -0700757 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
758 ctableCount) != kSuccess) {
scroggo230d4ac2015-03-26 07:15:55 -0700759 SkCodecPrintf("failed to initialize the swizzler.\n");
scroggo05245902015-03-25 11:11:52 -0700760 return NULL;
761 }
762
763 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
764 if (fNumberPasses > 1) {
emmaleer0a4c3cb2015-06-22 10:40:21 -0700765 // interlaced image
766 return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this));
scroggo05245902015-03-25 11:11:52 -0700767 }
768
769 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
770}
771