blob: cd09f5f980d42e0769131f8d716297a244207228 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +000011#include "SkImageEncoder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColor.h"
13#include "SkColorPriv.h"
14#include "SkDither.h"
15#include "SkMath.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000016#include "SkRTConf.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkScaledBitmapSampler.h"
18#include "SkStream.h"
19#include "SkTemplates.h"
20#include "SkUtils.h"
epoger@google.com4ce738b2012-11-16 18:44:18 +000021#include "transform_scanline.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022extern "C" {
23#include "png.h"
24}
25
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +000026/* 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
halcanary@google.comfed30372013-10-04 12:46:45 +000043#if defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000044#define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS false
45#else // !defined(SK_DEBUG)
46#define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true
halcanary@google.comfed30372013-10-04 12:46:45 +000047#endif // defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000048SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings,
49 "images.png.suppressDecoderWarnings",
50 DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS,
51 "Suppress most PNG warnings when calling image decode "
52 "functions.");
53
halcanary@google.comfed30372013-10-04 12:46:45 +000054
55
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000056class SkPNGImageIndex {
57public:
scroggo@google.comb5571b32013-09-25 21:34:24 +000058 SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr)
scroggo@google.comc70a3aa2013-07-18 20:03:15 +000059 : fStream(stream)
60 , fPng_ptr(png_ptr)
61 , fInfo_ptr(info_ptr)
62 , fConfig(SkBitmap::kNo_Config) {
63 SkASSERT(stream != NULL);
64 stream->ref();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000065 }
66 ~SkPNGImageIndex() {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +000067 if (NULL != fPng_ptr) {
68 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000069 }
70 }
71
scroggo@google.comb5571b32013-09-25 21:34:24 +000072 SkAutoTUnref<SkStreamRewindable> fStream;
73 png_structp fPng_ptr;
74 png_infop fInfo_ptr;
75 SkBitmap::Config fConfig;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000076};
77
reed@android.com8a1c16f2008-12-17 15:59:43 +000078class SkPNGImageDecoder : public SkImageDecoder {
79public:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000080 SkPNGImageDecoder() {
81 fImageIndex = NULL;
82 }
83 virtual Format getFormat() const SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 return kPNG_Format;
85 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +000086
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000087 virtual ~SkPNGImageDecoder() {
88 SkDELETE(fImageIndex);
89 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000090
reed@android.com8a1c16f2008-12-17 15:59:43 +000091protected:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000092#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +000093 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000094 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000095#endif
96 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
97
98private:
99 SkPNGImageIndex* fImageIndex;
100
101 bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000102 bool decodePalette(png_structp png_ptr, png_infop info_ptr,
103 bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
104 SkColorTable **colorTablep);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000105 bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
106 SkBitmap::Config *config, bool *hasAlpha,
scroggo@google.com8d239242013-10-01 17:27:15 +0000107 SkPMColor *theTranspColor);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000108
109 typedef SkImageDecoder INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110};
111
112#ifndef png_jmpbuf
113# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
114#endif
115
116#define PNG_BYTES_TO_CHECK 4
117
118/* Automatically clean up after throwing an exception */
119struct PNGAutoClean {
120 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
121 ~PNGAutoClean() {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000122 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 }
124private:
125 png_structp png_ptr;
126 png_infop info_ptr;
127};
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000130 SkStream* sk_stream = (SkStream*) png_get_io_ptr(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 size_t bytes = sk_stream->read(data, length);
132 if (bytes != length) {
133 png_error(png_ptr, "Read Error!");
134 }
135}
136
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000137#ifdef SK_BUILD_FOR_ANDROID
138static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
scroggo@google.comb5571b32013-09-25 21:34:24 +0000139 SkStreamRewindable* sk_stream = (SkStreamRewindable*) png_get_io_ptr(png_ptr);
scroggo@google.com4d213ab2013-08-28 13:08:54 +0000140 if (!sk_stream->rewind()) {
141 png_error(png_ptr, "Failed to rewind stream!");
142 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000143 (void)sk_stream->skip(offset);
144}
145#endif
146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
148 SkImageDecoder::Peeker* peeker =
149 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
150 // peek() returning true means continue decoding
151 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
152 1 : -1;
153}
154
155static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000156 SkDEBUGF(("------ png error %s\n", msg));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 longjmp(png_jmpbuf(png_ptr), 1);
158}
159
160static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
161 for (int i = 0; i < count; i++) {
162 uint8_t* tmp = storage;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000163 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 }
165}
166
167static bool pos_le(int value, int max) {
168 return value > 0 && value <= max;
169}
170
171static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
172 SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 bool reallyHasAlpha = false;
175
176 for (int y = bm->height() - 1; y >= 0; --y) {
177 SkPMColor* p = bm->getAddr32(0, y);
178 for (int x = bm->width() - 1; x >= 0; --x) {
179 if (match == *p) {
180 *p = 0;
181 reallyHasAlpha = true;
182 }
183 p += 1;
184 }
185 }
186 return reallyHasAlpha;
187}
188
reed@android.com3f1f06a2010-03-03 21:04:12 +0000189static bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
reed@android.comb6137c32009-07-29 20:56:52 +0000190 bool srcHasAlpha) {
reed@android.com3f1f06a2010-03-03 21:04:12 +0000191 switch (dstConfig) {
reed@android.comb6137c32009-07-29 20:56:52 +0000192 case SkBitmap::kARGB_8888_Config:
193 case SkBitmap::kARGB_4444_Config:
194 return true;
195 case SkBitmap::kRGB_565_Config:
196 // only return true if the src is opaque (since 565 is opaque)
197 return !srcHasAlpha;
198 default:
199 return false;
200 }
201}
202
203// call only if color_type is PALETTE. Returns true if the ctable has alpha
204static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
205 png_bytep trans;
206 int num_trans;
207
208 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
209 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
210 return num_trans > 0;
211 }
212 return false;
reed@android.com11344262009-07-08 20:09:23 +0000213}
214
halcanary@google.comfed30372013-10-04 12:46:45 +0000215void do_nothing_warning_fn(png_structp, png_const_charp) {
216 /* do nothing */
217}
218
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000219bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
220 png_infop *info_ptrp) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 /* Create and initialize the png_struct with the desired error handler
222 * functions. If you want to use the default stderr and longjump method,
223 * you can supply NULL for the last three parameters. We also supply the
224 * the compiler header file version, so that we know if the application
225 * was compiled with a compatible version of the library. */
halcanary@google.comfed30372013-10-04 12:46:45 +0000226
halcanary@google.comfed30372013-10-04 12:46:45 +0000227 png_error_ptr user_warning_fn =
228 (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;
229 /* NULL means to leave as default library behavior. */
halcanary@google.com2a103182013-10-14 12:49:15 +0000230 /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */
halcanary@google.comfed30372013-10-04 12:46:45 +0000231 /* To suppress warnings with a SK_DEBUG binary, set the
232 * environment variable "skia_images_png_suppressDecoderWarnings"
233 * to "true". Inside a program that links to skia:
234 * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */
halcanary@google.comfed30372013-10-04 12:46:45 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
halcanary@google.comfed30372013-10-04 12:46:45 +0000237 NULL, sk_error_fn, user_warning_fn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
239 if (png_ptr == NULL) {
240 return false;
241 }
halcanary@google.comfed30372013-10-04 12:46:45 +0000242
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000243 *png_ptrp = png_ptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 /* Allocate/initialize the memory for image information. */
246 png_infop info_ptr = png_create_info_struct(png_ptr);
247 if (info_ptr == NULL) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000248 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 return false;
250 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000251 *info_ptrp = info_ptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252
253 /* Set error handling if you are using the setjmp/longjmp method (this is
254 * the normal method of doing things with libpng). REQUIRED unless you
255 * set up your own error handlers in the png_create_read_struct() earlier.
256 */
257 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo@google.com5401cd02013-11-12 15:30:06 +0000258 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 return false;
260 }
261
262 /* If you are using replacement read functions, instead of calling
263 * png_init_io() here you would call:
264 */
265 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000266#ifdef SK_BUILD_FOR_ANDROID
267 png_set_seek_fn(png_ptr, sk_seek_fn);
268#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 /* where user_io_ptr is a structure you want available to the callbacks */
270 /* If we have already read some of the signature */
271// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
272
273 // hookup our peeker so we can see any user-chunks the caller may be interested in
274 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
275 if (this->getPeeker()) {
276 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
277 }
278
279 /* The call to png_read_info() gives us all of the information from the
280 * PNG file before the first IDAT (image data chunk). */
281 png_read_info(png_ptr, info_ptr);
282 png_uint_32 origWidth, origHeight;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000283 int bitDepth, colorType;
284 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
285 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286
287 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000288 if (bitDepth == 16) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 png_set_strip_16(png_ptr);
290 }
291 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
292 * byte into separate bytes (useful for paletted and grayscale images). */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000293 if (bitDepth < 8) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 png_set_packing(png_ptr);
295 }
296 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000297 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000298 png_set_expand_gray_1_2_4_to_8(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000300
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000301 return true;
302}
303
304bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
305 Mode mode) {
306 png_structp png_ptr;
307 png_infop info_ptr;
308
309 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
310 return false;
311 }
312
scroggo@google.comfeeca3c2013-11-12 14:38:41 +0000313 PNGAutoClean autoClean(png_ptr, info_ptr);
314
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000315 if (setjmp(png_jmpbuf(png_ptr))) {
316 return false;
317 }
318
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000319 png_uint_32 origWidth, origHeight;
320 int bitDepth, colorType, interlaceType;
321 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
322 &colorType, &interlaceType, int_p_NULL, int_p_NULL);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 SkBitmap::Config config;
325 bool hasAlpha = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
rmistry@google.comd6176b02012-08-23 18:14:13 +0000327
scroggo@google.com8d239242013-10-01 17:27:15 +0000328 if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 return false;
330 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 const int sampleSize = this->getSampleSize();
333 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000334 decodedBitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
djsollen@google.com446cf712014-02-19 21:45:35 +0000336 // we should communicate alphaType, even if we early-return in bounds-only-mode.
337 if (this->getRequireUnpremultipliedColors()) {
338 decodedBitmap->setAlphaType(kUnpremul_SkAlphaType);
339 }
340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
342 return true;
343 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000344
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 // from here down we are concerned with colortables and pixels
346
347 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
348 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
349 // draw lots faster if we can flag the bitmap has being opaque
350 bool reallyHasAlpha = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 SkColorTable* colorTable = NULL;
352
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000353 if (colorType == PNG_COLOR_TYPE_PALETTE) {
354 decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 SkAutoUnref aur(colorTable);
358
scroggo@google.combc69ce92013-07-09 15:45:14 +0000359 if (!this->allocPixelRef(decodedBitmap,
360 SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
361 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 SkAutoLockPixels alp(*decodedBitmap);
365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 /* Turn on interlace handling. REQUIRED if you are not using
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000367 * png_read_image(). To see how to handle interlacing passes,
368 * see the png_read_row() method below:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000370 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
371 png_set_interlace_handling(png_ptr) : 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372
373 /* Optional call to gamma correct and add the background to the palette
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000374 * and update info structure. REQUIRED if you are expecting libpng to
375 * update the palette for you (ie you selected such a transform above).
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 */
377 png_read_update_info(png_ptr, info_ptr);
378
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000379 if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
380 && 1 == sampleSize) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000381 if (SkBitmap::kA8_Config == config) {
382 // For an A8 bitmap, we assume there is an alpha for speed. It is
383 // possible the bitmap is opaque, but that is an unlikely use case
384 // since it would not be very interesting.
385 reallyHasAlpha = true;
386 // A8 is only allowed if the original was GRAY.
387 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
388 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 for (int i = 0; i < number_passes; i++) {
390 for (png_uint_32 y = 0; y < origHeight; y++) {
391 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000392 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 }
394 }
395 } else {
396 SkScaledBitmapSampler::SrcConfig sc;
397 int srcBytesPerPixel = 4;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000398
reed@android.com11344262009-07-08 20:09:23 +0000399 if (colorTable != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 sc = SkScaledBitmapSampler::kIndex;
401 srcBytesPerPixel = 1;
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000402 } else if (SkBitmap::kA8_Config == config) {
403 // A8 is only allowed if the original was GRAY.
404 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
405 sc = SkScaledBitmapSampler::kGray;
406 srcBytesPerPixel = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 } else if (hasAlpha) {
408 sc = SkScaledBitmapSampler::kRGBA;
409 } else {
410 sc = SkScaledBitmapSampler::kRGBX;
411 }
reed@android.com11344262009-07-08 20:09:23 +0000412
413 /* We have to pass the colortable explicitly, since we may have one
414 even if our decodedBitmap doesn't, due to the request that we
415 upscale png's palette to a direct model
416 */
417 SkAutoLockColors ctLock(colorTable);
scroggo@google.com8d239242013-10-01 17:27:15 +0000418 if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) {
reed@android.com862e91b2009-04-28 15:27:07 +0000419 return false;
420 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 const int height = decodedBitmap->height();
422
reed@android.com862e91b2009-04-28 15:27:07 +0000423 if (number_passes > 1) {
424 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
425 uint8_t* base = (uint8_t*)storage.get();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000426 size_t rowBytes = origWidth * srcBytesPerPixel;
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000427
reed@android.com862e91b2009-04-28 15:27:07 +0000428 for (int i = 0; i < number_passes; i++) {
429 uint8_t* row = base;
430 for (png_uint_32 y = 0; y < origHeight; y++) {
431 uint8_t* bmRow = row;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000432 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
433 row += rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000434 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 }
reed@android.com862e91b2009-04-28 15:27:07 +0000436 // now sample it
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000437 base += sampler.srcY0() * rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000438 for (int y = 0; y < height; y++) {
439 reallyHasAlpha |= sampler.next(base);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000440 base += sampler.srcDY() * rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000441 }
442 } else {
443 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 uint8_t* srcRow = (uint8_t*)storage.get();
445 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
446
447 for (int y = 0; y < height; y++) {
448 uint8_t* tmp = srcRow;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000449 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 reallyHasAlpha |= sampler.next(srcRow);
451 if (y < height - 1) {
452 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
453 }
454 }
reed@android.com862e91b2009-04-28 15:27:07 +0000455
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 // skip the rest of the rows (if any)
457 png_uint_32 read = (height - 1) * sampler.srcDY() +
458 sampler.srcY0() + 1;
459 SkASSERT(read <= origHeight);
460 skip_src_rows(png_ptr, srcRow, origHeight - read);
461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 }
463
464 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
465 png_read_end(png_ptr, info_ptr);
466
467 if (0 != theTranspColor) {
468 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
469 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000470 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
471 switch (decodedBitmap->config()) {
472 case SkBitmap::kIndex8_Config:
473 // Fall through.
474 case SkBitmap::kARGB_4444_Config:
475 // We have chosen not to support unpremul for these configs.
476 return false;
477 default: {
478 // Fall through to finish the decode. This config either
479 // supports unpremul or it is irrelevant because it has no
480 // alpha (or only alpha).
481 // These brackets prevent a warning.
482 }
483 }
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000484 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000485
djsollen@google.com446cf712014-02-19 21:45:35 +0000486 if (!reallyHasAlpha) {
487 decodedBitmap->setAlphaType(kOpaque_SkAlphaType);
reed@google.com383a6972013-10-21 14:00:07 +0000488 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 return true;
490}
491
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000492
493
494bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000495 SkBitmap::Config* SK_RESTRICT configp,
496 bool* SK_RESTRICT hasAlphap,
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000497 SkPMColor* SK_RESTRICT theTranspColorp) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000498 png_uint_32 origWidth, origHeight;
499 int bitDepth, colorType;
500 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
501 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
502
503 // check for sBIT chunk data, in case we should disable dithering because
504 // our data is not truely 8bits per component
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000505 png_color_8p sig_bit;
scroggo@google.com8d239242013-10-01 17:27:15 +0000506 if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000507#if 0
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000508 SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
509 sig_bit->blue, sig_bit->alpha);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000510#endif
511 // 0 seems to indicate no information available
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000512 if (pos_le(sig_bit->red, SK_R16_BITS) &&
513 pos_le(sig_bit->green, SK_G16_BITS) &&
514 pos_le(sig_bit->blue, SK_B16_BITS)) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000515 this->setDitherImage(false);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000516 }
517 }
518
519 if (colorType == PNG_COLOR_TYPE_PALETTE) {
520 bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
521 *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
522 // now see if we can upscale to their requested config
523 if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
524 *configp = SkBitmap::kIndex8_Config;
525 }
526 } else {
527 png_color_16p transpColor = NULL;
528 int numTransp = 0;
529
530 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
531
532 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
533
534 if (valid && numTransp == 1 && transpColor != NULL) {
535 /* Compute our transparent color, which we'll match against later.
536 We don't really handle 16bit components properly here, since we
537 do our compare *after* the values have been knocked down to 8bit
538 which means we will find more matches than we should. The real
539 fix seems to be to see the actual 16bit components, do the
540 compare, and then knock it down to 8bits ourselves.
541 */
542 if (colorType & PNG_COLOR_MASK_COLOR) {
543 if (16 == bitDepth) {
544 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
545 transpColor->green >> 8,
546 transpColor->blue >> 8);
547 } else {
halcanary@google.comfed30372013-10-04 12:46:45 +0000548 /* We apply the mask because in a very small
549 number of corrupt PNGs, (transpColor->red > 255)
550 and (bitDepth == 8), for certain versions of libpng. */
551 *theTranspColorp = SkPackARGB32(0xFF,
552 0xFF & (transpColor->red),
553 0xFF & (transpColor->green),
554 0xFF & (transpColor->blue));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000555 }
556 } else { // gray
557 if (16 == bitDepth) {
558 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
559 transpColor->gray >> 8,
560 transpColor->gray >> 8);
561 } else {
halcanary@google.comfed30372013-10-04 12:46:45 +0000562 /* We apply the mask because in a very small
563 number of corrupt PNGs, (transpColor->red >
564 255) and (bitDepth == 8), for certain versions
565 of libpng. For safety we assume the same could
566 happen with a grayscale PNG. */
567 *theTranspColorp = SkPackARGB32(0xFF,
568 0xFF & (transpColor->gray),
569 0xFF & (transpColor->gray),
570 0xFF & (transpColor->gray));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000571 }
572 }
573 }
574
575 if (valid ||
576 PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
577 PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
578 *hasAlphap = true;
579 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000580
581 SrcDepth srcDepth = k32Bit_SrcDepth;
582 if (PNG_COLOR_TYPE_GRAY == colorType) {
583 srcDepth = k8BitGray_SrcDepth;
scroggo@google.com8e2ef012013-07-18 20:14:45 +0000584 // Remove this assert, which fails on desk_pokemonwiki.skp
585 //SkASSERT(!*hasAlphap);
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000586 }
587
588 *configp = this->getPrefConfig(srcDepth, *hasAlphap);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000589 // now match the request against our capabilities
590 if (*hasAlphap) {
591 if (*configp != SkBitmap::kARGB_4444_Config) {
592 *configp = SkBitmap::kARGB_8888_Config;
593 }
594 } else {
scroggo@google.com354fd972013-10-02 15:50:19 +0000595 if (SkBitmap::kA8_Config == *configp) {
596 if (k8BitGray_SrcDepth != srcDepth) {
597 // Converting a non grayscale image to A8 is not currently supported.
598 *configp = SkBitmap::kARGB_8888_Config;
599 }
600 } else if (*configp != SkBitmap::kRGB_565_Config &&
601 *configp != SkBitmap::kARGB_4444_Config) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000602 *configp = SkBitmap::kARGB_8888_Config;
603 }
604 }
605 }
606
607 // sanity check for size
608 {
reed@google.com57212f92013-12-30 14:40:38 +0000609 int64_t size = sk_64_mul(origWidth, origHeight);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000610 // now check that if we are 4-bytes per pixel, we also don't overflow
reed@google.com57212f92013-12-30 14:40:38 +0000611 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000612 return false;
613 }
614 }
615
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000616 if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
617 return false;
618 }
619
620 // If the image has alpha and the decoder wants unpremultiplied
621 // colors, the only supported config is 8888.
622 if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
623 *configp = SkBitmap::kARGB_8888_Config;
624 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000625
626 if (fImageIndex != NULL) {
627 if (SkBitmap::kNo_Config == fImageIndex->fConfig) {
628 // This is the first time for this subset decode. From now on,
629 // all decodes must be in the same config.
630 fImageIndex->fConfig = *configp;
631 } else if (fImageIndex->fConfig != *configp) {
632 // Requesting a different config for a subsequent decode is not
633 // supported. Report failure before we make changes to png_ptr.
634 return false;
635 }
636 }
637
638 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType
639 && *configp != SkBitmap::kA8_Config;
640
641 // Unless the user is requesting A8, convert a grayscale image into RGB.
642 // GRAY_ALPHA will always be converted to RGB
643 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
644 png_set_gray_to_rgb(png_ptr);
645 }
646
647 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
648 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
649 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
650 }
651
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000652 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000653}
654
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000655typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
656
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000657bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
658 bool *hasAlphap, bool *reallyHasAlphap,
659 SkColorTable **colorTablep) {
660 int numPalette;
661 png_colorp palette;
662 png_bytep trans;
663 int numTrans;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000664
665 png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
666
667 /* BUGGY IMAGE WORKAROUND
668
669 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
670 which is a problem since we use the byte as an index. To work around this we grow
671 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
672 */
673 int colorCount = numPalette + (numPalette < 256);
reed@google.com0a6151d2013-10-10 14:44:56 +0000674 SkPMColor colorStorage[256]; // worst-case storage
675 SkPMColor* colorPtr = colorStorage;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000676
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000677 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
678 png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
679 *hasAlphap = (numTrans > 0);
680 } else {
681 numTrans = 0;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000682 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000683
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000684 // check for bad images that might make us crash
685 if (numTrans > numPalette) {
686 numTrans = numPalette;
687 }
688
689 int index = 0;
690 int transLessThanFF = 0;
691
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000692 // Choose which function to use to create the color table. If the final destination's
693 // config is unpremultiplied, the color table will store unpremultiplied colors.
694 PackColorProc proc;
695 if (this->getRequireUnpremultipliedColors()) {
696 proc = &SkPackARGB32NoCheck;
697 } else {
698 proc = &SkPreMultiplyARGB;
699 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000700 for (; index < numTrans; index++) {
701 transLessThanFF |= (int)*trans - 0xFF;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000702 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000703 palette++;
704 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000705 bool reallyHasAlpha = (transLessThanFF < 0);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000706
707 for (; index < numPalette; index++) {
708 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
709 palette++;
710 }
711
712 // see BUGGY IMAGE WORKAROUND comment above
713 if (numPalette < 256) {
714 *colorPtr = colorPtr[-1];
715 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000716
717 SkAlphaType alphaType = kOpaque_SkAlphaType;
718 if (reallyHasAlpha) {
719 if (this->getRequireUnpremultipliedColors()) {
720 alphaType = kUnpremul_SkAlphaType;
721 } else {
722 alphaType = kPremul_SkAlphaType;
723 }
724 }
725
726 *colorTablep = SkNEW_ARGS(SkColorTable,
727 (colorStorage, colorCount, alphaType));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000728 *reallyHasAlphap = reallyHasAlpha;
729 return true;
730}
731
732#ifdef SK_BUILD_FOR_ANDROID
733
scroggo@google.comb5571b32013-09-25 21:34:24 +0000734bool SkPNGImageDecoder::onBuildTileIndex(SkStreamRewindable* sk_stream, int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000735 png_structp png_ptr;
736 png_infop info_ptr;
737
738 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
739 return false;
740 }
741
742 if (setjmp(png_jmpbuf(png_ptr)) != 0) {
743 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
744 return false;
745 }
746
747 png_uint_32 origWidth, origHeight;
748 int bitDepth, colorType;
749 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
750 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
751
752 *width = origWidth;
753 *height = origHeight;
754
755 png_build_index(png_ptr);
756
757 if (fImageIndex) {
758 SkDELETE(fImageIndex);
759 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000760 fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (sk_stream, png_ptr, info_ptr));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000761
762 return true;
763}
764
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000765bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
766 if (NULL == fImageIndex) {
767 return false;
768 }
769
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000770 png_structp png_ptr = fImageIndex->fPng_ptr;
771 png_infop info_ptr = fImageIndex->fInfo_ptr;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000772 if (setjmp(png_jmpbuf(png_ptr))) {
773 return false;
774 }
775
776 png_uint_32 origWidth, origHeight;
777 int bitDepth, colorType, interlaceType;
778 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
779 &colorType, &interlaceType, int_p_NULL, int_p_NULL);
780
781 SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
782
783 if (!rect.intersect(region)) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000784 // If the requested region is entirely outside the image, just
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000785 // returns false
786 return false;
787 }
788
789 SkBitmap::Config config;
790 bool hasAlpha = false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000791 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
792
scroggo@google.com8d239242013-10-01 17:27:15 +0000793 if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000794 return false;
795 }
796
797 const int sampleSize = this->getSampleSize();
798 SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
799
800 SkBitmap decodedBitmap;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000801 decodedBitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000802
803 // from here down we are concerned with colortables and pixels
804
805 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
806 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
807 // draw lots faster if we can flag the bitmap has being opaque
808 bool reallyHasAlpha = false;
809 SkColorTable* colorTable = NULL;
810
811 if (colorType == PNG_COLOR_TYPE_PALETTE) {
812 decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
813 }
814
815 SkAutoUnref aur(colorTable);
816
817 // Check ahead of time if the swap(dest, src) is possible.
818 // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
819 // If no, then we will use alloc to allocate pixels to prevent garbage collection.
820 int w = rect.width() / sampleSize;
821 int h = rect.height() / sampleSize;
822 const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
823 (h == decodedBitmap.height()) && bm->isNull();
824 const bool needColorTable = SkBitmap::kIndex8_Config == config;
825 if (swapOnly) {
826 if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
827 return false;
828 }
829 } else {
830 if (!decodedBitmap.allocPixels(NULL, needColorTable ? colorTable : NULL)) {
831 return false;
832 }
833 }
834 SkAutoLockPixels alp(decodedBitmap);
835
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000836 /* Turn on interlace handling. REQUIRED if you are not using
837 * png_read_image(). To see how to handle interlacing passes,
838 * see the png_read_row() method below:
839 */
840 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
841 png_set_interlace_handling(png_ptr) : 1;
842
843 /* Optional call to gamma correct and add the background to the palette
844 * and update info structure. REQUIRED if you are expecting libpng to
845 * update the palette for you (ie you selected such a transform above).
846 */
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000847
848 // Direct access to png_ptr fields is deprecated in libpng > 1.2.
849#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000850 png_ptr->pass = 0;
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000851#else
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000852 // FIXME: This sets pass as desired, but also sets iwidth. Is that ok?
853 png_set_interlaced_pass(png_ptr, 0);
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000854#endif
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000855 png_read_update_info(png_ptr, info_ptr);
856
857 int actualTop = rect.fTop;
858
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000859 if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
860 && 1 == sampleSize) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000861 if (SkBitmap::kA8_Config == config) {
862 // For an A8 bitmap, we assume there is an alpha for speed. It is
863 // possible the bitmap is opaque, but that is an unlikely use case
864 // since it would not be very interesting.
865 reallyHasAlpha = true;
866 // A8 is only allowed if the original was GRAY.
867 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
868 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000869
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000870 for (int i = 0; i < number_passes; i++) {
871 png_configure_decoder(png_ptr, &actualTop, i);
872 for (int j = 0; j < rect.fTop - actualTop; j++) {
873 uint8_t* bmRow = decodedBitmap.getAddr8(0, 0);
874 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
875 }
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000876 png_uint_32 bitmapHeight = (png_uint_32) decodedBitmap.height();
877 for (png_uint_32 y = 0; y < bitmapHeight; y++) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000878 uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
879 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
880 }
881 }
882 } else {
883 SkScaledBitmapSampler::SrcConfig sc;
884 int srcBytesPerPixel = 4;
885
886 if (colorTable != NULL) {
887 sc = SkScaledBitmapSampler::kIndex;
888 srcBytesPerPixel = 1;
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000889 } else if (SkBitmap::kA8_Config == config) {
890 // A8 is only allowed if the original was GRAY.
891 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
892 sc = SkScaledBitmapSampler::kGray;
893 srcBytesPerPixel = 1;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000894 } else if (hasAlpha) {
895 sc = SkScaledBitmapSampler::kRGBA;
896 } else {
897 sc = SkScaledBitmapSampler::kRGBX;
898 }
899
900 /* We have to pass the colortable explicitly, since we may have one
901 even if our decodedBitmap doesn't, due to the request that we
902 upscale png's palette to a direct model
903 */
904 SkAutoLockColors ctLock(colorTable);
scroggo@google.com8d239242013-10-01 17:27:15 +0000905 if (!sampler.begin(&decodedBitmap, sc, *this, ctLock.colors())) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000906 return false;
907 }
908 const int height = decodedBitmap.height();
909
910 if (number_passes > 1) {
911 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
912 uint8_t* base = (uint8_t*)storage.get();
913 size_t rb = origWidth * srcBytesPerPixel;
914
915 for (int i = 0; i < number_passes; i++) {
916 png_configure_decoder(png_ptr, &actualTop, i);
917 for (int j = 0; j < rect.fTop - actualTop; j++) {
918 uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
919 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
920 }
921 uint8_t* row = base;
922 for (int32_t y = 0; y < rect.height(); y++) {
923 uint8_t* bmRow = row;
924 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
925 row += rb;
926 }
927 }
928 // now sample it
929 base += sampler.srcY0() * rb;
930 for (int y = 0; y < height; y++) {
931 reallyHasAlpha |= sampler.next(base);
932 base += sampler.srcDY() * rb;
933 }
934 } else {
935 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
936 uint8_t* srcRow = (uint8_t*)storage.get();
937
938 png_configure_decoder(png_ptr, &actualTop, 0);
939 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
940
941 for (int i = 0; i < rect.fTop - actualTop; i++) {
942 uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
943 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
944 }
945 for (int y = 0; y < height; y++) {
946 uint8_t* tmp = srcRow;
947 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
948 reallyHasAlpha |= sampler.next(srcRow);
949 if (y < height - 1) {
950 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
951 }
952 }
953 }
954 }
955
956 if (0 != theTranspColor) {
957 reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
958 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000959 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
960 switch (decodedBitmap.config()) {
961 case SkBitmap::kIndex8_Config:
962 // Fall through.
963 case SkBitmap::kARGB_4444_Config:
964 // We have chosen not to support unpremul for these configs.
965 return false;
966 default: {
967 // Fall through to finish the decode. This config either
968 // supports unpremul or it is irrelevant because it has no
969 // alpha (or only alpha).
970 // These brackets prevent a warning.
971 }
972 }
commit-bot@chromium.org546f70c2013-10-03 17:13:38 +0000973 }
reed@google.com383a6972013-10-21 14:00:07 +0000974 SkAlphaType alphaType = kOpaque_SkAlphaType;
975 if (reallyHasAlpha) {
976 if (this->getRequireUnpremultipliedColors()) {
977 alphaType = kUnpremul_SkAlphaType;
978 } else {
979 alphaType = kPremul_SkAlphaType;
980 }
981 }
982 decodedBitmap.setAlphaType(alphaType);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000983
984 if (swapOnly) {
985 bm->swap(decodedBitmap);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000986 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000987 }
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000988 return this->cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
989 region.width(), region.height(), 0, rect.y());
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000990}
991#endif
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993///////////////////////////////////////////////////////////////////////////////
994
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995#include "SkColorPriv.h"
996#include "SkUnPreMultiply.h"
997
998static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000999 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 if (!sk_stream->write(data, len)) {
1001 png_error(png_ptr, "sk_write_fn Error!");
1002 }
1003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005static transform_scanline_proc choose_proc(SkBitmap::Config config,
1006 bool hasAlpha) {
1007 // we don't care about search on alpha if we're kIndex8, since only the
1008 // colortable packing cares about that distinction, not the pixels
1009 if (SkBitmap::kIndex8_Config == config) {
1010 hasAlpha = false; // we store false in the table entries for kIndex8
1011 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001012
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 static const struct {
1014 SkBitmap::Config fConfig;
1015 bool fHasAlpha;
1016 transform_scanline_proc fProc;
1017 } gMap[] = {
1018 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
1019 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
1020 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
1021 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
1022 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
epoger@google.com4ce738b2012-11-16 18:44:18 +00001023 { SkBitmap::kIndex8_Config, false, transform_scanline_memcpy },
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 };
1025
1026 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
1027 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
1028 return gMap[i].fProc;
1029 }
1030 }
1031 sk_throw();
1032 return NULL;
1033}
1034
1035// return the minimum legal bitdepth (by png standards) for this many colortable
1036// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
1037// we can use fewer bits per in png
1038static int computeBitDepth(int colorCount) {
1039#if 0
1040 int bits = SkNextLog2(colorCount);
1041 SkASSERT(bits >= 1 && bits <= 8);
1042 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
1043 return SkNextPow2(bits);
1044#else
1045 // for the moment, we don't know how to pack bitdepth < 8
1046 return 8;
1047#endif
1048}
1049
1050/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
1051 pack trans[] and return the number of trans[] entries written. If hasAlpha
1052 is false, the return value will always be 0.
rmistry@google.comd6176b02012-08-23 18:14:13 +00001053
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 Note: this routine takes care of unpremultiplying the RGB values when we
1055 have alpha in the colortable, since png doesn't support premul colors
1056*/
reed@android.com6f252972009-01-14 16:46:16 +00001057static inline int pack_palette(SkColorTable* ctable,
reed@android.comb50a60c2009-01-14 17:51:08 +00001058 png_color* SK_RESTRICT palette,
1059 png_byte* SK_RESTRICT trans, bool hasAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 SkAutoLockColors alc(ctable);
1061 const SkPMColor* SK_RESTRICT colors = alc.colors();
1062 const int ctCount = ctable->count();
1063 int i, num_trans = 0;
1064
1065 if (hasAlpha) {
1066 /* first see if we have some number of fully opaque at the end of the
1067 ctable. PNG allows num_trans < num_palette, but all of the trans
1068 entries must come first in the palette. If I was smarter, I'd
1069 reorder the indices and ctable so that all non-opaque colors came
1070 first in the palette. But, since that would slow down the encode,
1071 I'm leaving the indices and ctable order as is, and just looking
1072 at the tail of the ctable for opaqueness.
1073 */
1074 num_trans = ctCount;
1075 for (i = ctCount - 1; i >= 0; --i) {
1076 if (SkGetPackedA32(colors[i]) != 0xFF) {
1077 break;
1078 }
1079 num_trans -= 1;
1080 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001081
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 const SkUnPreMultiply::Scale* SK_RESTRICT table =
1083 SkUnPreMultiply::GetScaleTable();
1084
1085 for (i = 0; i < num_trans; i++) {
1086 const SkPMColor c = *colors++;
1087 const unsigned a = SkGetPackedA32(c);
1088 const SkUnPreMultiply::Scale s = table[a];
1089 trans[i] = a;
1090 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
1091 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
1092 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001093 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 // now fall out of this if-block to use common code for the trailing
1095 // opaque entries
1096 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001097
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 // these (remaining) entries are opaque
1099 for (i = num_trans; i < ctCount; i++) {
1100 SkPMColor c = *colors++;
1101 palette[i].red = SkGetPackedR32(c);
1102 palette[i].green = SkGetPackedG32(c);
1103 palette[i].blue = SkGetPackedB32(c);
1104 }
1105 return num_trans;
1106}
1107
1108class SkPNGImageEncoder : public SkImageEncoder {
1109protected:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001110 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001111private:
1112 bool doEncode(SkWStream* stream, const SkBitmap& bm,
1113 const bool& hasAlpha, int colorType,
1114 int bitDepth, SkBitmap::Config config,
1115 png_color_8& sig_bit);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001116
1117 typedef SkImageEncoder INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118};
1119
1120bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
1121 int /*quality*/) {
reed@google.com44699382013-10-31 17:28:30 +00001122 SkBitmap::Config config = bitmap.config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123
1124 const bool hasAlpha = !bitmap.isOpaque();
1125 int colorType = PNG_COLOR_MASK_COLOR;
1126 int bitDepth = 8; // default for color
1127 png_color_8 sig_bit;
1128
1129 switch (config) {
1130 case SkBitmap::kIndex8_Config:
1131 colorType |= PNG_COLOR_MASK_PALETTE;
1132 // fall through to the ARGB_8888 case
1133 case SkBitmap::kARGB_8888_Config:
1134 sig_bit.red = 8;
1135 sig_bit.green = 8;
1136 sig_bit.blue = 8;
1137 sig_bit.alpha = 8;
1138 break;
1139 case SkBitmap::kARGB_4444_Config:
1140 sig_bit.red = 4;
1141 sig_bit.green = 4;
1142 sig_bit.blue = 4;
1143 sig_bit.alpha = 4;
1144 break;
1145 case SkBitmap::kRGB_565_Config:
1146 sig_bit.red = 5;
1147 sig_bit.green = 6;
1148 sig_bit.blue = 5;
1149 sig_bit.alpha = 0;
1150 break;
1151 default:
1152 return false;
1153 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001154
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 if (hasAlpha) {
1156 // don't specify alpha if we're a palette, even if our ctable has alpha
1157 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
1158 colorType |= PNG_COLOR_MASK_ALPHA;
1159 }
1160 } else {
1161 sig_bit.alpha = 0;
1162 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001163
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 SkAutoLockPixels alp(bitmap);
1165 // readyToDraw checks for pixels (and colortable if that is required)
1166 if (!bitmap.readyToDraw()) {
1167 return false;
1168 }
1169
1170 // we must do this after we have locked the pixels
1171 SkColorTable* ctable = bitmap.getColorTable();
1172 if (NULL != ctable) {
1173 if (ctable->count() == 0) {
1174 return false;
1175 }
1176 // check if we can store in fewer than 8 bits
1177 bitDepth = computeBitDepth(ctable->count());
1178 }
1179
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001180 return doEncode(stream, bitmap, hasAlpha, colorType,
1181 bitDepth, config, sig_bit);
1182}
1183
1184bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
1185 const bool& hasAlpha, int colorType,
1186 int bitDepth, SkBitmap::Config config,
1187 png_color_8& sig_bit) {
1188
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 png_structp png_ptr;
1190 png_infop info_ptr;
1191
1192 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
1193 NULL);
1194 if (NULL == png_ptr) {
1195 return false;
1196 }
1197
1198 info_ptr = png_create_info_struct(png_ptr);
1199 if (NULL == info_ptr) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001200 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 return false;
1202 }
1203
1204 /* Set error handling. REQUIRED if you aren't supplying your own
1205 * error handling functions in the png_create_write_struct() call.
1206 */
1207 if (setjmp(png_jmpbuf(png_ptr))) {
1208 png_destroy_write_struct(&png_ptr, &info_ptr);
1209 return false;
1210 }
1211
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001212 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213
1214 /* Set the image information here. Width and height are up to 2^31,
1215 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
1216 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
1217 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
1218 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
1219 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
1220 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
1221 */
1222
1223 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
1224 bitDepth, colorType,
1225 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1226 PNG_FILTER_TYPE_BASE);
1227
reed@android.com61898772009-07-07 19:38:01 +00001228 // set our colortable/trans arrays if needed
1229 png_color paletteColors[256];
1230 png_byte trans[256];
1231 if (SkBitmap::kIndex8_Config == config) {
1232 SkColorTable* ct = bitmap.getColorTable();
1233 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
1234 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
1235 if (numTrans > 0) {
1236 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
1237 }
1238 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239
1240 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
1241 png_write_info(png_ptr, info_ptr);
1242
1243 const char* srcImage = (const char*)bitmap.getPixels();
1244 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
1245 char* storage = (char*)rowStorage.get();
1246 transform_scanline_proc proc = choose_proc(config, hasAlpha);
1247
1248 for (int y = 0; y < bitmap.height(); y++) {
1249 png_bytep row_ptr = (png_bytep)storage;
1250 proc(srcImage, bitmap.width(), storage);
1251 png_write_rows(png_ptr, &row_ptr, 1);
1252 srcImage += bitmap.rowBytes();
1253 }
1254
1255 png_write_end(png_ptr, info_ptr);
1256
1257 /* clean up after the write, and free any memory allocated */
1258 png_destroy_write_struct(&png_ptr, &info_ptr);
1259 return true;
1260}
1261
reed@android.com00bf85a2009-01-22 13:04:56 +00001262///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001263DEFINE_DECODER_CREATOR(PNGImageDecoder);
1264DEFINE_ENCODER_CREATOR(PNGImageEncoder);
1265///////////////////////////////////////////////////////////////////////////////
reed@android.com00bf85a2009-01-22 13:04:56 +00001266
scroggo@google.comb5571b32013-09-25 21:34:24 +00001267static bool is_png(SkStreamRewindable* stream) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001268 char buf[PNG_BYTES_TO_CHECK];
1269 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
1270 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001271 return true;
1272 }
1273 return false;
1274}
1275
scroggo@google.comb5571b32013-09-25 21:34:24 +00001276SkImageDecoder* sk_libpng_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001277 if (is_png(stream)) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001278 return SkNEW(SkPNGImageDecoder);
1279 }
1280 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281}
1282
scroggo@google.comb5571b32013-09-25 21:34:24 +00001283static SkImageDecoder::Format get_format_png(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001284 if (is_png(stream)) {
1285 return SkImageDecoder::kPNG_Format;
1286 }
1287 return SkImageDecoder::kUnknown_Format;
1288}
1289
reed@android.comdfee5792010-04-15 14:24:50 +00001290SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001291 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
1292}
1293
mtklein@google.combd6343b2013-09-04 17:20:18 +00001294static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory);
1295static SkImageDecoder_FormatReg gFormatReg(get_format_png);
1296static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory);