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"
20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "transform_scanline.h"
210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectextern "C" {
230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "png.h"
240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenclass SkPNGImageIndex {
2758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenpublic:
2858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageIndex() {
2958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        inputStream = NULL;
3058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_ptr = NULL;
3158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
3258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    virtual ~SkPNGImageIndex() {
3358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (png_ptr) {
3458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
3558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
3658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (inputStream) {
3758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            delete inputStream;
3858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
3958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
4058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
4158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr;
4258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkStream *inputStream;
4358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen};
4458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectclass SkPNGImageDecoder : public SkImageDecoder {
460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectpublic:
4758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageDecoder() {
4858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        index = NULL;
4958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    virtual Format getFormat() const {
510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return kPNG_Format;
520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
5358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    virtual ~SkPNGImageDecoder() {
5458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (index) {
5558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            delete index;
5658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
5758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
5858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprotected:
6014496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen    virtual bool onBuildTileIndex(SkStream *stream,
6114496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen             int *width, int *height);
62943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region);
630a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
6458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenprivate:
6658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool onDecodeInit(SkStream* stream, png_structp *png_ptrp,
6758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_infop *info_ptrp);
6858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool decodePalette(png_structp png_ptr, png_infop info_ptr,
6958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep);
7058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
7158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkBitmap::Config *config, bool *hasAlpha, bool *doDither,
7258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkPMColor *theTranspColor);
7358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPNGImageIndex *index;
740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#ifndef png_jmpbuf
770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#define PNG_BYTES_TO_CHECK 4
810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project/* Automatically clean up after throwing an exception */
830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstruct PNGAutoClean {
840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    ~PNGAutoClean() {
860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprivate:
890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr;
900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr;
910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    size_t bytes = sk_stream->read(data, length);
960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bytes != length) {
970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_error(png_ptr, "Read Error!");
980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenstatic void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
10258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
10358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    sk_stream->rewind();
10458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    (void)sk_stream->skip(offset);
10558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
10658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
1070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
1080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkImageDecoder::Peeker* peeker =
1090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                    (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
1100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // peek() returning true means continue decoding
1110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
1120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            1 : -1;
1130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
1160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
1170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkDebugf("------ png error %s\n", msg);
1180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
1190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    longjmp(png_jmpbuf(png_ptr), 1);
1200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
1230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = 0; i < count; i++) {
1240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        uint8_t* tmp = storage;
1250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
1260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic bool pos_le(int value, int max) {
1300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return value > 0 && value <= max;
1310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
1340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
1350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool reallyHasAlpha = false;
1370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int y = bm->height() - 1; y >= 0; --y) {
1390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor* p = bm->getAddr32(0, y);
1400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (int x = bm->width() - 1; x >= 0; --x) {
1410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (match == *p) {
1420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                *p = 0;
1430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                reallyHasAlpha = true;
1440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
1450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            p += 1;
1460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
1470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return reallyHasAlpha;
1490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1510a81c953145c77abea5ca1df9e84c62d9da96094Mike Reedstatic bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
152fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed                                      bool srcHasAlpha) {
1530a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed    switch (dstConfig) {
154fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kARGB_8888_Config:
155fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kARGB_4444_Config:
156fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return true;
157fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        case SkBitmap::kRGB_565_Config:
158fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            // only return true if the src is opaque (since 565 is opaque)
159fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return !srcHasAlpha;
160fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        default:
161fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed            return false;
162fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    }
163fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed}
164fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed
165fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed// call only if color_type is PALETTE. Returns true if the ctable has alpha
166fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reedstatic bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
167fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    png_bytep trans;
168fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    int num_trans;
169fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed
170fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
171fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
172fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        return num_trans > 0;
173fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    }
174fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed    return false;
17518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed}
17618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
17758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream,
17858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_structp *png_ptrp, png_infop *info_ptrp)
17958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen{
1800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Create and initialize the png_struct with the desired error handler
1810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * functions.  If you want to use the default stderr and longjump method,
1820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * you can supply NULL for the last three parameters.  We also supply the
1830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the compiler header file version, so that we know if the application
1840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * was compiled with a compatible version of the library.  */
1850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        NULL, sk_error_fn, NULL);
1870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);
1880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (png_ptr == NULL) {
1890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
1900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
19158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *png_ptrp = png_ptr;
1920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Allocate/initialize the memory for image information. */
1940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr = png_create_info_struct(png_ptr);
1950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (info_ptr == NULL) {
1960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
1970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
1980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
19958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *info_ptrp = info_ptr;
2000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set error handling if you are using the setjmp/longjmp method (this is
2020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the normal method of doing things with libpng).  REQUIRED unless you
2030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * set up your own error handlers in the png_create_read_struct() earlier.
2040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
2050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (setjmp(png_jmpbuf(png_ptr))) {
2060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
2070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* If you are using replacement read functions, instead of calling
2100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * png_init_io() here you would call:
2110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
2120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
21358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_set_seek_fn(png_ptr, sk_seek_fn);
2140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* where user_io_ptr is a structure you want available to the callbacks */
2150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* If we have already read some of the signature */
21658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
2170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // hookup our peeker so we can see any user-chunks the caller may be interested in
2190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
2200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (this->getPeeker()) {
2210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
2220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* The call to png_read_info() gives us all of the information from the
2250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG file before the first IDAT (image data chunk). */
2260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_read_info(png_ptr, info_ptr);
2270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_uint_32 origWidth, origHeight;
2280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bit_depth, color_type, interlace_type;
22958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
23058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
2310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
2320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
2330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bit_depth == 16) {
2340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_strip_16(png_ptr);
2350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
2370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project     * byte into separate bytes (useful for paletted and grayscale images). */
2380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (bit_depth < 8) {
2390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_packing(png_ptr);
2400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
2410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
2420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
2430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_gray_1_2_4_to_8(png_ptr);
2440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
24558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Make a grayscale image into RGB. */
2470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_GRAY ||
2480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
2490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_gray_to_rgb(png_ptr);
2500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
25158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
25258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
25358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
25458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
25558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                                 Mode mode) {
25658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
25758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr;
25858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
25958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
26058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
26158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
26258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (setjmp(png_jmpbuf(png_ptr))) {
26458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
26558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
26658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    PNGAutoClean autoClean(png_ptr, info_ptr);
26858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
26958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
27058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
27158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
27258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
27358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkBitmap::Config    config;
2750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool                hasAlpha = false;
2760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool                doDither = this->getDitherImage();
2770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
27858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
27958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
28058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &doDither, &theTranspColor) == false) {
28158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
28258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
28358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
28458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int sampleSize = this->getSampleSize();
28558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
28658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
2879717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    decodedBitmap->lockPixels();
2889717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    void* rowptr = (void*) decodedBitmap->getPixels();
2899717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    bool reuseBitmap = (rowptr != NULL);
2909717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    decodedBitmap->unlockPixels();
2919717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
2929717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase            sampler.scaledHeight() != decodedBitmap->height())) {
2939717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        // Dimensions must match
2949717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        return false;
2959717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
2969717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase
2979717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (!reuseBitmap) {
2989717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        decodedBitmap->setConfig(config, sampler.scaledWidth(),
2999717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                 sampler.scaledHeight(), 0);
3009717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
30158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
30258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return true;
30358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
30458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
30558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // from here down we are concerned with colortables and pixels
30658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
30758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
30858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
30958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // draw lots faster if we can flag the bitmap has being opaque
31058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool reallyHasAlpha = false;
31158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkColorTable* colorTable = NULL;
31258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
31358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (color_type == PNG_COLOR_TYPE_PALETTE) {
31458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        decodePalette(png_ptr, info_ptr, &hasAlpha,
31558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &reallyHasAlpha, &colorTable);
31658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
31758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
31858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoUnref aur(colorTable);
31958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
3209717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (!reuseBitmap) {
3219717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        if (!this->allocPixelRef(decodedBitmap,
3229717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                 SkBitmap::kIndex8_Config == config ?
3239717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase                                    colorTable : NULL)) {
3249717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase            return false;
3259717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        }
32658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
32758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
32858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoLockPixels alp(*decodedBitmap);
32958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
33058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Add filler (or alpha) byte (before/after each RGB triplet) */
33158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
33258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
33358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
33458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
33558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Turn on interlace handling.  REQUIRED if you are not using
33658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * png_read_image().  To see how to handle interlacing passes,
33758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * see the png_read_row() method below:
33858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    */
33958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
34058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                        png_set_interlace_handling(png_ptr) : 1;
34158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
34258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* Optional call to gamma correct and add the background to the palette
34358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * and update info structure.  REQUIRED if you are expecting libpng to
34458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    * update the palette for you (ie you selected such a transform above).
34558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    */
34658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_read_update_info(png_ptr, info_ptr);
34758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
34858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
34958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        for (int i = 0; i < number_passes; i++) {
35058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (png_uint_32 y = 0; y < origHeight; y++) {
35158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
35258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
35358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
35458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
35558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    } else {
35658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkScaledBitmapSampler::SrcConfig sc;
35758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        int srcBytesPerPixel = 4;
35858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
35958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (colorTable != NULL) {
36058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kIndex;
36158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            srcBytesPerPixel = 1;
36258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else if (hasAlpha) {
36358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kRGBA;
36458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else {
36558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            sc = SkScaledBitmapSampler::kRGBX;
36658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
36758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
36858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        /*  We have to pass the colortable explicitly, since we may have one
36958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            even if our decodedBitmap doesn't, due to the request that we
37058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            upscale png's palette to a direct model
37158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen         */
37258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkAutoLockColors ctLock(colorTable);
37358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
37458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            return false;
37558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
37658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        const int height = decodedBitmap->height();
37758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
37858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (number_passes > 1) {
37958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
38058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            uint8_t* base = (uint8_t*)storage.get();
38158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            size_t rb = origWidth * srcBytesPerPixel;
38258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
38358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int i = 0; i < number_passes; i++) {
38458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* row = base;
38558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                for (png_uint_32 y = 0; y < origHeight; y++) {
38658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    uint8_t* bmRow = row;
38758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
38858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    row += rb;
38958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
39058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
39158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            // now sample it
39258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            base += sampler.srcY0() * rb;
39358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int y = 0; y < height; y++) {
39458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                reallyHasAlpha |= sampler.next(base);
39558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                base += sampler.srcDY() * rb;
39658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
39758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        } else {
39858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
39958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            uint8_t* srcRow = (uint8_t*)storage.get();
40058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
40158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
40258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int y = 0; y < height; y++) {
40358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* tmp = srcRow;
40458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
40558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                reallyHasAlpha |= sampler.next(srcRow);
40658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                if (y < height - 1) {
40758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
40858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
40958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
41058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
41158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            // skip the rest of the rows (if any)
41258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_uint_32 read = (height - 1) * sampler.srcDY() +
41358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                               sampler.srcY0() + 1;
41458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            SkASSERT(read <= origHeight);
41558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            skip_src_rows(png_ptr, srcRow, origHeight - read);
41658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        }
41758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
41858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
41958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
42058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_read_end(png_ptr, info_ptr);
42158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
42258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (0 != theTranspColor) {
42358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
42458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
42558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    decodedBitmap->setIsOpaque(!reallyHasAlpha);
4269717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    if (reuseBitmap) {
4279717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase        decodedBitmap->notifyPixelsChanged();
4289717bd93d86e12d2b9a506179493dd50613e99b2Chet Haase    }
42958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
43058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
43158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43214496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chenbool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width,
43314496db08e9c54a5012933b8c25a58348627d646Wei-Ta Chen        int *height) {
43458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr;
43558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop   info_ptr;
43658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index = new SkPNGImageIndex();
43858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
43958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
44058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
44158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
44258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4432dad62296f6b4c462b329c48b3bcc31cfdc56accRuben Brunk    if (setjmp(png_jmpbuf(png_ptr)) != 0) {
4442dad62296f6b4c462b329c48b3bcc31cfdc56accRuben Brunk        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
4452dad62296f6b4c462b329c48b3bcc31cfdc56accRuben Brunk        return false;
4462dad62296f6b4c462b329c48b3bcc31cfdc56accRuben Brunk    }
4472dad62296f6b4c462b329c48b3bcc31cfdc56accRuben Brunk
44858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
44958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
45058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
45158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
45258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
45358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *width = origWidth;
45458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *height = origHeight;
45558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
45658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_build_index(png_ptr);
45758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index->png_ptr = png_ptr;
45858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    this->index->info_ptr = info_ptr;
45958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
46058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
46158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
46258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
46358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
46458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        SkPMColor *theTranspColorp) {
46558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
46658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
46758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
46858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
46958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // check for sBIT chunk data, in case we should disable dithering because
4710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // our data is not truely 8bits per component
47258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (*doDitherp) {
4730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
4740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
4750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
4760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 info_ptr->sig_bit.alpha);
4770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
4780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // 0 seems to indicate no information available
4790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
4800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
4810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
48258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *doDitherp = false;
4830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
4840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
48558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_PALETTE) {
487fda848cde04cfd40ec1be93b30752008f7eb2396Mike Reed        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
48858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
4890a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        // now see if we can upscale to their requested config
49058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
49158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *configp = SkBitmap::kIndex8_Config;
492421adab1be5bb9c1cd419138ceda375aef649077Mike Reed        }
4930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
4940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_color_16p   transpColor = NULL;
4950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        int             numTransp = 0;
49658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
49858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
4990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
50058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
5010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (valid && numTransp == 1 && transpColor != NULL) {
5020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            /*  Compute our transparent color, which we'll match against later.
5030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                We don't really handle 16bit components properly here, since we
5040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                do our compare *after* the values have been knocked down to 8bit
5050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                which means we will find more matches than we should. The real
5060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                fix seems to be to see the actual 16bit components, do the
5070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                compare, and then knock it down to 8bits ourselves.
5080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            */
5090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (color_type & PNG_COLOR_MASK_COLOR) {
5100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (16 == bit_depth) {
51158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
5120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                              transpColor->green >> 8, transpColor->blue >> 8);
5130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                } else {
51458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
5150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                      transpColor->green, transpColor->blue);
5160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
5170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            } else {    // gray
5180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (16 == bit_depth) {
51958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
5200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                              transpColor->gray >> 8, transpColor->gray >> 8);
5210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                } else {
52258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
5230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                          transpColor->gray, transpColor->gray);
5240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
5250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
5270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
5280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (valid ||
5290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
5300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
53158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            *hasAlphap = true;
5320a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        }
53358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
5340a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        // now match the request against our capabilities
53558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        if (*hasAlphap) {
53658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            if (*configp != SkBitmap::kARGB_4444_Config) {
53758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp = SkBitmap::kARGB_8888_Config;
5380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5390a81c953145c77abea5ca1df9e84c62d9da96094Mike Reed        } else {
54058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            if (*configp != SkBitmap::kRGB_565_Config &&
54158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp != SkBitmap::kARGB_4444_Config) {
54258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                *configp = SkBitmap::kARGB_8888_Config;
5430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
5440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
5450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
54618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
54718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    // sanity check for size
54818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    {
54918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        Sk64 size;
55018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        size.setMul(origWidth, origHeight);
55118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (size.isNeg() || !size.is32()) {
55218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
55318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
55418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        // now check that if we are 4-bytes per pixel, we also don't overflow
55518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (size.get32() > (0x7FFFFFFF >> 2)) {
55618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
55718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
55818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    }
55918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
56058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
5610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
5620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
56358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
56458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
56558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
56658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wenbool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
56758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
56858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int num_palette;
56958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_colorp palette;
57058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_bytep trans;
57158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int num_trans;
57258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool reallyHasAlpha = false;
57358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkColorTable* colorTable = NULL;
57458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
57658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    /*  BUGGY IMAGE WORKAROUND
57858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
57958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
58058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        which is a problem since we use the byte as an index. To work around this we grow
58158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
58258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        */
58358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int colorCount = num_palette + (num_palette < 256);
58458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
58558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
58658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
58758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPMColor* colorPtr = colorTable->lockColors();
58858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
58958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
59058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *hasAlphap = (num_trans > 0);
59158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    } else {
59258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        num_trans = 0;
59358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
59458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
59558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // check for bad images that might make us crash
59658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (num_trans > num_palette) {
59758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        num_trans = num_palette;
59858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
59958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
60058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int index = 0;
60158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int transLessThanFF = 0;
60258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
60358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    for (; index < num_trans; index++) {
60458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        transLessThanFF |= (int)*trans - 0xFF;
60558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
60658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        palette++;
60758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
60858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    reallyHasAlpha |= (transLessThanFF < 0);
60958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
61058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    for (; index < num_palette; index++) {
61158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
61258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        palette++;
61358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
61458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
61558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    // see BUGGY IMAGE WORKAROUND comment above
61658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (num_palette < 256) {
61758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        *colorPtr = colorPtr[-1];
61858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
61958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    colorTable->unlockColors(true);
62058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *colorTablep = colorTable;
62158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    *reallyHasAlphap = reallyHasAlpha;
62258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    return true;
62358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen}
62458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
625943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Linbool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
62658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int i;
62758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_structp png_ptr = this->index->png_ptr;
62858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_infop info_ptr = this->index->info_ptr;
62958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (setjmp(png_jmpbuf(png_ptr))) {
63058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
63158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
63258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
63358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_uint_32 origWidth, origHeight;
63458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int bit_depth, color_type, interlace_type;
63558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
63658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
63758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
638943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
639943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin
640943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (!rect.intersect(region)) {
641943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        // If the requested region is entirely outsides the image, just
642943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        // returns false
643943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        return false;
644943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    }
645943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin
64658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkBitmap::Config    config;
64758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool                hasAlpha = false;
64858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    bool                doDither = this->getDitherImage();
64958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
65058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
65158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
65258971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &doDither, &theTranspColor) == false) {
65358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        return false;
65458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    }
65558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const int sampleSize = this->getSampleSize();
657943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
65858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
65958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkBitmap *decodedBitmap = new SkBitmap;
66058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoTDelete<SkBitmap> adb(decodedBitmap);
6610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    decodedBitmap->setConfig(config, sampler.scaledWidth(),
6630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                             sampler.scaledHeight(), 0);
66458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // from here down we are concerned with colortables and pixels
6660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
6680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
6690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // draw lots faster if we can flag the bitmap has being opaque
6700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool reallyHasAlpha = false;
6710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkColorTable* colorTable = NULL;
6720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
6730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_PALETTE) {
67458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen        decodePalette(png_ptr, info_ptr, &hasAlpha,
67558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                &reallyHasAlpha, &colorTable);
6760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
67758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
6780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoUnref aur(colorTable);
6790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
680c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // Check ahead of time if the swap(dest, src) is possible in crop or not.
681c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
682c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin    // If no, then we will use alloc to allocate pixels to prevent garbage collection.
683943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    int w = rect.width() / sampleSize;
684943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    int h = rect.height() / sampleSize;
685943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    bool swapOnly = (rect == region) && (w == decodedBitmap->width()) &&
686943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                    (h == decodedBitmap->height()) &&
687943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                    ((0 - rect.x()) / sampleSize == 0) && bm->isNull();
688943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (swapOnly) {
689c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        if (!this->allocPixelRef(decodedBitmap,
690c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin                SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
691c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            return false;
692c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        }
693943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    } else {
694c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        if (!decodedBitmap->allocPixels(
695c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
696c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin            return false;
697c85ca84d9f8bd5fe91b4315481858d01757ee500Owen Lin        }
6980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
69958971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    SkAutoLockPixels alp(*decodedBitmap);
7000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
7010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Add filler (or alpha) byte (before/after each RGB triplet) */
7020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
7030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
7040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
7050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
7060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Turn on interlace handling.  REQUIRED if you are not using
7070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * png_read_image().  To see how to handle interlacing passes,
7080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * see the png_read_row() method below:
7090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
71058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
7110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                        png_set_interlace_handling(png_ptr) : 1;
7120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
7130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Optional call to gamma correct and add the background to the palette
7140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * and update info structure.  REQUIRED if you are expecting libpng to
7150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * update the palette for you (ie you selected such a transform above).
7160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
71758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    png_ptr->pass = 0;
7180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_read_update_info(png_ptr, info_ptr);
7190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
72058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen    int actualTop = rect.fTop;
72158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
7220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
7230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (int i = 0; i < number_passes; i++) {
72458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_configure_decoder(png_ptr, &actualTop, i);
72558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int j = 0; j < rect.fTop - actualTop; j++) {
72658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
72758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
72858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
7290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            for (png_uint_32 y = 0; y < origHeight; y++) {
7300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
7310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
7320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
7330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
7340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
7350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkScaledBitmapSampler::SrcConfig sc;
7360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        int srcBytesPerPixel = 4;
73758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
73818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (colorTable != NULL) {
7390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kIndex;
7400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            srcBytesPerPixel = 1;
7410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        } else if (hasAlpha) {
7420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kRGBA;
7430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        } else {
7440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sc = SkScaledBitmapSampler::kRGBX;
7450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
7460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
74718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        /*  We have to pass the colortable explicitly, since we may have one
74818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            even if our decodedBitmap doesn't, due to the request that we
74918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            upscale png's palette to a direct model
75018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed         */
75118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        SkAutoLockColors ctLock(colorTable);
75218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
75318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            return false;
75418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
7550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        const int height = decodedBitmap->height();
7560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
75718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (number_passes > 1) {
75818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
75918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            uint8_t* base = (uint8_t*)storage.get();
76018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            size_t rb = origWidth * srcBytesPerPixel;
76118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed
76218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            for (int i = 0; i < number_passes; i++) {
76358971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_configure_decoder(png_ptr, &actualTop, i);
76458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                for (int j = 0; j < rect.fTop - actualTop; j++) {
7656699e7ea2e981dccc2f3c41b5dcf1c860b11558dJean-Baptiste Queru                    uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels();
76658971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
76758971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                }
76818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                uint8_t* row = base;
769943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                for (png_uint_32 y = 0; y < rect.height(); y++) {
77018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    uint8_t* bmRow = row;
77118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
77218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                    row += rb;
77318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                }
7740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
77518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            // now sample it
77618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            base += sampler.srcY0() * rb;
77718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            for (int y = 0; y < height; y++) {
77818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                reallyHasAlpha |= sampler.next(base);
77918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed                base += sampler.srcDY() * rb;
78018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            }
78118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        } else {
78218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
7830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            uint8_t* srcRow = (uint8_t*)storage.get();
78458971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen
78558971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            png_configure_decoder(png_ptr, &actualTop, 0);
7860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
7870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
78858971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            for (int i = 0; i < rect.fTop - actualTop; i++) {
7896699e7ea2e981dccc2f3c41b5dcf1c860b11558dJean-Baptiste Queru                uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels();
79058971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
79158971879258a502a7568fbad0e6852e78c3eca3cJoseph Wen            }
7920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            for (int y = 0; y < height; y++) {
7930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                uint8_t* tmp = srcRow;
7940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
7950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                reallyHasAlpha |= sampler.next(srcRow);
7960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (y < height - 1) {
7970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
7980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                }
7990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
8000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
8010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
802943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    if (swapOnly) {
803943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        bm->swap(*decodedBitmap);
804943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    } else {
805943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin        cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(),
806943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin                   region.width(), region.height(), 0, rect.y());
807943e1fde4c6ecf0eb8998cd86012caa341a02ccfOwen Lin    }
8080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (0 != theTranspColor) {
8100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
8110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    decodedBitmap->setIsOpaque(!reallyHasAlpha);
8130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return true;
8140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project///////////////////////////////////////////////////////////////////////////////
8170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkColorPriv.h"
8190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkUnPreMultiply.h"
8200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
8220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
8230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (!sk_stream->write(data, len)) {
8240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_error(png_ptr, "sk_write_fn Error!");
8250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic transform_scanline_proc choose_proc(SkBitmap::Config config,
8290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                           bool hasAlpha) {
8300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we don't care about search on alpha if we're kIndex8, since only the
8310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // colortable packing cares about that distinction, not the pixels
8320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (SkBitmap::kIndex8_Config == config) {
8330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        hasAlpha = false;   // we store false in the table entries for kIndex8
8340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    static const struct {
8370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkBitmap::Config        fConfig;
8380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bool                    fHasAlpha;
8390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        transform_scanline_proc fProc;
8400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } gMap[] = {
8410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kRGB_565_Config,    false,  transform_scanline_565 },
8420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_8888_Config,  false,  transform_scanline_888 },
8430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_8888_Config,  true,   transform_scanline_8888 },
8440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_4444_Config,  false,  transform_scanline_444 },
8450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        { SkBitmap::kARGB_4444_Config,  true,   transform_scanline_4444 },
846363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        { SkBitmap::kIndex8_Config,     false,  transform_scanline_memcpy },
8470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    };
8480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
8500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
8510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return gMap[i].fProc;
8520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
8530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
8540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    sk_throw();
8550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return NULL;
8560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// return the minimum legal bitdepth (by png standards) for this many colortable
8590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
8600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project// we can use fewer bits per in png
8610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic int computeBitDepth(int colorCount) {
8620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if 0
8630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bits = SkNextLog2(colorCount);
8640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkASSERT(bits >= 1 && bits <= 8);
8650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
8660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return SkNextPow2(bits);
8670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#else
8680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // for the moment, we don't know how to pack bitdepth < 8
8690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return 8;
8700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
8710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
8720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project/*  Pack palette[] with the corresponding colors, and if hasAlpha is true, also
8740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    pack trans[] and return the number of trans[] entries written. If hasAlpha
8750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    is false, the return value will always be 0.
8760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    Note: this routine takes care of unpremultiplying the RGB values when we
8780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    have alpha in the colortable, since png doesn't support premul colors
8790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project*/
8800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic inline int pack_palette(SkColorTable* ctable,
8810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                               png_color* SK_RESTRICT palette,
8820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                               png_byte* SK_RESTRICT trans, bool hasAlpha) {
8830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoLockColors alc(ctable);
8840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const SkPMColor* SK_RESTRICT colors = alc.colors();
8850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const int ctCount = ctable->count();
8860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int i, num_trans = 0;
8870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
8880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (hasAlpha) {
8890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        /*  first see if we have some number of fully opaque at the end of the
8900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            ctable. PNG allows num_trans < num_palette, but all of the trans
8910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            entries must come first in the palette. If I was smarter, I'd
8920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            reorder the indices and ctable so that all non-opaque colors came
8930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            first in the palette. But, since that would slow down the encode,
8940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            I'm leaving the indices and ctable order as is, and just looking
8950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            at the tail of the ctable for opaqueness.
8960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        */
8970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        num_trans = ctCount;
8980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (i = ctCount - 1; i >= 0; --i) {
8990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (SkGetPackedA32(colors[i]) != 0xFF) {
9000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                break;
9010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
9020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            num_trans -= 1;
9030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        const SkUnPreMultiply::Scale* SK_RESTRICT table =
9060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                            SkUnPreMultiply::GetScaleTable();
9070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        for (i = 0; i < num_trans; i++) {
9090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const SkPMColor c = *colors++;
9100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const unsigned a = SkGetPackedA32(c);
9110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            const SkUnPreMultiply::Scale s = table[a];
9120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            trans[i] = a;
9130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
9140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
9150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
9160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // now fall out of this if-block to use common code for the trailing
9180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // opaque entries
9190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // these (remaining) entries are opaque
9220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (i = num_trans; i < ctCount; i++) {
9230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkPMColor c = *colors++;
9240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].red = SkGetPackedR32(c);
9250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].green = SkGetPackedG32(c);
9260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        palette[i].blue = SkGetPackedB32(c);
9270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return num_trans;
9290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
9300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectclass SkPNGImageEncoder : public SkImageEncoder {
9320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectprotected:
9330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
9341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerprivate:
9351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool doEncode(SkWStream* stream, const SkBitmap& bm,
9361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  const bool& hasAlpha, int colorType,
9371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  int bitDepth, SkBitmap::Config config,
9381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  png_color_8& sig_bit);
9390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
9400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectbool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
9420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                 int /*quality*/) {
9430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkBitmap::Config config = bitmap.getConfig();
9440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const bool hasAlpha = !bitmap.isOpaque();
9460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int colorType = PNG_COLOR_MASK_COLOR;
9470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    int bitDepth = 8;   // default for color
9480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_color_8 sig_bit;
9490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    switch (config) {
9510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kIndex8_Config:
9520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            colorType |= PNG_COLOR_MASK_PALETTE;
9530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            // fall through to the ARGB_8888 case
9540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kARGB_8888_Config:
9550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 8;
9560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 8;
9570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 8;
9580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 8;
9590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
9600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kARGB_4444_Config:
9610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 4;
9620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 4;
9630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 4;
9640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 4;
9650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
9660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        case SkBitmap::kRGB_565_Config:
9670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.red = 5;
9680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.green = 6;
9690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.blue = 5;
9700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            sig_bit.alpha = 0;
9710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
9720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        default:
9730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return false;
9740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (hasAlpha) {
9770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // don't specify alpha if we're a palette, even if our ctable has alpha
9780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
9790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            colorType |= PNG_COLOR_MASK_ALPHA;
9800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    } else {
9820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        sig_bit.alpha = 0;
9830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoLockPixels alp(bitmap);
9860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // readyToDraw checks for pixels (and colortable if that is required)
9870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (!bitmap.readyToDraw()) {
9880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
9890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
9900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
9910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    // we must do this after we have locked the pixels
9920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkColorTable* ctable = bitmap.getColorTable();
9930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL != ctable) {
9940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (ctable->count() == 0) {
9950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            return false;
9960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
9970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        // check if we can store in fewer than 8 bits
9980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        bitDepth = computeBitDepth(ctable->count());
9990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    return doEncode(stream, bitmap, hasAlpha, colorType,
10021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                    bitDepth, config, sig_bit);
10031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}
10041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
10051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerbool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
10061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  const bool& hasAlpha, int colorType,
10071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  int bitDepth, SkBitmap::Config config,
10081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                  png_color_8& sig_bit) {
10091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
10100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_structp png_ptr;
10110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_infop info_ptr;
10120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
10140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                                      NULL);
10150910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL == png_ptr) {
10160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
10170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    info_ptr = png_create_info_struct(png_ptr);
10200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (NULL == info_ptr) {
10210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
10220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
10230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set error handling.  REQUIRED if you aren't supplying your own
10260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * error handling functions in the png_create_write_struct() call.
10270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
10280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (setjmp(png_jmpbuf(png_ptr))) {
10290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_destroy_write_struct(&png_ptr, &info_ptr);
10300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return false;
10310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
10340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* Set the image information here.  Width and height are up to 2^31,
10360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
10370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
10380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
10390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
10400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
10410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
10420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    */
10430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
10450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 bitDepth, colorType,
10460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
10470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                 PNG_FILTER_TYPE_BASE);
10480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
104918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    // set our colortable/trans arrays if needed
105018987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    png_color paletteColors[256];
105118987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    png_byte trans[256];
105218987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    if (SkBitmap::kIndex8_Config == config) {
105318987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        SkColorTable* ct = bitmap.getColorTable();
105418987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
105518987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
105618987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        if (numTrans > 0) {
105718987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
105818987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed        }
105918987486da6931c8698a13e3fbac3b22f0c965a6Mike Reed    }
10600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
10620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_write_info(png_ptr, info_ptr);
10630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const char* srcImage = (const char*)bitmap.getPixels();
10650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
10660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    char* storage = (char*)rowStorage.get();
10670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    transform_scanline_proc proc = choose_proc(config, hasAlpha);
10680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int y = 0; y < bitmap.height(); y++) {
10700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_bytep row_ptr = (png_bytep)storage;
10710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        proc(srcImage, bitmap.width(), storage);
10720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        png_write_rows(png_ptr, &row_ptr, 1);
10730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        srcImage += bitmap.rowBytes();
10740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
10750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_write_end(png_ptr, info_ptr);
10770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    /* clean up after the write, and free any memory allocated */
10790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    png_destroy_write_struct(&png_ptr, &info_ptr);
10800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return true;
10810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
10820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project///////////////////////////////////////////////////////////////////////////////
108480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruDEFINE_DECODER_CREATOR(PNGImageDecoder);
108580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruDEFINE_ENCODER_CREATOR(PNGImageEncoder);
108680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru///////////////////////////////////////////////////////////////////////////////
10870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
10880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkTRegistry.h"
10890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1090579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed#ifdef SK_ENABLE_LIBPNG
1091579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed    SkImageDecoder* sk_libpng_dfactory(SkStream*);
1092579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed    SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
1093579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed#endif
1094579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reed
1095579d35c405d37b1f8c9afdefaa37e836fb6b956fMike ReedSkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
10960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    char buf[PNG_BYTES_TO_CHECK];
10970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
10980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
10990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return SkNEW(SkPNGImageDecoder);
11000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
11010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return NULL;
11020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
11030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1104579d35c405d37b1f8c9afdefaa37e836fb6b956fMike ReedSkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
11050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
11060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
11070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1108579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reedstatic SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
1109579d35c405d37b1f8c9afdefaa37e836fb6b956fMike Reedstatic SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
1110