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