blob: f42af38fea707787f0fc760dba74d5486acf0572 [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"
14#include "SkSize.h"
15#include "SkStream.h"
16#include "SkSwizzler.h"
17
18///////////////////////////////////////////////////////////////////////////////
19// Helper macros
20///////////////////////////////////////////////////////////////////////////////
21
22#ifndef png_jmpbuf
23# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
24#endif
25
26/* These were dropped in libpng >= 1.4 */
27#ifndef png_infopp_NULL
28 #define png_infopp_NULL NULL
29#endif
30
31#ifndef png_bytepp_NULL
32 #define png_bytepp_NULL NULL
33#endif
34
35#ifndef int_p_NULL
36 #define int_p_NULL NULL
37#endif
38
39#ifndef png_flush_ptr_NULL
40 #define png_flush_ptr_NULL NULL
41#endif
42
43///////////////////////////////////////////////////////////////////////////////
44// Callback functions
45///////////////////////////////////////////////////////////////////////////////
46
47static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
48 SkDebugf("------ png error %s\n", msg);
49 longjmp(png_jmpbuf(png_ptr), 1);
50}
51
52static void sk_read_fn(png_structp png_ptr, png_bytep data,
53 png_size_t length) {
54 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
55 const size_t bytes = stream->read(data, length);
56 if (bytes != length) {
57 // FIXME: We want to report the fact that the stream was truncated.
58 // One way to do that might be to pass a enum to longjmp so setjmp can
59 // specify the failure.
60 png_error(png_ptr, "Read Error!");
61 }
62}
63
64///////////////////////////////////////////////////////////////////////////////
65// Helpers
66///////////////////////////////////////////////////////////////////////////////
67
68class AutoCleanPng : public SkNoncopyable {
69public:
70 AutoCleanPng(png_structp png_ptr)
71 : fPng_ptr(png_ptr)
72 , fInfo_ptr(NULL) {}
73
74 ~AutoCleanPng() {
75 // fInfo_ptr will never be non-NULL unless fPng_ptr is.
76 if (fPng_ptr) {
77 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
78 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
79 }
80 }
81
82 void setInfoPtr(png_infop info_ptr) {
83 SkASSERT(NULL == fInfo_ptr);
84 fInfo_ptr = info_ptr;
85 }
86
87 void detach() {
88 fPng_ptr = NULL;
89 fInfo_ptr = NULL;
90 }
91
92private:
93 png_structp fPng_ptr;
94 png_infop fInfo_ptr;
95};
96#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
97
98// call only if color_type is PALETTE. Returns true if the ctable has alpha
99static bool has_transparency_in_palette(png_structp png_ptr,
100 png_infop info_ptr) {
101 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
102 return false;
103 }
104
105 png_bytep trans;
106 int num_trans;
107 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
108 return num_trans > 0;
109}
110
111// Method for coverting to either an SkPMColor or a similarly packed
112// unpremultiplied color.
113typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
114
115// Note: SkColorTable claims to store SkPMColors, which is not necessarily
116// the case here.
117SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr,
118 bool premultiply, SkAlphaType* outAlphaType) {
119 SkASSERT(outAlphaType != NULL);
120 int numPalette;
121 png_colorp palette;
122 png_bytep trans;
123
124 if (!png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette)) {
125 return NULL;
126 }
127
128 /* BUGGY IMAGE WORKAROUND
129
130 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
131 which is a problem since we use the byte as an index. To work around this we grow
132 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
133 */
134 const int colorCount = numPalette + (numPalette < 256);
135 // Note: These are not necessarily SkPMColors.
136 SkPMColor colorStorage[256]; // worst-case storage
137 SkPMColor* colorPtr = colorStorage;
138
139 int numTrans;
140 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
141 png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
142 } else {
143 numTrans = 0;
144 }
145
146 // check for bad images that might make us crash
147 if (numTrans > numPalette) {
148 numTrans = numPalette;
149 }
150
151 int index = 0;
152 int transLessThanFF = 0;
153
154 // Choose which function to use to create the color table. If the final destination's
155 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
156 PackColorProc proc;
157 if (premultiply) {
158 proc = &SkPreMultiplyARGB;
159 } else {
160 proc = &SkPackARGB32NoCheck;
161 }
162 for (; index < numTrans; index++) {
163 transLessThanFF |= (int)*trans - 0xFF;
164 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
165 palette++;
166 }
167
168 if (transLessThanFF < 0) {
169 *outAlphaType = premultiply ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
170 } else {
171 *outAlphaType = kOpaque_SkAlphaType;
172 }
173
174 for (; index < numPalette; index++) {
175 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
176 palette++;
177 }
178
179 // see BUGGY IMAGE WORKAROUND comment above
180 if (numPalette < 256) {
181 *colorPtr = colorPtr[-1];
182 }
183
184 return SkNEW_ARGS(SkColorTable, (colorStorage, colorCount));
185}
186
187///////////////////////////////////////////////////////////////////////////////
188// Creation
189///////////////////////////////////////////////////////////////////////////////
190
191#define PNG_BYTES_TO_CHECK 4
192
193bool SkPngCodec::IsPng(SkStream* stream) {
194 char buf[PNG_BYTES_TO_CHECK];
195 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
196 return false;
197 }
198 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
199 return false;
200 }
201 return true;
202}
203
204SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
205 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
206 // FIXME: Allow silencing warnings.
207 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
208 sk_error_fn, NULL);
209 if (!png_ptr) {
210 return NULL;
211 }
212
213 AutoCleanPng autoClean(png_ptr);
214
215 png_infop info_ptr = png_create_info_struct(png_ptr);
216 if (info_ptr == NULL) {
217 return NULL;
218 }
219
220 autoClean.setInfoPtr(info_ptr);
221
222 // FIXME: Could we use the return value of setjmp to specify the type of
223 // error?
224 if (setjmp(png_jmpbuf(png_ptr))) {
225 return NULL;
226 }
227
228 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
229
230 // FIXME: This is where the old code hooks up the Peeker. Does it need to
231 // be set this early? (i.e. where are the user chunks? early in the stream,
232 // potentially?)
233 // If it does, we need to figure out a way to set it here.
234
235 // The call to png_read_info() gives us all of the information from the
236 // PNG file before the first IDAT (image data chunk).
237 png_read_info(png_ptr, info_ptr);
238 png_uint_32 origWidth, origHeight;
239 int bitDepth, colorType;
240 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
241 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
242
243 // sanity check for size
244 {
245 int64_t size = sk_64_mul(origWidth, origHeight);
246 // now check that if we are 4-bytes per pixel, we also don't overflow
247 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
248 return NULL;
249 }
250 }
251
252 // Tell libpng to strip 16 bit/color files down to 8 bits/color
253 if (bitDepth == 16) {
254 png_set_strip_16(png_ptr);
255 }
256#ifdef PNG_READ_PACK_SUPPORTED
257 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
258 // byte into separate bytes (useful for paletted and grayscale images).
259 if (bitDepth < 8) {
260 png_set_packing(png_ptr);
261 }
262#endif
263 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
264 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
265 png_set_expand_gray_1_2_4_to_8(png_ptr);
266 }
267
268
269 // Now determine the default SkColorType and SkAlphaType.
270 SkColorType skColorType;
271 SkAlphaType skAlphaType;
272 switch (colorType) {
273 case PNG_COLOR_TYPE_PALETTE:
274 // Technically, this is true of the data, but I don't think we want
275 // to support it.
276 // skColorType = kIndex8_SkColorType;
277 skColorType = kN32_SkColorType;
278 skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
279 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
280 break;
281 case PNG_COLOR_TYPE_GRAY:
282 if (false) {
283 // FIXME: Is this the wrong default behavior? This means if the
284 // caller supplies the info we gave them, they'll get Alpha 8.
285 skColorType = kAlpha_8_SkColorType;
286 // FIXME: Strangely, the canonical type for Alpha 8 is Premul.
287 skAlphaType = kPremul_SkAlphaType;
288 } else {
289 skColorType = kN32_SkColorType;
290 skAlphaType = kOpaque_SkAlphaType;
291 }
292 break;
293 default:
294 // Note: This *almost* mimics the code in SkImageDecoder_libpng.
295 // has_transparency_in_palette makes an additional check - whether
296 // numTrans is greater than 0. Why does the other code not make that
297 // check?
298 if (has_transparency_in_palette(png_ptr, info_ptr)
299 || PNG_COLOR_TYPE_RGB_ALPHA == colorType
300 || PNG_COLOR_TYPE_GRAY_ALPHA == colorType)
301 {
302 skAlphaType = kUnpremul_SkAlphaType;
303 } else {
304 skAlphaType = kOpaque_SkAlphaType;
305 }
306 skColorType = kN32_SkColorType;
307 break;
308 }
309
310 {
311 // FIXME: Again, this block needs to go into onGetPixels.
312 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType;
313
314 // Unless the user is requesting A8, convert a grayscale image into RGB.
315 // GRAY_ALPHA will always be converted to RGB
316 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
317 png_set_gray_to_rgb(png_ptr);
318 }
319
320 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
321 // FIXME: It seems like we could just use RGB as the SrcConfig here.
322 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
323 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
324 }
325 }
326
327 // FIXME: Also need to check for sRGB (skbug.com/3471).
328
329 SkImageInfo info = SkImageInfo::Make(origWidth, origHeight, skColorType,
330 skAlphaType);
331 SkCodec* codec = SkNEW_ARGS(SkPngCodec, (info, stream, png_ptr, info_ptr));
332 autoClean.detach();
333 return codec;
334}
335
336SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
337 png_structp png_ptr, png_infop info_ptr)
338 : INHERITED(info, stream)
339 , fPng_ptr(png_ptr)
340 , fInfo_ptr(info_ptr) {}
341
342SkPngCodec::~SkPngCodec() {
343 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
344}
345
346///////////////////////////////////////////////////////////////////////////////
347// Getting the pixels
348///////////////////////////////////////////////////////////////////////////////
349
scroggof24f2242015-03-03 08:59:20 -0800350static bool conversion_possible(const SkImageInfo& A, const SkImageInfo& B) {
351 // TODO: Support other conversions
352 if (A.colorType() != B.colorType()) {
353 return false;
354 }
355 if (A.profileType() != B.profileType()) {
356 return false;
357 }
358 if (A.alphaType() == B.alphaType()) {
359 return true;
360 }
361 return premul_and_unpremul(A.alphaType(), B.alphaType())
362 || premul_and_unpremul(B.alphaType(), A.alphaType());
363}
364
365SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
366 size_t rowBytes, SkPMColor ctable[],
367 int* ctableCount) {
368 if (!this->rewindIfNeeded()) {
369 return kCouldNotRewind;
370 }
371 if (requestedInfo.dimensions() != this->getOriginalInfo().dimensions()) {
372 return kInvalidScale;
373 }
374 if (!conversion_possible(requestedInfo, this->getOriginalInfo())) {
375 return kInvalidConversion;
376 }
377
378 SkBitmap decodedBitmap;
379 // If installPixels would have failed, getPixels should have failed before
380 // calling onGetPixels.
381 SkAssertResult(decodedBitmap.installPixels(requestedInfo, dst, rowBytes));
382
383 // Initialize all non-trivial objects before setjmp.
384 SkAutoTUnref<SkColorTable> colorTable;
385 SkAutoTDelete<SkSwizzler> swizzler;
386 SkAutoMalloc storage; // Scratch memory for pre-swizzled rows.
387
388 // FIXME: Could we use the return value of setjmp to specify the type of
389 // error?
390 if (setjmp(png_jmpbuf(fPng_ptr))) {
391 SkDebugf("setjmp long jump!\n");
392 return kInvalidInput;
393 }
394
395 // FIXME: We already retrieved this information. Store it in SkPngCodec?
396 png_uint_32 origWidth, origHeight;
397 int bitDepth, pngColorType, interlaceType;
398 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
399 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
400
401 const int numberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
402 png_set_interlace_handling(fPng_ptr) : 1;
403
404 SkSwizzler::SrcConfig sc;
405 bool reallyHasAlpha = false;
406 if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
407 sc = SkSwizzler::kIndex;
408 SkAlphaType at = requestedInfo.alphaType();
409 colorTable.reset(decode_palette(fPng_ptr, fInfo_ptr,
410 kPremul_SkAlphaType == at,
411 &at));
412 if (!colorTable) {
413 return kInvalidInput;
414 }
415
416 reallyHasAlpha = (at != kOpaque_SkAlphaType);
417
418 if (at != requestedInfo.alphaType()) {
419 // It turns out the image is opaque.
420 SkASSERT(kOpaque_SkAlphaType == at);
421 }
422 } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
423 // Note: we check the destination, since otherwise we would have
424 // told png to upscale.
425 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
426 sc = SkSwizzler::kGray;
427 } else if (this->getOriginalInfo().alphaType() == kOpaque_SkAlphaType) {
428 sc = SkSwizzler::kRGBX;
429 } else {
430 sc = SkSwizzler::kRGBA;
431 }
432 const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL;
433 // TODO: Support skipZeroes.
434 swizzler.reset(SkSwizzler::CreateSwizzler(sc, colors, requestedInfo,
435 dst, rowBytes, false));
436 if (!swizzler) {
437 // FIXME: CreateSwizzler could fail for another reason.
438 return kUnimplemented;
439 }
440
441 // FIXME: Here is where we should likely insert some of the modifications
442 // made in the factory.
443 png_read_update_info(fPng_ptr, fInfo_ptr);
444
445 if (numberPasses > 1) {
446 const int width = requestedInfo.width();
447 const int height = requestedInfo.height();
448 const int bpp = SkSwizzler::BytesPerPixel(sc);
449 const size_t rowBytes = width * bpp;
450
451 storage.reset(width * height * bpp);
452 uint8_t* const base = static_cast<uint8_t*>(storage.get());
453
454 for (int i = 0; i < numberPasses; i++) {
455 uint8_t* row = base;
456 for (int y = 0; y < height; y++) {
457 uint8_t* bmRow = row;
458 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
459 row += rowBytes;
460 }
461 }
462
463 // Now swizzle it.
464 uint8_t* row = base;
465 for (int y = 0; y < height; y++) {
msarett74114382015-03-16 11:55:18 -0700466 reallyHasAlpha |= !SkSwizzler::IsOpaque(swizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800467 row += rowBytes;
468 }
469 } else {
470 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(sc));
471 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
472 for (int y = 0; y < requestedInfo.height(); y++) {
473 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
msarett74114382015-03-16 11:55:18 -0700474 reallyHasAlpha |= !SkSwizzler::IsOpaque(swizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800475 }
476 }
477
478 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
479 png_read_end(fPng_ptr, fInfo_ptr);
480
481 // FIXME: do we need substituteTranspColor?
482
483 if (reallyHasAlpha && requestedInfo.alphaType() != kOpaque_SkAlphaType) {
484 // FIXME: We want to alert the caller. Is this the right way?
485 SkImageInfo* modInfo = const_cast<SkImageInfo*>(&requestedInfo);
486 *modInfo = requestedInfo.makeAlphaType(kOpaque_SkAlphaType);
487 }
488 return kSuccess;
489}