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