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) {
9792a84