PageRenderTime 112ms CodeModel.GetById 38ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 0ms

/src/compiler/android/jni/ftk/SkImageDecoder_libpng.cpp

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