11cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
21cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger/*
31cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Copyright 2006 The Android Open Source Project
41cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger *
51cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Use of this source code is governed by a BSD-style license that can be
61cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * found in the LICENSE file.
71cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger */
81cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
90910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkImageDecoder.h"
110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkImageEncoder.h"
120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkColor.h"
130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkColorPriv.h"
140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkDither.h"
150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkMath.h"
160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkScaledBitmapSampler.h"
170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkStream.h"
180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkTemplates.h"
190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkUtils.h"
200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectextern "C" {
220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "png.h"
230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenclass SkPNGImageIndex {
2658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenpublic:
2758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageIndex() {
2858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        inputStream = NULL;
2958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_ptr = NULL;
3058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
3158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    virtual ~SkPNGImageIndex() {
3258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (png_ptr) {
3358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
3458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
3558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (inputStream) {
3658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            delete inputStream;
3758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
3858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
3958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
4058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr;
4158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkStream *inputStream;
4258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen};
4358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectclass SkPNGImageDecoder : public SkImageDecoder {
450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectpublic:
4658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageDecoder() {
4758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        index = NULL;
4858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    virtual Format getFormat() const {
500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return kPNG_Format;
510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
5258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    virtual ~SkPNGImageDecoder() {
5358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (index) {
5458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            delete index;
5558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
5658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
5758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprotected:
5914496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen    virtual bool onBuildTileIndex(SkStream *stream,
6014496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen             int *width, int *height);
61943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region);
620a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
6358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenprivate:
6558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool onDecodeInit(SkStream* stream, png_structp *png_ptrp,
6658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_infop *info_ptrp);
6758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool decodePalette(png_structp png_ptr, png_infop info_ptr,
6858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep);
6958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
7058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkBitmap::Config *config, bool *hasAlpha, bool *doDither,
7158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkPMColor *theTranspColor);
7258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageIndex *index;
730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#ifndef png_jmpbuf
760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#define PNG_BYTES_TO_CHECK 4
800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project/* Automatically clean up after throwing an exception */
820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstruct PNGAutoClean {
830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    ~PNGAutoClean() {
850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprivate:
880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr;
890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr;
900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    size_t bytes = sk_stream->read(data, length);
950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bytes != length) {
960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_error(png_ptr, "Read Error!");
970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenstatic void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
10158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
10258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    sk_stream->rewind();
10358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    (void)sk_stream->skip(offset);
10458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
10558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
1060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
1070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkImageDecoder::Peeker* peeker =
1080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                    (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
1090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // peek() returning true means continue decoding
1100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
1110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            1 : -1;
1120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
1150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
1160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkDebugf("------ png error %s\n", msg);
1170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
1180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    longjmp(png_jmpbuf(png_ptr), 1);
1190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
1220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < count; i++) {
1230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        uint8_t* tmp = storage;
1240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
1250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic bool pos_le(int value, int max) {
1290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return value > 0 && value <= max;
1300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
1330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
1340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool reallyHasAlpha = false;
1360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int y = bm->height() - 1; y >= 0; --y) {
1380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor* p = bm->getAddr32(0, y);
1390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (int x = bm->width() - 1; x >= 0; --x) {
1400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (match == *p) {
1410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                *p = 0;
1420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                reallyHasAlpha = true;
1430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
1440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            p += 1;
1450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
1460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return reallyHasAlpha;
1480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1500a81c953145c77abea5ca1df9e84c62d9da96094Mike Reedstatic bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
151fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed                                      bool srcHasAlpha) {
1520a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed    switch (dstConfig) {
153fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kARGB_8888_Config:
154fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kARGB_4444_Config:
155fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return true;
156fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kRGB_565_Config:
157fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            // only return true if the src is opaque (since 565 is opaque)
158fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return !srcHasAlpha;
159fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        default:
160fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return false;
161fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    }
162fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed}
163fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed
164fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed// call only if color_type is PALETTE. Returns true if the ctable has alpha
165fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reedstatic bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
166fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    png_bytep trans;
167fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    int num_trans;
168fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed
169fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
170fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
171fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        return num_trans > 0;
172fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    }
173fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    return false;
17418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed}
17518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
17658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream,
17758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_structp *png_ptrp, png_infop *info_ptrp)
17858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen{
1790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Create and initialize the png_struct with the desired error handler
1800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * functions.  If you want to use the default stderr and longjump method,
1810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * you can supply NULL for the last three parameters.  We also supply the
1820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the compiler header file version, so that we know if the application
1830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * was compiled with a compatible version of the library.  */
1840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        NULL, sk_error_fn, NULL);
1860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);
1870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (png_ptr == NULL) {
1880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
1890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
19058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *png_ptrp = png_ptr;
1910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Allocate/initialize the memory for image information. */
1930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr = png_create_info_struct(png_ptr);
1940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (info_ptr == NULL) {
1950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
1960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
1970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
19858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *info_ptrp = info_ptr;
1990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set error handling if you are using the setjmp/longjmp method (this is
2010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the normal method of doing things with libpng).  REQUIRED unless you
2020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * set up your own error handlers in the png_create_read_struct() earlier.
2030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
2040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (setjmp(png_jmpbuf(png_ptr))) {
2050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
2060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* If you are using replacement read functions, instead of calling
2090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * png_init_io() here you would call:
2100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
2110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
21258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_set_seek_fn(png_ptr, sk_seek_fn);
2130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* where user_io_ptr is a structure you want available to the callbacks */
2140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* If we have already read some of the signature */
21558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
2160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // hookup our peeker so we can see any user-chunks the caller may be interested in
2180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
2190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (this->getPeeker()) {
2200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
2210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* The call to png_read_info() gives us all of the information from the
2240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG file before the first IDAT (image data chunk). */
2250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_read_info(png_ptr, info_ptr);
2260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_uint_32 origWidth, origHeight;
2270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bit_depth, color_type, interlace_type;
22858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
22958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
2300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
2320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bit_depth == 16) {
2330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_strip_16(png_ptr);
2340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
2360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project     * byte into separate bytes (useful for paletted and grayscale images). */
2370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bit_depth < 8) {
2380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_packing(png_ptr);
2390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
2410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
2420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_gray_1_2_4_to_8(png_ptr);
2430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
24458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Make a grayscale image into RGB. */
2460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_GRAY ||
2470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
2480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_gray_to_rgb(png_ptr);
2490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
25058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
25158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
25258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
25358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
25458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                                 Mode mode) {
25558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
25658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr;
25758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
25858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
25958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
26058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
26158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (setjmp(png_jmpbuf(png_ptr))) {
26358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
26458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
26558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    PNGAutoClean autoClean(png_ptr, info_ptr);
26758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
26958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
27058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
27158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
27258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkBitmap::Config    config;
2740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool                hasAlpha = false;
2750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool                doDither = this->getDitherImage();
2760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
27758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
27858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
27958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &doDither, &theTranspColor) == false) {
28058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
28158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
28258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
28358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int sampleSize = this->getSampleSize();
28458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
28558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2869717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    decodedBitmap->lockPixels();
2879717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    void* rowptr = (void*) decodedBitmap->getPixels();
2889717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    bool reuseBitmap = (rowptr != NULL);
2899717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    decodedBitmap->unlockPixels();
2909717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
2919717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase            sampler.scaledHeight() != decodedBitmap->height())) {
2929717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        // Dimensions must match
2939717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        return false;
2949717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
2959717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase
2969717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (!reuseBitmap) {
2979717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        decodedBitmap->setConfig(config, sampler.scaledWidth(),
2989717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                 sampler.scaledHeight(), 0);
2999717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
30058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
30158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return true;
30258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
30358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
30458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // from here down we are concerned with colortables and pixels
30558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
30658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
30758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
30858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // draw lots faster if we can flag the bitmap has being opaque
30958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool reallyHasAlpha = false;
31058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkColorTable* colorTable = NULL;
31158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
31258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (color_type == PNG_COLOR_TYPE_PALETTE) {
31358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        decodePalette(png_ptr, info_ptr, &hasAlpha,
31458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &reallyHasAlpha, &colorTable);
31558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
31658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
31758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoUnref aur(colorTable);
31858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
3199717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (!reuseBitmap) {
3209717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        if (!this->allocPixelRef(decodedBitmap,
3219717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                 SkBitmap::kIndex8_Config == config ?
3229717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                    colorTable : NULL)) {
3239717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase            return false;
3249717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        }
32558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
32658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
32758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoLockPixels alp(*decodedBitmap);
32858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
32958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Add filler (or alpha) byte (before/after each RGB triplet) */
33058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
33158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
33258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
33358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
33458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Turn on interlace handling.  REQUIRED if you are not using
33558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * png_read_image().  To see how to handle interlacing passes,
33658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * see the png_read_row() method below:
33758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    */
33858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
33958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                        png_set_interlace_handling(png_ptr) : 1;
34058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
34158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Optional call to gamma correct and add the background to the palette
34258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * and update info structure.  REQUIRED if you are expecting libpng to
34358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * update the palette for you (ie you selected such a transform above).
34458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    */
34558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_read_update_info(png_ptr, info_ptr);
34658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
34758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
34858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        for (int i = 0; i < number_passes; i++) {
34958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (png_uint_32 y = 0; y < origHeight; y++) {
35058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
35158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
35258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
35358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
35458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    } else {
35558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkScaledBitmapSampler::SrcConfig sc;
35658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        int srcBytesPerPixel = 4;
35758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
35858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (colorTable != NULL) {
35958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kIndex;
36058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            srcBytesPerPixel = 1;
36158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else if (hasAlpha) {
36258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kRGBA;
36358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else {
36458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kRGBX;
36558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
36658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
36758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        /*  We have to pass the colortable explicitly, since we may have one
36858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            even if our decodedBitmap doesn't, due to the request that we
36958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            upscale png's palette to a direct model
37058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen         */
37158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkAutoLockColors ctLock(colorTable);
37258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
37358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            return false;
37458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
37558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        const int height = decodedBitmap->height();
37658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
37758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (number_passes > 1) {
37858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
37958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            uint8_t* base = (uint8_t*)storage.get();
38058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            size_t rb = origWidth * srcBytesPerPixel;
38158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
38258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int i = 0; i < number_passes; i++) {
38358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* row = base;
38458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                for (png_uint_32 y = 0; y < origHeight; y++) {
38558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    uint8_t* bmRow = row;
38658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
38758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    row += rb;
38858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
38958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
39058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            // now sample it
39158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            base += sampler.srcY0() * rb;
39258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int y = 0; y < height; y++) {
39358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                reallyHasAlpha |= sampler.next(base);
39458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                base += sampler.srcDY() * rb;
39558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
39658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else {
39758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
39858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            uint8_t* srcRow = (uint8_t*)storage.get();
39958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
40058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
40158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int y = 0; y < height; y++) {
40258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* tmp = srcRow;
40358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
40458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                reallyHasAlpha |= sampler.next(srcRow);
40558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                if (y < height - 1) {
40658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
40758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
40858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
40958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
41058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            // skip the rest of the rows (if any)
41158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_uint_32 read = (height - 1) * sampler.srcDY() +
41258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                               sampler.srcY0() + 1;
41358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkASSERT(read <= origHeight);
41458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            skip_src_rows(png_ptr, srcRow, origHeight - read);
41558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
41658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
41758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
41858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
41958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_read_end(png_ptr, info_ptr);
42058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
42158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (0 != theTranspColor) {
42258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
42358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
42458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    decodedBitmap->setIsOpaque(!reallyHasAlpha);
4259717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (reuseBitmap) {
4269717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        decodedBitmap->notifyPixelsChanged();
4279717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
42858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
42958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
43058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43114496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chenbool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width,
43214496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen        int *height) {
43358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
43458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop   info_ptr;
43558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index = new SkPNGImageIndex();
43758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
43958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
44058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
44158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
44258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
44358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
44458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
44558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
44658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
44758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *width = origWidth;
44858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *height = origHeight;
44958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
45058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_build_index(png_ptr);
45158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index->png_ptr = png_ptr;
45258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index->info_ptr = info_ptr;
45358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
45458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
45558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
45658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
45758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
45858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkPMColor *theTranspColorp) {
45958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
46058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
46158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
46258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
46358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // check for sBIT chunk data, in case we should disable dithering because
4650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // our data is not truely 8bits per component
46658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (*doDitherp) {
4670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
4680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
4690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
4700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 info_ptr->sig_bit.alpha);
4710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
4720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // 0 seems to indicate no information available
4730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
4740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
4750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
47658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *doDitherp = false;
4770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
4780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
47958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_PALETTE) {
481fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
48258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
4830a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        // now see if we can upscale to their requested config
48458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
48558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *configp = SkBitmap::kIndex8_Config;
486421adab1be5bb9c1cd419138ceda375aef649077Mike Reed        }
4870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
4880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_color_16p   transpColor = NULL;
4890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        int             numTransp = 0;
49058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
49258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
49458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (valid && numTransp == 1 && transpColor != NULL) {
4960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            /*  Compute our transparent color, which we'll match against later.
4970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                We don't really handle 16bit components properly here, since we
4980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                do our compare *after* the values have been knocked down to 8bit
4990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                which means we will find more matches than we should. The real
5000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                fix seems to be to see the actual 16bit components, do the
5010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                compare, and then knock it down to 8bits ourselves.
5020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            */
5030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (color_type & PNG_COLOR_MASK_COLOR) {
5040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (16 == bit_depth) {
50558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
5060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                              transpColor->green >> 8, transpColor->blue >> 8);
5070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                } else {
50858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
5090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                      transpColor->green, transpColor->blue);
5100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
5110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            } else {    // gray
5120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (16 == bit_depth) {
51358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
5140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                              transpColor->gray >> 8, transpColor->gray >> 8);
5150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                } else {
51658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
5170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                          transpColor->gray, transpColor->gray);
5180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
5190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
5210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
5220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (valid ||
5230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
5240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
52558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *hasAlphap = true;
5260a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        }
52758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
5280a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        // now match the request against our capabilities
52958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (*hasAlphap) {
53058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            if (*configp != SkBitmap::kARGB_4444_Config) {
53158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp = SkBitmap::kARGB_8888_Config;
5320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5330a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        } else {
53458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            if (*configp != SkBitmap::kRGB_565_Config &&
53558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp != SkBitmap::kARGB_4444_Config) {
53658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp = SkBitmap::kARGB_8888_Config;
5370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
5390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
54018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
54118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    // sanity check for size
54218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    {
54318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        Sk64 size;
54418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        size.setMul(origWidth, origHeight);
54518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (size.isNeg() || !size.is32()) {
54618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
54718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
54818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        // now check that if we are 4-bytes per pixel, we also don't overflow
54918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (size.get32() > (0x7FFFFFFF >> 2)) {
55018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
55118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
55218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    }
55318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
55458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
5550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
5560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
55758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
55858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
55958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
56058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
56158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
56258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int num_palette;
56358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_colorp palette;
56458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_bytep trans;
56558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int num_trans;
56658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool reallyHasAlpha = false;
56758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkColorTable* colorTable = NULL;
56858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
56958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
57058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /*  BUGGY IMAGE WORKAROUND
57258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
57458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        which is a problem since we use the byte as an index. To work around this we grow
57558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
57658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        */
57758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int colorCount = num_palette + (num_palette < 256);
57858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
58058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
58158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPMColor* colorPtr = colorTable->lockColors();
58258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
58358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
58458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *hasAlphap = (num_trans > 0);
58558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    } else {
58658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        num_trans = 0;
58758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
58858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
58958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // check for bad images that might make us crash
59058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (num_trans > num_palette) {
59158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        num_trans = num_palette;
59258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
59358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
59458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int index = 0;
59558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int transLessThanFF = 0;
59658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
59758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    for (; index < num_trans; index++) {
59858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        transLessThanFF |= (int)*trans - 0xFF;
59958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
60058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        palette++;
60158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
60258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    reallyHasAlpha |= (transLessThanFF < 0);
60358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
60458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    for (; index < num_palette; index++) {
60558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
60658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        palette++;
60758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
60858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
60958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // see BUGGY IMAGE WORKAROUND comment above
61058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (num_palette < 256) {
61158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr = colorPtr[-1];
61258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
61358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    colorTable->unlockColors(true);
61458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *colorTablep = colorTable;
61558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *reallyHasAlphap = reallyHasAlpha;
61658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
61758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
61858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
619943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Linbool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
62058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int i;
62158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr = this->index->png_ptr;
62258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr = this->index->info_ptr;
62358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (setjmp(png_jmpbuf(png_ptr))) {
62458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
62558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
62658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
62758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
62858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
62958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
63058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
63158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
632943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
633943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin
634943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (!rect.intersect(region)) {
635943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        // If the requested region is entirely outsides the image, just
636943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        // returns false
637943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        return false;
638943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    }
639943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin
64058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkBitmap::Config    config;
64158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool                hasAlpha = false;
64258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool                doDither = this->getDitherImage();
64358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
64458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
64558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
64658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &doDither, &theTranspColor) == false) {
64758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
64858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
64958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const int sampleSize = this->getSampleSize();
651943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
65258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
65358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkBitmap *decodedBitmap = new SkBitmap;
65458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoTDelete<SkBitmap> adb(decodedBitmap);
6550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    decodedBitmap->setConfig(config, sampler.scaledWidth(),
6570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                             sampler.scaledHeight(), 0);
65858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // from here down we are concerned with colortables and pixels
6600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
6620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
6630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // draw lots faster if we can flag the bitmap has being opaque
6640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool reallyHasAlpha = false;
6650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkColorTable* colorTable = NULL;
6660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_PALETTE) {
66858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        decodePalette(png_ptr, info_ptr, &hasAlpha,
66958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &reallyHasAlpha, &colorTable);
6700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
67158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoUnref aur(colorTable);
6730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
674c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // Check ahead of time if the swap(dest, src) is possible in crop or not.
675c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
676c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // If no, then we will use alloc to allocate pixels to prevent garbage collection.
677943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    int w = rect.width() / sampleSize;
678943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    int h = rect.height() / sampleSize;
679943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    bool swapOnly = (rect == region) && (w == decodedBitmap->width()) &&
680943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                    (h == decodedBitmap->height()) &&
681943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                    ((0 - rect.x()) / sampleSize == 0) && bm->isNull();
682943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (swapOnly) {
683c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        if (!this->allocPixelRef(decodedBitmap,
684c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin                SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
685c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            return false;
686c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        }
687943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    } else {
688c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        if (!decodedBitmap->allocPixels(
689c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
690c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            return false;
691c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        }
6920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
69358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoLockPixels alp(*decodedBitmap);
6940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Add filler (or alpha) byte (before/after each RGB triplet) */
6960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
6970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
6980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
6990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
7000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Turn on interlace handling.  REQUIRED if you are not using
7010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * png_read_image().  To see how to handle interlacing passes,
7020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * see the png_read_row() method below:
7030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
70458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
7050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                        png_set_interlace_handling(png_ptr) : 1;
7060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
7070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Optional call to gamma correct and add the background to the palette
7080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * and update info structure.  REQUIRED if you are expecting libpng to
7090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * update the palette for you (ie you selected such a transform above).
7100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
71158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_ptr->pass = 0;
7120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_read_update_info(png_ptr, info_ptr);
7130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
71458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int actualTop = rect.fTop;
71558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
7160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
7170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (int i = 0; i < number_passes; i++) {
71858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_configure_decoder(png_ptr, &actualTop, i);
71958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int j = 0; j < rect.fTop - actualTop; j++) {
72058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
72158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
72258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
7230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            for (png_uint_32 y = 0; y < origHeight; y++) {
7240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
7250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
7260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
7270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
7280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
7290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkScaledBitmapSampler::SrcConfig sc;
7300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        int srcBytesPerPixel = 4;
73158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
73218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (colorTable != NULL) {
7330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kIndex;
7340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            srcBytesPerPixel = 1;
7350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        } else if (hasAlpha) {
7360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kRGBA;
7370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        } else {
7380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kRGBX;
7390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
7400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
74118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        /*  We have to pass the colortable explicitly, since we may have one
74218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            even if our decodedBitmap doesn't, due to the request that we
74318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            upscale png's palette to a direct model
74418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed         */
74518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        SkAutoLockColors ctLock(colorTable);
74618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
74718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
74818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
7490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        const int height = decodedBitmap->height();
7500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
75118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (number_passes > 1) {
75218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
75318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            uint8_t* base = (uint8_t*)storage.get();
75418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            size_t rb = origWidth * srcBytesPerPixel;
75518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
75618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            for (int i = 0; i < number_passes; i++) {
75758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_configure_decoder(png_ptr, &actualTop, i);
75858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                for (int j = 0; j < rect.fTop - actualTop; j++) {
75958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
76058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
76158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
76218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                uint8_t* row = base;
763943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                for (png_uint_32 y = 0; y < rect.height(); y++) {
76418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    uint8_t* bmRow = row;
76518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
76618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    row += rb;
76718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                }
7680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
76918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            // now sample it
77018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            base += sampler.srcY0() * rb;
77118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            for (int y = 0; y < height; y++) {
77218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                reallyHasAlpha |= sampler.next(base);
77318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                base += sampler.srcDY() * rb;
77418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            }
77518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        } else {
77618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
7770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            uint8_t* srcRow = (uint8_t*)storage.get();
77858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
77958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_configure_decoder(png_ptr, &actualTop, 0);
7800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
7810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
78258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int i = 0; i < rect.fTop - actualTop; i++) {
78358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
78458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
78558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
7860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            for (int y = 0; y < height; y++) {
7870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                uint8_t* tmp = srcRow;
7880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
7890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                reallyHasAlpha |= sampler.next(srcRow);
7900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (y < height - 1) {
7910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
7920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
7930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
7940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
7950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
796943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (swapOnly) {
797943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        bm->swap(*decodedBitmap);
798943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    } else {
799943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(),
800943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                   region.width(), region.height(), 0, rect.y());
801943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    }
8020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (0 != theTranspColor) {
8040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
8050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    decodedBitmap->setIsOpaque(!reallyHasAlpha);
8070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return true;
8080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project///////////////////////////////////////////////////////////////////////////////
8110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkColorPriv.h"
8130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkUnPreMultiply.h"
8140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
8160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
8170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (!sk_stream->write(data, len)) {
8180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_error(png_ptr, "sk_write_fn Error!");
8190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projecttypedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
8230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                        int width, char* SK_RESTRICT dst);
8240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_565(const char* SK_RESTRICT src, int width,
8260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                   char* SK_RESTRICT dst) {
8270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
8280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < width; i++) {
8290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned c = *srcP++;
8300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked16ToR32(c);
8310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked16ToG32(c);
8320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked16ToB32(c);
8330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_888(const char* SK_RESTRICT src, int width,
8370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                   char* SK_RESTRICT dst) {
8380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
8390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < width; i++) {
8400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor c = *srcP++;
8410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkGetPackedR32(c);
8420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkGetPackedG32(c);
8430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkGetPackedB32(c);
8440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_444(const char* SK_RESTRICT src, int width,
8480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                   char* SK_RESTRICT dst) {
8490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
8500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < width; i++) {
8510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor16 c = *srcP++;
8520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked4444ToR32(c);
8530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked4444ToG32(c);
8540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = SkPacked4444ToB32(c);
8550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_8888(const char* SK_RESTRICT src, int width,
8590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                    char* SK_RESTRICT dst) {
8600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
8610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkUnPreMultiply::Scale* SK_RESTRICT table =
8620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                              SkUnPreMultiply::GetScaleTable();
8630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < width; i++) {
8650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor c = *srcP++;
8660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned a = SkGetPackedA32(c);
8670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned r = SkGetPackedR32(c);
8680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned g = SkGetPackedG32(c);
8690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned b = SkGetPackedB32(c);
8700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (0 != a && 255 != a) {
8720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            SkUnPreMultiply::Scale scale = table[a];
8730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            r = SkUnPreMultiply::ApplyScale(scale, r);
8740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            g = SkUnPreMultiply::ApplyScale(scale, g);
8750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            b = SkUnPreMultiply::ApplyScale(scale, b);
8760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
8770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = r;
8780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = g;
8790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = b;
8800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = a;
8810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_4444(const char* SK_RESTRICT src, int width,
8850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                    char* SK_RESTRICT dst) {
8860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
8870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkUnPreMultiply::Scale* SK_RESTRICT table =
8880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                              SkUnPreMultiply::GetScaleTable();
8890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < width; i++) {
8910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor16 c = *srcP++;
8920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned a = SkPacked4444ToA32(c);
8930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned r = SkPacked4444ToR32(c);
8940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned g = SkPacked4444ToG32(c);
8950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned b = SkPacked4444ToB32(c);
8960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (0 != a && 255 != a) {
8980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            SkUnPreMultiply::Scale scale = table[a];
8990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            r = SkUnPreMultiply::ApplyScale(scale, r);
9000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            g = SkUnPreMultiply::ApplyScale(scale, g);
9010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            b = SkUnPreMultiply::ApplyScale(scale, b);
9020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = r;
9040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = g;
9050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = b;
9060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        *dst++ = a;
9070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
9090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void transform_scanline_index8(const char* SK_RESTRICT src, int width,
9110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                      char* SK_RESTRICT dst) {
9120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    memcpy(dst, src, width);
9130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
9140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic transform_scanline_proc choose_proc(SkBitmap::Config config,
9160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                           bool hasAlpha) {
9170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we don't care about search on alpha if we're kIndex8, since only the
9180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // colortable packing cares about that distinction, not the pixels
9190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (SkBitmap::kIndex8_Config == config) {
9200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        hasAlpha = false;   // we store false in the table entries for kIndex8
9210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    static const struct {
9240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkBitmap::Config        fConfig;
9250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bool                    fHasAlpha;
9260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        transform_scanline_proc fProc;
9270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } gMap[] = {
9280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kRGB_565_Config,    false,  transform_scanline_565 },
9290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_8888_Config,  false,  transform_scanline_888 },
9300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_8888_Config,  true,   transform_scanline_8888 },
9310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_4444_Config,  false,  transform_scanline_444 },
9320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_4444_Config,  true,   transform_scanline_4444 },
9330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kIndex8_Config,     false,   transform_scanline_index8 },
9340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    };
9350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
9370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
9380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return gMap[i].fProc;
9390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    sk_throw();
9420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return NULL;
9430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
9440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// return the minimum legal bitdepth (by png standards) for this many colortable
9460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
9470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// we can use fewer bits per in png
9480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic int computeBitDepth(int colorCount) {
9490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
9500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bits = SkNextLog2(colorCount);
9510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkASSERT(bits >= 1 && bits <= 8);
9520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
9530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return SkNextPow2(bits);
9540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#else
9550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // for the moment, we don't know how to pack bitdepth < 8
9560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return 8;
9570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
9580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
9590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project/*  Pack palette[] with the corresponding colors, and if hasAlpha is true, also
9610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    pack trans[] and return the number of trans[] entries written. If hasAlpha
9620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    is false, the return value will always be 0.
9630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    Note: this routine takes care of unpremultiplying the RGB values when we
9650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    have alpha in the colortable, since png doesn't support premul colors
9660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project*/
9670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic inline int pack_palette(SkColorTable* ctable,
9680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                               png_color* SK_RESTRICT palette,
9690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                               png_byte* SK_RESTRICT trans, bool hasAlpha) {
9700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoLockColors alc(ctable);
9710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor* SK_RESTRICT colors = alc.colors();
9720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const int ctCount = ctable->count();
9730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int i, num_trans = 0;
9740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (hasAlpha) {
9760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        /*  first see if we have some number of fully opaque at the end of the
9770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            ctable. PNG allows num_trans < num_palette, but all of the trans
9780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            entries must come first in the palette. If I was smarter, I'd
9790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            reorder the indices and ctable so that all non-opaque colors came
9800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            first in the palette. But, since that would slow down the encode,
9810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            I'm leaving the indices and ctable order as is, and just looking
9820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            at the tail of the ctable for opaqueness.
9830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        */
9840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        num_trans = ctCount;
9850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (i = ctCount - 1; i >= 0; --i) {
9860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (SkGetPackedA32(colors[i]) != 0xFF) {
9870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                break;
9880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
9890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            num_trans -= 1;
9900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        const SkUnPreMultiply::Scale* SK_RESTRICT table =
9930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                            SkUnPreMultiply::GetScaleTable();
9940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (i = 0; i < num_trans; i++) {
9960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const SkPMColor c = *colors++;
9970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const unsigned a = SkGetPackedA32(c);
9980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const SkUnPreMultiply::Scale s = table[a];
9990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            trans[i] = a;
10000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
10010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
10020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
10030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
10040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // now fall out of this if-block to use common code for the trailing
10050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // opaque entries
10060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // these (remaining) entries are opaque
10090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (i = num_trans; i < ctCount; i++) {
10100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor c = *colors++;
10110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].red = SkGetPackedR32(c);
10120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].green = SkGetPackedG32(c);
10130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].blue = SkGetPackedB32(c);
10140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return num_trans;
10160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
10170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectclass SkPNGImageEncoder : public SkImageEncoder {
10190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprotected:
10200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
10211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerprivate:
10221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool doEncode(SkWStream* stream, const SkBitmap& bm,
10231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  const bool& hasAlpha, int colorType,
10241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  int bitDepth, SkBitmap::Config config,
10251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  png_color_8& sig_bit);
10260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
10270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectbool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
10290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                 int /*quality*/) {
10300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkBitmap::Config config = bitmap.getConfig();
10310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const bool hasAlpha = !bitmap.isOpaque();
10330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int colorType = PNG_COLOR_MASK_COLOR;
10340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bitDepth = 8;   // default for color
10350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_color_8 sig_bit;
10360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    switch (config) {
10380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kIndex8_Config:
10390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            colorType |= PNG_COLOR_MASK_PALETTE;
10400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            // fall through to the ARGB_8888 case
10410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kARGB_8888_Config:
10420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 8;
10430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 8;
10440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 8;
10450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 8;
10460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
10470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kARGB_4444_Config:
10480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 4;
10490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 4;
10500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 4;
10510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 4;
10520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
10530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kRGB_565_Config:
10540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 5;
10550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 6;
10560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 5;
10570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 0;
10580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
10590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        default:
10600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return false;
10610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (hasAlpha) {
10640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // don't specify alpha if we're a palette, even if our ctable has alpha
10650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
10660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            colorType |= PNG_COLOR_MASK_ALPHA;
10670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
10680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
10690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        sig_bit.alpha = 0;
10700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoLockPixels alp(bitmap);
10730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // readyToDraw checks for pixels (and colortable if that is required)
10740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (!bitmap.readyToDraw()) {
10750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
10760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we must do this after we have locked the pixels
10790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkColorTable* ctable = bitmap.getColorTable();
10800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL != ctable) {
10810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (ctable->count() == 0) {
10820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return false;
10830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
10840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // check if we can store in fewer than 8 bits
10850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bitDepth = computeBitDepth(ctable->count());
10860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    return doEncode(stream, bitmap, hasAlpha, colorType,
10891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                    bitDepth, config, sig_bit);
10901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}
10911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
10921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerbool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
10931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  const bool& hasAlpha, int colorType,
10941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  int bitDepth, SkBitmap::Config config,
10951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  png_color_8& sig_bit) {
10961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
10970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr;
10980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr;
10990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
11010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                      NULL);
11020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL == png_ptr) {
11030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
11040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    info_ptr = png_create_info_struct(png_ptr);
11070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL == info_ptr) {
11080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
11090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
11100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set error handling.  REQUIRED if you aren't supplying your own
11130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * error handling functions in the png_create_write_struct() call.
11140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
11150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (setjmp(png_jmpbuf(png_ptr))) {
11160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_write_struct(&png_ptr, &info_ptr);
11170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
11180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
11210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set the image information here.  Width and height are up to 2^31,
11230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
11240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
11250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
11260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
11270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
11280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
11290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
11300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
11320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 bitDepth, colorType,
11330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
11340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 PNG_FILTER_TYPE_BASE);
11350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
113618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    // set our colortable/trans arrays if needed
113718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    png_color paletteColors[256];
113818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    png_byte trans[256];
113918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    if (SkBitmap::kIndex8_Config == config) {
114018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        SkColorTable* ct = bitmap.getColorTable();
114118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
114218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
114318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (numTrans > 0) {
114418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
114518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
114618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    }
11470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
11490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_write_info(png_ptr, info_ptr);
11500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const char* srcImage = (const char*)bitmap.getPixels();
11520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
11530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    char* storage = (char*)rowStorage.get();
11540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    transform_scanline_proc proc = choose_proc(config, hasAlpha);
11550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int y = 0; y < bitmap.height(); y++) {
11570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_bytep row_ptr = (png_bytep)storage;
11580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        proc(srcImage, bitmap.width(), storage);
11590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_write_rows(png_ptr, &row_ptr, 1);
11600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        srcImage += bitmap.rowBytes();
11610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_write_end(png_ptr, info_ptr);
11640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* clean up after the write, and free any memory allocated */
11660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_destroy_write_struct(&png_ptr, &info_ptr);
11670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return true;
11680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
11690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project///////////////////////////////////////////////////////////////////////////////
11710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
11720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkTRegistry.h"
11730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1174579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed#ifdef SK_ENABLE_LIBPNG
1175579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed    SkImageDecoder* sk_libpng_dfactory(SkStream*);
1176579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed    SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
1177579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed#endif
1178579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed
1179579d35c405d37b1f8c9afdefaa37e836fb6b956fMike ReedSkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
11800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    char buf[PNG_BYTES_TO_CHECK];
11810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
11820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
11830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return SkNEW(SkPNGImageDecoder);
11840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return NULL;
11860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
11870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1188579d35c405d37b1f8c9afdefaa37e836fb6b956fMike ReedSkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
11890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
11900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
11910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1192579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reedstatic SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
1193579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reedstatic SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
1194