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
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define NOISY(x) //x
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipng_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    AaptFile* aaptfile = (AaptFile*) png_get_io_ptr(png_ptr);
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t err = aaptfile->writeData(data, length);
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (err != NO_ERROR) {
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_error(png_ptr, "Write Error");
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipng_flush_aapt_file(png_structp png_ptr)
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// This holds an image as 8bpp RGBA.
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistruct image_info
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
386381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    image_info() : rows(NULL), is9Patch(false),
396381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { }
406381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ~image_info() {
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (rows && rows != allocRows) {
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            free(rows);
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (allocRows) {
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (int i=0; i<(int)allocHeight; i++) {
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                free(allocRows[i]);
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            free(allocRows);
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
516381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(xDivs);
526381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(yDivs);
536381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        free(colors);
546381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    }
556381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath
566381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    void* serialize9patch() {
576381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors);
586381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
596381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        return serialized;
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 width;
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 height;
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 9-patch info.
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool is9Patch;
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    Res_png_9patch info9Patch;
696381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* xDivs;
706381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* yDivs;
716381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    uint32_t* colors;
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Layout padding, if relevant
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool haveLayoutBounds;
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsLeft;
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsTop;
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsRight;
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int32_t layoutBoundsBottom;
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
8047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // Round rect outline description
8147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsLeft;
8247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsTop;
8347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsRight;
8447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int32_t outlineInsetsBottom;
8547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    float outlineRadius;
8677b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t outlineAlpha;
8747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 allocHeight;
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp allocRows;
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
92859e19f97d481bf07c768087d8eb3031fac77aadJohn Reckstatic void log_warning(png_structp png_ptr, png_const_charp warning_message)
93859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck{
94859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    const char* imageName = (const char*) png_get_error_ptr(png_ptr);
95859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    fprintf(stderr, "%s: libpng warning: %s\n", imageName, warning_message);
96859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck}
97859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void read_png(const char* imageName,
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     png_structp read_ptr, png_infop read_info,
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     image_info* outImageInfo)
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int color_type;
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bit_depth, interlace_type, compression_type;
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
106859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    png_set_error_fn(read_ptr, const_cast<char*>(imageName),
107859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck            NULL /* use default errorfn */, log_warning);
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_info(read_ptr, read_info);
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &outImageInfo->height, &bit_depth, &color_type,
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &interlace_type, &compression_type, NULL);
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //printf("Image %s:\n", imageName);
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //       color_type, bit_depth, interlace_type, compression_type);
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE)
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_palette_to_rgb(read_ptr);
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_expand_gray_1_2_4_to_8(read_ptr);
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        //printf("Has PNG_INFO_tRNS!\n");
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_tRNS_to_alpha(read_ptr);
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bit_depth == 16)
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_strip_16(read_ptr);
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_gray_to_rgb(read_ptr);
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
138859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck    png_set_interlace_handling(read_ptr);
139859e19f97d481bf07c768087d8eb3031fac77aadJohn Reck
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_update_info(read_ptr, read_info);
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->rows = (png_bytepp)malloc(
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outImageInfo->height * sizeof(png_bytep));
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->allocHeight = outImageInfo->height;
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    outImageInfo->allocRows = outImageInfo->rows;
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_rows(read_ptr, read_info, outImageInfo->rows);
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int)outImageInfo->height; i++)
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outImageInfo->rows[i] = (png_bytep)
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            malloc(png_get_rowbytes(read_ptr, read_info));
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_image(read_ptr, outImageInfo->rows);
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_read_end(read_ptr, read_info);
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 imageName,
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 (int)outImageInfo->width, (int)outImageInfo->height,
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 bit_depth, color_type,
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 interlace_type, compression_type));
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &outImageInfo->height, &bit_depth, &color_type,
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &interlace_type, &compression_type, NULL);
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_TRANSPARENT 0
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_WHITE 0xFFFFFFFF
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_TICK  0xFF000000
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskienum {
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_NONE,
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_TICK,
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_LAYOUT_BOUNDS,
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_TYPE_BOTH
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic int tick_type(png_bytep p, bool transparent, const char** outError)
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (transparent) {
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[3] == 0) {
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_NONE;
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color == COLOR_LAYOUT_BOUNDS_TICK) {
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_LAYOUT_BOUNDS;
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color == COLOR_TICK) {
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_TICK;
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Error cases
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[3] != 0xff) {
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return TICK_TYPE_NONE;
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outError = "Ticks in transparent frame must be black or red";
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_TICK;
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (p[3] != 0xFF) {
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "White frame must be a solid color (no alpha)";
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_WHITE) {
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_NONE;
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_TICK) {
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_TICK;
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color == COLOR_LAYOUT_BOUNDS_TICK) {
218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_LAYOUT_BOUNDS;
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "Ticks in white frame must be black or red";
223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return TICK_TYPE_NONE;
224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return TICK_TYPE_TICK;
226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskienum {
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_START,
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_INSIDE_1,
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    TICK_OUTSIDE_1
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_horizontal_ticks(
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row, int width, bool transparent, bool required,
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outLeft, int32_t* outRight, const char** outError,
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        uint8_t* outDivs, bool multipleAllowed)
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outLeft = *outRight = -1;
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int state = TICK_START;
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool found = false;
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=1; i<width-1; i++) {
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_START ||
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outLeft = i-1;
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outRight = width-2;
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                found = true;
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outDivs != NULL) {
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *outDivs += 2;
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_INSIDE_1;
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (state == TICK_OUTSIDE_1) {
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outError = "Can't have more than one marked region along edge";
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outLeft = i;
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return UNKNOWN_ERROR;
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (*outError == NULL) {
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_INSIDE_1) {
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // We're done with this div.  Move on to the next.
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outRight = i-1;
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outRight += 2;
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outLeft += 2;
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_OUTSIDE_1;
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outLeft = i;
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return UNKNOWN_ERROR;
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (required && !found) {
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "No marked region found along edge";
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outLeft = -1;
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_vertical_ticks(
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytepp rows, int offset, int height, bool transparent, bool required,
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outTop, int32_t* outBottom, const char** outError,
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        uint8_t* outDivs, bool multipleAllowed)
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outTop = *outBottom = -1;
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int state = TICK_START;
291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool found = false;
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=1; i<height-1; i++) {
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_START ||
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outTop = i-1;
298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outBottom = height-2;
299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                found = true;
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outDivs != NULL) {
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *outDivs += 2;
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_INSIDE_1;
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (state == TICK_OUTSIDE_1) {
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outError = "Can't have more than one marked region along edge";
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outTop = i;
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return UNKNOWN_ERROR;
308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (*outError == NULL) {
310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (state == TICK_INSIDE_1) {
311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // We're done with this div.  Move on to the next.
312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *outBottom = i-1;
313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outTop += 2;
314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outBottom += 2;
315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                state = TICK_OUTSIDE_1;
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *outTop = i;
319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return UNKNOWN_ERROR;
320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (required && !found) {
324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outError = "No marked region found along edge";
325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *outTop = -1;
326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_horizontal_layout_bounds_ticks(
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row, int width, bool transparent, bool required,
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outLeft, int32_t* outRight, const char** outError)
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outLeft = *outRight = 0;
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for left tick
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Starting with a layout padding tick
342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = 1;
343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i < width - 1) {
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outLeft)++;
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i++;
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(row + i * 4, transparent, outError);
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for right tick
354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Ending with a layout padding tick
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = width - 2;
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i > 1) {
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outRight)++;
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i--;
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(row+i*4, transparent, outError);
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t get_vertical_layout_bounds_ticks(
371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytepp rows, int offset, int height, bool transparent, bool required,
372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int32_t* outTop, int32_t* outBottom, const char** outError)
373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *outTop = *outBottom = 0;
376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for top tick
378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Starting with a layout padding tick
380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = 1;
381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i < height - 1) {
382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outTop)++;
383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i++;
384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(rows[i] + offset, transparent, outError);
385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Look for bottom tick
392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Ending with a layout padding tick
394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        i = height - 2;
395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        while (i > 1) {
396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (*outBottom)++;
397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            i--;
398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int tick = tick_type(rows[i] + offset, transparent, outError);
399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
40847cd8e921db73e894f94ec4729ade90da50996f5Chris Craikstatic void find_max_opacity(png_byte** rows,
40947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                             int startX, int startY, int endX, int endY, int dX, int dY,
41047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                             int* out_inset)
41147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
41247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    bool opaque_within_inset = true;
41377b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_opacity = 0;
41447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int inset = 0;
41547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    *out_inset = 0;
41647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
41747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        png_byte* color = rows[y] + x * 4;
41877b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t opacity = color[3];
41947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        if (opacity > max_opacity) {
42047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            max_opacity = opacity;
42147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            *out_inset = inset;
42247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        }
42347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        if (opacity == 0xff) return;
42447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
42547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
42647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
42777b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craikstatic uint8_t max_alpha_over_row(png_byte* row, int startX, int endX)
42847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
42977b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_alpha = 0;
43047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int x = startX; x < endX; x++) {
43177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t alpha = (row + x * 4)[3];
43277b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        if (alpha > max_alpha) max_alpha = alpha;
43347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
43477b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    return max_alpha;
43547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
43647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
43777b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craikstatic uint8_t max_alpha_over_col(png_byte** rows, int offsetX, int startY, int endY)
43847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
43977b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    uint8_t max_alpha = 0;
44047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    for (int y = startY; y < endY; y++) {
44177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        uint8_t alpha = (rows[y] + offsetX * 4)[3];
44277b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        if (alpha > max_alpha) max_alpha = alpha;
44347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
44477b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    return max_alpha;
44547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
44647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
44747cd8e921db73e894f94ec4729ade90da50996f5Chris Craikstatic void get_outline(image_info* image)
44847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik{
44947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int midX = image->width / 2;
45047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int midY = image->height / 2;
45147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int endX = image->width - 2;
45247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int endY = image->height - 2;
45347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
45447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // find left and right extent of nine patch content on center row
45547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    if (image->width > 4) {
45647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
45747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
45847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    } else {
45947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsLeft = 0;
46047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsRight = 0;
46147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
46247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
46347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // find top and bottom extent of nine patch content on center column
46447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    if (image->height > 4) {
46547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
46647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
46747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    } else {
46847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsTop = 0;
46947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        image->outlineInsetsBottom = 0;
47047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    }
47147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
47247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerStartX = 1 + image->outlineInsetsLeft;
47347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerStartY = 1 + image->outlineInsetsTop;
47447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerEndX = endX - image->outlineInsetsRight;
47547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerEndY = endY - image->outlineInsetsBottom;
47647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerMidX = (innerEndX + innerStartX) / 2;
47747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int innerMidY = (innerEndY + innerStartY) / 2;
47847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
47947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // assuming the image is a round rect, compute the radius by marching
48047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // diagonally from the top left corner towards the center
48177b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    image->outlineAlpha = max(max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX),
48277b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik            max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY));
48347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
48447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    int diagonalInset = 0;
48547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
48647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            &diagonalInset);
48747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
48847d86230823de5d372688dff2b6e363a607009a8Chris Craik    /* Determine source radius based upon inset:
48947d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
49047d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     sqrt(2) * r = sqrt(2) * i + r
49147d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     (sqrt(2) - 1) * r = sqrt(2) * i
49247d86230823de5d372688dff2b6e363a607009a8Chris Craik     *     r = sqrt(2) / (sqrt(2) - 1) * i
49347d86230823de5d372688dff2b6e363a607009a8Chris Craik     */
49447d86230823de5d372688dff2b6e363a607009a8Chris Craik    image->outlineRadius = 3.4142f * diagonalInset;
49547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
49677b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik    NOISY(printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
49747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            image->outlineInsetsLeft,
49847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            image->outlineInsetsTop,
49947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            image->outlineInsetsRight,
50047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            image->outlineInsetsBottom,
50147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik            image->outlineRadius,
50277b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik            image->outlineAlpha));
50347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik}
50447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
505282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
506282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic uint32_t get_color(
507282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows, int left, int top, int right, int bottom)
508282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
509282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytep color = rows[top] + left*4;
510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (left > right || top > bottom) {
512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return Res_png_9patch::TRANSPARENT_COLOR;
513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    while (top <= bottom) {
516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = left; i <= right; i++) {
517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep p = rows[top]+i*4;
518282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (color[3] == 0) {
519282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (p[3] != 0) {
520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return Res_png_9patch::NO_COLOR;
521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (p[0] != color[0] || p[1] != color[1]
523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                       || p[2] != color[2] || p[3] != color[3]) {
524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return Res_png_9patch::NO_COLOR;
525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        top++;
528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color[3] == 0) {
531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return Res_png_9patch::TRANSPARENT_COLOR;
532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void select_patch(
537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int which, int front, int back, int size, int* start, int* end)
538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    switch (which) {
540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case 0:
541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *start = 0;
542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *end = front-1;
543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case 1:
545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *start = front;
546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *end = back-1;
547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case 2:
549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *start = back;
550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *end = size-1;
551282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
552282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
553282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
554282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
555282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic uint32_t get_color(image_info* image, int hpatch, int vpatch)
556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int left, right, top, bottom;
558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    select_patch(
5596381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        hpatch, image->xDivs[0], image->xDivs[1],
560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->width, &left, &right);
561282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    select_patch(
5626381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        vpatch, image->yDivs[0], image->yDivs[1],
563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->height, &top, &bottom);
564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //       hpatch, vpatch, left, top, right, bottom);
566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const uint32_t c = get_color(image->rows, left, top, right, bottom);
567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left, top, right, bottom, c));
568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return c;
569282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
571282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic status_t do_9patch(const char* imageName, image_info* image)
572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->is9Patch = true;
574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int W = image->width;
576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int H = image->height;
577282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i, j;
578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxSizeXDivs = W * sizeof(int32_t);
580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxSizeYDivs = H * sizeof(int32_t);
5816381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs);
5826381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs);
583c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes    uint8_t numXDivs = 0;
584c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes    uint8_t numYDivs = 0;
585c367d48c55e5a3fa0df14fd62889e4bb6b63cb01Elliott Hughes
586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int8_t numColors;
587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int numRows;
588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int numCols;
589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int top;
590282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int left;
591282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int right;
592282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bottom;
593282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memset(xDivs, -1, maxSizeXDivs);
594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memset(yDivs, -1, maxSizeYDivs);
595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
597282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
598282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->layoutBoundsLeft = image->layoutBoundsRight =
599282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->layoutBoundsTop = image->layoutBoundsBottom = 0;
600282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
601282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytep p = image->rows[0];
602282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool transparent = p[3] == 0;
603282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool hasColor = false;
604282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
605282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const char* errorMsg = NULL;
606282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int errorPixel = -1;
607282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const char* errorEdge = NULL;
608282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
609282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int colorIndex = 0;
610282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
611282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Validate size...
612282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (W < 3 || H < 3) {
613282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
614282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
615282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
616282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
617282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Validate frame...
618282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!transparent &&
619282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
620282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Must have one-pixel frame that is either transparent or white";
621282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
622282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
623282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
624282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of sizing areas...
625282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
626282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                             &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
627282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = xDivs[0];
628282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "top";
629282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
630282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
631282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
632282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find top and bottom of sizing areas...
633282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
634282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                           &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
635282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = yDivs[0];
636282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "left";
637282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
638282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
639282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
640b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    // Copy patch size data into image...
641b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    image->info9Patch.numXDivs = numXDivs;
642b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes    image->info9Patch.numYDivs = numYDivs;
643b30296b5fda75bf383c7ab3f567eb41820747869Elliott Hughes
644282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of padding area...
645282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
646282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                             &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
647282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = image->info9Patch.paddingLeft;
648282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "bottom";
649282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
650282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
651282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
652282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find top and bottom of padding area...
653282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
654282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                           &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
655282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorPixel = image->info9Patch.paddingTop;
656282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorEdge = "right";
657282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
658282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
659282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
660282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find left and right of layout padding...
661282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
662282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsLeft,
663282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsRight, &errorMsg);
664282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
665282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
666282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsTop,
667282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        &image->layoutBoundsBottom, &errorMsg);
668282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
669282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->haveLayoutBounds = image->layoutBoundsLeft != 0
670282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsRight != 0
671282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsTop != 0
672282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                               || image->layoutBoundsBottom != 0;
673282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
674282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->haveLayoutBounds) {
675282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
676282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                image->layoutBoundsRight, image->layoutBoundsBottom));
677282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
678282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
67947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    // use opacity of pixels to estimate the round rect outline
68047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    get_outline(image);
68147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
682282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If padding is not yet specified, take values from size.
683282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->info9Patch.paddingLeft < 0) {
684282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingLeft = xDivs[0];
685282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingRight = W - 2 - xDivs[1];
686282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
687282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Adjust value to be correct!
688282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
689282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
690282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (image->info9Patch.paddingTop < 0) {
691282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingTop = yDivs[0];
692282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingBottom = H - 2 - yDivs[1];
693282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
694282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Adjust value to be correct!
695282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
696282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
697282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
698282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
69977b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik                 xDivs[0], xDivs[1],
70077b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik                 yDivs[0], yDivs[1]));
701282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
702282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
703282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
704282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
705282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Remove frame from image.
706282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep));
707282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=0; i<(H-2); i++) {
708282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        image->rows[i] = image->allocRows[i+1];
709282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
710282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
711282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->width -= 2;
712282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    W = image->width;
713282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->height -= 2;
714282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    H = image->height;
715282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
716282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Figure out the number of rows and columns in the N-patch
717282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numCols = numXDivs + 1;
718282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (xDivs[0] == 0) {  // Column 1 is strechable
719282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numCols--;
720282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
721282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (xDivs[numXDivs - 1] == W) {
722282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numCols--;
723282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
724282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numRows = numYDivs + 1;
725282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (yDivs[0] == 0) {  // Row 1 is strechable
726282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numRows--;
727282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
728282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (yDivs[numYDivs - 1] == H) {
729282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        numRows--;
730282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
731282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
732282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Make sure the amount of rows and columns will fit in the number of
733282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // colors we can use in the 9-patch format.
734282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (numRows * numCols > 0x7F) {
735282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        errorMsg = "Too many rows and columns in 9-patch perimeter";
736282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto getout;
737282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
738282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
739282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    numColors = numRows * numCols;
740282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image->info9Patch.numColors = numColors;
7416381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
742282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
743282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Fill in color information for each patch.
744282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
745282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    uint32_t c;
746282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    top = 0;
747282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
748282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The first row always starts with the top being at y=0 and the bottom
749282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
750282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // the first row is stretchable along the Y axis, otherwise it is fixed.
751282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The last row always ends with the bottom being bitmap.height and the top
752282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
753282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
754282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // the Y axis, otherwise it is fixed.
755282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //
756282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The first and last columns are similarly treated with respect to the X
757282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // axis.
758282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //
759282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The above is to help explain some of the special casing that goes on the
760282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // code below.
761282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
762282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // The initial yDiv and whether the first row is considered stretchable or
763282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // not depends on whether yDiv[0] was zero or not.
764282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = (yDivs[0] == 0 ? 1 : 0);
765282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          j <= numYDivs && top < H;
766282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          j++) {
767282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (j == numYDivs) {
768282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bottom = H;
769282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
770282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bottom = yDivs[j];
771282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
772282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        left = 0;
773282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // The initial xDiv and whether the first column is considered
774282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // stretchable or not depends on whether xDiv[0] was zero or not.
775282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = xDivs[0] == 0 ? 1 : 0;
776282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              i <= numXDivs && left < W;
777282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              i++) {
778282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == numXDivs) {
779282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                right = W;
780282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
781282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                right = xDivs[i];
782282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
783282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            c = get_color(image->rows, left, top, right - 1, bottom - 1);
7846381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            image->colors[colorIndex++] = c;
785282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
786282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            left = right;
787282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
788282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        top = bottom;
789282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
790282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
791282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(colorIndex == numColors);
792282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
793282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i=0; i<numColors; i++) {
794282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (hasColor) {
795282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == 0) printf("Colors in %s:\n ", imageName);
7966381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            printf(" #%08x", image->colors[i]);
797282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == numColors - 1) printf("\n");
798282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
799282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
800282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskigetout:
801282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (errorMsg) {
802282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr,
803282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            "ERROR: 9-patch image %s malformed.\n"
804282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            "       %s.\n", imageName, errorMsg);
805282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (errorEdge != NULL) {
806282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (errorPixel >= 0) {
807282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr,
808282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
809282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
810282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr,
811282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    "       Found along %s edge.\n", errorEdge);
812282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
813282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
814282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return UNKNOWN_ERROR;
815282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
816282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
817282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
818282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
8196381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamathstatic void checkNinePatchSerialization(Res_png_9patch* inPatch,  void* data)
820282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
821282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t patchSize = inPatch->serializedSize();
8226381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    void* newData = malloc(patchSize);
823282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    memcpy(newData, data, patchSize);
824282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    Res_png_9patch* outPatch = inPatch->deserialize(newData);
825282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // deserialization is done in place, so outPatch == newData
826282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch == newData);
827282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->numXDivs == inPatch->numXDivs);
828282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->numYDivs == inPatch->numYDivs);
829282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingLeft == inPatch->paddingLeft);
830282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingRight == inPatch->paddingRight);
831282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingTop == inPatch->paddingTop);
832282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(outPatch->paddingBottom == inPatch->paddingBottom);
833282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numXDivs; i++) {
834282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
835282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
836282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numYDivs; i++) {
837282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
838282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
839282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (int i = 0; i < outPatch->numColors; i++) {
840282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert(outPatch->colors[i] == inPatch->colors[i]);
841282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
842282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(newData);
843282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
844282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
845282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void dump_image(int w, int h, png_bytepp rows, int color_type)
846282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
847282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i, j, rr, gg, bb, aa;
848282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
849282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bpp;
850282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
851282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 1;
852282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
853282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 2;
854282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
855282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // We use a padding byte even when there is no alpha
856282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bpp = 4;
857282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
858282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Unknown color type %d.\n", color_type);
859282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
860282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
861282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = 0; j < h; j++) {
862282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row = rows[j];
863282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = 0; i < w; i++) {
864282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rr = row[0];
865282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            gg = row[1];
866282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bb = row[2];
867282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            aa = row[3];
868282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            row += bpp;
869282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
870282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == 0) {
871282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf("Row %d:", j);
872282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
873282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            switch (bpp) {
874282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 1:
875282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d)", rr);
876282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
877282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 2:
878282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d", rr, gg);
879282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
880282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 3:
881282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d %d)", rr, gg, bb);
882282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
883282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            case 4:
884282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (%d %d %d %d)", rr, gg, bb, aa);
885282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                break;
886282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
887282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (i == (w - 1)) {
888282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                NOISY(printf("\n"));
889282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
890282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
891282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
892282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
893282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
894282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define MAX(a,b) ((a)>(b)?(a):(b))
895282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define ABS(a)   ((a)<0?-(a):(a))
896282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
897282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
898282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          png_colorp rgbPalette, png_bytep alphaPalette,
899282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          int *paletteEntries, bool *hasTransparency, int *colorType,
900282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          png_bytepp outRows)
901282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
902282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int w = imageInfo.width;
903282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int h = imageInfo.height;
904282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i, j, rr, gg, bb, aa, idx;
905282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    uint32_t colors[256], col;
906282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int num_colors = 0;
907282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int maxGrayDeviation = 0;
908282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
909282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isOpaque = true;
910282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isPalette = true;
911282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool isGrayscale = true;
912282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
913282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Scan the entire image and determine if:
914282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 1. Every pixel has R == G == B (grayscale)
915282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 2. Every pixel has A == 255 (opaque)
916282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 3. There are no more than 256 distinct RGBA colors
917282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
918282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // NOISY(printf("Initial image data:\n"));
919282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
920282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
921282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (j = 0; j < h; j++) {
922282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep row = imageInfo.rows[j];
923282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_bytep out = outRows[j];
924282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = 0; i < w; i++) {
925282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rr = *row++;
926282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            gg = *row++;
927282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            bb = *row++;
928282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            aa = *row++;
929282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
930282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int odev = maxGrayDeviation;
931282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
932282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
933282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
934282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (maxGrayDeviation > odev) {
935282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
936282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                             maxGrayDeviation, i, j, rr, gg, bb, aa));
937282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
938282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
939282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really grayscale
940282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isGrayscale) {
941282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (rr != gg || rr != bb) {
942282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
943282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                  i, j, rr, gg, bb, aa));
944282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    isGrayscale = false;
945282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
946282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
947282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
948282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really opaque
949282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isOpaque) {
950282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (aa != 0xff) {
951282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
952282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                 i, j, rr, gg, bb, aa));
953282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    isOpaque = false;
954282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
955282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
956282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
957282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Check if image is really <= 256 colors
958282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isPalette) {
959282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
960282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                bool match = false;
961282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                for (idx = 0; idx < num_colors; idx++) {
962282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (colors[idx] == col) {
963282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        match = true;
964282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        break;
965282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
966282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
967282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
968282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // Write the palette index for the pixel to outRows optimistically
969282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // We might overwrite it later if we decide to encode as gray or
970282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // gray + alpha
971282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *out++ = idx;
972282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (!match) {
973282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (num_colors == 256) {
974282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        NOISY(printf("Found 257th color at %d, %d\n", i, j));
975282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        isPalette = false;
976282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } else {
977282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        colors[num_colors++] = col;
978282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
979282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
980282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
981282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
982282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
983282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
984282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *paletteEntries = 0;
985282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    *hasTransparency = !isOpaque;
986282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bpp = isOpaque ? 3 : 4;
987282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int paletteSize = w * h + bpp * num_colors;
988282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
989282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"));
990282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false"));
991282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false"));
992282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
993282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 paletteSize, 2 * w * h, bpp * w * h));
994282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance));
995282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
996282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Choose the best color type for the image.
997282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
998282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
999282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
1000282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
1001282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
1002282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (isGrayscale) {
1003282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (isOpaque) {
1004282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
1005282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
1006282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Use a simple heuristic to determine whether using a palette will
1007282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // save space versus using gray + alpha for each pixel.
1008282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // This doesn't take into account chunk overhead, filtering, LZ
1009282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // compression, etc.
1010282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (isPalette && (paletteSize < 2 * w * h)) {
1011282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
1012282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
1013282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
1014282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
1015282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1016282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (isPalette && (paletteSize < bpp * w * h)) {
1017282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *colorType = PNG_COLOR_TYPE_PALETTE;
1018282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1019282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (maxGrayDeviation <= grayscaleTolerance) {
1020282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
1021282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
1022282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
1023282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
1024282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1025282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1026282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1027282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Perform postprocessing of the image or palette data based on the final
1028282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // color type chosen
1029282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1030282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (*colorType == PNG_COLOR_TYPE_PALETTE) {
1031282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Create separate RGB and Alpha palettes and set the number of colors
1032282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        *paletteEntries = num_colors;
1033282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1034282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Create the RGB and alpha palettes
1035282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int idx = 0; idx < num_colors; idx++) {
1036282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            col = colors[idx];
1037282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
1038282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
1039282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
1040282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            alphaPalette[idx]     = (png_byte)  (col        & 0xff);
1041282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1042282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
1043282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // If the image is gray or gray + alpha, compact the pixels into outRows
1044282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (j = 0; j < h; j++) {
1045282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep row = imageInfo.rows[j];
1046282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_bytep out = outRows[j];
1047282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (i = 0; i < w; i++) {
1048282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                rr = *row++;
1049282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                gg = *row++;
1050282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                bb = *row++;
1051282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                aa = *row++;
1052282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1053282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (isGrayscale) {
1054282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = rr;
1055282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } else {
1056282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
1057282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
1058282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (!isOpaque) {
1059282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    *out++ = aa;
1060282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
1061282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski           }
1062282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1063282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1064282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1065282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1066282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1067282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic void write_png(const char* imageName,
1068282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                      png_structp write_ptr, png_infop write_info,
1069282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                      image_info& imageInfo, int grayscaleTolerance)
1070282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1071282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool optimize = true;
1072282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_uint_32 width, height;
1073282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int color_type;
1074282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int bit_depth, interlace_type, compression_type;
1075282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
1076282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
107747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    png_unknown_chunk unknowns[3];
1078282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    unknowns[0].data = NULL;
1079282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    unknowns[1].data = NULL;
108047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    unknowns[2].data = NULL;
1081282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1082282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
1083282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (outRows == (png_bytepp) 0) {
1084282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Can't allocate output buffer!\n");
1085282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        exit(1);
1086282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1087282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int) imageInfo.height; i++) {
1088282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
1089282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (outRows[i] == (png_bytep) 0) {
1090282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Can't allocate output buffer!\n");
1091282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            exit(1);
1092282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1093282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1094282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1095282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
1096282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1097282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName,
1098282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          (int) imageInfo.width, (int) imageInfo.height));
1099282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_color rgbPalette[256];
1101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_byte alphaPalette[256];
1102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool hasTransparency;
1103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int paletteEntries;
1104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
1106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                  &paletteEntries, &hasTransparency, &color_type, outRows);
1107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If the image is a 9-patch, we need to preserve it as a ARGB file to make
1109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // sure the pixels will not be pre-dithered/clamped until we decide they are
1110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
1111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
1112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    switch (color_type) {
1116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case PNG_COLOR_TYPE_PALETTE:
1117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
1118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     imageName, paletteEntries,
1119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     hasTransparency ? " (with alpha)" : ""));
1120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
1121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case PNG_COLOR_TYPE_GRAY:
1122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
1123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
1124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case PNG_COLOR_TYPE_GRAY_ALPHA:
1125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
1126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
1127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case PNG_COLOR_TYPE_RGB:
1128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
1129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
1130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    case PNG_COLOR_TYPE_RGB_ALPHA:
1131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
1132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break;
1133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
1136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 8, color_type, PNG_INTERLACE_NONE,
1137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_PALETTE) {
1140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
1141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (hasTransparency) {
1142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
1143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
1145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
1147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (imageInfo.is9Patch) {
115047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0);
115147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int p_index = imageInfo.haveLayoutBounds ? 2 : 1;
115247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int b_index = 1;
115347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int o_index = 0;
115447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
115547cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
1156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_byte *chunk_names = imageInfo.haveLayoutBounds
115747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                ? (png_byte*)"npOl\0npLb\0npTc\0"
115847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik                : (png_byte*)"npOl\0npTc";
115947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
116047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // base 9 patch data
1161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NOISY(printf("Adding 9-patch info...\n"));
1162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        strcpy((char*)unknowns[p_index].name, "npTc");
11636381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
1164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
1165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // TODO: remove the check below when everything works
1166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
1167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
116847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // automatically generated 9 patch outline data
116947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        int chunk_size = sizeof(png_uint_32) * 6;
117047cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        strcpy((char*)unknowns[o_index].name, "npOl");
117147cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
117247cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        png_byte outputData[chunk_size];
117347cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
117447cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        ((float*) outputData)[4] = imageInfo.outlineRadius;
117577b5cad3efedd20f2b7cc14d87ccce1b0261960aChris Craik        ((png_uint_32*) outputData)[5] = imageInfo.outlineAlpha;
117647cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        memcpy(unknowns[o_index].data, &outputData, chunk_size);
117747cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        unknowns[o_index].size = chunk_size;
117847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik
117947cd8e921db73e894f94ec4729ade90da50996f5Chris Craik        // optional optical inset / layout bounds data
1180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (imageInfo.haveLayoutBounds) {
1181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int chunk_size = sizeof(png_uint_32) * 4;
1182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            strcpy((char*)unknowns[b_index].name, "npLb");
1183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
1184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
1185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            unknowns[b_index].size = chunk_size;
1186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = 0; i < chunk_count; i++) {
1189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            unknowns[i].location = PNG_HAVE_PLTE;
1190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                    chunk_names, chunk_count);
1193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
1194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#if PNG_LIBPNG_VER < 10600
1195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /* Deal with unknown chunk location bug in 1.5.x and earlier */
1196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
1197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (imageInfo.haveLayoutBounds) {
1198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
1199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#endif
1201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_info(write_ptr, write_info);
1205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_bytepp rows;
1207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
1208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (color_type == PNG_COLOR_TYPE_RGB) {
1209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
1210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        rows = imageInfo.rows;
1212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
1213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        rows = outRows;
1214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_image(write_ptr, rows);
1216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//     NOISY(printf("Final image data:\n"));
1218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//     dump_image(imageInfo.width, imageInfo.height, rows, color_type);
1219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_write_end(write_ptr, write_info);
1221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int) imageInfo.height; i++) {
1223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        free(outRows[i]);
1224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(outRows);
1226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(unknowns[0].data);
1227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    free(unknowns[1].data);
122847cd8e921db73e894f94ec4729ade90da50996f5Chris Craik    free(unknowns[2].data);
1229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_get_IHDR(write_ptr, write_info, &width, &height,
1231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &bit_depth, &color_type, &interlace_type,
1232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski       &compression_type, NULL);
1233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
1235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 (int)width, (int)height, bit_depth, color_type, interlace_type,
1236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                 compression_type));
1237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatus_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                         const sp<AaptFile>& file, String8* outNewLeafName)
1241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 ext(file->getPath().getPathExtension());
1243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // We currently only process PNG images.
1245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (strcmp(ext.string(), ".png") != 0) {
1246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return NO_ERROR;
1247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Example of renaming a file:
1250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //*outNewLeafName = file->getPath().getBasePath().getFileName();
1251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //outNewLeafName->append(".nupng");
1252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 printableName(file->getPrintableSource());
1254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Processing image: %s\n", printableName.string());
1257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp read_ptr = NULL;
1260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop read_info = NULL;
1261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    FILE* fp;
1262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image_info imageInfo;
1264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp write_ptr = NULL;
1266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop write_info = NULL;
1267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t error = UNKNOWN_ERROR;
1269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const size_t nameLen = file->getPath().length();
1271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(file->getSourceFile().string(), "rb");
1273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp == NULL) {
1274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
1275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        (png_error_ptr)NULL);
1280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_ptr) {
1281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_info = png_create_info_struct(read_ptr);
1285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_info) {
1286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(read_ptr))) {
1290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_init_io(read_ptr, fp);
1294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_png(printableName.string(), read_ptr, read_info, &imageInfo);
1296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (nameLen > 6) {
1298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        const char* name = file->getPath().string();
1299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
1301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                goto bail;
1302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
1303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        (png_error_ptr)NULL);
1308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_ptr)
1309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
1310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_info = png_create_info_struct(write_ptr);
1314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_info)
1315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
1316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_set_write_fn(write_ptr, (void*)file.get(),
1320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                     png_write_aapt_file, png_flush_aapt_file);
1321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(write_ptr)))
1323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
1324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
1325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_png(printableName.string(), write_ptr, write_info, imageInfo,
1328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              bundle->getGrayscaleTolerance());
1329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    error = NO_ERROR;
1331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fseek(fp, 0, SEEK_END);
1334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t oldSize = (size_t)ftell(fp);
1335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t newSize = file->getSize();
1336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float factor = ((float)newSize)/oldSize;
1337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int percent = (int)(factor*100);
1338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
1339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibail:
1342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (read_ptr) {
1343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp) {
1346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (write_ptr) {
1349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (error != NO_ERROR) {
1353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                file->getPrintableSource().string());
1355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return error;
1357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatus_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
1360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp read_ptr = NULL;
1362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop read_info = NULL;
1363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    FILE* fp;
1365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    image_info imageInfo;
1367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_structp write_ptr = NULL;
1369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_infop write_info = NULL;
1370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t error = UNKNOWN_ERROR;
1372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
1375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Get a file handler to read from
1378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(source.string(),"rb");
1379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fp == NULL) {
1380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
1381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to get a struct to read image data into
1385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_ptr) {
1387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to get a struct to read image info into
1393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_info = png_create_info_struct(read_ptr);
1394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!read_info) {
1395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set a jump point for libpng to long jump back to on error
1401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(read_ptr))) {
1402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up libpng to read from our file.
1408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_init_io(read_ptr,fp);
1409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Actually read data from the file
1411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    read_png(source.string(), read_ptr, read_info, &imageInfo);
1412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // We're done reading so we can clean up
1414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Find old file size before releasing handle
1415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fseek(fp, 0, SEEK_END);
1416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t oldSize = (size_t)ftell(fp);
1417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fclose(fp);
1418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_destroy_read_struct(&read_ptr, &read_info,NULL);
1419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Check to see if we're dealing with a 9-patch
1421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If we are, process appropriately
1422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (source.getBasePath().getPathExtension() == ".9")  {
1423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
1424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return error;
1425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
1426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to create a structure to hold the processed image data
1429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // that can be written to disk
1430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_ptr) {
1432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Call libpng to create a structure to hold processed image info that can
1437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // be written to disk
1438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_info = png_create_info_struct(write_ptr);
1439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!write_info) {
1440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Open up our destination file for writing
1445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fp = fopen(dest.string(), "wb");
1446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (!fp) {
1447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
1448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up libpng to write to our file
1453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_init_io(write_ptr, fp);
1454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Set up a jump for libpng to long jump back on on errors
1456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (setjmp(png_jmpbuf(write_ptr))) {
1457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
1458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        png_destroy_write_struct(&write_ptr, &write_info);
1459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return error;
1460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // Actually write out to the new png
1463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    write_png(dest.string(), write_ptr, write_info, imageInfo,
1464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski              bundle->getGrayscaleTolerance());
1465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
1467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Find the size of our new file
1468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        FILE* reader = fopen(dest.string(), "rb");
1469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fseek(reader, 0, SEEK_END);
1470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        size_t newSize = (size_t)ftell(reader);
1471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(reader);
1472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float factor = ((float)newSize)/oldSize;
1474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int percent = (int)(factor*100);
1475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("  (processed image to cache entry %s: %d%% size of source)\n",
1476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski               dest.string(), percent);
1477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //Clean up
1480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fclose(fp);
1481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    png_destroy_write_struct(&write_ptr, &write_info);
1482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
1484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1486e572c011feabf6319ba836cf5bc4c3baa0ba6a85Adam Lesinskistatus_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                          ResourceTable* table, const sp<AaptFile>& file)
1488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
1489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 ext(file->getPath().getPathExtension());
1490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // At this point, now that we have all the resource data, all we need to
1492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // do is compile XML files.
1493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (strcmp(ext.string(), ".xml") == 0) {
14949306a474e1b7509f4cdf252359dc301ce933ca1dAdam Lesinski        String16 resourceName(parseResourceName(file->getSourceFile().getPathLeaf()));
1495e572c011feabf6319ba836cf5bc4c3baa0ba6a85Adam Lesinski        return compileXmlFile(bundle, assets, resourceName, file, table);
1496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
1497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
1498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return NO_ERROR;
1499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
1500