blob: 1d1dd344330ebcbb47f7f1bb14134d63c6a45c26 [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 }
262
263 if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
264 return false;
265 }
266
267 const int sampleSize = this->getSampleSize();
268 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
269
270 decodedBitmap->setConfig(config, sampler.scaledWidth(),
271 sampler.scaledHeight(), 0);
272 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
273 return true;
274 }
275
276 // from here down we are concerned with colortables and pixels
277
278 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
279 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
280 // draw lots faster if we can flag the bitmap has being opaque
281 bool reallyHasAlpha = false;
282
283 SkColorTable* colorTable = NULL;
284
285 if (color_type == PNG_COLOR_TYPE_PALETTE) {
286 int num_palette;
287 png_colorp palette;
288 png_bytep trans;
289 int num_trans;
290
291 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
292
293 /* BUGGY IMAGE WORKAROUND
294
295 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
296 which is a problem since we use the byte as an index. To work around this we grow
297 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
298 */
299 int colorCount = num_palette + (num_palette < 256);
300
301 colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
302
303 SkPMColor* colorPtr = colorTable->lockColors();
304 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
305 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
306 hasAlpha = (num_trans > 0);
307 } else {
308 num_trans = 0;
309 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
310 }
311 // check for bad images that might make us crash
312 if (num_trans > num_palette) {
313 num_trans = num_palette;
314 }
315
316 int index = 0;
317 int transLessThanFF = 0;
318
319 for (; index < num_trans; index++) {
320 transLessThanFF |= (int)*trans - 0xFF;
321 *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
322 palette++;
323 }
324 reallyHasAlpha |= (transLessThanFF < 0);
325
326 for (; index < num_palette; index++) {
327 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
328 palette++;
329 }
330
331 // see BUGGY IMAGE WORKAROUND comment above
332 if (num_palette < 256) {
333 *colorPtr = colorPtr[-1];
334 }
335 colorTable->unlockColors(true);
336 }
337
338 SkAutoUnref aur(colorTable);
339
340 if (!this->allocPixelRef(decodedBitmap, colorTable)) {
341 return false;
342 }
343
344 SkAutoLockPixels alp(*decodedBitmap);
345
346 /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
347// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
348// ; // png_set_swap_alpha(png_ptr);
349
350 /* swap bytes of 16 bit files to least significant byte first */
351 // png_set_swap(png_ptr);
352
353 /* Add filler (or alpha) byte (before/after each RGB triplet) */
354 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
355 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
356 }
357
358 /* Turn on interlace handling. REQUIRED if you are not using
359 * png_read_image(). To see how to handle interlacing passes,
360 * see the png_read_row() method below:
361 */
362 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
363 png_set_interlace_handling(png_ptr) : 1;
364
365 /* Optional call to gamma correct and add the background to the palette
366 * and update info structure. REQUIRED if you are expecting libpng to
367 * update the palette for you (ie you selected such a transform above).
368 */
369 png_read_update_info(png_ptr, info_ptr);
370
371 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
372 for (int i = 0; i < number_passes; i++) {
373 for (png_uint_32 y = 0; y < origHeight; y++) {
374 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
375 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
376 }
377 }
378 } else {
379 SkScaledBitmapSampler::SrcConfig sc;
380 int srcBytesPerPixel = 4;
381
382 if (SkBitmap::kIndex8_Config == config) {
383 sc = SkScaledBitmapSampler::kIndex;
384 srcBytesPerPixel = 1;
385 } else if (hasAlpha) {
386 sc = SkScaledBitmapSampler::kRGBA;
387 } else {
388 sc = SkScaledBitmapSampler::kRGBX;
389 }
reed@android.com862e91b2009-04-28 15:27:07 +0000390 if (!sampler.begin(decodedBitmap, sc, doDither)) {
391 return false;
392 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 const int height = decodedBitmap->height();
394
reed@android.com862e91b2009-04-28 15:27:07 +0000395 if (number_passes > 1) {
396 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
397 uint8_t* base = (uint8_t*)storage.get();
398 size_t rb = origWidth * srcBytesPerPixel;
399
400 for (int i = 0; i < number_passes; i++) {
401 uint8_t* row = base;
402 for (png_uint_32 y = 0; y < origHeight; y++) {
403 uint8_t* bmRow = row;
404 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
405 row += rb;
406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 }
reed@android.com862e91b2009-04-28 15:27:07 +0000408 // now sample it
409 base += sampler.srcY0() * rb;
410 for (int y = 0; y < height; y++) {
411 reallyHasAlpha |= sampler.next(base);
412 base += sampler.srcDY() * rb;
413 }
414 } else {
415 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 uint8_t* srcRow = (uint8_t*)storage.get();
417 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
418
419 for (int y = 0; y < height; y++) {
420 uint8_t* tmp = srcRow;
421 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
422 reallyHasAlpha |= sampler.next(srcRow);
423 if (y < height - 1) {
424 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
425 }
426 }
reed@android.com862e91b2009-04-28 15:27:07 +0000427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 // skip the rest of the rows (if any)
429 png_uint_32 read = (height - 1) * sampler.srcDY() +
430 sampler.srcY0() + 1;
431 SkASSERT(read <= origHeight);
432 skip_src_rows(png_ptr, srcRow, origHeight - read);
433 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 }
435
436 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
437 png_read_end(png_ptr, info_ptr);
438
439 if (0 != theTranspColor) {
440 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
441 }
442 decodedBitmap->setIsOpaque(!reallyHasAlpha);
443 return true;
444}
445
446///////////////////////////////////////////////////////////////////////////////
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448#include "SkColorPriv.h"
449#include "SkUnPreMultiply.h"
450
451static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
452 SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
453 if (!sk_stream->write(data, len)) {
454 png_error(png_ptr, "sk_write_fn Error!");
455 }
456}
457
458typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
459 int width, char* SK_RESTRICT dst);
460
461static void transform_scanline_565(const char* SK_RESTRICT src, int width,
462 char* SK_RESTRICT dst) {
463 const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
464 for (int i = 0; i < width; i++) {
465 unsigned c = *srcP++;
466 *dst++ = SkPacked16ToR32(c);
467 *dst++ = SkPacked16ToG32(c);
468 *dst++ = SkPacked16ToB32(c);
469 }
470}
471
472static void transform_scanline_888(const char* SK_RESTRICT src, int width,
473 char* SK_RESTRICT dst) {
474 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
475 for (int i = 0; i < width; i++) {
476 SkPMColor c = *srcP++;
477 *dst++ = SkGetPackedR32(c);
478 *dst++ = SkGetPackedG32(c);
479 *dst++ = SkGetPackedB32(c);
480 }
481}
482
483static void transform_scanline_444(const char* SK_RESTRICT src, int width,
484 char* SK_RESTRICT dst) {
485 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
486 for (int i = 0; i < width; i++) {
487 SkPMColor16 c = *srcP++;
488 *dst++ = SkPacked4444ToR32(c);
489 *dst++ = SkPacked4444ToG32(c);
490 *dst++ = SkPacked4444ToB32(c);
491 }
492}
493
494static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
495 char* SK_RESTRICT dst) {
496 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
497 const SkUnPreMultiply::Scale* SK_RESTRICT table =
498 SkUnPreMultiply::GetScaleTable();
499
500 for (int i = 0; i < width; i++) {
501 SkPMColor c = *srcP++;
502 unsigned a = SkGetPackedA32(c);
503 unsigned r = SkGetPackedR32(c);
504 unsigned g = SkGetPackedG32(c);
505 unsigned b = SkGetPackedB32(c);
506
507 if (0 != a && 255 != a) {
508 SkUnPreMultiply::Scale scale = table[a];
509 r = SkUnPreMultiply::ApplyScale(scale, r);
510 g = SkUnPreMultiply::ApplyScale(scale, g);
511 b = SkUnPreMultiply::ApplyScale(scale, b);
512 }
513 *dst++ = r;
514 *dst++ = g;
515 *dst++ = b;
516 *dst++ = a;
517 }
518}
519
520static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
521 char* SK_RESTRICT dst) {
522 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
523 const SkUnPreMultiply::Scale* SK_RESTRICT table =
524 SkUnPreMultiply::GetScaleTable();
525
526 for (int i = 0; i < width; i++) {
527 SkPMColor16 c = *srcP++;
528 unsigned a = SkPacked4444ToA32(c);
529 unsigned r = SkPacked4444ToR32(c);
530 unsigned g = SkPacked4444ToG32(c);
531 unsigned b = SkPacked4444ToB32(c);
532
533 if (0 != a && 255 != a) {
534 SkUnPreMultiply::Scale scale = table[a];
535 r = SkUnPreMultiply::ApplyScale(scale, r);
536 g = SkUnPreMultiply::ApplyScale(scale, g);
537 b = SkUnPreMultiply::ApplyScale(scale, b);
538 }
539 *dst++ = r;
540 *dst++ = g;
541 *dst++ = b;
542 *dst++ = a;
543 }
544}
545
546static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
547 char* SK_RESTRICT dst) {
548 memcpy(dst, src, width);
549}
550
551static transform_scanline_proc choose_proc(SkBitmap::Config config,
552 bool hasAlpha) {
553 // we don't care about search on alpha if we're kIndex8, since only the
554 // colortable packing cares about that distinction, not the pixels
555 if (SkBitmap::kIndex8_Config == config) {
556 hasAlpha = false; // we store false in the table entries for kIndex8
557 }
558
559 static const struct {
560 SkBitmap::Config fConfig;
561 bool fHasAlpha;
562 transform_scanline_proc fProc;
563 } gMap[] = {
564 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
565 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
566 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
567 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
568 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
569 { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
570 };
571
572 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
573 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
574 return gMap[i].fProc;
575 }
576 }
577 sk_throw();
578 return NULL;
579}
580
581// return the minimum legal bitdepth (by png standards) for this many colortable
582// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
583// we can use fewer bits per in png
584static int computeBitDepth(int colorCount) {
585#if 0
586 int bits = SkNextLog2(colorCount);
587 SkASSERT(bits >= 1 && bits <= 8);
588 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
589 return SkNextPow2(bits);
590#else
591 // for the moment, we don't know how to pack bitdepth < 8
592 return 8;
593#endif
594}
595
596/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
597 pack trans[] and return the number of trans[] entries written. If hasAlpha
598 is false, the return value will always be 0.
599
600 Note: this routine takes care of unpremultiplying the RGB values when we
601 have alpha in the colortable, since png doesn't support premul colors
602*/
reed@android.com6f252972009-01-14 16:46:16 +0000603static inline int pack_palette(SkColorTable* ctable,
reed@android.comb50a60c2009-01-14 17:51:08 +0000604 png_color* SK_RESTRICT palette,
605 png_byte* SK_RESTRICT trans, bool hasAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 SkAutoLockColors alc(ctable);
607 const SkPMColor* SK_RESTRICT colors = alc.colors();
608 const int ctCount = ctable->count();
609 int i, num_trans = 0;
610
611 if (hasAlpha) {
612 /* first see if we have some number of fully opaque at the end of the
613 ctable. PNG allows num_trans < num_palette, but all of the trans
614 entries must come first in the palette. If I was smarter, I'd
615 reorder the indices and ctable so that all non-opaque colors came
616 first in the palette. But, since that would slow down the encode,
617 I'm leaving the indices and ctable order as is, and just looking
618 at the tail of the ctable for opaqueness.
619 */
620 num_trans = ctCount;
621 for (i = ctCount - 1; i >= 0; --i) {
622 if (SkGetPackedA32(colors[i]) != 0xFF) {
623 break;
624 }
625 num_trans -= 1;
626 }
627
628 const SkUnPreMultiply::Scale* SK_RESTRICT table =
629 SkUnPreMultiply::GetScaleTable();
630
631 for (i = 0; i < num_trans; i++) {
632 const SkPMColor c = *colors++;
633 const unsigned a = SkGetPackedA32(c);
634 const SkUnPreMultiply::Scale s = table[a];
635 trans[i] = a;
636 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
637 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
638 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
639 }
640 // now fall out of this if-block to use common code for the trailing
641 // opaque entries
642 }
643
644 // these (remaining) entries are opaque
645 for (i = num_trans; i < ctCount; i++) {
646 SkPMColor c = *colors++;
647 palette[i].red = SkGetPackedR32(c);
648 palette[i].green = SkGetPackedG32(c);
649 palette[i].blue = SkGetPackedB32(c);
650 }
651 return num_trans;
652}
653
654class SkPNGImageEncoder : public SkImageEncoder {
655protected:
656 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
657};
658
659bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
660 int /*quality*/) {
661 SkBitmap::Config config = bitmap.getConfig();
662
663 const bool hasAlpha = !bitmap.isOpaque();
664 int colorType = PNG_COLOR_MASK_COLOR;
665 int bitDepth = 8; // default for color
666 png_color_8 sig_bit;
667
668 switch (config) {
669 case SkBitmap::kIndex8_Config:
670 colorType |= PNG_COLOR_MASK_PALETTE;
671 // fall through to the ARGB_8888 case
672 case SkBitmap::kARGB_8888_Config:
673 sig_bit.red = 8;
674 sig_bit.green = 8;
675 sig_bit.blue = 8;
676 sig_bit.alpha = 8;
677 break;
678 case SkBitmap::kARGB_4444_Config:
679 sig_bit.red = 4;
680 sig_bit.green = 4;
681 sig_bit.blue = 4;
682 sig_bit.alpha = 4;
683 break;
684 case SkBitmap::kRGB_565_Config:
685 sig_bit.red = 5;
686 sig_bit.green = 6;
687 sig_bit.blue = 5;
688 sig_bit.alpha = 0;
689 break;
690 default:
691 return false;
692 }
693
694 if (hasAlpha) {
695 // don't specify alpha if we're a palette, even if our ctable has alpha
696 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
697 colorType |= PNG_COLOR_MASK_ALPHA;
698 }
699 } else {
700 sig_bit.alpha = 0;
701 }
702
703 SkAutoLockPixels alp(bitmap);
704 // readyToDraw checks for pixels (and colortable if that is required)
705 if (!bitmap.readyToDraw()) {
706 return false;
707 }
708
709 // we must do this after we have locked the pixels
710 SkColorTable* ctable = bitmap.getColorTable();
711 if (NULL != ctable) {
712 if (ctable->count() == 0) {
713 return false;
714 }
715 // check if we can store in fewer than 8 bits
716 bitDepth = computeBitDepth(ctable->count());
717 }
718
719 png_structp png_ptr;
720 png_infop info_ptr;
721
722 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
723 NULL);
724 if (NULL == png_ptr) {
725 return false;
726 }
727
728 info_ptr = png_create_info_struct(png_ptr);
729 if (NULL == info_ptr) {
730 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
731 return false;
732 }
733
734 /* Set error handling. REQUIRED if you aren't supplying your own
735 * error handling functions in the png_create_write_struct() call.
736 */
737 if (setjmp(png_jmpbuf(png_ptr))) {
738 png_destroy_write_struct(&png_ptr, &info_ptr);
739 return false;
740 }
741
742 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
743
744 /* Set the image information here. Width and height are up to 2^31,
745 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
746 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
747 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
748 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
749 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
750 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
751 */
752
753 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
754 bitDepth, colorType,
755 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
756 PNG_FILTER_TYPE_BASE);
757
758#if 0 // need to support this some day
759 /* set the palette if there is one. REQUIRED for indexed-color images */
760 palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
761 * png_sizeof (png_color));
762 /* ... set palette colors ... */
763 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
764 /* You must not free palette here, because png_set_PLTE only makes a link to
765 the palette that you malloced. Wait until you are about to destroy
766 the png structure. */
767#endif
768
769 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
770 png_write_info(png_ptr, info_ptr);
771
772 const char* srcImage = (const char*)bitmap.getPixels();
773 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
774 char* storage = (char*)rowStorage.get();
775 transform_scanline_proc proc = choose_proc(config, hasAlpha);
776
777 for (int y = 0; y < bitmap.height(); y++) {
778 png_bytep row_ptr = (png_bytep)storage;
779 proc(srcImage, bitmap.width(), storage);
780 png_write_rows(png_ptr, &row_ptr, 1);
781 srcImage += bitmap.rowBytes();
782 }
783
784 png_write_end(png_ptr, info_ptr);
785
786 /* clean up after the write, and free any memory allocated */
787 png_destroy_write_struct(&png_ptr, &info_ptr);
788 return true;
789}
790
reed@android.com00bf85a2009-01-22 13:04:56 +0000791///////////////////////////////////////////////////////////////////////////////
792
793#include "SkTRegistry.h"
794
795static SkImageDecoder* DFactory(SkStream* stream) {
796 char buf[PNG_BYTES_TO_CHECK];
797 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
798 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
799 return SkNEW(SkPNGImageDecoder);
800 }
801 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802}
803
reed@android.com00bf85a2009-01-22 13:04:56 +0000804static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
805 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
806}
807
808static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
809static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);