blob: 245654dbfd3dbcb3c735b1f8ccdb44fe26433a8a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +00009#include "SkImageEncoder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkColor.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkMath.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000014#include "SkRTConf.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkScaledBitmapSampler.h"
16#include "SkStream.h"
17#include "SkTemplates.h"
18#include "SkUtils.h"
epoger@google.com4ce738b2012-11-16 18:44:18 +000019#include "transform_scanline.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020extern "C" {
21#include "png.h"
22}
23
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +000024/* These were dropped in libpng >= 1.4 */
25#ifndef png_infopp_NULL
26#define png_infopp_NULL NULL
27#endif
28
29#ifndef png_bytepp_NULL
30#define png_bytepp_NULL NULL
31#endif
32
33#ifndef int_p_NULL
34#define int_p_NULL NULL
35#endif
36
37#ifndef png_flush_ptr_NULL
38#define png_flush_ptr_NULL NULL
39#endif
40
halcanary@google.comfed30372013-10-04 12:46:45 +000041#if defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000042#define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS false
43#else // !defined(SK_DEBUG)
44#define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true
halcanary@google.comfed30372013-10-04 12:46:45 +000045#endif // defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000046SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings,
47 "images.png.suppressDecoderWarnings",
48 DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS,
49 "Suppress most PNG warnings when calling image decode "
50 "functions.");
51
halcanary@google.comfed30372013-10-04 12:46:45 +000052
53
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000054class SkPNGImageIndex {
55public:
scroggoa1193e42015-01-21 12:09:53 -080056 // Takes ownership of stream.
scroggo@google.comb5571b32013-09-25 21:34:24 +000057 SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr)
scroggo@google.comc70a3aa2013-07-18 20:03:15 +000058 : fStream(stream)
59 , fPng_ptr(png_ptr)
60 , fInfo_ptr(info_ptr)
reed6c225732014-06-09 19:52:07 -070061 , fColorType(kUnknown_SkColorType) {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +000062 SkASSERT(stream != NULL);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000063 }
64 ~SkPNGImageIndex() {
bsalomon49f085d2014-09-05 13:34:00 -070065 if (fPng_ptr) {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +000066 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000067 }
68 }
69
scroggoa1193e42015-01-21 12:09:53 -080070 SkAutoTDelete<SkStreamRewindable> fStream;
scroggo@google.comb5571b32013-09-25 21:34:24 +000071 png_structp fPng_ptr;
72 png_infop fInfo_ptr;
reed6c225732014-06-09 19:52:07 -070073 SkColorType fColorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000074};
75
reed@android.com8a1c16f2008-12-17 15:59:43 +000076class SkPNGImageDecoder : public SkImageDecoder {
77public:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000078 SkPNGImageDecoder() {
79 fImageIndex = NULL;
80 }
mtklein72c9faa2015-01-09 10:06:39 -080081 Format getFormat() const SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 return kPNG_Format;
83 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +000084
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000085 virtual ~SkPNGImageDecoder() {
86 SkDELETE(fImageIndex);
87 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000088
reed@android.com8a1c16f2008-12-17 15:59:43 +000089protected:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000090#ifdef SK_BUILD_FOR_ANDROID
mtklein72c9faa2015-01-09 10:06:39 -080091 bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
92 bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000093#endif
mtklein72c9faa2015-01-09 10:06:39 -080094 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000095
96private:
97 SkPNGImageIndex* fImageIndex;
98
99 bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
dml78acf962015-03-18 06:03:29 -0700100 bool decodePalette(png_structp png_ptr, png_infop info_ptr, int bitDepth,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000101 bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
102 SkColorTable **colorTablep);
reed0689d7b2014-06-14 05:30:20 -0700103 bool getBitmapColorType(png_structp, png_infop, SkColorType*, bool* hasAlpha,
104 SkPMColor* theTranspColor);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000105
106 typedef SkImageDecoder INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107};
108
109#ifndef png_jmpbuf
110# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
111#endif
112
113#define PNG_BYTES_TO_CHECK 4
114
115/* Automatically clean up after throwing an exception */
116struct PNGAutoClean {
117 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
118 ~PNGAutoClean() {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000119 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 }
121private:
122 png_structp png_ptr;
123 png_infop info_ptr;
124};
125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000127 SkStream* sk_stream = (SkStream*) png_get_io_ptr(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 size_t bytes = sk_stream->read(data, length);
129 if (bytes != length) {
130 png_error(png_ptr, "Read Error!");
131 }
132}
133
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000134#ifdef SK_BUILD_FOR_ANDROID
135static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
scroggo@google.comb5571b32013-09-25 21:34:24 +0000136 SkStreamRewindable* sk_stream = (SkStreamRewindable*) png_get_io_ptr(png_ptr);
scroggo@google.com4d213ab2013-08-28 13:08:54 +0000137 if (!sk_stream->rewind()) {
138 png_error(png_ptr, "Failed to rewind stream!");
139 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000140 (void)sk_stream->skip(offset);
141}
142#endif
143
kkinnunen93b255b2014-10-19 22:07:23 -0700144#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
146 SkImageDecoder::Peeker* peeker =
147 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
148 // peek() returning true means continue decoding
149 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
150 1 : -1;
151}
kkinnunen93b255b2014-10-19 22:07:23 -0700152#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153
154static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000155 SkDEBUGF(("------ png error %s\n", msg));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 longjmp(png_jmpbuf(png_ptr), 1);
157}
158
159static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
160 for (int i = 0; i < count; i++) {
161 uint8_t* tmp = storage;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000162 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 }
164}
165
166static bool pos_le(int value, int max) {
167 return value > 0 && value <= max;
168}
169
170static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
reed0689d7b2014-06-14 05:30:20 -0700171 SkASSERT(bm->colorType() == kN32_SkColorType);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 bool reallyHasAlpha = false;
174
175 for (int y = bm->height() - 1; y >= 0; --y) {
176 SkPMColor* p = bm->getAddr32(0, y);
177 for (int x = bm->width() - 1; x >= 0; --x) {
178 if (match == *p) {
179 *p = 0;
180 reallyHasAlpha = true;
181 }
182 p += 1;
183 }
184 }
185 return reallyHasAlpha;
186}
187
reed6c225732014-06-09 19:52:07 -0700188static bool canUpscalePaletteToConfig(SkColorType dstColorType, bool srcHasAlpha) {
189 switch (dstColorType) {
190 case kN32_SkColorType:
191 case kARGB_4444_SkColorType:
reed@android.comb6137c32009-07-29 20:56:52 +0000192 return true;
reed6c225732014-06-09 19:52:07 -0700193 case kRGB_565_SkColorType:
reed@android.comb6137c32009-07-29 20:56:52 +0000194 // only return true if the src is opaque (since 565 is opaque)
195 return !srcHasAlpha;
196 default:
197 return false;
198 }
199}
200
201// call only if color_type is PALETTE. Returns true if the ctable has alpha
202static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
203 png_bytep trans;
204 int num_trans;
205
206 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
207 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
208 return num_trans > 0;
209 }
210 return false;
reed@android.com11344262009-07-08 20:09:23 +0000211}
212
halcanary@google.comfed30372013-10-04 12:46:45 +0000213void do_nothing_warning_fn(png_structp, png_const_charp) {
214 /* do nothing */
215}
216
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000217bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
218 png_infop *info_ptrp) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 /* Create and initialize the png_struct with the desired error handler
220 * functions. If you want to use the default stderr and longjump method,
221 * you can supply NULL for the last three parameters. We also supply the
222 * the compiler header file version, so that we know if the application
223 * was compiled with a compatible version of the library. */
halcanary@google.comfed30372013-10-04 12:46:45 +0000224
halcanary@google.comfed30372013-10-04 12:46:45 +0000225 png_error_ptr user_warning_fn =
226 (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;
227 /* NULL means to leave as default library behavior. */
halcanary@google.com2a103182013-10-14 12:49:15 +0000228 /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */
halcanary@google.comfed30372013-10-04 12:46:45 +0000229 /* To suppress warnings with a SK_DEBUG binary, set the
230 * environment variable "skia_images_png_suppressDecoderWarnings"
231 * to "true". Inside a program that links to skia:
232 * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */
halcanary@google.comfed30372013-10-04 12:46:45 +0000233
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
halcanary@google.comfed30372013-10-04 12:46:45 +0000235 NULL, sk_error_fn, user_warning_fn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
237 if (png_ptr == NULL) {
238 return false;
239 }
halcanary@google.comfed30372013-10-04 12:46:45 +0000240
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000241 *png_ptrp = png_ptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
243 /* Allocate/initialize the memory for image information. */
244 png_infop info_ptr = png_create_info_struct(png_ptr);
245 if (info_ptr == NULL) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000246 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 return false;
248 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000249 *info_ptrp = info_ptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 /* Set error handling if you are using the setjmp/longjmp method (this is
252 * the normal method of doing things with libpng). REQUIRED unless you
253 * set up your own error handlers in the png_create_read_struct() earlier.
254 */
255 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo@google.com5401cd02013-11-12 15:30:06 +0000256 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 return false;
258 }
259
260 /* If you are using replacement read functions, instead of calling
261 * png_init_io() here you would call:
262 */
263 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000264#ifdef SK_BUILD_FOR_ANDROID
265 png_set_seek_fn(png_ptr, sk_seek_fn);
266#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 /* where user_io_ptr is a structure you want available to the callbacks */
268 /* If we have already read some of the signature */
269// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
270
kkinnunen93b255b2014-10-19 22:07:23 -0700271#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 // hookup our peeker so we can see any user-chunks the caller may be interested in
273 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
274 if (this->getPeeker()) {
275 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
276 }
kkinnunen93b255b2014-10-19 22:07:23 -0700277#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 /* The call to png_read_info() gives us all of the information from the
279 * PNG file before the first IDAT (image data chunk). */
280 png_read_info(png_ptr, info_ptr);
281 png_uint_32 origWidth, origHeight;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000282 int bitDepth, colorType;
283 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
284 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
286 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000287 if (bitDepth == 16) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 png_set_strip_16(png_ptr);
289 }
kkinnunen93b255b2014-10-19 22:07:23 -0700290#ifdef PNG_READ_PACK_SUPPORTED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 /* 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 }
kkinnunen93b255b2014-10-19 22:07:23 -0700296#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000298 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000299 png_set_expand_gray_1_2_4_to_8(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000301
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000302 return true;
303}
304
scroggo2a120802014-10-22 12:07:00 -0700305SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
306 Mode mode) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000307 png_structp png_ptr;
308 png_infop info_ptr;
309
310 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
scroggo2a120802014-10-22 12:07:00 -0700311 return kFailure;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000312 }
313
scroggo@google.comfeeca3c2013-11-12 14:38:41 +0000314 PNGAutoClean autoClean(png_ptr, info_ptr);
315
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000316 if (setjmp(png_jmpbuf(png_ptr))) {
scroggo2a120802014-10-22 12:07:00 -0700317 return kFailure;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000318 }
319
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000320 png_uint_32 origWidth, origHeight;
reed6c225732014-06-09 19:52:07 -0700321 int bitDepth, pngColorType, interlaceType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000322 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
reed6c225732014-06-09 19:52:07 -0700323 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000324
reed6c225732014-06-09 19:52:07 -0700325 SkColorType colorType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 bool hasAlpha = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
rmistry@google.comd6176b02012-08-23 18:14:13 +0000328
reed0689d7b2014-06-14 05:30:20 -0700329 if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {
scroggo2a120802014-10-22 12:07:00 -0700330 return kFailure;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000332
reed6c225732014-06-09 19:52:07 -0700333 SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ?
334 kUnpremul_SkAlphaType : kPremul_SkAlphaType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 const int sampleSize = this->getSampleSize();
336 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
reed6c225732014-06-09 19:52:07 -0700337 decodedBitmap->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
338 colorType, alphaType));
djsollen@google.com446cf712014-02-19 21:45:35 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700341 return kSuccess;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000343
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 // from here down we are concerned with colortables and pixels
345
346 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
347 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
348 // draw lots faster if we can flag the bitmap has being opaque
349 bool reallyHasAlpha = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 SkColorTable* colorTable = NULL;
351
reed6c225732014-06-09 19:52:07 -0700352 if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
dml78acf962015-03-18 06:03:29 -0700353 decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000355
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 SkAutoUnref aur(colorTable);
357
scroggo@google.combc69ce92013-07-09 15:45:14 +0000358 if (!this->allocPixelRef(decodedBitmap,
reed6c225732014-06-09 19:52:07 -0700359 kIndex_8_SkColorType == colorType ? colorTable : NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700360 return kFailure;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 SkAutoLockPixels alp(*decodedBitmap);
364
dmlf45796e2015-03-11 11:12:54 -0700365 // Repeat setjmp, otherwise variables declared since the last call (e.g. alp
366 // and aur) won't get their destructors called in case of a failure.
367 if (setjmp(png_jmpbuf(png_ptr))) {
368 return kFailure;
369 }
370
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 /* Turn on interlace handling. REQUIRED if you are not using
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000372 * png_read_image(). To see how to handle interlacing passes,
373 * see the png_read_row() method below:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 */
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000375 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
376 png_set_interlace_handling(png_ptr) : 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
378 /* Optional call to gamma correct and add the background to the palette
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000379 * and update info structure. REQUIRED if you are expecting libpng to
380 * update the palette for you (ie you selected such a transform above).
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 */
382 png_read_update_info(png_ptr, info_ptr);
383
reed6c225732014-06-09 19:52:07 -0700384 if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) &&
385 1 == sampleSize) {
386 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000387 // For an A8 bitmap, we assume there is an alpha for speed. It is
388 // possible the bitmap is opaque, but that is an unlikely use case
389 // since it would not be very interesting.
390 reallyHasAlpha = true;
391 // A8 is only allowed if the original was GRAY.
reed6c225732014-06-09 19:52:07 -0700392 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000393 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 for (int i = 0; i < number_passes; i++) {
395 for (png_uint_32 y = 0; y < origHeight; y++) {
396 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000397 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 }
399 }
400 } else {
401 SkScaledBitmapSampler::SrcConfig sc;
402 int srcBytesPerPixel = 4;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000403
reed@android.com11344262009-07-08 20:09:23 +0000404 if (colorTable != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 sc = SkScaledBitmapSampler::kIndex;
406 srcBytesPerPixel = 1;
reed6c225732014-06-09 19:52:07 -0700407 } else if (kAlpha_8_SkColorType == colorType) {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000408 // A8 is only allowed if the original was GRAY.
reed6c225732014-06-09 19:52:07 -0700409 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000410 sc = SkScaledBitmapSampler::kGray;
411 srcBytesPerPixel = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 } else if (hasAlpha) {
413 sc = SkScaledBitmapSampler::kRGBA;
414 } else {
415 sc = SkScaledBitmapSampler::kRGBX;
416 }
reed@android.com11344262009-07-08 20:09:23 +0000417
418 /* We have to pass the colortable explicitly, since we may have one
419 even if our decodedBitmap doesn't, due to the request that we
420 upscale png's palette to a direct model
421 */
mtklein775b8192014-12-02 09:11:25 -0800422 const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL;
423 if (!sampler.begin(decodedBitmap, sc, *this, colors)) {
scroggo2a120802014-10-22 12:07:00 -0700424 return kFailure;
reed@android.com862e91b2009-04-28 15:27:07 +0000425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 const int height = decodedBitmap->height();
427
reed@android.com862e91b2009-04-28 15:27:07 +0000428 if (number_passes > 1) {
429 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
430 uint8_t* base = (uint8_t*)storage.get();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000431 size_t rowBytes = origWidth * srcBytesPerPixel;
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000432
reed@android.com862e91b2009-04-28 15:27:07 +0000433 for (int i = 0; i < number_passes; i++) {
434 uint8_t* row = base;
435 for (png_uint_32 y = 0; y < origHeight; y++) {
436 uint8_t* bmRow = row;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000437 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
438 row += rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 }
reed@android.com862e91b2009-04-28 15:27:07 +0000441 // now sample it
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000442 base += sampler.srcY0() * rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000443 for (int y = 0; y < height; y++) {
444 reallyHasAlpha |= sampler.next(base);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000445 base += sampler.srcDY() * rowBytes;
reed@android.com862e91b2009-04-28 15:27:07 +0000446 }
447 } else {
448 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 uint8_t* srcRow = (uint8_t*)storage.get();
450 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
451
452 for (int y = 0; y < height; y++) {
453 uint8_t* tmp = srcRow;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000454 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 reallyHasAlpha |= sampler.next(srcRow);
456 if (y < height - 1) {
457 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
458 }
459 }
reed@android.com862e91b2009-04-28 15:27:07 +0000460
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 // skip the rest of the rows (if any)
462 png_uint_32 read = (height - 1) * sampler.srcDY() +
463 sampler.srcY0() + 1;
464 SkASSERT(read <= origHeight);
465 skip_src_rows(png_ptr, srcRow, origHeight - read);
466 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 }
468
469 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
470 png_read_end(png_ptr, info_ptr);
471
472 if (0 != theTranspColor) {
473 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
474 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000475 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
reed0689d7b2014-06-14 05:30:20 -0700476 switch (decodedBitmap->colorType()) {
477 case kIndex_8_SkColorType:
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000478 // Fall through.
reed0689d7b2014-06-14 05:30:20 -0700479 case kARGB_4444_SkColorType:
480 // We have chosen not to support unpremul for these colortypes.
scroggo2a120802014-10-22 12:07:00 -0700481 return kFailure;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000482 default: {
reed0689d7b2014-06-14 05:30:20 -0700483 // Fall through to finish the decode. This colortype either
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000484 // supports unpremul or it is irrelevant because it has no
485 // alpha (or only alpha).
486 // These brackets prevent a warning.
487 }
488 }
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000489 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000490
djsollen@google.com446cf712014-02-19 21:45:35 +0000491 if (!reallyHasAlpha) {
492 decodedBitmap->setAlphaType(kOpaque_SkAlphaType);
reed@google.com383a6972013-10-21 14:00:07 +0000493 }
scroggo2a120802014-10-22 12:07:00 -0700494 return kSuccess;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495}
496
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000497
498
reed0689d7b2014-06-14 05:30:20 -0700499bool SkPNGImageDecoder::getBitmapColorType(png_structp png_ptr, png_infop info_ptr,
500 SkColorType* colorTypep,
501 bool* hasAlphap,
502 SkPMColor* SK_RESTRICT theTranspColorp) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000503 png_uint_32 origWidth, origHeight;
504 int bitDepth, colorType;
505 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
506 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
507
kkinnunen93b255b2014-10-19 22:07:23 -0700508#ifdef PNG_sBIT_SUPPORTED
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000509 // check for sBIT chunk data, in case we should disable dithering because
510 // our data is not truely 8bits per component
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000511 png_color_8p sig_bit;
scroggo@google.com8d239242013-10-01 17:27:15 +0000512 if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000513#if 0
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000514 SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
515 sig_bit->blue, sig_bit->alpha);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000516#endif
517 // 0 seems to indicate no information available
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000518 if (pos_le(sig_bit->red, SK_R16_BITS) &&
519 pos_le(sig_bit->green, SK_G16_BITS) &&
520 pos_le(sig_bit->blue, SK_B16_BITS)) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000521 this->setDitherImage(false);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000522 }
523 }
kkinnunen93b255b2014-10-19 22:07:23 -0700524#endif
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000525
526 if (colorType == PNG_COLOR_TYPE_PALETTE) {
527 bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
reed6c225732014-06-09 19:52:07 -0700528 *colorTypep = this->getPrefColorType(kIndex_SrcDepth, paletteHasAlpha);
reed0689d7b2014-06-14 05:30:20 -0700529 // now see if we can upscale to their requested colortype
reed6c225732014-06-09 19:52:07 -0700530 if (!canUpscalePaletteToConfig(*colorTypep, paletteHasAlpha)) {
531 *colorTypep = kIndex_8_SkColorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000532 }
533 } else {
534 png_color_16p transpColor = NULL;
535 int numTransp = 0;
536
537 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
538
539 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
540
541 if (valid && numTransp == 1 && transpColor != NULL) {
542 /* Compute our transparent color, which we'll match against later.
543 We don't really handle 16bit components properly here, since we
544 do our compare *after* the values have been knocked down to 8bit
545 which means we will find more matches than we should. The real
546 fix seems to be to see the actual 16bit components, do the
547 compare, and then knock it down to 8bits ourselves.
548 */
549 if (colorType & PNG_COLOR_MASK_COLOR) {
550 if (16 == bitDepth) {
551 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
552 transpColor->green >> 8,
553 transpColor->blue >> 8);
554 } else {
halcanary@google.comfed30372013-10-04 12:46:45 +0000555 /* We apply the mask because in a very small
556 number of corrupt PNGs, (transpColor->red > 255)
557 and (bitDepth == 8), for certain versions of libpng. */
558 *theTranspColorp = SkPackARGB32(0xFF,
559 0xFF & (transpColor->red),
560 0xFF & (transpColor->green),
561 0xFF & (transpColor->blue));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000562 }
563 } else { // gray
564 if (16 == bitDepth) {
565 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
566 transpColor->gray >> 8,
567 transpColor->gray >> 8);
568 } else {
halcanary@google.comfed30372013-10-04 12:46:45 +0000569 /* We apply the mask because in a very small
570 number of corrupt PNGs, (transpColor->red >
571 255) and (bitDepth == 8), for certain versions
572 of libpng. For safety we assume the same could
573 happen with a grayscale PNG. */
574 *theTranspColorp = SkPackARGB32(0xFF,
575 0xFF & (transpColor->gray),
576 0xFF & (transpColor->gray),
577 0xFF & (transpColor->gray));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000578 }
579 }
580 }
581
582 if (valid ||
583 PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
584 PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
585 *hasAlphap = true;
586 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000587
588 SrcDepth srcDepth = k32Bit_SrcDepth;
589 if (PNG_COLOR_TYPE_GRAY == colorType) {
590 srcDepth = k8BitGray_SrcDepth;
scroggo@google.com8e2ef012013-07-18 20:14:45 +0000591 // Remove this assert, which fails on desk_pokemonwiki.skp
592 //SkASSERT(!*hasAlphap);
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000593 }
594
reed6c225732014-06-09 19:52:07 -0700595 *colorTypep = this->getPrefColorType(srcDepth, *hasAlphap);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000596 // now match the request against our capabilities
597 if (*hasAlphap) {
reed6c225732014-06-09 19:52:07 -0700598 if (*colorTypep != kARGB_4444_SkColorType) {
599 *colorTypep = kN32_SkColorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000600 }
601 } else {
reed6c225732014-06-09 19:52:07 -0700602 if (kAlpha_8_SkColorType == *colorTypep) {
scroggo@google.com354fd972013-10-02 15:50:19 +0000603 if (k8BitGray_SrcDepth != srcDepth) {
604 // Converting a non grayscale image to A8 is not currently supported.
reed6c225732014-06-09 19:52:07 -0700605 *colorTypep = kN32_SkColorType;
scroggo@google.com354fd972013-10-02 15:50:19 +0000606 }
reed6c225732014-06-09 19:52:07 -0700607 } else if (*colorTypep != kRGB_565_SkColorType &&
608 *colorTypep != kARGB_4444_SkColorType) {
609 *colorTypep = kN32_SkColorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000610 }
611 }
612 }
613
614 // sanity check for size
615 {
reed@google.com57212f92013-12-30 14:40:38 +0000616 int64_t size = sk_64_mul(origWidth, origHeight);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000617 // now check that if we are 4-bytes per pixel, we also don't overflow
reed@google.com57212f92013-12-30 14:40:38 +0000618 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000619 return false;
620 }
621 }
622
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000623 // If the image has alpha and the decoder wants unpremultiplied
reed0689d7b2014-06-14 05:30:20 -0700624 // colors, the only supported colortype is 8888.
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000625 if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
reed6c225732014-06-09 19:52:07 -0700626 *colorTypep = kN32_SkColorType;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000627 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000628
629 if (fImageIndex != NULL) {
reed6c225732014-06-09 19:52:07 -0700630 if (kUnknown_SkColorType == fImageIndex->fColorType) {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000631 // This is the first time for this subset decode. From now on,
reed0689d7b2014-06-14 05:30:20 -0700632 // all decodes must be in the same colortype.
reed6c225732014-06-09 19:52:07 -0700633 fImageIndex->fColorType = *colorTypep;
634 } else if (fImageIndex->fColorType != *colorTypep) {
635 // Requesting a different colortype for a subsequent decode is not
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000636 // supported. Report failure before we make changes to png_ptr.
637 return false;
638 }
639 }
640
reed6c225732014-06-09 19:52:07 -0700641 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && *colorTypep != kAlpha_8_SkColorType;
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000642
643 // Unless the user is requesting A8, convert a grayscale image into RGB.
644 // GRAY_ALPHA will always be converted to RGB
645 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
646 png_set_gray_to_rgb(png_ptr);
647 }
648
649 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
650 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
651 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
652 }
653
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000654 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000655}
656
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000657typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
658
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000659bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
dml78acf962015-03-18 06:03:29 -0700660 int bitDepth, bool *hasAlphap,
661 bool *reallyHasAlphap,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000662 SkColorTable **colorTablep) {
663 int numPalette;
664 png_colorp palette;
665 png_bytep trans;
666 int numTrans;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000667
668 png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
669
reed@google.com0a6151d2013-10-10 14:44:56 +0000670 SkPMColor colorStorage[256]; // worst-case storage
671 SkPMColor* colorPtr = colorStorage;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000672
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000673 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
674 png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
675 *hasAlphap = (numTrans > 0);
676 } else {
677 numTrans = 0;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000678 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000679
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000680 // check for bad images that might make us crash
681 if (numTrans > numPalette) {
682 numTrans = numPalette;
683 }
684
685 int index = 0;
686 int transLessThanFF = 0;
687
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000688 // Choose which function to use to create the color table. If the final destination's
reed0689d7b2014-06-14 05:30:20 -0700689 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000690 PackColorProc proc;
691 if (this->getRequireUnpremultipliedColors()) {
692 proc = &SkPackARGB32NoCheck;
693 } else {
694 proc = &SkPreMultiplyARGB;
695 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000696 for (; index < numTrans; index++) {
697 transLessThanFF |= (int)*trans - 0xFF;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000698 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000699 palette++;
700 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000701 bool reallyHasAlpha = (transLessThanFF < 0);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000702
703 for (; index < numPalette; index++) {
704 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
705 palette++;
706 }
707
dml78acf962015-03-18 06:03:29 -0700708 /* BUGGY IMAGE WORKAROUND
709
710 Invalid images could contain pixel values that are greater than the number of palette
711 entries. Since we use pixel values as indices into the palette this could result in reading
712 beyond the end of the palette which could leak the contents of uninitialized memory. To
713 ensure this doesn't happen, we grow the colortable to the maximum size that can be
714 addressed by the bitdepth of the image and fill it with the last palette color or black if
715 the palette is empty (really broken image).
716 */
717 int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
718 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
719 for (; index < colorCount; index++) {
720 *colorPtr++ = lastColor;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000721 }
reed@google.com0a6151d2013-10-10 14:44:56 +0000722
reedc5e15a12014-09-29 12:10:27 -0700723 *colorTablep = SkNEW_ARGS(SkColorTable, (colorStorage, colorCount));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000724 *reallyHasAlphap = reallyHasAlpha;
725 return true;
726}
727
728#ifdef SK_BUILD_FOR_ANDROID
729
scroggo@google.comb5571b32013-09-25 21:34:24 +0000730bool SkPNGImageDecoder::onBuildTileIndex(SkStreamRewindable* sk_stream, int *width, int *height) {
scroggoa1193e42015-01-21 12:09:53 -0800731 SkAutoTDelete<SkStreamRewindable> streamDeleter(sk_stream);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000732 png_structp png_ptr;
733 png_infop info_ptr;
734
735 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
736 return false;
737 }
738
739 if (setjmp(png_jmpbuf(png_ptr)) != 0) {
740 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
741 return false;
742 }
743
744 png_uint_32 origWidth, origHeight;
745 int bitDepth, colorType;
746 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
747 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
748
749 *width = origWidth;
750 *height = origHeight;
751
752 png_build_index(png_ptr);
753
754 if (fImageIndex) {
755 SkDELETE(fImageIndex);
756 }
scroggoa1193e42015-01-21 12:09:53 -0800757 fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (streamDeleter.detach(), png_ptr, info_ptr));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000758
759 return true;
760}
761
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000762bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
763 if (NULL == fImageIndex) {
764 return false;
765 }
766
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000767 png_structp png_ptr = fImageIndex->fPng_ptr;
768 png_infop info_ptr = fImageIndex->fInfo_ptr;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000769 if (setjmp(png_jmpbuf(png_ptr))) {
770 return false;
771 }
772
773 png_uint_32 origWidth, origHeight;
reed6c225732014-06-09 19:52:07 -0700774 int bitDepth, pngColorType, interlaceType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000775 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
reed6c225732014-06-09 19:52:07 -0700776 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000777
778 SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
779
780 if (!rect.intersect(region)) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000781 // If the requested region is entirely outside the image, just
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000782 // returns false
783 return false;
784 }
785
reed6c225732014-06-09 19:52:07 -0700786 SkColorType colorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000787 bool hasAlpha = false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000788 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
789
reed0689d7b2014-06-14 05:30:20 -0700790 if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000791 return false;
792 }
793
794 const int sampleSize = this->getSampleSize();
795 SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
796
797 SkBitmap decodedBitmap;
reed6c225732014-06-09 19:52:07 -0700798 decodedBitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
799 colorType, kPremul_SkAlphaType));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000800
801 // from here down we are concerned with colortables and pixels
802
803 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
804 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
805 // draw lots faster if we can flag the bitmap has being opaque
806 bool reallyHasAlpha = false;
807 SkColorTable* colorTable = NULL;
808
reed6c225732014-06-09 19:52:07 -0700809 if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
dml78acf962015-03-18 06:03:29 -0700810 decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000811 }
812
813 SkAutoUnref aur(colorTable);
814
815 // Check ahead of time if the swap(dest, src) is possible.
816 // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
817 // If no, then we will use alloc to allocate pixels to prevent garbage collection.
818 int w = rect.width() / sampleSize;
819 int h = rect.height() / sampleSize;
820 const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
821 (h == decodedBitmap.height()) && bm->isNull();
reed6c225732014-06-09 19:52:07 -0700822 const bool needColorTable = kIndex_8_SkColorType == colorType;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000823 if (swapOnly) {
824 if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
825 return false;
826 }
827 } else {
reed84825042014-09-02 12:50:45 -0700828 if (!decodedBitmap.tryAllocPixels(NULL, needColorTable ? colorTable : NULL)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000829 return false;
830 }
831 }
832 SkAutoLockPixels alp(decodedBitmap);
833
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000834 /* Turn on interlace handling. REQUIRED if you are not using
835 * png_read_image(). To see how to handle interlacing passes,
836 * see the png_read_row() method below:
837 */
838 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
839 png_set_interlace_handling(png_ptr) : 1;
840
841 /* Optional call to gamma correct and add the background to the palette
842 * and update info structure. REQUIRED if you are expecting libpng to
843 * update the palette for you (ie you selected such a transform above).
844 */
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000845
846 // Direct access to png_ptr fields is deprecated in libpng > 1.2.
847#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000848 png_ptr->pass = 0;
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000849#else
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000850 // FIXME: This sets pass as desired, but also sets iwidth. Is that ok?
851 png_set_interlaced_pass(png_ptr, 0);
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000852#endif
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000853 png_read_update_info(png_ptr, info_ptr);
854
855 int actualTop = rect.fTop;
856
reed6c225732014-06-09 19:52:07 -0700857 if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType)
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000858 && 1 == sampleSize) {
reed6c225732014-06-09 19:52:07 -0700859 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000860 // For an A8 bitmap, we assume there is an alpha for speed. It is
861 // possible the bitmap is opaque, but that is an unlikely use case
862 // since it would not be very interesting.
863 reallyHasAlpha = true;
864 // A8 is only allowed if the original was GRAY.
reed6c225732014-06-09 19:52:07 -0700865 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000866 }
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000867
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000868 for (int i = 0; i < number_passes; i++) {
869 png_configure_decoder(png_ptr, &actualTop, i);
870 for (int j = 0; j < rect.fTop - actualTop; j++) {
871 uint8_t* bmRow = decodedBitmap.getAddr8(0, 0);
872 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
873 }
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000874 png_uint_32 bitmapHeight = (png_uint_32) decodedBitmap.height();
875 for (png_uint_32 y = 0; y < bitmapHeight; y++) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000876 uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
877 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
878 }
879 }
880 } else {
881 SkScaledBitmapSampler::SrcConfig sc;
882 int srcBytesPerPixel = 4;
883
884 if (colorTable != NULL) {
885 sc = SkScaledBitmapSampler::kIndex;
886 srcBytesPerPixel = 1;
reed6c225732014-06-09 19:52:07 -0700887 } else if (kAlpha_8_SkColorType == colorType) {
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000888 // A8 is only allowed if the original was GRAY.
reed6c225732014-06-09 19:52:07 -0700889 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo@google.comc70a3aa2013-07-18 20:03:15 +0000890 sc = SkScaledBitmapSampler::kGray;
891 srcBytesPerPixel = 1;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000892 } else if (hasAlpha) {
893 sc = SkScaledBitmapSampler::kRGBA;
894 } else {
895 sc = SkScaledBitmapSampler::kRGBX;
896 }
897
898 /* We have to pass the colortable explicitly, since we may have one
899 even if our decodedBitmap doesn't, due to the request that we
900 upscale png's palette to a direct model
901 */
mtklein775b8192014-12-02 09:11:25 -0800902 const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL;
903 if (!sampler.begin(&decodedBitmap, sc, *this, colors)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000904 return false;
905 }
906 const int height = decodedBitmap.height();
907
908 if (number_passes > 1) {
909 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
910 uint8_t* base = (uint8_t*)storage.get();
911 size_t rb = origWidth * srcBytesPerPixel;
912
913 for (int i = 0; i < number_passes; i++) {
914 png_configure_decoder(png_ptr, &actualTop, i);
915 for (int j = 0; j < rect.fTop - actualTop; j++) {
scroggofc7063b2014-07-25 13:54:43 -0700916 png_read_rows(png_ptr, &base, png_bytepp_NULL, 1);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000917 }
918 uint8_t* row = base;
919 for (int32_t y = 0; y < rect.height(); y++) {
920 uint8_t* bmRow = row;
921 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
922 row += rb;
923 }
924 }
925 // now sample it
926 base += sampler.srcY0() * rb;
927 for (int y = 0; y < height; y++) {
928 reallyHasAlpha |= sampler.next(base);
929 base += sampler.srcDY() * rb;
930 }
931 } else {
932 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
933 uint8_t* srcRow = (uint8_t*)storage.get();
934
935 png_configure_decoder(png_ptr, &actualTop, 0);
936 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
937
938 for (int i = 0; i < rect.fTop - actualTop; i++) {
scroggofc7063b2014-07-25 13:54:43 -0700939 png_read_rows(png_ptr, &srcRow, png_bytepp_NULL, 1);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000940 }
941 for (int y = 0; y < height; y++) {
942 uint8_t* tmp = srcRow;
943 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
944 reallyHasAlpha |= sampler.next(srcRow);
945 if (y < height - 1) {
946 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
947 }
948 }
949 }
950 }
951
952 if (0 != theTranspColor) {
953 reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
954 }
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000955 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
reed6c225732014-06-09 19:52:07 -0700956 switch (decodedBitmap.colorType()) {
957 case kIndex_8_SkColorType:
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000958 // Fall through.
reed6c225732014-06-09 19:52:07 -0700959 case kARGB_4444_SkColorType:
960 // We have chosen not to support unpremul for these colortypess.
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000961 return false;
962 default: {
963 // Fall through to finish the decode. This config either
964 // supports unpremul or it is irrelevant because it has no
965 // alpha (or only alpha).
966 // These brackets prevent a warning.
967 }
968 }
commit-bot@chromium.org546f70c2013-10-03 17:13:38 +0000969 }
reed@google.com383a6972013-10-21 14:00:07 +0000970 SkAlphaType alphaType = kOpaque_SkAlphaType;
971 if (reallyHasAlpha) {
972 if (this->getRequireUnpremultipliedColors()) {
973 alphaType = kUnpremul_SkAlphaType;
974 } else {
975 alphaType = kPremul_SkAlphaType;
976 }
977 }
978 decodedBitmap.setAlphaType(alphaType);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000979
980 if (swapOnly) {
981 bm->swap(decodedBitmap);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000982 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000983 }
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000984 return this->cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
985 region.width(), region.height(), 0, rect.y());
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000986}
987#endif
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989///////////////////////////////////////////////////////////////////////////////
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991#include "SkColorPriv.h"
992#include "SkUnPreMultiply.h"
993
994static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +0000995 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 if (!sk_stream->write(data, len)) {
997 png_error(png_ptr, "sk_write_fn Error!");
998 }
999}
1000
reed0689d7b2014-06-14 05:30:20 -07001001static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 // we don't care about search on alpha if we're kIndex8, since only the
1003 // colortable packing cares about that distinction, not the pixels
reed0689d7b2014-06-14 05:30:20 -07001004 if (kIndex_8_SkColorType == ct) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 hasAlpha = false; // we store false in the table entries for kIndex8
1006 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001007
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 static const struct {
reed0689d7b2014-06-14 05:30:20 -07001009 SkColorType fColorType;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 bool fHasAlpha;
1011 transform_scanline_proc fProc;
1012 } gMap[] = {
reed0689d7b2014-06-14 05:30:20 -07001013 { kRGB_565_SkColorType, false, transform_scanline_565 },
1014 { kN32_SkColorType, false, transform_scanline_888 },
1015 { kN32_SkColorType, true, transform_scanline_8888 },
1016 { kARGB_4444_SkColorType, false, transform_scanline_444 },
1017 { kARGB_4444_SkColorType, true, transform_scanline_4444 },
1018 { kIndex_8_SkColorType, false, transform_scanline_memcpy },
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 };
1020
1021 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
reed0689d7b2014-06-14 05:30:20 -07001022 if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 return gMap[i].fProc;
1024 }
1025 }
1026 sk_throw();
1027 return NULL;
1028}
1029
1030// return the minimum legal bitdepth (by png standards) for this many colortable
1031// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
1032// we can use fewer bits per in png
1033static int computeBitDepth(int colorCount) {
1034#if 0
1035 int bits = SkNextLog2(colorCount);
1036 SkASSERT(bits >= 1 && bits <= 8);
1037 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
1038 return SkNextPow2(bits);
1039#else
1040 // for the moment, we don't know how to pack bitdepth < 8
1041 return 8;
1042#endif
1043}
1044
1045/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
1046 pack trans[] and return the number of trans[] entries written. If hasAlpha
1047 is false, the return value will always be 0.
rmistry@google.comd6176b02012-08-23 18:14:13 +00001048
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 Note: this routine takes care of unpremultiplying the RGB values when we
1050 have alpha in the colortable, since png doesn't support premul colors
1051*/
reed@android.com6f252972009-01-14 16:46:16 +00001052static inline int pack_palette(SkColorTable* ctable,
reed@android.comb50a60c2009-01-14 17:51:08 +00001053 png_color* SK_RESTRICT palette,
1054 png_byte* SK_RESTRICT trans, bool hasAlpha) {
mtklein775b8192014-12-02 09:11:25 -08001055 const SkPMColor* SK_RESTRICT colors = ctable ? ctable->readColors() : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 const int ctCount = ctable->count();
1057 int i, num_trans = 0;
1058
1059 if (hasAlpha) {
1060 /* first see if we have some number of fully opaque at the end of the
1061 ctable. PNG allows num_trans < num_palette, but all of the trans
1062 entries must come first in the palette. If I was smarter, I'd
1063 reorder the indices and ctable so that all non-opaque colors came
1064 first in the palette. But, since that would slow down the encode,
1065 I'm leaving the indices and ctable order as is, and just looking
1066 at the tail of the ctable for opaqueness.
1067 */
1068 num_trans = ctCount;
1069 for (i = ctCount - 1; i >= 0; --i) {
1070 if (SkGetPackedA32(colors[i]) != 0xFF) {
1071 break;
1072 }
1073 num_trans -= 1;
1074 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 const SkUnPreMultiply::Scale* SK_RESTRICT table =
1077 SkUnPreMultiply::GetScaleTable();
1078
1079 for (i = 0; i < num_trans; i++) {
1080 const SkPMColor c = *colors++;
1081 const unsigned a = SkGetPackedA32(c);
1082 const SkUnPreMultiply::Scale s = table[a];
1083 trans[i] = a;
1084 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
1085 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
1086 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 // now fall out of this if-block to use common code for the trailing
1089 // opaque entries
1090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001091
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 // these (remaining) entries are opaque
1093 for (i = num_trans; i < ctCount; i++) {
1094 SkPMColor c = *colors++;
1095 palette[i].red = SkGetPackedR32(c);
1096 palette[i].green = SkGetPackedG32(c);
1097 palette[i].blue = SkGetPackedB32(c);
1098 }
1099 return num_trans;
1100}
1101
1102class SkPNGImageEncoder : public SkImageEncoder {
1103protected:
mtklein72c9faa2015-01-09 10:06:39 -08001104 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001105private:
1106 bool doEncode(SkWStream* stream, const SkBitmap& bm,
1107 const bool& hasAlpha, int colorType,
reed0689d7b2014-06-14 05:30:20 -07001108 int bitDepth, SkColorType ct,
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001109 png_color_8& sig_bit);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001110
1111 typedef SkImageEncoder INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112};
1113
reed0689d7b2014-06-14 05:30:20 -07001114bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int /*quality*/) {
1115 SkColorType ct = bitmap.colorType();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116
1117 const bool hasAlpha = !bitmap.isOpaque();
1118 int colorType = PNG_COLOR_MASK_COLOR;
1119 int bitDepth = 8; // default for color
1120 png_color_8 sig_bit;
1121
reed0689d7b2014-06-14 05:30:20 -07001122 switch (ct) {
1123 case kIndex_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 colorType |= PNG_COLOR_MASK_PALETTE;
1125 // fall through to the ARGB_8888 case
reed0689d7b2014-06-14 05:30:20 -07001126 case kN32_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 sig_bit.red = 8;
1128 sig_bit.green = 8;
1129 sig_bit.blue = 8;
1130 sig_bit.alpha = 8;
1131 break;
reed0689d7b2014-06-14 05:30:20 -07001132 case kARGB_4444_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 sig_bit.red = 4;
1134 sig_bit.green = 4;
1135 sig_bit.blue = 4;
1136 sig_bit.alpha = 4;
1137 break;
reed0689d7b2014-06-14 05:30:20 -07001138 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 sig_bit.red = 5;
1140 sig_bit.green = 6;
1141 sig_bit.blue = 5;
1142 sig_bit.alpha = 0;
1143 break;
1144 default:
1145 return false;
1146 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001147
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 if (hasAlpha) {
1149 // don't specify alpha if we're a palette, even if our ctable has alpha
1150 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
1151 colorType |= PNG_COLOR_MASK_ALPHA;
1152 }
1153 } else {
1154 sig_bit.alpha = 0;
1155 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001156
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 SkAutoLockPixels alp(bitmap);
1158 // readyToDraw checks for pixels (and colortable if that is required)
1159 if (!bitmap.readyToDraw()) {
1160 return false;
1161 }
1162
1163 // we must do this after we have locked the pixels
1164 SkColorTable* ctable = bitmap.getColorTable();
bsalomon49f085d2014-09-05 13:34:00 -07001165 if (ctable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 if (ctable->count() == 0) {
1167 return false;
1168 }
1169 // check if we can store in fewer than 8 bits
1170 bitDepth = computeBitDepth(ctable->count());
1171 }
1172
reed0689d7b2014-06-14 05:30:20 -07001173 return doEncode(stream, bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit);
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001174}
1175
1176bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
1177 const bool& hasAlpha, int colorType,
reed0689d7b2014-06-14 05:30:20 -07001178 int bitDepth, SkColorType ct,
tomhudson@google.com5c210c72011-07-28 21:06:40 +00001179 png_color_8& sig_bit) {
1180
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 png_structp png_ptr;
1182 png_infop info_ptr;
1183
1184 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
1185 NULL);
1186 if (NULL == png_ptr) {
1187 return false;
1188 }
1189
1190 info_ptr = png_create_info_struct(png_ptr);
1191 if (NULL == info_ptr) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001192 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 return false;
1194 }
1195
1196 /* Set error handling. REQUIRED if you aren't supplying your own
1197 * error handling functions in the png_create_write_struct() call.
1198 */
1199 if (setjmp(png_jmpbuf(png_ptr))) {
1200 png_destroy_write_struct(&png_ptr, &info_ptr);
1201 return false;
1202 }
1203
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001204 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205
1206 /* Set the image information here. Width and height are up to 2^31,
1207 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
1208 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
1209 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
1210 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
1211 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
1212 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
1213 */
1214
1215 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
1216 bitDepth, colorType,
1217 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1218 PNG_FILTER_TYPE_BASE);
1219
reed@android.com61898772009-07-07 19:38:01 +00001220 // set our colortable/trans arrays if needed
1221 png_color paletteColors[256];
1222 png_byte trans[256];
reed0689d7b2014-06-14 05:30:20 -07001223 if (kIndex_8_SkColorType == ct) {
reed@android.com61898772009-07-07 19:38:01 +00001224 SkColorTable* ct = bitmap.getColorTable();
1225 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
1226 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
1227 if (numTrans > 0) {
1228 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
1229 }
1230 }
kkinnunen93b255b2014-10-19 22:07:23 -07001231#ifdef PNG_sBIT_SUPPORTED
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
kkinnunen93b255b2014-10-19 22:07:23 -07001233#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 png_write_info(png_ptr, info_ptr);
1235
1236 const char* srcImage = (const char*)bitmap.getPixels();
1237 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
1238 char* storage = (char*)rowStorage.get();
reed0689d7b2014-06-14 05:30:20 -07001239 transform_scanline_proc proc = choose_proc(ct, hasAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240
1241 for (int y = 0; y < bitmap.height(); y++) {
1242 png_bytep row_ptr = (png_bytep)storage;
1243 proc(srcImage, bitmap.width(), storage);
1244 png_write_rows(png_ptr, &row_ptr, 1);
1245 srcImage += bitmap.rowBytes();
1246 }
1247
1248 png_write_end(png_ptr, info_ptr);
1249
1250 /* clean up after the write, and free any memory allocated */
1251 png_destroy_write_struct(&png_ptr, &info_ptr);
1252 return true;
1253}
1254
reed@android.com00bf85a2009-01-22 13:04:56 +00001255///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001256DEFINE_DECODER_CREATOR(PNGImageDecoder);
1257DEFINE_ENCODER_CREATOR(PNGImageEncoder);
1258///////////////////////////////////////////////////////////////////////////////
reed@android.com00bf85a2009-01-22 13:04:56 +00001259
scroggo@google.comb5571b32013-09-25 21:34:24 +00001260static bool is_png(SkStreamRewindable* stream) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001261 char buf[PNG_BYTES_TO_CHECK];
1262 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
1263 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001264 return true;
1265 }
1266 return false;
1267}
1268
scroggo@google.comb5571b32013-09-25 21:34:24 +00001269SkImageDecoder* sk_libpng_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001270 if (is_png(stream)) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001271 return SkNEW(SkPNGImageDecoder);
1272 }
1273 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
scroggo@google.comb5571b32013-09-25 21:34:24 +00001276static SkImageDecoder::Format get_format_png(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001277 if (is_png(stream)) {
1278 return SkImageDecoder::kPNG_Format;
1279 }
1280 return SkImageDecoder::kUnknown_Format;
1281}
1282
reed@android.comdfee5792010-04-15 14:24:50 +00001283SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
reed@android.com00bf85a2009-01-22 13:04:56 +00001284 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
1285}
1286
mtklein@google.combd6343b2013-09-04 17:20:18 +00001287static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory);
1288static SkImageDecoder_FormatReg gFormatReg(get_format_png);
1289static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory);