• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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