1
2 /*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10 #include "SkImageDecoder.h"
11 #include "SkImageEncoder.h"
12 #include "SkColor.h"
13 #include "SkColorPriv.h"
14 #include "SkDither.h"
15 #include "SkMath.h"
16 #include "SkScaledBitmapSampler.h"
17 #include "SkStream.h"
18 #include "SkTemplates.h"
19 #include "SkUtils.h"
20
21 extern "C" {
22 #include "png.h"
23 }
24
25 class SkPNGImageIndex {
26 public:
SkPNGImageIndex()27 SkPNGImageIndex() {
28 inputStream = NULL;
29 png_ptr = NULL;
30 }
~SkPNGImageIndex()31 virtual ~SkPNGImageIndex() {
32 if (png_ptr) {
33 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
34 }
35 if (inputStream) {
36 delete inputStream;
37 }
38 }
39 png_structp png_ptr;
40 png_infop info_ptr;
41 SkStream *inputStream;
42 };
43
44 class SkPNGImageDecoder : public SkImageDecoder {
45 public:
SkPNGImageDecoder()46 SkPNGImageDecoder() {
47 index = NULL;
48 }
getFormat() const49 virtual Format getFormat() const {
50 return kPNG_Format;
51 }
~SkPNGImageDecoder()52 virtual ~SkPNGImageDecoder() {
53 if (index) {
54 delete index;
55 }
56 }
57
58 protected:
59 virtual bool onBuildTileIndex(SkStream *stream,
60 int *width, int *height);
61 virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region);
62 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
63
64 private:
65 bool onDecodeInit(SkStream* stream, png_structp *png_ptrp,
66 png_infop *info_ptrp);
67 bool decodePalette(png_structp png_ptr, png_infop info_ptr,
68 bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep);
69 bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
70 SkBitmap::Config *config, bool *hasAlpha, bool *doDither,
71 SkPMColor *theTranspColor);
72 SkPNGImageIndex *index;
73 };
74
75 #ifndef png_jmpbuf
76 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
77 #endif
78
79 #define PNG_BYTES_TO_CHECK 4
80
81 /* Automatically clean up after throwing an exception */
82 struct PNGAutoClean {
PNGAutoCleanPNGAutoClean83 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
~PNGAutoCleanPNGAutoClean84 ~PNGAutoClean() {
85 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
86 }
87 private:
88 png_structp png_ptr;
89 png_infop info_ptr;
90 };
91
sk_read_fn(png_structp png_ptr,png_bytep data,png_size_t length)92 static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
93 SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
94 size_t bytes = sk_stream->read(data, length);
95 if (bytes != length) {
96 png_error(png_ptr, "Read Error!");
97 }
98 }
99
sk_seek_fn(png_structp png_ptr,png_uint_32 offset)100 static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
101 SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
102 sk_stream->rewind();
103 (void)sk_stream->skip(offset);
104 }
105
sk_read_user_chunk(png_structp png_ptr,png_unknown_chunkp chunk)106 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
107 SkImageDecoder::Peeker* peeker =
108 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
109 // peek() returning true means continue decoding
110 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
111 1 : -1;
112 }
113
sk_error_fn(png_structp png_ptr,png_const_charp msg)114 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
115 #if 0
116 SkDebugf("------ png error %s\n", msg);
117 #endif
118 longjmp(png_jmpbuf(png_ptr), 1);
119 }
120
skip_src_rows(png_structp png_ptr,uint8_t storage[],int count)121 static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
122 for (int i = 0; i < count; i++) {
123 uint8_t* tmp = storage;
124 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
125 }
126 }
127
pos_le(int value,int max)128 static bool pos_le(int value, int max) {
129 return value > 0 && value <= max;
130 }
131
substituteTranspColor(SkBitmap * bm,SkPMColor match)132 static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
133 SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
134
135 bool reallyHasAlpha = false;
136
137 for (int y = bm->height() - 1; y >= 0; --y) {
138 SkPMColor* p = bm->getAddr32(0, y);
139 for (int x = bm->width() - 1; x >= 0; --x) {
140 if (match == *p) {
141 *p = 0;
142 reallyHasAlpha = true;
143 }
144 p += 1;
145 }
146 }
147 return reallyHasAlpha;
148 }
149
canUpscalePaletteToConfig(SkBitmap::Config dstConfig,bool srcHasAlpha)150 static bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
151 bool srcHasAlpha) {
152 switch (dstConfig) {
153 case SkBitmap::kARGB_8888_Config:
154 case SkBitmap::kARGB_4444_Config:
155 return true;
156 case SkBitmap::kRGB_565_Config:
157 // only return true if the src is opaque (since 565 is opaque)
158 return !srcHasAlpha;
159 default:
160 return false;
161 }
162 }
163
164 // call only if color_type is PALETTE. Returns true if the ctable has alpha
hasTransparencyInPalette(png_structp png_ptr,png_infop info_ptr)165 static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
166 png_bytep trans;
167 int num_trans;
168
169 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
170 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
171 return num_trans > 0;
172 }
173 return false;
174 }
175
onDecodeInit(SkStream * sk_stream,png_structp * png_ptrp,png_infop * info_ptrp)176 bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream,
177 png_structp *png_ptrp, png_infop *info_ptrp)
178 {
179 /* Create and initialize the png_struct with the desired error handler
180 * functions. If you want to use the default stderr and longjump method,
181 * you can supply NULL for the last three parameters. We also supply the
182 * the compiler header file version, so that we know if the application
183 * was compiled with a compatible version of the library. */
184 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
185 NULL, sk_error_fn, NULL);
186 // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
187 if (png_ptr == NULL) {
188 return false;
189 }
190 *png_ptrp = png_ptr;
191
192 /* Allocate/initialize the memory for image information. */
193 png_infop info_ptr = png_create_info_struct(png_ptr);
194 if (info_ptr == NULL) {
195 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
196 return false;
197 }
198 *info_ptrp = info_ptr;
199
200 /* Set error handling if you are using the setjmp/longjmp method (this is
201 * the normal method of doing things with libpng). REQUIRED unless you
202 * set up your own error handlers in the png_create_read_struct() earlier.
203 */
204 if (setjmp(png_jmpbuf(png_ptr))) {
205 return false;
206 }
207
208 /* If you are using replacement read functions, instead of calling
209 * png_init_io() here you would call:
210 */
211 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
212 png_set_seek_fn(png_ptr, sk_seek_fn);
213 /* where user_io_ptr is a structure you want available to the callbacks */
214 /* If we have already read some of the signature */
215 // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
216
217 // hookup our peeker so we can see any user-chunks the caller may be interested in
218 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
219 if (this->getPeeker()) {
220 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
221 }
222
223 /* The call to png_read_info() gives us all of the information from the
224 * PNG file before the first IDAT (image data chunk). */
225 png_read_info(png_ptr, info_ptr);
226 png_uint_32 origWidth, origHeight;
227 int bit_depth, color_type, interlace_type;
228 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
229 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
230
231 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
232 if (bit_depth == 16) {
233 png_set_strip_16(png_ptr);
234 }
235 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
236 * byte into separate bytes (useful for paletted and grayscale images). */
237 if (bit_depth < 8) {
238 png_set_packing(png_ptr);
239 }
240 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
241 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
242 png_set_gray_1_2_4_to_8(png_ptr);
243 }
244
245 /* Make a grayscale image into RGB. */
246 if (color_type == PNG_COLOR_TYPE_GRAY ||
247 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
248 png_set_gray_to_rgb(png_ptr);
249 }
250 return true;
251 }
252
onDecode(SkStream * sk_stream,SkBitmap * decodedBitmap,Mode mode)253 bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
254 Mode mode) {
255 png_structp png_ptr;
256 png_infop info_ptr;
257
258 if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
259 return false;
260 }
261
262 if (setjmp(png_jmpbuf(png_ptr))) {
263 return false;
264 }
265
266 PNGAutoClean autoClean(png_ptr, info_ptr);
267
268 png_uint_32 origWidth, origHeight;
269 int bit_depth, color_type, interlace_type;
270 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
271 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
272
273 SkBitmap::Config config;
274 bool hasAlpha = false;
275 bool doDither = this->getDitherImage();
276 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
277
278 if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
279 &doDither, &theTranspColor) == false) {
280 return false;
281 }
282
283 const int sampleSize = this->getSampleSize();
284 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
285
286 decodedBitmap->lockPixels();
287 void* rowptr = (void*) decodedBitmap->getPixels();
288 bool reuseBitmap = (rowptr != NULL);
289 decodedBitmap->unlockPixels();
290 if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
291 sampler.scaledHeight() != decodedBitmap->height())) {
292 // Dimensions must match
293 return false;
294 }
295
296 if (!reuseBitmap) {
297 decodedBitmap->setConfig(config, sampler.scaledWidth(),
298 sampler.scaledHeight(), 0);
299 }
300 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
301 return true;
302 }
303
304 // from here down we are concerned with colortables and pixels
305
306 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
307 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
308 // draw lots faster if we can flag the bitmap has being opaque
309 bool reallyHasAlpha = false;
310 SkColorTable* colorTable = NULL;
311
312 if (color_type == PNG_COLOR_TYPE_PALETTE) {
313 decodePalette(png_ptr, info_ptr, &hasAlpha,
314 &reallyHasAlpha, &colorTable);
315 }
316
317 SkAutoUnref aur(colorTable);
318
319 if (!reuseBitmap) {
320 if (!this->allocPixelRef(decodedBitmap,
321 SkBitmap::kIndex8_Config == config ?
322 colorTable : NULL)) {
323 return false;
324 }
325 }
326
327 SkAutoLockPixels alp(*decodedBitmap);
328
329 /* Add filler (or alpha) byte (before/after each RGB triplet) */
330 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
331 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
332 }
333
334 /* Turn on interlace handling. REQUIRED if you are not using
335 * png_read_image(). To see how to handle interlacing passes,
336 * see the png_read_row() method below:
337 */
338 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
339 png_set_interlace_handling(png_ptr) : 1;
340
341 /* Optional call to gamma correct and add the background to the palette
342 * and update info structure. REQUIRED if you are expecting libpng to
343 * update the palette for you (ie you selected such a transform above).
344 */
345 png_read_update_info(png_ptr, info_ptr);
346
347 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
348 for (int i = 0; i < number_passes; i++) {
349 for (png_uint_32 y = 0; y < origHeight; y++) {
350 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
351 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
352 }
353 }
354 } else {
355 SkScaledBitmapSampler::SrcConfig sc;
356 int srcBytesPerPixel = 4;
357
358 if (colorTable != NULL) {
359 sc = SkScaledBitmapSampler::kIndex;
360 srcBytesPerPixel = 1;
361 } else if (hasAlpha) {
362 sc = SkScaledBitmapSampler::kRGBA;
363 } else {
364 sc = SkScaledBitmapSampler::kRGBX;
365 }
366
367 /* We have to pass the colortable explicitly, since we may have one
368 even if our decodedBitmap doesn't, due to the request that we
369 upscale png's palette to a direct model
370 */
371 SkAutoLockColors ctLock(colorTable);
372 if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
373 return false;
374 }
375 const int height = decodedBitmap->height();
376
377 if (number_passes > 1) {
378 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
379 uint8_t* base = (uint8_t*)storage.get();
380 size_t rb = origWidth * srcBytesPerPixel;
381
382 for (int i = 0; i < number_passes; i++) {
383 uint8_t* row = base;
384 for (png_uint_32 y = 0; y < origHeight; y++) {
385 uint8_t* bmRow = row;
386 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
387 row += rb;
388 }
389 }
390 // now sample it
391 base += sampler.srcY0() * rb;
392 for (int y = 0; y < height; y++) {
393 reallyHasAlpha |= sampler.next(base);
394 base += sampler.srcDY() * rb;
395 }
396 } else {
397 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
398 uint8_t* srcRow = (uint8_t*)storage.get();
399 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
400
401 for (int y = 0; y < height; y++) {
402 uint8_t* tmp = srcRow;
403 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
404 reallyHasAlpha |= sampler.next(srcRow);
405 if (y < height - 1) {
406 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
407 }
408 }
409
410 // skip the rest of the rows (if any)
411 png_uint_32 read = (height - 1) * sampler.srcDY() +
412 sampler.srcY0() + 1;
413 SkASSERT(read <= origHeight);
414 skip_src_rows(png_ptr, srcRow, origHeight - read);
415 }
416 }
417
418 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
419 png_read_end(png_ptr, info_ptr);
420
421 if (0 != theTranspColor) {
422 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
423 }
424 decodedBitmap->setIsOpaque(!reallyHasAlpha);
425 if (reuseBitmap) {
426 decodedBitmap->notifyPixelsChanged();
427 }
428 return true;
429 }
430
onBuildTileIndex(SkStream * sk_stream,int * width,int * height)431 bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width,
432 int *height) {
433 png_structp png_ptr;
434 png_infop info_ptr;
435
436 this->index = new SkPNGImageIndex();
437
438 if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
439 return false;
440 }
441
442 int bit_depth, color_type, interlace_type;
443 png_uint_32 origWidth, origHeight;
444 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
445 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
446
447 *width = origWidth;
448 *height = origHeight;
449
450 png_build_index(png_ptr);
451 this->index->png_ptr = png_ptr;
452 this->index->info_ptr = info_ptr;
453 return true;
454 }
455
getBitmapConfig(png_structp png_ptr,png_infop info_ptr,SkBitmap::Config * configp,bool * hasAlphap,bool * doDitherp,SkPMColor * theTranspColorp)456 bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
457 SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
458 SkPMColor *theTranspColorp) {
459 png_uint_32 origWidth, origHeight;
460 int bit_depth, color_type, interlace_type;
461 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
462 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
463
464 // check for sBIT chunk data, in case we should disable dithering because
465 // our data is not truely 8bits per component
466 if (*doDitherp) {
467 #if 0
468 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
469 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
470 info_ptr->sig_bit.alpha);
471 #endif
472 // 0 seems to indicate no information available
473 if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
474 pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
475 pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
476 *doDitherp = false;
477 }
478 }
479
480 if (color_type == PNG_COLOR_TYPE_PALETTE) {
481 bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
482 *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
483 // now see if we can upscale to their requested config
484 if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
485 *configp = SkBitmap::kIndex8_Config;
486 }
487 } else {
488 png_color_16p transpColor = NULL;
489 int numTransp = 0;
490
491 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
492
493 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
494
495 if (valid && numTransp == 1 && transpColor != NULL) {
496 /* Compute our transparent color, which we'll match against later.
497 We don't really handle 16bit components properly here, since we
498 do our compare *after* the values have been knocked down to 8bit
499 which means we will find more matches than we should. The real
500 fix seems to be to see the actual 16bit components, do the
501 compare, and then knock it down to 8bits ourselves.
502 */
503 if (color_type & PNG_COLOR_MASK_COLOR) {
504 if (16 == bit_depth) {
505 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
506 transpColor->green >> 8, transpColor->blue >> 8);
507 } else {
508 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
509 transpColor->green, transpColor->blue);
510 }
511 } else { // gray
512 if (16 == bit_depth) {
513 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
514 transpColor->gray >> 8, transpColor->gray >> 8);
515 } else {
516 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
517 transpColor->gray, transpColor->gray);
518 }
519 }
520 }
521
522 if (valid ||
523 PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
524 PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
525 *hasAlphap = true;
526 }
527 *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
528 // now match the request against our capabilities
529 if (*hasAlphap) {
530 if (*configp != SkBitmap::kARGB_4444_Config) {
531 *configp = SkBitmap::kARGB_8888_Config;
532 }
533 } else {
534 if (*configp != SkBitmap::kRGB_565_Config &&
535 *configp != SkBitmap::kARGB_4444_Config) {
536 *configp = SkBitmap::kARGB_8888_Config;
537 }
538 }
539 }
540
541 // sanity check for size
542 {
543 Sk64 size;
544 size.setMul(origWidth, origHeight);
545 if (size.isNeg() || !size.is32()) {
546 return false;
547 }
548 // now check that if we are 4-bytes per pixel, we also don't overflow
549 if (size.get32() > (0x7FFFFFFF >> 2)) {
550 return false;
551 }
552 }
553
554 if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
555 return false;
556 }
557 return true;
558 }
559
decodePalette(png_structp png_ptr,png_infop info_ptr,bool * hasAlphap,bool * reallyHasAlphap,SkColorTable ** colorTablep)560 bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
561 bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
562 int num_palette;
563 png_colorp palette;
564 png_bytep trans;
565 int num_trans;
566 bool reallyHasAlpha = false;
567 SkColorTable* colorTable = NULL;
568
569 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
570
571 /* BUGGY IMAGE WORKAROUND
572
573 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
574 which is a problem since we use the byte as an index. To work around this we grow
575 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
576 */
577 int colorCount = num_palette + (num_palette < 256);
578
579 colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
580
581 SkPMColor* colorPtr = colorTable->lockColors();
582 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
583 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
584 *hasAlphap = (num_trans > 0);
585 } else {
586 num_trans = 0;
587 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
588 }
589 // check for bad images that might make us crash
590 if (num_trans > num_palette) {
591 num_trans = num_palette;
592 }
593
594 int index = 0;
595 int transLessThanFF = 0;
596
597 for (; index < num_trans; index++) {
598 transLessThanFF |= (int)*trans - 0xFF;
599 *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
600 palette++;
601 }
602 reallyHasAlpha |= (transLessThanFF < 0);
603
604 for (; index < num_palette; index++) {
605 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
606 palette++;
607 }
608
609 // see BUGGY IMAGE WORKAROUND comment above
610 if (num_palette < 256) {
611 *colorPtr = colorPtr[-1];
612 }
613 colorTable->unlockColors(true);
614 *colorTablep = colorTable;
615 *reallyHasAlphap = reallyHasAlpha;
616 return true;
617 }
618
onDecodeRegion(SkBitmap * bm,SkIRect region)619 bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
620 int i;
621 png_structp png_ptr = this->index->png_ptr;
622 png_infop info_ptr = this->index->info_ptr;
623 if (setjmp(png_jmpbuf(png_ptr))) {
624 return false;
625 }
626
627 png_uint_32 origWidth, origHeight;
628 int bit_depth, color_type, interlace_type;
629 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
630 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
631
632 SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
633
634 if (!rect.intersect(region)) {
635 // If the requested region is entirely outsides the image, just
636 // returns false
637 return false;
638 }
639
640 SkBitmap::Config config;
641 bool hasAlpha = false;
642 bool doDither = this->getDitherImage();
643 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
644
645 if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
646 &doDither, &theTranspColor) == false) {
647 return false;
648 }
649
650 const int sampleSize = this->getSampleSize();
651 SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
652
653 SkBitmap *decodedBitmap = new SkBitmap;
654 SkAutoTDelete<SkBitmap> adb(decodedBitmap);
655
656 decodedBitmap->setConfig(config, sampler.scaledWidth(),
657 sampler.scaledHeight(), 0);
658
659 // from here down we are concerned with colortables and pixels
660
661 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
662 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
663 // draw lots faster if we can flag the bitmap has being opaque
664 bool reallyHasAlpha = false;
665 SkColorTable* colorTable = NULL;
666
667 if (color_type == PNG_COLOR_TYPE_PALETTE) {
668 decodePalette(png_ptr, info_ptr, &hasAlpha,
669 &reallyHasAlpha, &colorTable);
670 }
671
672 SkAutoUnref aur(colorTable);
673
674 // Check ahead of time if the swap(dest, src) is possible in crop or not.
675 // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
676 // If no, then we will use alloc to allocate pixels to prevent garbage collection.
677 int w = rect.width() / sampleSize;
678 int h = rect.height() / sampleSize;
679 bool swapOnly = (rect == region) && (w == decodedBitmap->width()) &&
680 (h == decodedBitmap->height()) &&
681 ((0 - rect.x()) / sampleSize == 0) && bm->isNull();
682 if (swapOnly) {
683 if (!this->allocPixelRef(decodedBitmap,
684 SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
685 return false;
686 }
687 } else {
688 if (!decodedBitmap->allocPixels(
689 NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
690 return false;
691 }
692 }
693 SkAutoLockPixels alp(*decodedBitmap);
694
695 /* Add filler (or alpha) byte (before/after each RGB triplet) */
696 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
697 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
698 }
699
700 /* Turn on interlace handling. REQUIRED if you are not using
701 * png_read_image(). To see how to handle interlacing passes,
702 * see the png_read_row() method below:
703 */
704 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
705 png_set_interlace_handling(png_ptr) : 1;
706
707 /* Optional call to gamma correct and add the background to the palette
708 * and update info structure. REQUIRED if you are expecting libpng to
709 * update the palette for you (ie you selected such a transform above).
710 */
711 png_ptr->pass = 0;
712 png_read_update_info(png_ptr, info_ptr);
713
714 int actualTop = rect.fTop;
715
716 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
717 for (int i = 0; i < number_passes; i++) {
718 png_configure_decoder(png_ptr, &actualTop, i);
719 for (int j = 0; j < rect.fTop - actualTop; j++) {
720 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
721 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
722 }
723 for (png_uint_32 y = 0; y < origHeight; y++) {
724 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
725 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
726 }
727 }
728 } else {
729 SkScaledBitmapSampler::SrcConfig sc;
730 int srcBytesPerPixel = 4;
731
732 if (colorTable != NULL) {
733 sc = SkScaledBitmapSampler::kIndex;
734 srcBytesPerPixel = 1;
735 } else if (hasAlpha) {
736 sc = SkScaledBitmapSampler::kRGBA;
737 } else {
738 sc = SkScaledBitmapSampler::kRGBX;
739 }
740
741 /* We have to pass the colortable explicitly, since we may have one
742 even if our decodedBitmap doesn't, due to the request that we
743 upscale png's palette to a direct model
744 */
745 SkAutoLockColors ctLock(colorTable);
746 if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
747 return false;
748 }
749 const int height = decodedBitmap->height();
750
751 if (number_passes > 1) {
752 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
753 uint8_t* base = (uint8_t*)storage.get();
754 size_t rb = origWidth * srcBytesPerPixel;
755
756 for (int i = 0; i < number_passes; i++) {
757 png_configure_decoder(png_ptr, &actualTop, i);
758 for (int j = 0; j < rect.fTop - actualTop; j++) {
759 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
760 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
761 }
762 uint8_t* row = base;
763 for (png_uint_32 y = 0; y < rect.height(); y++) {
764 uint8_t* bmRow = row;
765 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
766 row += rb;
767 }
768 }
769 // now sample it
770 base += sampler.srcY0() * rb;
771 for (int y = 0; y < height; y++) {
772 reallyHasAlpha |= sampler.next(base);
773 base += sampler.srcDY() * rb;
774 }
775 } else {
776 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
777 uint8_t* srcRow = (uint8_t*)storage.get();
778
779 png_configure_decoder(png_ptr, &actualTop, 0);
780 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
781
782 for (int i = 0; i < rect.fTop - actualTop; i++) {
783 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
784 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
785 }
786 for (int y = 0; y < height; y++) {
787 uint8_t* tmp = srcRow;
788 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
789 reallyHasAlpha |= sampler.next(srcRow);
790 if (y < height - 1) {
791 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
792 }
793 }
794 }
795 }
796 if (swapOnly) {
797 bm->swap(*decodedBitmap);
798 } else {
799 cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(),
800 region.width(), region.height(), 0, rect.y());
801 }
802
803 if (0 != theTranspColor) {
804 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
805 }
806 decodedBitmap->setIsOpaque(!reallyHasAlpha);
807 return true;
808 }
809
810 ///////////////////////////////////////////////////////////////////////////////
811
812 #include "SkColorPriv.h"
813 #include "SkUnPreMultiply.h"
814
sk_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)815 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
816 SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
817 if (!sk_stream->write(data, len)) {
818 png_error(png_ptr, "sk_write_fn Error!");
819 }
820 }
821
822 typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
823 int width, char* SK_RESTRICT dst);
824
transform_scanline_565(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)825 static void transform_scanline_565(const char* SK_RESTRICT src, int width,
826 char* SK_RESTRICT dst) {
827 const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
828 for (int i = 0; i < width; i++) {
829 unsigned c = *srcP++;
830 *dst++ = SkPacked16ToR32(c);
831 *dst++ = SkPacked16ToG32(c);
832 *dst++ = SkPacked16ToB32(c);
833 }
834 }
835
transform_scanline_888(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)836 static void transform_scanline_888(const char* SK_RESTRICT src, int width,
837 char* SK_RESTRICT dst) {
838 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
839 for (int i = 0; i < width; i++) {
840 SkPMColor c = *srcP++;
841 *dst++ = SkGetPackedR32(c);
842 *dst++ = SkGetPackedG32(c);
843 *dst++ = SkGetPackedB32(c);
844 }
845 }
846
transform_scanline_444(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)847 static void transform_scanline_444(const char* SK_RESTRICT src, int width,
848 char* SK_RESTRICT dst) {
849 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
850 for (int i = 0; i < width; i++) {
851 SkPMColor16 c = *srcP++;
852 *dst++ = SkPacked4444ToR32(c);
853 *dst++ = SkPacked4444ToG32(c);
854 *dst++ = SkPacked4444ToB32(c);
855 }
856 }
857
transform_scanline_8888(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)858 static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
859 char* SK_RESTRICT dst) {
860 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
861 const SkUnPreMultiply::Scale* SK_RESTRICT table =
862 SkUnPreMultiply::GetScaleTable();
863
864 for (int i = 0; i < width; i++) {
865 SkPMColor c = *srcP++;
866 unsigned a = SkGetPackedA32(c);
867 unsigned r = SkGetPackedR32(c);
868 unsigned g = SkGetPackedG32(c);
869 unsigned b = SkGetPackedB32(c);
870
871 if (0 != a && 255 != a) {
872 SkUnPreMultiply::Scale scale = table[a];
873 r = SkUnPreMultiply::ApplyScale(scale, r);
874 g = SkUnPreMultiply::ApplyScale(scale, g);
875 b = SkUnPreMultiply::ApplyScale(scale, b);
876 }
877 *dst++ = r;
878 *dst++ = g;
879 *dst++ = b;
880 *dst++ = a;
881 }
882 }
883
transform_scanline_4444(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)884 static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
885 char* SK_RESTRICT dst) {
886 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
887 const SkUnPreMultiply::Scale* SK_RESTRICT table =
888 SkUnPreMultiply::GetScaleTable();
889
890 for (int i = 0; i < width; i++) {
891 SkPMColor16 c = *srcP++;
892 unsigned a = SkPacked4444ToA32(c);
893 unsigned r = SkPacked4444ToR32(c);
894 unsigned g = SkPacked4444ToG32(c);
895 unsigned b = SkPacked4444ToB32(c);
896
897 if (0 != a && 255 != a) {
898 SkUnPreMultiply::Scale scale = table[a];
899 r = SkUnPreMultiply::ApplyScale(scale, r);
900 g = SkUnPreMultiply::ApplyScale(scale, g);
901 b = SkUnPreMultiply::ApplyScale(scale, b);
902 }
903 *dst++ = r;
904 *dst++ = g;
905 *dst++ = b;
906 *dst++ = a;
907 }
908 }
909
transform_scanline_index8(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)910 static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
911 char* SK_RESTRICT dst) {
912 memcpy(dst, src, width);
913 }
914
choose_proc(SkBitmap::Config config,bool hasAlpha)915 static transform_scanline_proc choose_proc(SkBitmap::Config config,
916 bool hasAlpha) {
917 // we don't care about search on alpha if we're kIndex8, since only the
918 // colortable packing cares about that distinction, not the pixels
919 if (SkBitmap::kIndex8_Config == config) {
920 hasAlpha = false; // we store false in the table entries for kIndex8
921 }
922
923 static const struct {
924 SkBitmap::Config fConfig;
925 bool fHasAlpha;
926 transform_scanline_proc fProc;
927 } gMap[] = {
928 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
929 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
930 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
931 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
932 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
933 { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
934 };
935
936 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
937 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
938 return gMap[i].fProc;
939 }
940 }
941 sk_throw();
942 return NULL;
943 }
944
945 // return the minimum legal bitdepth (by png standards) for this many colortable
946 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
947 // we can use fewer bits per in png
computeBitDepth(int colorCount)948 static int computeBitDepth(int colorCount) {
949 #if 0
950 int bits = SkNextLog2(colorCount);
951 SkASSERT(bits >= 1 && bits <= 8);
952 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
953 return SkNextPow2(bits);
954 #else
955 // for the moment, we don't know how to pack bitdepth < 8
956 return 8;
957 #endif
958 }
959
960 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
961 pack trans[] and return the number of trans[] entries written. If hasAlpha
962 is false, the return value will always be 0.
963
964 Note: this routine takes care of unpremultiplying the RGB values when we
965 have alpha in the colortable, since png doesn't support premul colors
966 */
pack_palette(SkColorTable * ctable,png_color * SK_RESTRICT palette,png_byte * SK_RESTRICT trans,bool hasAlpha)967 static inline int pack_palette(SkColorTable* ctable,
968 png_color* SK_RESTRICT palette,
969 png_byte* SK_RESTRICT trans, bool hasAlpha) {
970 SkAutoLockColors alc(ctable);
971 const SkPMColor* SK_RESTRICT colors = alc.colors();
972 const int ctCount = ctable->count();
973 int i, num_trans = 0;
974
975 if (hasAlpha) {
976 /* first see if we have some number of fully opaque at the end of the
977 ctable. PNG allows num_trans < num_palette, but all of the trans
978 entries must come first in the palette. If I was smarter, I'd
979 reorder the indices and ctable so that all non-opaque colors came
980 first in the palette. But, since that would slow down the encode,
981 I'm leaving the indices and ctable order as is, and just looking
982 at the tail of the ctable for opaqueness.
983 */
984 num_trans = ctCount;
985 for (i = ctCount - 1; i >= 0; --i) {
986 if (SkGetPackedA32(colors[i]) != 0xFF) {
987 break;
988 }
989 num_trans -= 1;
990 }
991
992 const SkUnPreMultiply::Scale* SK_RESTRICT table =
993 SkUnPreMultiply::GetScaleTable();
994
995 for (i = 0; i < num_trans; i++) {
996 const SkPMColor c = *colors++;
997 const unsigned a = SkGetPackedA32(c);
998 const SkUnPreMultiply::Scale s = table[a];
999 trans[i] = a;
1000 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
1001 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
1002 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
1003 }
1004 // now fall out of this if-block to use common code for the trailing
1005 // opaque entries
1006 }
1007
1008 // these (remaining) entries are opaque
1009 for (i = num_trans; i < ctCount; i++) {
1010 SkPMColor c = *colors++;
1011 palette[i].red = SkGetPackedR32(c);
1012 palette[i].green = SkGetPackedG32(c);
1013 palette[i].blue = SkGetPackedB32(c);
1014 }
1015 return num_trans;
1016 }
1017
1018 class SkPNGImageEncoder : public SkImageEncoder {
1019 protected:
1020 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
1021 private:
1022 bool doEncode(SkWStream* stream, const SkBitmap& bm,
1023 const bool& hasAlpha, int colorType,
1024 int bitDepth, SkBitmap::Config config,
1025 png_color_8& sig_bit);
1026 };
1027
onEncode(SkWStream * stream,const SkBitmap & bitmap,int)1028 bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
1029 int /*quality*/) {
1030 SkBitmap::Config config = bitmap.getConfig();
1031
1032 const bool hasAlpha = !bitmap.isOpaque();
1033 int colorType = PNG_COLOR_MASK_COLOR;
1034 int bitDepth = 8; // default for color
1035 png_color_8 sig_bit;
1036
1037 switch (config) {
1038 case SkBitmap::kIndex8_Config:
1039 colorType |= PNG_COLOR_MASK_PALETTE;
1040 // fall through to the ARGB_8888 case
1041 case SkBitmap::kARGB_8888_Config:
1042 sig_bit.red = 8;
1043 sig_bit.green = 8;
1044 sig_bit.blue = 8;
1045 sig_bit.alpha = 8;
1046 break;
1047 case SkBitmap::kARGB_4444_Config:
1048 sig_bit.red = 4;
1049 sig_bit.green = 4;
1050 sig_bit.blue = 4;
1051 sig_bit.alpha = 4;
1052 break;
1053 case SkBitmap::kRGB_565_Config:
1054 sig_bit.red = 5;
1055 sig_bit.green = 6;
1056 sig_bit.blue = 5;
1057 sig_bit.alpha = 0;
1058 break;
1059 default:
1060 return false;
1061 }
1062
1063 if (hasAlpha) {
1064 // don't specify alpha if we're a palette, even if our ctable has alpha
1065 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
1066 colorType |= PNG_COLOR_MASK_ALPHA;
1067 }
1068 } else {
1069 sig_bit.alpha = 0;
1070 }
1071
1072 SkAutoLockPixels alp(bitmap);
1073 // readyToDraw checks for pixels (and colortable if that is required)
1074 if (!bitmap.readyToDraw()) {
1075 return false;
1076 }
1077
1078 // we must do this after we have locked the pixels
1079 SkColorTable* ctable = bitmap.getColorTable();
1080 if (NULL != ctable) {
1081 if (ctable->count() == 0) {
1082 return false;
1083 }
1084 // check if we can store in fewer than 8 bits
1085 bitDepth = computeBitDepth(ctable->count());
1086 }
1087
1088 return doEncode(stream, bitmap, hasAlpha, colorType,
1089 bitDepth, config, sig_bit);
1090 }
1091
doEncode(SkWStream * stream,const SkBitmap & bitmap,const bool & hasAlpha,int colorType,int bitDepth,SkBitmap::Config config,png_color_8 & sig_bit)1092 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
1093 const bool& hasAlpha, int colorType,
1094 int bitDepth, SkBitmap::Config config,
1095 png_color_8& sig_bit) {
1096
1097 png_structp png_ptr;
1098 png_infop info_ptr;
1099
1100 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
1101 NULL);
1102 if (NULL == png_ptr) {
1103 return false;
1104 }
1105
1106 info_ptr = png_create_info_struct(png_ptr);
1107 if (NULL == info_ptr) {
1108 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
1109 return false;
1110 }
1111
1112 /* Set error handling. REQUIRED if you aren't supplying your own
1113 * error handling functions in the png_create_write_struct() call.
1114 */
1115 if (setjmp(png_jmpbuf(png_ptr))) {
1116 png_destroy_write_struct(&png_ptr, &info_ptr);
1117 return false;
1118 }
1119
1120 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
1121
1122 /* Set the image information here. Width and height are up to 2^31,
1123 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
1124 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
1125 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
1126 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
1127 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
1128 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
1129 */
1130
1131 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
1132 bitDepth, colorType,
1133 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1134 PNG_FILTER_TYPE_BASE);
1135
1136 // set our colortable/trans arrays if needed
1137 png_color paletteColors[256];
1138 png_byte trans[256];
1139 if (SkBitmap::kIndex8_Config == config) {
1140 SkColorTable* ct = bitmap.getColorTable();
1141 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
1142 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
1143 if (numTrans > 0) {
1144 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
1145 }
1146 }
1147
1148 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
1149 png_write_info(png_ptr, info_ptr);
1150
1151 const char* srcImage = (const char*)bitmap.getPixels();
1152 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
1153 char* storage = (char*)rowStorage.get();
1154 transform_scanline_proc proc = choose_proc(config, hasAlpha);
1155
1156 for (int y = 0; y < bitmap.height(); y++) {
1157 png_bytep row_ptr = (png_bytep)storage;
1158 proc(srcImage, bitmap.width(), storage);
1159 png_write_rows(png_ptr, &row_ptr, 1);
1160 srcImage += bitmap.rowBytes();
1161 }
1162
1163 png_write_end(png_ptr, info_ptr);
1164
1165 /* clean up after the write, and free any memory allocated */
1166 png_destroy_write_struct(&png_ptr, &info_ptr);
1167 return true;
1168 }
1169
1170 ///////////////////////////////////////////////////////////////////////////////
1171
1172 #include "SkTRegistry.h"
1173
1174 #ifdef SK_ENABLE_LIBPNG
1175 SkImageDecoder* sk_libpng_dfactory(SkStream*);
1176 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
1177 #endif
1178
sk_libpng_dfactory(SkStream * stream)1179 SkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
1180 char buf[PNG_BYTES_TO_CHECK];
1181 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
1182 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
1183 return SkNEW(SkPNGImageDecoder);
1184 }
1185 return NULL;
1186 }
1187
sk_libpng_efactory(SkImageEncoder::Type t)1188 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
1189 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
1190 }
1191
1192 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
1193 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
1194