blob: 81e73ef85460934d8a686b21b91ab8426312197e [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/images/SkImageDecoder_libpng.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +000019#include "SkImageEncoder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkColor.h"
21#include "SkColorPriv.h"
22#include "SkDither.h"
23#include "SkMath.h"
24#include "SkScaledBitmapSampler.h"
25#include "SkStream.h"
26#include "SkTemplates.h"
27#include "SkUtils.h"
28
29extern "C" {
30#include "png.h"
31}
32
33class SkPNGImageDecoder : public SkImageDecoder {
34public:
35 virtual Format getFormat() const {
36 return kPNG_Format;
37 }
38
39protected:
40 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
41 SkBitmap::Config pref, Mode);
42};
43
44#ifndef png_jmpbuf
45# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
46#endif
47
48#define PNG_BYTES_TO_CHECK 4
49
50/* Automatically clean up after throwing an exception */
51struct PNGAutoClean {
52 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
53 ~PNGAutoClean() {
54 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
55 }
56private:
57 png_structp png_ptr;
58 png_infop info_ptr;
59};
60
reed@android.com8a1c16f2008-12-17 15:59:43 +000061static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
62 SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
63 size_t bytes = sk_stream->read(data, length);
64 if (bytes != length) {
65 png_error(png_ptr, "Read Error!");
66 }
67}
68
69static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
70 SkImageDecoder::Peeker* peeker =
71 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
72 // peek() returning true means continue decoding
73 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
74 1 : -1;
75}
76
77static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
78#if 0
79 SkDebugf("------ png error %s\n", msg);
80#endif
81 longjmp(png_jmpbuf(png_ptr), 1);
82}
83
84static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
85 for (int i = 0; i < count; i++) {
86 uint8_t* tmp = storage;
87 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
88 }
89}
90
91static bool pos_le(int value, int max) {
92 return value > 0 && value <= max;
93}
94
95static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
96 SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
97
98 bool reallyHasAlpha = false;
99
100 for (int y = bm->height() - 1; y >= 0; --y) {
101 SkPMColor* p = bm->getAddr32(0, y);
102 for (int x = bm->width() - 1; x >= 0; --x) {
103 if (match == *p) {
104 *p = 0;
105 reallyHasAlpha = true;
106 }
107 p += 1;
108 }
109 }
110 return reallyHasAlpha;
111}
112
113bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
114 SkBitmap::Config prefConfig, Mode mode) {
115// SkAutoTrace apr("SkPNGImageDecoder::onDecode");
116
117 /* Create and initialize the png_struct with the desired error handler
118 * functions. If you want to use the default stderr and longjump method,
119 * you can supply NULL for the last three parameters. We also supply the
120 * the compiler header file version, so that we know if the application
121 * was compiled with a compatible version of the library. */
122 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
123 NULL, sk_error_fn, NULL);
124 // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
125 if (png_ptr == NULL) {
126 return false;
127 }
128
129 /* Allocate/initialize the memory for image information. */
130 png_infop info_ptr = png_create_info_struct(png_ptr);
131 if (info_ptr == NULL) {
132 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
133 return false;
134 }
135
136 PNGAutoClean autoClean(png_ptr, info_ptr);
137
138 /* Set error handling if you are using the setjmp/longjmp method (this is
139 * the normal method of doing things with libpng). REQUIRED unless you
140 * set up your own error handlers in the png_create_read_struct() earlier.
141 */
142 if (setjmp(png_jmpbuf(png_ptr))) {
143 return false;
144 }
145
146 /* If you are using replacement read functions, instead of calling
147 * png_init_io() here you would call:
148 */
149 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
150 /* where user_io_ptr is a structure you want available to the callbacks */
151 /* If we have already read some of the signature */
152// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
153
154 // hookup our peeker so we can see any user-chunks the caller may be interested in
155 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
156 if (this->getPeeker()) {
157 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
158 }
159
160 /* The call to png_read_info() gives us all of the information from the
161 * PNG file before the first IDAT (image data chunk). */
162 png_read_info(png_ptr, info_ptr);
163 png_uint_32 origWidth, origHeight;
164 int bit_depth, color_type, interlace_type;
165 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type,
166 &interlace_type, int_p_NULL, int_p_NULL);
167
168 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
169 if (bit_depth == 16) {
170 png_set_strip_16(png_ptr);
171 }
172 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
173 * byte into separate bytes (useful for paletted and grayscale images). */
174 if (bit_depth < 8) {
175 png_set_packing(png_ptr);
176 }
177 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
178 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
179 png_set_gray_1_2_4_to_8(png_ptr);
180 }
181
182 /* Make a grayscale image into RGB. */
183 if (color_type == PNG_COLOR_TYPE_GRAY ||
184 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
185 png_set_gray_to_rgb(png_ptr);
186 }
187
188 SkBitmap::Config config;
189 bool hasAlpha = false;
190 bool doDither = this->getDitherImage();
191 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
192
193 // check for sBIT chunk data, in case we should disable dithering because
194 // our data is not truely 8bits per component
195 if (doDither) {
196#if 0
197 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
198 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
199 info_ptr->sig_bit.alpha);
200#endif
201 // 0 seems to indicate no information available
202 if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
203 pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
204 pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
205 doDither = false;
206 }
207 }
208
209 if (color_type == PNG_COLOR_TYPE_PALETTE) {
210 config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha
211 } else {
212 png_color_16p transpColor = NULL;
213 int numTransp = 0;
214
215 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
216
217 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
218
219 if (valid && numTransp == 1 && transpColor != NULL) {
220 /* Compute our transparent color, which we'll match against later.
221 We don't really handle 16bit components properly here, since we
222 do our compare *after* the values have been knocked down to 8bit
223 which means we will find more matches than we should. The real
224 fix seems to be to see the actual 16bit components, do the
225 compare, and then knock it down to 8bits ourselves.
226 */
227 if (color_type & PNG_COLOR_MASK_COLOR) {
228 if (16 == bit_depth) {
229 theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
230 transpColor->green >> 8, transpColor->blue >> 8);
231 } else {
232 theTranspColor = SkPackARGB32(0xFF, transpColor->red,
233 transpColor->green, transpColor->blue);
234 }
235 } else { // gray
236 if (16 == bit_depth) {
237 theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
238 transpColor->gray >> 8, transpColor->gray >> 8);
239 } else {
240 theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
241 transpColor->gray, transpColor->gray);
242 }
243 }
244 }
245
246 if (valid ||
247 PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
248 PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
249 hasAlpha = true;
250 config = SkBitmap::kARGB_8888_Config;
251 } else { // we get to choose the config
252 config = prefConfig;
253 if (config == SkBitmap::kNo_Config) {
254 config = SkImageDecoder::GetDeviceConfig();
255 }
256 if (config != SkBitmap::kRGB_565_Config &&
257 config != SkBitmap::kARGB_4444_Config) {
258 config = SkBitmap::kARGB_8888_Config;
259 }
260 }
261 }
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000262
263 // sanity check for size
264 {
265 Sk64 size;
266 size.setMul(origWidth, origHeight);
267 if (size.isNeg() || !size.is32()) {
268 return false;
269 }
270 // now check that if we are 4-bytes per pixel, we also don't overflow
271 if (size.get32() > (0x7FFFFFFF >> 2)) {
272 return false;
273 }
274 }
275
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
277 return false;
278 }
279
280 const int sampleSize = this->getSampleSize();
281 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
282
283 decodedBitmap->setConfig(config, sampler.scaledWidth(),
284 sampler.scaledHeight(), 0);
285 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
286 return true;
287 }
288
289 // from here down we are concerned with colortables and pixels
290
291 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
292 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
293 // draw lots faster if we can flag the bitmap has being opaque
294 bool reallyHasAlpha = false;
295
296 SkColorTable* colorTable = NULL;
297
298 if (color_type == PNG_COLOR_TYPE_PALETTE) {
299 int num_palette;
300 png_colorp palette;
301 png_bytep trans;
302 int num_trans;
303
304 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
305
306 /* BUGGY IMAGE WORKAROUND
307
308 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
309 which is a problem since we use the byte as an index. To work around this we grow
310 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
311 */
312 int colorCount = num_palette + (num_palette < 256);
313
314 colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
315
316 SkPMColor* colorPtr = colorTable->lockColors();
317 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
318 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
319 hasAlpha = (num_trans > 0);
320 } else {
321 num_trans = 0;
322 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
323 }
324 // check for bad images that might make us crash
325 if (num_trans > num_palette) {
326 num_trans = num_palette;
327 }
328
329 int index = 0;
330 int transLessThanFF = 0;
331
332 for (; index < num_trans; index++) {
333 transLessThanFF |= (int)*trans - 0xFF;
334 *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
335 palette++;
336 }
337 reallyHasAlpha |= (transLessThanFF < 0);
338
339 for (; index < num_palette; index++) {
340 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
341 palette++;
342 }
343
344 // see BUGGY IMAGE WORKAROUND comment above
345 if (num_palette < 256) {
346 *colorPtr = colorPtr[-1];
347 }
348 colorTable->unlockColors(true);
349 }
350
351 SkAutoUnref aur(colorTable);
352
353 if (!this->allocPixelRef(decodedBitmap, colorTable)) {
354 return false;
355 }
356
357 SkAutoLockPixels alp(*decodedBitmap);
358
359 /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
360// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
361// ; // png_set_swap_alpha(png_ptr);
362
363 /* swap bytes of 16 bit files to least significant byte first */
364 // png_set_swap(png_ptr);
365
366 /* Add filler (or alpha) byte (before/after each RGB triplet) */
367 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
368 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
369 }
370
371 /* Turn on interlace handling. REQUIRED if you are not using
372 * png_read_image(). To see how to handle interlacing passes,
373 * see the png_read_row() method below:
374 */
375 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
376 png_set_interlace_handling(png_ptr) : 1;
377
378 /* Optional call to gamma correct and add the background to the palette
379 * and update info structure. REQUIRED if you are expecting libpng to
380 * update the palette for you (ie you selected such a transform above).
381 */
382 png_read_update_info(png_ptr, info_ptr);
383
384 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
385 for (int i = 0; i < number_passes; i++) {
386 for (png_uint_32 y = 0; y < origHeight; y++) {
387 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
388 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
389 }
390 }
391 } else {
392 SkScaledBitmapSampler::SrcConfig sc;
393 int srcBytesPerPixel = 4;
394
395 if (SkBitmap::kIndex8_Config == config) {
396 sc = SkScaledBitmapSampler::kIndex;
397 srcBytesPerPixel = 1;
398 } else if (hasAlpha) {
399 sc = SkScaledBitmapSampler::kRGBA;
400 } else {
401 sc = SkScaledBitmapSampler::kRGBX;
402 }
reed@android.com862e91b2009-04-28 15:27:07 +0000403 if (!sampler.begin(decodedBitmap, sc, doDither)) {
404 return false;
405 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 const int height = decodedBitmap->height();
407
reed@android.com862e91b2009-04-28 15:27:07 +0000408 if (number_passes > 1) {
409 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
410 uint8_t* base = (uint8_t*)storage.get();
411 size_t rb = origWidth * srcBytesPerPixel;
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000412
reed@android.com862e91b2009-04-28 15:27:07 +0000413 for (int i = 0; i < number_passes; i++) {
414 uint8_t* row = base;
415 for (png_uint_32 y = 0; y < origHeight; y++) {
416 uint8_t* bmRow = row;
417 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
418 row += rb;
419 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 }
reed@android.com862e91b2009-04-28 15:27:07 +0000421 // now sample it
422 base += sampler.srcY0() * rb;
423 for (int y = 0; y < height; y++) {
424 reallyHasAlpha |= sampler.next(base);
425 base += sampler.srcDY() * rb;
426 }
427 } else {
428 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 uint8_t* srcRow = (uint8_t*)storage.get();
430 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
431
432 for (int y = 0; y < height; y++) {
433 uint8_t* tmp = srcRow;
434 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
435 reallyHasAlpha |= sampler.next(srcRow);
436 if (y < height - 1) {
437 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
438 }
439 }
reed@android.com862e91b2009-04-28 15:27:07 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 // skip the rest of the rows (if any)
442 png_uint_32 read = (height - 1) * sampler.srcDY() +
443 sampler.srcY0() + 1;
444 SkASSERT(read <= origHeight);
445 skip_src_rows(png_ptr, srcRow, origHeight - read);
446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 }
448
449 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
450 png_read_end(png_ptr, info_ptr);
451
452 if (0 != theTranspColor) {
453 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
454 }
455 decodedBitmap->setIsOpaque(!reallyHasAlpha);
456 return true;
457}
458
459///////////////////////////////////////////////////////////////////////////////
460
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461#include "SkColorPriv.h"
462#include "SkUnPreMultiply.h"
463
464static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
465 SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
466 if (!sk_stream->write(data, len)) {
467 png_error(png_ptr, "sk_write_fn Error!");
468 }
469}
470
471typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
472 int width, char* SK_RESTRICT dst);
473
474static void transform_scanline_565(const char* SK_RESTRICT src, int width,
475 char* SK_RESTRICT dst) {
476 const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
477 for (int i = 0; i < width; i++) {
478 unsigned c = *srcP++;
479 *dst++ = SkPacked16ToR32(c);
480 *dst++ = SkPacked16ToG32(c);
481 *dst++ = SkPacked16ToB32(c);
482 }
483}
484
485static void transform_scanline_888(const char* SK_RESTRICT src, int width,
486 char* SK_RESTRICT dst) {
487 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
488 for (int i = 0; i < width; i++) {
489 SkPMColor c = *srcP++;
490 *dst++ = SkGetPackedR32(c);
491 *dst++ = SkGetPackedG32(c);
492 *dst++ = SkGetPackedB32(c);
493 }
494}
495
496static void transform_scanline_444(const char* SK_RESTRICT src, int width,
497 char* SK_RESTRICT dst) {
498 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
499 for (int i = 0; i < width; i++) {
500 SkPMColor16 c = *srcP++;
501 *dst++ = SkPacked4444ToR32(c);
502 *dst++ = SkPacked4444ToG32(c);
503 *dst++ = SkPacked4444ToB32(c);
504 }
505}
506
507static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
508 char* SK_RESTRICT dst) {
509 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
510 const SkUnPreMultiply::Scale* SK_RESTRICT table =
511 SkUnPreMultiply::GetScaleTable();
512
513 for (int i = 0; i < width; i++) {
514 SkPMColor c = *srcP++;
515 unsigned a = SkGetPackedA32(c);
516 unsigned r = SkGetPackedR32(c);
517 unsigned g = SkGetPackedG32(c);
518 unsigned b = SkGetPackedB32(c);
519
520 if (0 != a && 255 != a) {
521 SkUnPreMultiply::Scale scale = table[a];
522 r = SkUnPreMultiply::ApplyScale(scale, r);
523 g = SkUnPreMultiply::ApplyScale(scale, g);
524 b = SkUnPreMultiply::ApplyScale(scale, b);
525 }
526 *dst++ = r;
527 *dst++ = g;
528 *dst++ = b;
529 *dst++ = a;
530 }
531}
532
533static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
534 char* SK_RESTRICT dst) {
535 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
536 const SkUnPreMultiply::Scale* SK_RESTRICT table =
537 SkUnPreMultiply::GetScaleTable();
538
539 for (int i = 0; i < width; i++) {
540 SkPMColor16 c = *srcP++;
541 unsigned a = SkPacked4444ToA32(c);
542 unsigned r = SkPacked4444ToR32(c);
543 unsigned g = SkPacked4444ToG32(c);
544 unsigned b = SkPacked4444ToB32(c);
545
546 if (0 != a && 255 != a) {
547 SkUnPreMultiply::Scale scale = table[a];
548 r = SkUnPreMultiply::ApplyScale(scale, r);
549 g = SkUnPreMultiply::ApplyScale(scale, g);
550 b = SkUnPreMultiply::ApplyScale(scale, b);
551 }
552 *dst++ = r;
553 *dst++ = g;
554 *dst++ = b;
555 *dst++ = a;
556 }
557}
558
559static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
560 char* SK_RESTRICT dst) {
561 memcpy(dst, src, width);
562}
563
564static transform_scanline_proc choose_proc(SkBitmap::Config config,
565 bool hasAlpha) {
566 // we don't care about search on alpha if we're kIndex8, since only the
567 // colortable packing cares about that distinction, not the pixels
568 if (SkBitmap::kIndex8_Config == config) {
569 hasAlpha = false; // we store false in the table entries for kIndex8
570 }
571
572 static const struct {
573 SkBitmap::Config fConfig;
574 bool fHasAlpha;
575 transform_scanline_proc fProc;
576 } gMap[] = {
577 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
578 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
579 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
580 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
581 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
582 { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
583 };
584
585 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
586 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
587 return gMap[i].fProc;
588 }
589 }
590 sk_throw();
591 return NULL;
592}
593
594// return the minimum legal bitdepth (by png standards) for this many colortable
595// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
596// we can use fewer bits per in png
597static int computeBitDepth(int colorCount) {
598#if 0
599 int bits = SkNextLog2(colorCount);
600 SkASSERT(bits >= 1 && bits <= 8);
601 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
602 return SkNextPow2(bits);
603#else
604 // for the moment, we don't know how to pack bitdepth < 8
605 return 8;
606#endif
607}
608
609/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
610 pack trans[] and return the number of trans[] entries written. If hasAlpha
611 is false, the return value will always be 0.
612
613 Note: this routine takes care of unpremultiplying the RGB values when we
614 have alpha in the colortable, since png doesn't support premul colors
615*/
reed@android.com6f252972009-01-14 16:46:16 +0000616static inline int pack_palette(SkColorTable* ctable,
reed@android.comb50a60c2009-01-14 17:51:08 +0000617 png_color* SK_RESTRICT palette,
618 png_byte* SK_RESTRICT trans, bool hasAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 SkAutoLockColors alc(ctable);
620 const SkPMColor* SK_RESTRICT colors = alc.colors();
621 const int ctCount = ctable->count();
622 int i, num_trans = 0;
623
624 if (hasAlpha) {
625 /* first see if we have some number of fully opaque at the end of the
626 ctable. PNG allows num_trans < num_palette, but all of the trans
627 entries must come first in the palette. If I was smarter, I'd
628 reorder the indices and ctable so that all non-opaque colors came
629 first in the palette. But, since that would slow down the encode,
630 I'm leaving the indices and ctable order as is, and just looking
631 at the tail of the ctable for opaqueness.
632 */
633 num_trans = ctCount;
634 for (i = ctCount - 1; i >= 0; --i) {
635 if (SkGetPackedA32(colors[i]) != 0xFF) {
636 break;
637 }
638 num_trans -= 1;
639 }
640
641 const SkUnPreMultiply::Scale* SK_RESTRICT table =
642 SkUnPreMultiply::GetScaleTable();
643
644 for (i = 0; i < num_trans; i++) {
645 const SkPMColor c = *colors++;
646 const unsigned a = SkGetPackedA32(c);
647 const SkUnPreMultiply::Scale s = table[a];
648 trans[i] = a;
649 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
650 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
651 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
652 }
653 // now fall out of this if-block to use common code for the trailing
654 // opaque entries
655 }
656
657 // these (remaining) entries are opaque
658 for (i = num_trans; i < ctCount; i++) {
659 SkPMColor c = *colors++;
660 palette[i].red = SkGetPackedR32(c);
661 palette[i].green = SkGetPackedG32(c);
662 palette[i].blue = SkGetPackedB32(c);
663 }
664 return num_trans;
665}
666
667class SkPNGImageEncoder : public SkImageEncoder {
668protected:
669 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
670};
671
672bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
673 int /*quality*/) {
674 SkBitmap::Config config = bitmap.getConfig();
675
676 const bool hasAlpha = !bitmap.isOpaque();
677 int colorType = PNG_COLOR_MASK_COLOR;
678 int bitDepth = 8; // default for color
679 png_color_8 sig_bit;
680
681 switch (config) {
682 case SkBitmap::kIndex8_Config:
683 colorType |= PNG_COLOR_MASK_PALETTE;
684 // fall through to the ARGB_8888 case
685 case SkBitmap::kARGB_8888_Config:
686 sig_bit.red = 8;
687 sig_bit.green = 8;
688 sig_bit.blue = 8;
689 sig_bit.alpha = 8;
690 break;
691 case SkBitmap::kARGB_4444_Config:
692 sig_bit.red = 4;
693 sig_bit.green = 4;
694 sig_bit.blue = 4;
695 sig_bit.alpha = 4;
696 break;
697 case SkBitmap::kRGB_565_Config:
698 sig_bit.red = 5;
699 sig_bit.green = 6;
700 sig_bit.blue = 5;
701 sig_bit.alpha = 0;
702 break;
703 default:
704 return false;
705 }
706
707 if (hasAlpha) {
708 // don't specify alpha if we're a palette, even if our ctable has alpha
709 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
710 colorType |= PNG_COLOR_MASK_ALPHA;
711 }
712 } else {
713 sig_bit.alpha = 0;
714 }
715
716 SkAutoLockPixels alp(bitmap);
717 // readyToDraw checks for pixels (and colortable if that is required)
718 if (!bitmap.readyToDraw()) {
719 return false;
720 }
721
722 // we must do this after we have locked the pixels
723 SkColorTable* ctable = bitmap.getColorTable();
724 if (NULL != ctable) {
725 if (ctable->count() == 0) {
726 return false;
727 }
728 // check if we can store in fewer than 8 bits
729 bitDepth = computeBitDepth(ctable->count());
730 }
731
732 png_structp png_ptr;
733 png_infop info_ptr;
734
735 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
736 NULL);
737 if (NULL == png_ptr) {
738 return false;
739 }
740
741 info_ptr = png_create_info_struct(png_ptr);
742 if (NULL == info_ptr) {
743 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
744 return false;
745 }
746
747 /* Set error handling. REQUIRED if you aren't supplying your own
748 * error handling functions in the png_create_write_struct() call.
749 */
750 if (setjmp(png_jmpbuf(png_ptr))) {
751 png_destroy_write_struct(&png_ptr, &info_ptr);
752 return false;
753 }
754
755 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
756
757 /* Set the image information here. Width and height are up to 2^31,
758 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
759 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
760 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
761 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
762 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
763 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
764 */
765
766 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
767 bitDepth, colorType,
768 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
769 PNG_FILTER_TYPE_BASE);
770
reed@android.com61898772009-07-07 19:38:01 +0000771 // set our colortable/trans arrays if needed
772 png_color paletteColors[256];
773 png_byte trans[256];
774 if (SkBitmap::kIndex8_Config == config) {
775 SkColorTable* ct = bitmap.getColorTable();
776 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
777 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
778 if (numTrans > 0) {
779 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
780 }
781 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782
783 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
784 png_write_info(png_ptr, info_ptr);
785
786 const char* srcImage = (const char*)bitmap.getPixels();
787 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
788 char* storage = (char*)rowStorage.get();
789 transform_scanline_proc proc = choose_proc(config, hasAlpha);
790
791 for (int y = 0; y < bitmap.height(); y++) {
792 png_bytep row_ptr = (png_bytep)storage;
793 proc(srcImage, bitmap.width(), storage);
794 png_write_rows(png_ptr, &row_ptr, 1);
795 srcImage += bitmap.rowBytes();
796 }
797
798 png_write_end(png_ptr, info_ptr);
799
800 /* clean up after the write, and free any memory allocated */
801 png_destroy_write_struct(&png_ptr, &info_ptr);
802 return true;
803}
804
reed@android.com00bf85a2009-01-22 13:04:56 +0000805///////////////////////////////////////////////////////////////////////////////
806
807#include "SkTRegistry.h"
808
809static SkImageDecoder* DFactory(SkStream* stream) {
810 char buf[PNG_BYTES_TO_CHECK];
811 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
812 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
813 return SkNEW(SkPNGImageDecoder);
814 }
815 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816}
817
reed@android.com00bf85a2009-01-22 13:04:56 +0000818static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
819 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
820}
821
822static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
823static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);