1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// Copyright 2006 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// Build resource files from raw assets.
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define PNG_INTERNAL
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "Images.h"
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/ResourceTypes.h>
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/ByteOrder.h>
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <png.h>
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <zlib.h>
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
172412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe// Change this to true for noisy debug output.
182412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampestatic const bool kIsDebug = false;
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipng_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    AaptFile* aaptfile = (AaptFile*) png_get_io_ptr(png_ptr);
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t err = aaptfile->writeData(data, length);
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (err != NO_ERROR) {
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_error(png_ptr, "Write Error");
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void
322412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampepng_flush_aapt_file(png_structp /* png_ptr */)
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// This holds an image as 8bpp RGBA.
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistruct image_info
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
396381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    image_info() : rows(NULL), is9Patch(false),
406381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { }
416381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ~image_info() {
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (rows && rows != allocRows) {
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            free(rows);
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (allocRows) {
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (int i=0; i<(int)allocHeight; i++) {
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                free(allocRows[i]);
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            free(allocRows);
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
526381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(xDivs);
536381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(yDivs);
546381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(colors);
556381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    }
566381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath
576381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    void* serialize9patch() {
586381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors);
596381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
606381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        return serialized;
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 width;
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 height;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows;
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 9-patch info.
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool is9Patch;
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    Res_png_9patch info9Patch;
706381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* xDivs;
716381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* yDivs;
726381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    uint32_t* colors;
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Layout padding, if relevant
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool haveLayoutBounds;
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsLeft;
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsTop;
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsRight;
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsBottom;
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
8147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // Round rect outline description
8247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsLeft;
8347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsTop;
8447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsRight;
8547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsBottom;
8647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    float outlineRadius;
8777b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t outlineAlpha;
8847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 allocHeight;
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp allocRows;
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
93859e19f97d481bf07c768087d8eb3031fac77aadJohn Reckstatic void log_warning(png_structp png_ptr, png_const_charp warning_message)
94859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck{
95859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    const char* imageName = (const char*) png_get_error_ptr(png_ptr);
96859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    fprintf(stderr, "%s: libpng warning: %s\n", imageName, warning_message);
97859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck}
98859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void read_png(const char* imageName,
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     png_structp read_ptr, png_infop read_info,
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     image_info* outImageInfo)
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int color_type;
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bit_depth, interlace_type, compression_type;
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
107859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    png_set_error_fn(read_ptr, const_cast<char*>(imageName),
108859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck            NULL /* use default errorfn */, log_warning);
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_info(read_ptr, read_info);
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &outImageInfo->height, &bit_depth, &color_type,
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &interlace_type, &compression_type, NULL);
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //printf("Image %s:\n", imageName);
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //       color_type, bit_depth, interlace_type, compression_type);
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE)
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_palette_to_rgb(read_ptr);
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_expand_gray_1_2_4_to_8(read_ptr);
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        //printf("Has PNG_INFO_tRNS!\n");
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_tRNS_to_alpha(read_ptr);
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bit_depth == 16)
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_strip_16(read_ptr);
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_gray_to_rgb(read_ptr);
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
139859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    png_set_interlace_handling(read_ptr);
140859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_update_info(read_ptr, read_info);
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->rows = (png_bytepp)malloc(
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outImageInfo->height * sizeof(png_bytep));
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->allocHeight = outImageInfo->height;
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->allocRows = outImageInfo->rows;
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_rows(read_ptr, read_info, outImageInfo->rows);
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int)outImageInfo->height; i++)
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outImageInfo->rows[i] = (png_bytep)
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            malloc(png_get_rowbytes(read_ptr, read_info));
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_image(read_ptr, outImageInfo->rows);
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_end(read_ptr, read_info);
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1602412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
1612412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
1622412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                imageName,
1632412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                (int)outImageInfo->width, (int)outImageInfo->height,
1642412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                bit_depth, color_type,
1652412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                interlace_type, compression_type);
1662412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &outImageInfo->height, &bit_depth, &color_type,
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &interlace_type, &compression_type, NULL);
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_TRANSPARENT 0
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_WHITE 0xFFFFFFFF
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_TICK  0xFF000000
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskienum {
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_NONE,
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_TICK,
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_LAYOUT_BOUNDS,
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_BOTH
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic int tick_type(png_bytep p, bool transparent, const char** outError)
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (transparent) {
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[3] == 0) {
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_NONE;
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color == COLOR_LAYOUT_BOUNDS_TICK) {
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_LAYOUT_BOUNDS;
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color == COLOR_TICK) {
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_TICK;
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Error cases
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[3] != 0xff) {
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_NONE;
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outError = "Ticks in transparent frame must be black or red";
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_TICK;
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (p[3] != 0xFF) {
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "White frame must be a solid color (no alpha)";
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_WHITE) {
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_NONE;
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_TICK) {
218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_TICK;
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_LAYOUT_BOUNDS_TICK) {
221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_LAYOUT_BOUNDS;
222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "Ticks in white frame must be black or red";
226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_NONE;
227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return TICK_TYPE_TICK;
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskienum {
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_START,
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_INSIDE_1,
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_OUTSIDE_1
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_horizontal_ticks(
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row, int width, bool transparent, bool required,
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outLeft, int32_t* outRight, const char** outError,
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        uint8_t* outDivs, bool multipleAllowed)
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outLeft = *outRight = -1;
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int state = TICK_START;
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool found = false;
246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=1; i<width-1; i++) {
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_START ||
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outLeft = i-1;
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outRight = width-2;
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                found = true;
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outDivs != NULL) {
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *outDivs += 2;
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_INSIDE_1;
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (state == TICK_OUTSIDE_1) {
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outError = "Can't have more than one marked region along edge";
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outLeft = i;
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return UNKNOWN_ERROR;
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (*outError == NULL) {
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_INSIDE_1) {
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // We're done with this div.  Move on to the next.
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outRight = i-1;
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outRight += 2;
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outLeft += 2;
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_OUTSIDE_1;
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outLeft = i;
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return UNKNOWN_ERROR;
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (required && !found) {
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "No marked region found along edge";
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outLeft = -1;
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_vertical_ticks(
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytepp rows, int offset, int height, bool transparent, bool required,
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outTop, int32_t* outBottom, const char** outError,
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        uint8_t* outDivs, bool multipleAllowed)
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outTop = *outBottom = -1;
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int state = TICK_START;
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool found = false;
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=1; i<height-1; i++) {
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_START ||
299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outTop = i-1;
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outBottom = height-2;
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                found = true;
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outDivs != NULL) {
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *outDivs += 2;
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_INSIDE_1;
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (state == TICK_OUTSIDE_1) {
308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outError = "Can't have more than one marked region along edge";
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outTop = i;
310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return UNKNOWN_ERROR;
311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (*outError == NULL) {
313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_INSIDE_1) {
314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // We're done with this div.  Move on to the next.
315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outBottom = i-1;
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outTop += 2;
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outBottom += 2;
318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_OUTSIDE_1;
319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outTop = i;
322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return UNKNOWN_ERROR;
323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (required && !found) {
327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "No marked region found along edge";
328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outTop = -1;
329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_horizontal_layout_bounds_ticks(
3362412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        png_bytep row, int width, bool transparent, bool /* required */,
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outLeft, int32_t* outRight, const char** outError)
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outLeft = *outRight = 0;
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for left tick
343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Starting with a layout padding tick
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = 1;
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i < width - 1) {
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outLeft)++;
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i++;
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(row + i * 4, transparent, outError);
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for right tick
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Ending with a layout padding tick
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = width - 2;
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i > 1) {
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outRight)++;
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i--;
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(row+i*4, transparent, outError);
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_vertical_layout_bounds_ticks(
3742412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        png_bytepp rows, int offset, int height, bool transparent, bool /* required */,
375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outTop, int32_t* outBottom, const char** outError)
376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outTop = *outBottom = 0;
379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for top tick
381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Starting with a layout padding tick
383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = 1;
384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i < height - 1) {
385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outTop)++;
386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i++;
387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(rows[i] + offset, transparent, outError);
388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for bottom tick
395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Ending with a layout padding tick
397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = height - 2;
398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i > 1) {
399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outBottom)++;
400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i--;
401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(rows[i] + offset, transparent, outError);
402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
41147cd8e921db73e894f94ec4729ade90da50996f5Chris Craikstatic void find_max_opacity(png_byte** rows,
41247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                             int startX, int startY, int endX, int endY, int dX, int dY,
41347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                             int* out_inset)
41447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
41577b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_opacity = 0;
41647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int inset = 0;
41747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    *out_inset = 0;
41847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
41947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        png_byte* color = rows[y] + x * 4;
42077b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t opacity = color[3];
42147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        if (opacity > max_opacity) {
42247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            max_opacity = opacity;
42347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            *out_inset = inset;
42447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        }
42547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        if (opacity == 0xff) return;
42647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
42747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
42847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
42977b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craikstatic uint8_t max_alpha_over_row(png_byte* row, int startX, int endX)
43047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
43177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_alpha = 0;
43247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int x = startX; x < endX; x++) {
43377b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t alpha = (row + x * 4)[3];
43477b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        if (alpha > max_alpha) max_alpha = alpha;
43547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
43677b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    return max_alpha;
43747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
43847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
43977b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craikstatic uint8_t max_alpha_over_col(png_byte** rows, int offsetX, int startY, int endY)
44047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
44177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_alpha = 0;
44247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int y = startY; y < endY; y++) {
44377b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t alpha = (rows[y] + offsetX * 4)[3];
44477b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        if (alpha > max_alpha) max_alpha = alpha;
44547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
44677b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    return max_alpha;
44747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
44847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
44947cd8e921db73e894f94ec4729ade90da50996f5Chris Craikstatic void get_outline(image_info* image)
45047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
45147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int midX = image->width / 2;
45247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int midY = image->height / 2;
45347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int endX = image->width - 2;
45447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int endY = image->height - 2;
45547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
45647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // find left and right extent of nine patch content on center row
45747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    if (image->width > 4) {
45847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
45947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
46047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    } else {
46147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsLeft = 0;
46247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsRight = 0;
46347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
46447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
46547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // find top and bottom extent of nine patch content on center column
46647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    if (image->height > 4) {
46747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
46847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
46947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    } else {
47047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsTop = 0;
47147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsBottom = 0;
47247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
47347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
47447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerStartX = 1 + image->outlineInsetsLeft;
47547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerStartY = 1 + image->outlineInsetsTop;
47647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerEndX = endX - image->outlineInsetsRight;
47747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerEndY = endY - image->outlineInsetsBottom;
47847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerMidX = (innerEndX + innerStartX) / 2;
47947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerMidY = (innerEndY + innerStartY) / 2;
48047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
48147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // assuming the image is a round rect, compute the radius by marching
48247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // diagonally from the top left corner towards the center
483030f536009b56dbcc23d284541e51562bd9a6ed3Dan Albert    image->outlineAlpha = std::max(
484030f536009b56dbcc23d284541e51562bd9a6ed3Dan Albert        max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX),
485030f536009b56dbcc23d284541e51562bd9a6ed3Dan Albert        max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY));
48647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
48747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int diagonalInset = 0;
48847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
48947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            &diagonalInset);
49047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
49147d86230823de5d372688dff2b6e363a607009a8Chris Craik    /* Determine source radius based upon inset:
49247d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
49347d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     sqrt(2) * r = sqrt(2) * i + r
49447d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     (sqrt(2) - 1) * r = sqrt(2) * i
49547d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     r = sqrt(2) / (sqrt(2) - 1) * i
49647d86230823de5d372688dff2b6e363a607009a8Chris Craik     */
49747d86230823de5d372688dff2b6e363a607009a8Chris Craik    image->outlineRadius = 3.4142f * diagonalInset;
49847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
4998daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe    if (kIsDebug) {
5008daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe        printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
5018daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineInsetsLeft,
5028daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineInsetsTop,
5038daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineInsetsRight,
5048daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineInsetsBottom,
5058daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineRadius,
5068daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                image->outlineAlpha);
5078daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe    }
50847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
50947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic uint32_t get_color(
512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows, int left, int top, int right, int bottom)
513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytep color = rows[top] + left*4;
515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (left > right || top > bottom) {
517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return Res_png_9patch::TRANSPARENT_COLOR;
518282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
519282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    while (top <= bottom) {
521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = left; i <= right; i++) {
522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep p = rows[top]+i*4;
523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (color[3] == 0) {
524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (p[3] != 0) {
525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return Res_png_9patch::NO_COLOR;
526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (p[0] != color[0] || p[1] != color[1]
528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                       || p[2] != color[2] || p[3] != color[3]) {
529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return Res_png_9patch::NO_COLOR;
530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        top++;
533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color[3] == 0) {
536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return Res_png_9patch::TRANSPARENT_COLOR;
537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t do_9patch(const char* imageName, image_info* image)
542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->is9Patch = true;
544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int W = image->width;
546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int H = image->height;
547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i, j;
548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxSizeXDivs = W * sizeof(int32_t);
550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxSizeYDivs = H * sizeof(int32_t);
5516381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs);
5526381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs);
553c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes    uint8_t numXDivs = 0;
554c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes    uint8_t numYDivs = 0;
555c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes
556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int8_t numColors;
557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int numRows;
558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int numCols;
559282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int top;
560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int left;
561282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int right;
562282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bottom;
563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memset(xDivs, -1, maxSizeXDivs);
564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memset(yDivs, -1, maxSizeYDivs);
565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->layoutBoundsLeft = image->layoutBoundsRight =
569282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->layoutBoundsTop = image->layoutBoundsBottom = 0;
570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
571282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytep p = image->rows[0];
572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool transparent = p[3] == 0;
573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool hasColor = false;
574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const char* errorMsg = NULL;
576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int errorPixel = -1;
577282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const char* errorEdge = NULL;
578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int colorIndex = 0;
580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
581282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Validate size...
582282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (W < 3 || H < 3) {
583282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
584282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
585282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Validate frame...
588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!transparent &&
589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
590282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Must have one-pixel frame that is either transparent or white";
591282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
592282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
593282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of sizing areas...
595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                             &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
597282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = xDivs[0];
598282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "top";
599282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
600282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
601282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
602282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find top and bottom of sizing areas...
603282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
604282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                           &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
605282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = yDivs[0];
606282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "left";
607282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
608282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
609282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
610b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    // Copy patch size data into image...
611b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    image->info9Patch.numXDivs = numXDivs;
612b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    image->info9Patch.numYDivs = numYDivs;
613b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes
614282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of padding area...
615282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
616282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                             &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
617282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = image->info9Patch.paddingLeft;
618282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "bottom";
619282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
620282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
621282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
622282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find top and bottom of padding area...
623282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
624282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                           &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
625282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = image->info9Patch.paddingTop;
626282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "right";
627282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
628282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
629282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
630282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of layout padding...
631282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
632282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsLeft,
633282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsRight, &errorMsg);
634282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
635282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
636282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsTop,
637282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsBottom, &errorMsg);
638282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
639282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->haveLayoutBounds = image->layoutBoundsLeft != 0
640282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsRight != 0
641282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsTop != 0
642282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsBottom != 0;
643282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
644282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->haveLayoutBounds) {
6452412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        if (kIsDebug) {
6462412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
6472412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    image->layoutBoundsRight, image->layoutBoundsBottom);
6482412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        }
649282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
650282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
65147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // use opacity of pixels to estimate the round rect outline
65247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    get_outline(image);
65347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
654282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If padding is not yet specified, take values from size.
655282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->info9Patch.paddingLeft < 0) {
656282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingLeft = xDivs[0];
657282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingRight = W - 2 - xDivs[1];
658282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
659282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Adjust value to be correct!
660282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
661282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
662282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->info9Patch.paddingTop < 0) {
663282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingTop = yDivs[0];
664282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingBottom = H - 2 - yDivs[1];
665282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
666282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Adjust value to be correct!
667282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
668282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
669282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
6702412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
6712412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
6728daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                xDivs[0], xDivs[1],
6738daabceb2efddebe2e7c0b2425ad9f8ef62c0a5cAndreas Gampe                yDivs[0], yDivs[1]);
6742412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
6752412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
6762412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
6772412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
678282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
679282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Remove frame from image.
680282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep));
681282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=0; i<(H-2); i++) {
682282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->rows[i] = image->allocRows[i+1];
683282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
684282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
685282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->width -= 2;
686282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    W = image->width;
687282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->height -= 2;
688282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    H = image->height;
689282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
690282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Figure out the number of rows and columns in the N-patch
691282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numCols = numXDivs + 1;
692282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (xDivs[0] == 0) {  // Column 1 is strechable
693282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numCols--;
694282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
695282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (xDivs[numXDivs - 1] == W) {
696282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numCols--;
697282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
698282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numRows = numYDivs + 1;
699282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (yDivs[0] == 0) {  // Row 1 is strechable
700282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numRows--;
701282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
702282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (yDivs[numYDivs - 1] == H) {
703282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numRows--;
704282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
705282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
706282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Make sure the amount of rows and columns will fit in the number of
707282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // colors we can use in the 9-patch format.
708282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (numRows * numCols > 0x7F) {
709282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Too many rows and columns in 9-patch perimeter";
710282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
711282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
712282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
713282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numColors = numRows * numCols;
714282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->info9Patch.numColors = numColors;
7156381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
716282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
717282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Fill in color information for each patch.
718282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
719282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    uint32_t c;
720282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    top = 0;
721282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
722282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The first row always starts with the top being at y=0 and the bottom
723282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
724282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // the first row is stretchable along the Y axis, otherwise it is fixed.
725282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The last row always ends with the bottom being bitmap.height and the top
726282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
727282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
728282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // the Y axis, otherwise it is fixed.
729282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //
730282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The first and last columns are similarly treated with respect to the X
731282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // axis.
732282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //
733282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The above is to help explain some of the special casing that goes on the
734282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // code below.
735282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
736282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The initial yDiv and whether the first row is considered stretchable or
737282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // not depends on whether yDiv[0] was zero or not.
738282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = (yDivs[0] == 0 ? 1 : 0);
739282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          j <= numYDivs && top < H;
740282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          j++) {
741282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (j == numYDivs) {
742282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bottom = H;
743282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
744282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bottom = yDivs[j];
745282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
746282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        left = 0;
747282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // The initial xDiv and whether the first column is considered
748282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // stretchable or not depends on whether xDiv[0] was zero or not.
749282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = xDivs[0] == 0 ? 1 : 0;
750282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              i <= numXDivs && left < W;
751282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              i++) {
752282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == numXDivs) {
753282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                right = W;
754282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
755282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                right = xDivs[i];
756282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
757282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            c = get_color(image->rows, left, top, right - 1, bottom - 1);
7586381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            image->colors[colorIndex++] = c;
7592412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            if (kIsDebug) {
7602412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                if (c != Res_png_9patch::NO_COLOR)
7612412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    hasColor = true;
7622412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            }
763282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            left = right;
764282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
765282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        top = bottom;
766282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
767282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
768282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(colorIndex == numColors);
769282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
770282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=0; i<numColors; i++) {
771282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (hasColor) {
772282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == 0) printf("Colors in %s:\n ", imageName);
7736381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            printf(" #%08x", image->colors[i]);
774282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == numColors - 1) printf("\n");
775282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
776282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
777282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskigetout:
778282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (errorMsg) {
779282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr,
780282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            "ERROR: 9-patch image %s malformed.\n"
781282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            "       %s.\n", imageName, errorMsg);
782282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (errorEdge != NULL) {
783282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (errorPixel >= 0) {
784282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr,
785282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
786282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
787282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr,
788282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    "       Found along %s edge.\n", errorEdge);
789282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
790282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
791282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
792282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
793282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
794282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
795282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
7966381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamathstatic void checkNinePatchSerialization(Res_png_9patch* inPatch,  void* data)
797282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
798282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t patchSize = inPatch->serializedSize();
7996381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    void* newData = malloc(patchSize);
800282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memcpy(newData, data, patchSize);
801282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    Res_png_9patch* outPatch = inPatch->deserialize(newData);
802282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // deserialization is done in place, so outPatch == newData
803282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch == newData);
804282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->numXDivs == inPatch->numXDivs);
805282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->numYDivs == inPatch->numYDivs);
806282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingLeft == inPatch->paddingLeft);
807282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingRight == inPatch->paddingRight);
808282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingTop == inPatch->paddingTop);
809282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingBottom == inPatch->paddingBottom);
810282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numXDivs; i++) {
811282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
812282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
813282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numYDivs; i++) {
814282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
815282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
816282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numColors; i++) {
817282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->colors[i] == inPatch->colors[i]);
818282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
819282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(newData);
820282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
821282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
822282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void dump_image(int w, int h, png_bytepp rows, int color_type)
823282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
824282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i, j, rr, gg, bb, aa;
825282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
826282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bpp;
827282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
828282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 1;
829282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
830282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 2;
831282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
832282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // We use a padding byte even when there is no alpha
833282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 4;
834282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
835282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Unknown color type %d.\n", color_type);
836282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
837282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
838282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = 0; j < h; j++) {
839282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row = rows[j];
840282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = 0; i < w; i++) {
841282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rr = row[0];
842282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            gg = row[1];
843282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bb = row[2];
844282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            aa = row[3];
845282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            row += bpp;
846282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
847282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == 0) {
848282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf("Row %d:", j);
849282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
850282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            switch (bpp) {
851282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 1:
852282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d)", rr);
853282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
854282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 2:
855282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d", rr, gg);
856282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
857282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 3:
858282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d %d)", rr, gg, bb);
859282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
860282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 4:
861282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d %d %d)", rr, gg, bb, aa);
862282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
863282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
864282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == (w - 1)) {
8652412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                printf("\n");
866282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
867282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
868282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
869282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
870282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
871282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define MAX(a,b) ((a)>(b)?(a):(b))
872282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define ABS(a)   ((a)<0?-(a):(a))
873282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
874282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
875282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          png_colorp rgbPalette, png_bytep alphaPalette,
8762a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                          int *paletteEntries, int *alphaPaletteEntries, bool *hasTransparency,
8772a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                          int *colorType, png_bytepp outRows)
878282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
879282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int w = imageInfo.width;
880282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int h = imageInfo.height;
8812a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    int i, j, rr, gg, bb, aa, idx;;
8822a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    uint32_t opaqueColors[256], alphaColors[256];
8832a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    uint32_t col;
8842a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    int numOpaqueColors = 0, numAlphaColors = 0;
885282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxGrayDeviation = 0;
886282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
887282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isOpaque = true;
888282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isPalette = true;
889282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isGrayscale = true;
890282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
891282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Scan the entire image and determine if:
892282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 1. Every pixel has R == G == B (grayscale)
893282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 2. Every pixel has A == 255 (opaque)
894282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 3. There are no more than 256 distinct RGBA colors
8952a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    //        We will track opaque colors separately from colors with
8962a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    //        alpha.  This allows us to reencode the color table more
8972a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    //        efficiently (color tables entries without a corresponding
8982a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    //        alpha value are assumed to be opaque).
899282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
9002412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
9012412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Initial image data:\n");
9022412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
9032412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
904282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
905282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = 0; j < h; j++) {
906282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row = imageInfo.rows[j];
907282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep out = outRows[j];
908282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = 0; i < w; i++) {
90933fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett
91033fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // Make sure any zero alpha pixels are fully zeroed.  On average,
91133fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // each of our PNG assets seem to have about four distinct pixels
91233fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // with zero alpha.
91333fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // There are several advantages to setting these to zero:
91433fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // (1) Images are more likely able to be encodable with a palette.
91533fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // (2) Image palettes will be smaller.
91633fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            // (3) Premultiplied and unpremultiplied PNG decodes can skip
91733fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            //     writing zeros to memory, often saving significant numbers
91833fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            //     of memory pages.
91933fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            aa = *(row + 3);
92033fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            if (aa == 0) {
92133fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                rr = 0;
92233fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                gg = 0;
92333fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                bb = 0;
92433fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett
92533fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                // Also set red, green, and blue to zero in "row".  If we later
92633fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                // decide to encode the PNG as RGB or RGBA, we will use the
92733fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                // values stored there.
92833fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                *(row) = 0;
92933fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                *(row + 1) = 0;
93033fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                *(row + 2) = 0;
93133fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            } else {
93233fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                rr = *(row);
93333fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                gg = *(row + 1);
93433fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett                bb = *(row + 2);
93533fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            }
93633fcd11bc296fa495a4f4584f782c1c0fe4fe592Matt Sarett            row += 4;
937282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
938282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int odev = maxGrayDeviation;
939282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
940282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
941282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
942282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (maxGrayDeviation > odev) {
9432412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                if (kIsDebug) {
9442412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
9452412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                            maxGrayDeviation, i, j, rr, gg, bb, aa);
9462412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                }
947282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
948282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
949282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really grayscale
950282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isGrayscale) {
951282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (rr != gg || rr != bb) {
9522412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    if (kIsDebug) {
9532412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                        printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
9542412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                                i, j, rr, gg, bb, aa);
9552412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    }
956282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    isGrayscale = false;
957282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
958282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
959282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
960282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really opaque
961282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isOpaque) {
962282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (aa != 0xff) {
9632412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    if (kIsDebug) {
9642412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                        printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
9652412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                                i, j, rr, gg, bb, aa);
9662412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    }
967282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    isOpaque = false;
968282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
969282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
970282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
971282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really <= 256 colors
972282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isPalette) {
973282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
974282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                bool match = false;
9752a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
9762a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                if (aa == 0xff) {
9772a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    for (idx = 0; idx < numOpaqueColors; idx++) {
9782a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        if (opaqueColors[idx] == col) {
9792a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            match = true;
9802a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            break;
9812a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        }
9822a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    }
9832a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
9842a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    if (!match) {
9852a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        if (numOpaqueColors < 256) {
9862a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            opaqueColors[numOpaqueColors] = col;
9872a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        }
9882a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        numOpaqueColors++;
9892a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    }
9902a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
9912a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // Write the palette index for the pixel to outRows optimistically.
9922a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // We might overwrite it later if we decide to encode as gray or
9932a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // gray + alpha.  We may also need to overwrite it when we combine
9942a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // into a single palette.
9952a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    *out++ = idx;
9962a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                } else {
9972a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    for (idx = 0; idx < numAlphaColors; idx++) {
9982a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        if (alphaColors[idx] == col) {
9992a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            match = true;
10002a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            break;
10012a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        }
1002282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
1003282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
10042a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    if (!match) {
10052a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        if (numAlphaColors < 256) {
10062a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                            alphaColors[numAlphaColors] = col;
10072412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                        }
10082a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        numAlphaColors++;
10092a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    }
10102a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
10112a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // Write the palette index for the pixel to outRows optimistically.
10122a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // We might overwrite it later if we decide to encode as gray or
10132a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    // gray + alpha.
10142a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    *out++ = idx;
10152a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                }
10162a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
10172a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                if (numOpaqueColors + numAlphaColors > 256) {
10182a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    if (kIsDebug) {
10192a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                        printf("Found 257th color at %d, %d\n", i, j);
1020282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
10212a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    isPalette = false;
1022282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
1023282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
1024282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1025282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1026282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
10272a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    // If we decide to encode the image using a palette, we will reset these counts
10282a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    // to the appropriate values later.  Initializing them here avoids compiler
10292a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    // complaints about uses of possibly uninitialized variables.
1030282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *paletteEntries = 0;
10312a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    *alphaPaletteEntries = 0;
10322a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
1033282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *hasTransparency = !isOpaque;
10342a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    int paletteSize = w * h + 3 * numOpaqueColors + 4 * numAlphaColors;
1035282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
10362a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    int bpp = isOpaque ? 3 : 4;
10372412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
10382412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
10392412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("isOpaque = %s\n", isOpaque ? "true" : "false");
10402412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("isPalette = %s\n", isPalette ? "true" : "false");
10412412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
10422412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                paletteSize, 2 * w * h, bpp * w * h);
10432412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
10442412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
1045282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1046282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Choose the best color type for the image.
1047282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
1048282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
1049282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
1050282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
1051282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
1052282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (isGrayscale) {
1053282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (isOpaque) {
1054282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
1055282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
1056282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Use a simple heuristic to determine whether using a palette will
1057282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // save space versus using gray + alpha for each pixel.
1058282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // This doesn't take into account chunk overhead, filtering, LZ
1059282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // compression, etc.
1060282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isPalette && (paletteSize < 2 * w * h)) {
1061282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
1062282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
1063282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
1064282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
1065282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1066282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (isPalette && (paletteSize < bpp * w * h)) {
1067282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *colorType = PNG_COLOR_TYPE_PALETTE;
1068282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1069282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (maxGrayDeviation <= grayscaleTolerance) {
1070282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
1071282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
1072282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
1073282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
1074282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1075282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1076282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1077282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Perform postprocessing of the image or palette data based on the final
1078282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // color type chosen
1079282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1080282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (*colorType == PNG_COLOR_TYPE_PALETTE) {
10812a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        // Combine the alphaColors and the opaqueColors into a single palette.
10822a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        // The alphaColors must be at the start of the palette.
10832a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        uint32_t* colors = alphaColors;
10842a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        memcpy(colors + numAlphaColors, opaqueColors, 4 * numOpaqueColors);
10852a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
10862a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        // Fix the indices of the opaque colors in the image.
10872a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        for (j = 0; j < h; j++) {
10882a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            png_bytep row = imageInfo.rows[j];
10892a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            png_bytep out = outRows[j];
10902a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            for (i = 0; i < w; i++) {
10912a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                uint32_t pixel = ((uint32_t*) row)[i];
10922a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                if (pixel >> 24 == 0xFF) {
10932a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    out[i] += numAlphaColors;
10942a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                }
10952a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            }
10962a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        }
10972a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett
1098282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Create separate RGB and Alpha palettes and set the number of colors
10992a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        int numColors = numOpaqueColors + numAlphaColors;
11002a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        *paletteEntries = numColors;
11012a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        *alphaPaletteEntries = numAlphaColors;
1102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Create the RGB and alpha palettes
11042a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett        for (int idx = 0; idx < numColors; idx++) {
1105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            col = colors[idx];
1106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
1107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
1108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
11092a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            if (idx < numAlphaColors) {
11102a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                alphaPalette[idx] = (png_byte)  (col        & 0xff);
11112a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            }
1112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
1114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // If the image is gray or gray + alpha, compact the pixels into outRows
1115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (j = 0; j < h; j++) {
1116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep row = imageInfo.rows[j];
1117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep out = outRows[j];
1118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (i = 0; i < w; i++) {
1119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                rr = *row++;
1120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                gg = *row++;
1121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                bb = *row++;
1122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                aa = *row++;
1123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (isGrayscale) {
1125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = rr;
1126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } else {
1127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
1128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
1129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (!isOpaque) {
1130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = aa;
1131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
1132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski           }
1133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void write_png(const char* imageName,
1138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                      png_structp write_ptr, png_infop write_info,
113948efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett                      image_info& imageInfo, const Bundle* bundle)
1140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 width, height;
1142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int color_type;
1143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bit_depth, interlace_type, compression_type;
1144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
1145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
114647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    png_unknown_chunk unknowns[3];
1147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    unknowns[0].data = NULL;
1148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    unknowns[1].data = NULL;
114947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    unknowns[2].data = NULL;
1150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
1152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (outRows == (png_bytepp) 0) {
1153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Can't allocate output buffer!\n");
1154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        exit(1);
1155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int) imageInfo.height; i++) {
1157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
1158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (outRows[i] == (png_bytep) 0) {
1159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Can't allocate output buffer!\n");
1160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            exit(1);
1161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
1165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
11662412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
11672412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Writing image %s: w = %d, h = %d\n", imageName,
11682412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                (int) imageInfo.width, (int) imageInfo.height);
11692412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
1170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_color rgbPalette[256];
1172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_byte alphaPalette[256];
1173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool hasTransparency;
11742a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett    int paletteEntries, alphaPaletteEntries;
1175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
117648efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    int grayscaleTolerance = bundle->getGrayscaleTolerance();
1177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
11782a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                  &paletteEntries, &alphaPaletteEntries, &hasTransparency, &color_type, outRows);
1179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
118048efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    // Legacy versions of aapt would always encode 9patch PNGs as RGBA.  This had the unintended
118148efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    // benefit of working around a bug decoding paletted images in Android 4.1.
118248efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    // https://code.google.com/p/android/issues/detail?id=34619
118348efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    //
118448efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    // If SDK_JELLY_BEAN is supported, we need to avoid a paletted encoding in order to not expose
118548efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    // this bug.
118648efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    if (!bundle->isMinSdkAtLeast(SDK_JELLY_BEAN_MR1)) {
118748efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett        if (imageInfo.is9Patch && PNG_COLOR_TYPE_PALETTE == color_type) {
118848efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett            if (hasTransparency) {
118948efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett                color_type = PNG_COLOR_TYPE_RGB_ALPHA;
119048efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett            } else {
119148efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett                color_type = PNG_COLOR_TYPE_RGB;
119248efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett            }
119348efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett        }
119448efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    }
119548efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett
11962412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
11972412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        switch (color_type) {
11982412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        case PNG_COLOR_TYPE_PALETTE:
11992412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
12002412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    imageName, paletteEntries,
12012412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                    hasTransparency ? " (with alpha)" : "");
12022412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            break;
12032412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        case PNG_COLOR_TYPE_GRAY:
12042412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName);
12052412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            break;
12062412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        case PNG_COLOR_TYPE_GRAY_ALPHA:
12072412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName);
12082412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            break;
12092412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        case PNG_COLOR_TYPE_RGB:
12102412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName);
12112412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            break;
12122412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        case PNG_COLOR_TYPE_RGB_ALPHA:
12132412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName);
12142412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            break;
12152412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        }
1216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
1219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 8, color_type, PNG_INTERLACE_NONE,
1220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE) {
1223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
1224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (hasTransparency) {
12252a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett            png_set_tRNS(write_ptr, write_info, alphaPalette, alphaPaletteEntries,
12262a84e23399840dc365cd03edcbfcf2727a17cd6eMatt Sarett                    (png_color_16p) 0);
1227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
1229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
1231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (imageInfo.is9Patch) {
123447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0);
123547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int p_index = imageInfo.haveLayoutBounds ? 2 : 1;
123647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int b_index = 1;
123747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int o_index = 0;
123847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
123947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
1240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_byte *chunk_names = imageInfo.haveLayoutBounds
124147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                ? (png_byte*)"npOl\0npLb\0npTc\0"
124247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                : (png_byte*)"npOl\0npTc";
124347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
124447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // base 9 patch data
12452412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        if (kIsDebug) {
12462412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe            printf("Adding 9-patch info...\n");
12472412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        }
1248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        strcpy((char*)unknowns[p_index].name, "npTc");
12496381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
1250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
1251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // TODO: remove the check below when everything works
1252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
1253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
125447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // automatically generated 9 patch outline data
125547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int chunk_size = sizeof(png_uint_32) * 6;
125647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        strcpy((char*)unknowns[o_index].name, "npOl");
125747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
125847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        png_byte outputData[chunk_size];
125947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
126047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        ((float*) outputData)[4] = imageInfo.outlineRadius;
126177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        ((png_uint_32*) outputData)[5] = imageInfo.outlineAlpha;
126247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        memcpy(unknowns[o_index].data, &outputData, chunk_size);
126347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        unknowns[o_index].size = chunk_size;
126447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
126547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // optional optical inset / layout bounds data
1266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (imageInfo.haveLayoutBounds) {
1267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int chunk_size = sizeof(png_uint_32) * 4;
1268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            strcpy((char*)unknowns[b_index].name, "npLb");
1269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
1270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
1271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            unknowns[b_index].size = chunk_size;
1272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = 0; i < chunk_count; i++) {
12753b1b68d6c764a4f60d034e57a94879b7df65fd43Matt Sarett            unknowns[i].location = PNG_HAVE_IHDR;
1276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                    chunk_names, chunk_count);
1279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
1280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_info(write_ptr, write_info);
1284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows;
1286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
1287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color_type == PNG_COLOR_TYPE_RGB) {
1288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
1289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        rows = imageInfo.rows;
1291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        rows = outRows;
1293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_image(write_ptr, rows);
1295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
12962412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
12972412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Final image data:\n");
12982412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        dump_image(imageInfo.width, imageInfo.height, rows, color_type);
12992412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
1300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_end(write_ptr, write_info);
1302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int) imageInfo.height; i++) {
1304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        free(outRows[i]);
1305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(outRows);
1307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(unknowns[0].data);
1308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(unknowns[1].data);
130947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    free(unknowns[2].data);
1310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(write_ptr, write_info, &width, &height,
1312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &bit_depth, &color_type, &interlace_type,
1313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &compression_type, NULL);
1314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
13152412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    if (kIsDebug) {
13162412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe        printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
13172412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                (int)width, (int)height, bit_depth, color_type, interlace_type,
13182412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                compression_type);
13192412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe    }
1320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1322b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampestatic bool read_png_protected(png_structp read_ptr, String8& printableName, png_infop read_info,
1323b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe                               const sp<AaptFile>& file, FILE* fp, image_info* imageInfo) {
1324b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    if (setjmp(png_jmpbuf(read_ptr))) {
1325b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe        return false;
1326b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    }
1327b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1328b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    png_init_io(read_ptr, fp);
1329b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1330b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    read_png(printableName.string(), read_ptr, read_info, imageInfo);
1331b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1332b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    const size_t nameLen = file->getPath().length();
1333b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    if (nameLen > 6) {
1334b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe        const char* name = file->getPath().string();
1335b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe        if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1336b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe            if (do_9patch(printableName.string(), imageInfo) != NO_ERROR) {
1337b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe                return false;
1338b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe            }
1339b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe        }
1340b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    }
1341b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1342b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    return true;
1343b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe}
1344b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1345b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampestatic bool write_png_protected(png_structp write_ptr, String8& printableName, png_infop write_info,
1346b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe                                image_info* imageInfo, const Bundle* bundle) {
1347b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    if (setjmp(png_jmpbuf(write_ptr))) {
1348b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe        return false;
1349b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    }
1350b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
135148efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    write_png(printableName.string(), write_ptr, write_info, *imageInfo, bundle);
1352b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
1353b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    return true;
1354b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe}
1355b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe
13562412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampestatus_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */,
13572412f84064c26b643c722ce914a97c4ec7776c69Andreas Gampe                         const sp<AaptFile>& file, String8* /* outNewLeafName */)
1358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 ext(file->getPath().getPathExtension());
1360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // We currently only process PNG images.
1362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (strcmp(ext.string(), ".png") != 0) {
1363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return NO_ERROR;
1364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Example of renaming a file:
1367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //*outNewLeafName = file->getPath().getBasePath().getFileName();
1368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //outNewLeafName->append(".nupng");
1369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 printableName(file->getPrintableSource());
1371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Processing image: %s\n", printableName.string());
1374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp read_ptr = NULL;
1377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop read_info = NULL;
1378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    FILE* fp;
1379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image_info imageInfo;
1381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp write_ptr = NULL;
1383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop write_info = NULL;
1384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t error = UNKNOWN_ERROR;
1386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(file->getSourceFile().string(), "rb");
1388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp == NULL) {
1389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
1390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        (png_error_ptr)NULL);
1395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_ptr) {
1396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_info = png_create_info_struct(read_ptr);
1400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_info) {
1401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1404b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) {
1405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        (png_error_ptr)NULL);
1410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_ptr)
1411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
1412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_info = png_create_info_struct(write_ptr);
1416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_info)
1417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
1418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_write_fn(write_ptr, (void*)file.get(),
1422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     png_write_aapt_file, png_flush_aapt_file);
1423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1424b8dc7bc582ee4acd9d6c1379701a002c5ddbe323Andreas Gampe    if (!write_png_protected(write_ptr, printableName, write_info, &imageInfo, bundle)) {
1425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    error = NO_ERROR;
1429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fseek(fp, 0, SEEK_END);
1432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t oldSize = (size_t)ftell(fp);
1433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t newSize = file->getSize();
1434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float factor = ((float)newSize)/oldSize;
1435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int percent = (int)(factor*100);
1436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
1437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibail:
1440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (read_ptr) {
1441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp) {
1444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (write_ptr) {
1447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (error != NO_ERROR) {
1451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                file->getPrintableSource().string());
1453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return error;
1455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatus_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
1458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp read_ptr = NULL;
1460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop read_info = NULL;
1461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    FILE* fp;
1463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image_info imageInfo;
1465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp write_ptr = NULL;
1467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop write_info = NULL;
1468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t error = UNKNOWN_ERROR;
1470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
1473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Get a file handler to read from
1476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(source.string(),"rb");
1477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp == NULL) {
1478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
1479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to get a struct to read image data into
1483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_ptr) {
1485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to get a struct to read image info into
1491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_info = png_create_info_struct(read_ptr);
1492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_info) {
1493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1494282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1495282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set a jump point for libpng to long jump back to on error
1499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(read_ptr))) {
1500282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1501282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1502282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1503282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1504282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1505282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up libpng to read from our file.
1506282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_init_io(read_ptr,fp);
1507282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1508282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Actually read data from the file
1509282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_png(source.string(), read_ptr, read_info, &imageInfo);
1510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // We're done reading so we can clean up
1512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find old file size before releasing handle
1513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fseek(fp, 0, SEEK_END);
1514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t oldSize = (size_t)ftell(fp);
1515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fclose(fp);
1516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_destroy_read_struct(&read_ptr, &read_info,NULL);
1517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1518282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Check to see if we're dealing with a 9-patch
1519282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If we are, process appropriately
1520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (source.getBasePath().getPathExtension() == ".9")  {
1521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
1522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return error;
1523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to create a structure to hold the processed image data
1527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // that can be written to disk
1528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_ptr) {
1530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to create a structure to hold processed image info that can
1535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // be written to disk
1536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_info = png_create_info_struct(write_ptr);
1537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_info) {
1538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Open up our destination file for writing
1543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(dest.string(), "wb");
1544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!fp) {
1545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
1546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up libpng to write to our file
1551282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_init_io(write_ptr, fp);
1552282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1553282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up a jump for libpng to long jump back on on errors
1554282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(write_ptr))) {
1555282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1559282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Actually write out to the new png
156148efde4fb14cfe160747a8148b8e1a810c8f1c6eMatt Sarett    write_png(dest.string(), write_ptr, write_info, imageInfo, bundle);
1562282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Find the size of our new file
1565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        FILE* reader = fopen(dest.string(), "rb");
1566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fseek(reader, 0, SEEK_END);
1567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t newSize = (size_t)ftell(reader);
1568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(reader);
1569282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float factor = ((float)newSize)/oldSize;
1571282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int percent = (int)(factor*100);
1572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("  (processed image to cache entry %s: %d%% size of source)\n",
1573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski               dest.string(), percent);
1574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //Clean up
1577282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fclose(fp);
1578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_destroy_write_struct(&write_ptr, &write_info);
1579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
1581282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1582282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1583e572c011feabf6319ba836cf5bc4c3baa0ba6a85Adam Lesinskistatus_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1584282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          ResourceTable* table, const sp<AaptFile>& file)
1585282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 ext(file->getPath().getPathExtension());
1587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // At this point, now that we have all the resource data, all we need to
1589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // do is compile XML files.
1590282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (strcmp(ext.string(), ".xml") == 0) {
15919306a474e1b7509f4cdf252359dc301ce933ca1dAdam Lesinski        String16 resourceName(parseResourceName(file->getSourceFile().getPathLeaf()));
1592e572c011feabf6319ba836cf5bc4c3baa0ba6a85Adam Lesinski        return compileXmlFile(bundle, assets, resourceName, file, table);
1593282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
1596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1597