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->setConfig(config, sampler.scaledWidth(),
295 sampler.scaledHeight(), 0);
296 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
297 return true;
298 }
299
300 // from here down we are concerned with colortables and pixels
301
302 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
303 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
304 // draw lots faster if we can flag the bitmap has being opaque
305 bool reallyHasAlpha = false;
306 SkColorTable* colorTable = NULL;
307
308 if (color_type == PNG_COLOR_TYPE_PALETTE) {
309 decodePalette(png_ptr, info_ptr, &hasAlpha,
310 &reallyHasAlpha, &colorTable);
311 }
312
313 SkAutoUnref aur(colorTable);
314
315 if (!this->allocPixelRef(decodedBitmap,
316 SkBitmap::kIndex8_Config == config ?
317 colorTable : NULL)) {
318 return false;
319 }
320
321 SkAutoLockPixels alp(*decodedBitmap);
322
323 /* Add filler (or alpha) byte (before/after each RGB triplet) */
324 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
325 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
326 }
327
328 /* Turn on interlace handling. REQUIRED if you are not using
329 * png_read_image(). To see how to handle interlacing passes,
330 * see the png_read_row() method below:
331 */
332 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
333 png_set_interlace_handling(png_ptr) : 1;
334
335 /* Optional call to gamma correct and add the background to the palette
336 * and update info structure. REQUIRED if you are expecting libpng to
337 * update the palette for you (ie you selected such a transform above).
338 */
339 png_read_update_info(png_ptr, info_ptr);
340
341 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
342 for (int i = 0; i < number_passes; i++) {
343 for (png_uint_32 y = 0; y < origHeight; y++) {
344 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
345 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
346 }
347 }
348 } else {
349 SkScaledBitmapSampler::SrcConfig sc;
350 int srcBytesPerPixel = 4;
351
352 if (colorTable != NULL) {
353 sc = SkScaledBitmapSampler::kIndex;
354 srcBytesPerPixel = 1;
355 } else if (hasAlpha) {
356 sc = SkScaledBitmapSampler::kRGBA;
357 } else {
358 sc = SkScaledBitmapSampler::kRGBX;
359 }
360
361 /* We have to pass the colortable explicitly, since we may have one
362 even if our decodedBitmap doesn't, due to the request that we
363 upscale png's palette to a direct model
364 */
365 SkAutoLockColors ctLock(colorTable);
366 if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
367 return false;
368 }
369 const int height = decodedBitmap->height();
370
371 if (number_passes > 1) {
372 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
373 uint8_t* base = (uint8_t*)storage.get();
374 size_t rb = origWidth * srcBytesPerPixel;
375
376 for (int i = 0; i < number_passes; i++) {
377 uint8_t* row = base;
378 for (png_uint_32 y = 0; y < origHeight; y++) {
379 uint8_t* bmRow = row;
380 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
381 row += rb;
382 }
383 }
384 // now sample it
385 base += sampler.srcY0() * rb;
386 for (int y = 0; y < height; y++) {
387 reallyHasAlpha |= sampler.next(base);
388 base += sampler.srcDY() * rb;
389 }
390 } else {
391 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
392 uint8_t* srcRow = (uint8_t*)storage.get();
393 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
394
395 for (int y = 0; y < height; y++) {
396 uint8_t* tmp = srcRow;
397 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
398 reallyHasAlpha |= sampler.next(srcRow);
399 if (y < height - 1) {
400 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
401 }
402 }
403
404 // skip the rest of the rows (if any)
405 png_uint_32 read = (height - 1) * sampler.srcDY() +
406 sampler.srcY0() + 1;
407 SkASSERT(read <= origHeight);
408 skip_src_rows(png_ptr, srcRow, origHeight - read);
409 }
410 }
411
412 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
413 png_read_end(png_ptr, info_ptr);
414
415 if (0 != theTranspColor) {
416 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
417 }
418 decodedBitmap->setIsOpaque(!reallyHasAlpha);
419 return true;
420 }
421
onBuildTileIndex(SkStream * sk_stream,int * width,int * height)422 bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width,
423 int *height) {
424 png_structp png_ptr;
425 png_infop info_ptr;
426
427 this->index = new SkPNGImageIndex();
428
429 if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
430 return false;
431 }
432
433 int bit_depth, color_type, interlace_type;
434 png_uint_32 origWidth, origHeight;
435 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
436 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
437
438 *width = origWidth;
439 *height = origHeight;
440
441 png_build_index(png_ptr);
442 this->index->png_ptr = png_ptr;
443 this->index->info_ptr = info_ptr;
444 return true;
445 }
446
getBitmapConfig(png_structp png_ptr,png_infop info_ptr,SkBitmap::Config * configp,bool * hasAlphap,bool * doDitherp,SkPMColor * theTranspColorp)447 bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
448 SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
449 SkPMColor *theTranspColorp) {
450 png_uint_32 origWidth, origHeight;
451 int bit_depth, color_type, interlace_type;
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 // check for sBIT chunk data, in case we should disable dithering because
456 // our data is not truely 8bits per component
457 if (*doDitherp) {
458 #if 0
459 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
460 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
461 info_ptr->sig_bit.alpha);
462 #endif
463 // 0 seems to indicate no information available
464 if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
465 pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
466 pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
467 *doDitherp = false;
468 }
469 }
470
471 if (color_type == PNG_COLOR_TYPE_PALETTE) {
472 bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
473 *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
474 // now see if we can upscale to their requested config
475 if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
476 *configp = SkBitmap::kIndex8_Config;
477 }
478 } else {
479 png_color_16p transpColor = NULL;
480 int numTransp = 0;
481
482 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
483
484 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
485
486 if (valid && numTransp == 1 && transpColor != NULL) {
487 /* Compute our transparent color, which we'll match against later.
488 We don't really handle 16bit components properly here, since we
489 do our compare *after* the values have been knocked down to 8bit
490 which means we will find more matches than we should. The real
491 fix seems to be to see the actual 16bit components, do the
492 compare, and then knock it down to 8bits ourselves.
493 */
494 if (color_type & PNG_COLOR_MASK_COLOR) {
495 if (16 == bit_depth) {
496 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
497 transpColor->green >> 8, transpColor->blue >> 8);
498 } else {
499 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
500 transpColor->green, transpColor->blue);
501 }
502 } else { // gray
503 if (16 == bit_depth) {
504 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
505 transpColor->gray >> 8, transpColor->gray >> 8);
506 } else {
507 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
508 transpColor->gray, transpColor->gray);
509 }
510 }
511 }
512
513 if (valid ||
514 PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
515 PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
516 *hasAlphap = true;
517 }
518 *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
519 // now match the request against our capabilities
520 if (*hasAlphap) {
521 if (*configp != SkBitmap::kARGB_4444_Config) {
522 *configp = SkBitmap::kARGB_8888_Config;
523 }
524 } else {
525 if (*configp != SkBitmap::kRGB_565_Config &&
526 *configp != SkBitmap::kARGB_4444_Config) {
527 *configp = SkBitmap::kARGB_8888_Config;
528 }
529 }
530 }
531
532 // sanity check for size
533 {
534 Sk64 size;
535 size.setMul(origWidth, origHeight);
536 if (size.isNeg() || !size.is32()) {
537 return false;
538 }
539 // now check that if we are 4-bytes per pixel, we also don't overflow
540 if (size.get32() > (0x7FFFFFFF >> 2)) {
541 return false;
542 }
543 }
544
545 if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
546 return false;
547 }
548 return true;
549 }
550
decodePalette(png_structp png_ptr,png_infop info_ptr,bool * hasAlphap,bool * reallyHasAlphap,SkColorTable ** colorTablep)551 bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
552 bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
553 int num_palette;
554 png_colorp palette;
555 png_bytep trans;
556 int num_trans;
557 bool reallyHasAlpha = false;
558 SkColorTable* colorTable = NULL;
559
560 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
561
562 /* BUGGY IMAGE WORKAROUND
563
564 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
565 which is a problem since we use the byte as an index. To work around this we grow
566 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
567 */
568 int colorCount = num_palette + (num_palette < 256);
569
570 colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
571
572 SkPMColor* colorPtr = colorTable->lockColors();
573 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
574 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
575 *hasAlphap = (num_trans > 0);
576 } else {
577 num_trans = 0;
578 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
579 }
580 // check for bad images that might make us crash
581 if (num_trans > num_palette) {
582 num_trans = num_palette;
583 }
584
585 int index = 0;
586 int transLessThanFF = 0;
587
588 for (; index < num_trans; index++) {
589 transLessThanFF |= (int)*trans - 0xFF;
590 *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
591 palette++;
592 }
593 reallyHasAlpha |= (transLessThanFF < 0);
594
595 for (; index < num_palette; index++) {
596 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
597 palette++;
598 }
599
600 // see BUGGY IMAGE WORKAROUND comment above
601 if (num_palette < 256) {
602 *colorPtr = colorPtr[-1];
603 }
604 colorTable->unlockColors(true);
605 *colorTablep = colorTable;
606 *reallyHasAlphap = reallyHasAlpha;
607 return true;
608 }
609
onDecodeRegion(SkBitmap * bm,SkIRect rect)610 bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) {
611 int i;
612 png_structp png_ptr = this->index->png_ptr;
613 png_infop info_ptr = this->index->info_ptr;
614 if (setjmp(png_jmpbuf(png_ptr))) {
615 return false;
616 }
617
618 int requestedHeight = rect.fBottom - rect.fTop;
619 int requestedWidth = rect.fRight - rect.fLeft;
620
621 png_uint_32 origWidth, origHeight;
622 int bit_depth, color_type, interlace_type;
623 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
624 &color_type, &interlace_type, int_p_NULL, int_p_NULL);
625
626 SkBitmap::Config config;
627 bool hasAlpha = false;
628 bool doDither = this->getDitherImage();
629 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
630
631 if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
632 &doDither, &theTranspColor) == false) {
633 return false;
634 }
635
636 const int sampleSize = this->getSampleSize();
637 SkScaledBitmapSampler sampler(origWidth, requestedHeight, sampleSize);
638
639 SkBitmap *decodedBitmap = new SkBitmap;
640 SkAutoTDelete<SkBitmap> adb(decodedBitmap);
641
642 decodedBitmap->setConfig(config, sampler.scaledWidth(),
643 sampler.scaledHeight(), 0);
644
645 // from here down we are concerned with colortables and pixels
646
647 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
648 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
649 // draw lots faster if we can flag the bitmap has being opaque
650 bool reallyHasAlpha = false;
651 SkColorTable* colorTable = NULL;
652
653 if (color_type == PNG_COLOR_TYPE_PALETTE) {
654 decodePalette(png_ptr, info_ptr, &hasAlpha,
655 &reallyHasAlpha, &colorTable);
656 }
657
658 SkAutoUnref aur(colorTable);
659
660 if (!this->allocPixelRef(decodedBitmap,
661 SkBitmap::kIndex8_Config == config ?
662 colorTable : NULL)) {
663 return false;
664 }
665
666 SkAutoLockPixels alp(*decodedBitmap);
667
668 /* Add filler (or alpha) byte (before/after each RGB triplet) */
669 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
670 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
671 }
672
673 /* Turn on interlace handling. REQUIRED if you are not using
674 * png_read_image(). To see how to handle interlacing passes,
675 * see the png_read_row() method below:
676 */
677 const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
678 png_set_interlace_handling(png_ptr) : 1;
679
680 /* Optional call to gamma correct and add the background to the palette
681 * and update info structure. REQUIRED if you are expecting libpng to
682 * update the palette for you (ie you selected such a transform above).
683 */
684 png_ptr->pass = 0;
685 png_read_update_info(png_ptr, info_ptr);
686
687 SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight);
688
689 int actualTop = rect.fTop;
690
691 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
692 for (int i = 0; i < number_passes; i++) {
693 png_configure_decoder(png_ptr, &actualTop, i);
694 for (int j = 0; j < rect.fTop - actualTop; j++) {
695 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
696 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
697 }
698 for (png_uint_32 y = 0; y < origHeight; y++) {
699 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
700 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
701 }
702 }
703 } else {
704 SkScaledBitmapSampler::SrcConfig sc;
705 int srcBytesPerPixel = 4;
706
707 if (colorTable != NULL) {
708 sc = SkScaledBitmapSampler::kIndex;
709 srcBytesPerPixel = 1;
710 } else if (hasAlpha) {
711 sc = SkScaledBitmapSampler::kRGBA;
712 } else {
713 sc = SkScaledBitmapSampler::kRGBX;
714 }
715
716 /* We have to pass the colortable explicitly, since we may have one
717 even if our decodedBitmap doesn't, due to the request that we
718 upscale png's palette to a direct model
719 */
720 SkAutoLockColors ctLock(colorTable);
721 if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
722 return false;
723 }
724 const int height = decodedBitmap->height();
725
726 if (number_passes > 1) {
727 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
728 uint8_t* base = (uint8_t*)storage.get();
729 size_t rb = origWidth * srcBytesPerPixel;
730
731 for (int i = 0; i < number_passes; i++) {
732 png_configure_decoder(png_ptr, &actualTop, i);
733 for (int j = 0; j < rect.fTop - actualTop; j++) {
734 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
735 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
736 }
737 uint8_t* row = base;
738 for (png_uint_32 y = 0; y < requestedHeight; y++) {
739 uint8_t* bmRow = row;
740 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
741 row += rb;
742 }
743 }
744 // now sample it
745 base += sampler.srcY0() * rb;
746 for (int y = 0; y < height; y++) {
747 reallyHasAlpha |= sampler.next(base);
748 base += sampler.srcDY() * rb;
749 }
750 } else {
751 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
752 uint8_t* srcRow = (uint8_t*)storage.get();
753
754 png_configure_decoder(png_ptr, &actualTop, 0);
755 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
756
757 for (int i = 0; i < rect.fTop - actualTop; i++) {
758 uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
759 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
760 }
761 for (int y = 0; y < height; y++) {
762 uint8_t* tmp = srcRow;
763 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
764 reallyHasAlpha |= sampler.next(srcRow);
765 if (y < height - 1) {
766 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
767 }
768 }
769 }
770 }
771 cropBitmap(bm, decodedBitmap, sampleSize, rect.fLeft, rect.fTop,
772 requestedWidth, requestedHeight, 0, rect.fTop);
773
774 if (0 != theTranspColor) {
775 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
776 }
777 decodedBitmap->setIsOpaque(!reallyHasAlpha);
778 return true;
779 }
780
781 ///////////////////////////////////////////////////////////////////////////////
782
783 #include "SkColorPriv.h"
784 #include "SkUnPreMultiply.h"
785
sk_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)786 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
787 SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
788 if (!sk_stream->write(data, len)) {
789 png_error(png_ptr, "sk_write_fn Error!");
790 }
791 }
792
793 typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
794 int width, char* SK_RESTRICT dst);
795
transform_scanline_565(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)796 static void transform_scanline_565(const char* SK_RESTRICT src, int width,
797 char* SK_RESTRICT dst) {
798 const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
799 for (int i = 0; i < width; i++) {
800 unsigned c = *srcP++;
801 *dst++ = SkPacked16ToR32(c);
802 *dst++ = SkPacked16ToG32(c);
803 *dst++ = SkPacked16ToB32(c);
804 }
805 }
806
transform_scanline_888(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)807 static void transform_scanline_888(const char* SK_RESTRICT src, int width,
808 char* SK_RESTRICT dst) {
809 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
810 for (int i = 0; i < width; i++) {
811 SkPMColor c = *srcP++;
812 *dst++ = SkGetPackedR32(c);
813 *dst++ = SkGetPackedG32(c);
814 *dst++ = SkGetPackedB32(c);
815 }
816 }
817
transform_scanline_444(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)818 static void transform_scanline_444(const char* SK_RESTRICT src, int width,
819 char* SK_RESTRICT dst) {
820 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
821 for (int i = 0; i < width; i++) {
822 SkPMColor16 c = *srcP++;
823 *dst++ = SkPacked4444ToR32(c);
824 *dst++ = SkPacked4444ToG32(c);
825 *dst++ = SkPacked4444ToB32(c);
826 }
827 }
828
transform_scanline_8888(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)829 static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
830 char* SK_RESTRICT dst) {
831 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
832 const SkUnPreMultiply::Scale* SK_RESTRICT table =
833 SkUnPreMultiply::GetScaleTable();
834
835 for (int i = 0; i < width; i++) {
836 SkPMColor c = *srcP++;
837 unsigned a = SkGetPackedA32(c);
838 unsigned r = SkGetPackedR32(c);
839 unsigned g = SkGetPackedG32(c);
840 unsigned b = SkGetPackedB32(c);
841
842 if (0 != a && 255 != a) {
843 SkUnPreMultiply::Scale scale = table[a];
844 r = SkUnPreMultiply::ApplyScale(scale, r);
845 g = SkUnPreMultiply::ApplyScale(scale, g);
846 b = SkUnPreMultiply::ApplyScale(scale, b);
847 }
848 *dst++ = r;
849 *dst++ = g;
850 *dst++ = b;
851 *dst++ = a;
852 }
853 }
854
transform_scanline_4444(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)855 static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
856 char* SK_RESTRICT dst) {
857 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
858 const SkUnPreMultiply::Scale* SK_RESTRICT table =
859 SkUnPreMultiply::GetScaleTable();
860
861 for (int i = 0; i < width; i++) {
862 SkPMColor16 c = *srcP++;
863 unsigned a = SkPacked4444ToA32(c);
864 unsigned r = SkPacked4444ToR32(c);
865 unsigned g = SkPacked4444ToG32(c);
866 unsigned b = SkPacked4444ToB32(c);
867
868 if (0 != a && 255 != a) {
869 SkUnPreMultiply::Scale scale = table[a];
870 r = SkUnPreMultiply::ApplyScale(scale, r);
871 g = SkUnPreMultiply::ApplyScale(scale, g);
872 b = SkUnPreMultiply::ApplyScale(scale, b);
873 }
874 *dst++ = r;
875 *dst++ = g;
876 *dst++ = b;
877 *dst++ = a;
878 }
879 }
880
transform_scanline_index8(const char * SK_RESTRICT src,int width,char * SK_RESTRICT dst)881 static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
882 char* SK_RESTRICT dst) {
883 memcpy(dst, src, width);
884 }
885
choose_proc(SkBitmap::Config config,bool hasAlpha)886 static transform_scanline_proc choose_proc(SkBitmap::Config config,
887 bool hasAlpha) {
888 // we don't care about search on alpha if we're kIndex8, since only the
889 // colortable packing cares about that distinction, not the pixels
890 if (SkBitmap::kIndex8_Config == config) {
891 hasAlpha = false; // we store false in the table entries for kIndex8
892 }
893
894 static const struct {
895 SkBitmap::Config fConfig;
896 bool fHasAlpha;
897 transform_scanline_proc fProc;
898 } gMap[] = {
899 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
900 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
901 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
902 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
903 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
904 { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
905 };
906
907 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
908 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
909 return gMap[i].fProc;
910 }
911 }
912 sk_throw();
913 return NULL;
914 }
915
916 // return the minimum legal bitdepth (by png standards) for this many colortable
917 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
918 // we can use fewer bits per in png
computeBitDepth(int colorCount)919 static int computeBitDepth(int colorCount) {
920 #if 0
921 int bits = SkNextLog2(colorCount);
922 SkASSERT(bits >= 1 && bits <= 8);
923 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
924 return SkNextPow2(bits);
925 #else
926 // for the moment, we don't know how to pack bitdepth < 8
927 return 8;
928 #endif
929 }
930
931 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
932 pack trans[] and return the number of trans[] entries written. If hasAlpha
933 is false, the return value will always be 0.
934
935 Note: this routine takes care of unpremultiplying the RGB values when we
936 have alpha in the colortable, since png doesn't support premul colors
937 */
pack_palette(SkColorTable * ctable,png_color * SK_RESTRICT palette,png_byte * SK_RESTRICT trans,bool hasAlpha)938 static inline int pack_palette(SkColorTable* ctable,
939 png_color* SK_RESTRICT palette,
940 png_byte* SK_RESTRICT trans, bool hasAlpha) {
941 SkAutoLockColors alc(ctable);
942 const SkPMColor* SK_RESTRICT colors = alc.colors();
943 const int ctCount = ctable->count();
944 int i, num_trans = 0;
945
946 if (hasAlpha) {
947 /* first see if we have some number of fully opaque at the end of the
948 ctable. PNG allows num_trans < num_palette, but all of the trans
949 entries must come first in the palette. If I was smarter, I'd
950 reorder the indices and ctable so that all non-opaque colors came
951 first in the palette. But, since that would slow down the encode,
952 I'm leaving the indices and ctable order as is, and just looking
953 at the tail of the ctable for opaqueness.
954 */
955 num_trans = ctCount;
956 for (i = ctCount - 1; i >= 0; --i) {
957 if (SkGetPackedA32(colors[i]) != 0xFF) {
958 break;
959 }
960 num_trans -= 1;
961 }
962
963 const SkUnPreMultiply::Scale* SK_RESTRICT table =
964 SkUnPreMultiply::GetScaleTable();
965
966 for (i = 0; i < num_trans; i++) {
967 const SkPMColor c = *colors++;
968 const unsigned a = SkGetPackedA32(c);
969 const SkUnPreMultiply::Scale s = table[a];
970 trans[i] = a;
971 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
972 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
973 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
974 }
975 // now fall out of this if-block to use common code for the trailing
976 // opaque entries
977 }
978
979 // these (remaining) entries are opaque
980 for (i = num_trans; i < ctCount; i++) {
981 SkPMColor c = *colors++;
982 palette[i].red = SkGetPackedR32(c);
983 palette[i].green = SkGetPackedG32(c);
984 palette[i].blue = SkGetPackedB32(c);
985 }
986 return num_trans;
987 }
988
989 class SkPNGImageEncoder : public SkImageEncoder {
990 protected:
991 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
992 };
993
onEncode(SkWStream * stream,const SkBitmap & bitmap,int)994 bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
995 int /*quality*/) {
996 SkBitmap::Config config = bitmap.getConfig();
997
998 const bool hasAlpha = !bitmap.isOpaque();
999 int colorType = PNG_COLOR_MASK_COLOR;
1000 int bitDepth = 8; // default for color
1001 png_color_8 sig_bit;
1002
1003 switch (config) {
1004 case SkBitmap::kIndex8_Config:
1005 colorType |= PNG_COLOR_MASK_PALETTE;
1006 // fall through to the ARGB_8888 case
1007 case SkBitmap::kARGB_8888_Config:
1008 sig_bit.red = 8;
1009 sig_bit.green = 8;
1010 sig_bit.blue = 8;
1011 sig_bit.alpha = 8;
1012 break;
1013 case SkBitmap::kARGB_4444_Config:
1014 sig_bit.red = 4;
1015 sig_bit.green = 4;
1016 sig_bit.blue = 4;
1017 sig_bit.alpha = 4;
1018 break;
1019 case SkBitmap::kRGB_565_Config:
1020 sig_bit.red = 5;
1021 sig_bit.green = 6;
1022 sig_bit.blue = 5;
1023 sig_bit.alpha = 0;
1024 break;
1025 default:
1026 return false;
1027 }
1028
1029 if (hasAlpha) {
1030 // don't specify alpha if we're a palette, even if our ctable has alpha
1031 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
1032 colorType |= PNG_COLOR_MASK_ALPHA;
1033 }
1034 } else {
1035 sig_bit.alpha = 0;
1036 }
1037
1038 SkAutoLockPixels alp(bitmap);
1039 // readyToDraw checks for pixels (and colortable if that is required)
1040 if (!bitmap.readyToDraw()) {
1041 return false;
1042 }
1043
1044 // we must do this after we have locked the pixels
1045 SkColorTable* ctable = bitmap.getColorTable();
1046 if (NULL != ctable) {
1047 if (ctable->count() == 0) {
1048 return false;
1049 }
1050 // check if we can store in fewer than 8 bits
1051 bitDepth = computeBitDepth(ctable->count());
1052 }
1053
1054 png_structp png_ptr;
1055 png_infop info_ptr;
1056
1057 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
1058 NULL);
1059 if (NULL == png_ptr) {
1060 return false;
1061 }
1062
1063 info_ptr = png_create_info_struct(png_ptr);
1064 if (NULL == info_ptr) {
1065 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
1066 return false;
1067 }
1068
1069 /* Set error handling. REQUIRED if you aren't supplying your own
1070 * error handling functions in the png_create_write_struct() call.
1071 */
1072 if (setjmp(png_jmpbuf(png_ptr))) {
1073 png_destroy_write_struct(&png_ptr, &info_ptr);
1074 return false;
1075 }
1076
1077 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
1078
1079 /* Set the image information here. Width and height are up to 2^31,
1080 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
1081 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
1082 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
1083 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
1084 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
1085 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
1086 */
1087
1088 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
1089 bitDepth, colorType,
1090 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1091 PNG_FILTER_TYPE_BASE);
1092
1093 // set our colortable/trans arrays if needed
1094 png_color paletteColors[256];
1095 png_byte trans[256];
1096 if (SkBitmap::kIndex8_Config == config) {
1097 SkColorTable* ct = bitmap.getColorTable();
1098 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
1099 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
1100 if (numTrans > 0) {
1101 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
1102 }
1103 }
1104
1105 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
1106 png_write_info(png_ptr, info_ptr);
1107
1108 const char* srcImage = (const char*)bitmap.getPixels();
1109 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
1110 char* storage = (char*)rowStorage.get();
1111 transform_scanline_proc proc = choose_proc(config, hasAlpha);
1112
1113 for (int y = 0; y < bitmap.height(); y++) {
1114 png_bytep row_ptr = (png_bytep)storage;
1115 proc(srcImage, bitmap.width(), storage);
1116 png_write_rows(png_ptr, &row_ptr, 1);
1117 srcImage += bitmap.rowBytes();
1118 }
1119
1120 png_write_end(png_ptr, info_ptr);
1121
1122 /* clean up after the write, and free any memory allocated */
1123 png_destroy_write_struct(&png_ptr, &info_ptr);
1124 return true;
1125 }
1126
1127 ///////////////////////////////////////////////////////////////////////////////
1128
1129 #include "SkTRegistry.h"
1130
DFactory(SkStream * stream)1131 static SkImageDecoder* DFactory(SkStream* stream) {
1132 char buf[PNG_BYTES_TO_CHECK];
1133 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
1134 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
1135 return SkNEW(SkPNGImageDecoder);
1136 }
1137 return NULL;
1138 }
1139
EFactory(SkImageEncoder::Type t)1140 static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
1141 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
1142 }
1143
1144 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
1145 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
1146