1*d57664e9SAndroid Build Coastguard Worker //
2*d57664e9SAndroid Build Coastguard Worker // Copyright 2006 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker //
4*d57664e9SAndroid Build Coastguard Worker // Build resource files from raw assets.
5*d57664e9SAndroid Build Coastguard Worker //
6*d57664e9SAndroid Build Coastguard Worker
7*d57664e9SAndroid Build Coastguard Worker #define PNG_INTERNAL
8*d57664e9SAndroid Build Coastguard Worker
9*d57664e9SAndroid Build Coastguard Worker #include "Images.h"
10*d57664e9SAndroid Build Coastguard Worker
11*d57664e9SAndroid Build Coastguard Worker #include <androidfw/PathUtils.h>
12*d57664e9SAndroid Build Coastguard Worker #include <androidfw/ResourceTypes.h>
13*d57664e9SAndroid Build Coastguard Worker #include <utils/ByteOrder.h>
14*d57664e9SAndroid Build Coastguard Worker
15*d57664e9SAndroid Build Coastguard Worker #include <png.h>
16*d57664e9SAndroid Build Coastguard Worker #include <zlib.h>
17*d57664e9SAndroid Build Coastguard Worker
18*d57664e9SAndroid Build Coastguard Worker // Change this to true for noisy debug output.
19*d57664e9SAndroid Build Coastguard Worker static const bool kIsDebug = false;
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker static void
png_write_aapt_file(png_structp png_ptr,png_bytep data,png_size_t length)22*d57664e9SAndroid Build Coastguard Worker png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
23*d57664e9SAndroid Build Coastguard Worker {
24*d57664e9SAndroid Build Coastguard Worker AaptFile* aaptfile = (AaptFile*) png_get_io_ptr(png_ptr);
25*d57664e9SAndroid Build Coastguard Worker status_t err = aaptfile->writeData(data, length);
26*d57664e9SAndroid Build Coastguard Worker if (err != NO_ERROR) {
27*d57664e9SAndroid Build Coastguard Worker png_error(png_ptr, "Write Error");
28*d57664e9SAndroid Build Coastguard Worker }
29*d57664e9SAndroid Build Coastguard Worker }
30*d57664e9SAndroid Build Coastguard Worker
31*d57664e9SAndroid Build Coastguard Worker
32*d57664e9SAndroid Build Coastguard Worker static void
png_flush_aapt_file(png_structp)33*d57664e9SAndroid Build Coastguard Worker png_flush_aapt_file(png_structp /* png_ptr */)
34*d57664e9SAndroid Build Coastguard Worker {
35*d57664e9SAndroid Build Coastguard Worker }
36*d57664e9SAndroid Build Coastguard Worker
37*d57664e9SAndroid Build Coastguard Worker // This holds an image as 8bpp RGBA.
38*d57664e9SAndroid Build Coastguard Worker struct image_info
39*d57664e9SAndroid Build Coastguard Worker {
image_infoimage_info40*d57664e9SAndroid Build Coastguard Worker image_info() : rows(NULL), is9Patch(false),
41*d57664e9SAndroid Build Coastguard Worker xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { }
42*d57664e9SAndroid Build Coastguard Worker
~image_infoimage_info43*d57664e9SAndroid Build Coastguard Worker ~image_info() {
44*d57664e9SAndroid Build Coastguard Worker if (rows && rows != allocRows) {
45*d57664e9SAndroid Build Coastguard Worker free(rows);
46*d57664e9SAndroid Build Coastguard Worker }
47*d57664e9SAndroid Build Coastguard Worker if (allocRows) {
48*d57664e9SAndroid Build Coastguard Worker for (int i=0; i<(int)allocHeight; i++) {
49*d57664e9SAndroid Build Coastguard Worker free(allocRows[i]);
50*d57664e9SAndroid Build Coastguard Worker }
51*d57664e9SAndroid Build Coastguard Worker free(allocRows);
52*d57664e9SAndroid Build Coastguard Worker }
53*d57664e9SAndroid Build Coastguard Worker free(xDivs);
54*d57664e9SAndroid Build Coastguard Worker free(yDivs);
55*d57664e9SAndroid Build Coastguard Worker free(colors);
56*d57664e9SAndroid Build Coastguard Worker }
57*d57664e9SAndroid Build Coastguard Worker
serialize9patchimage_info58*d57664e9SAndroid Build Coastguard Worker void* serialize9patch() {
59*d57664e9SAndroid Build Coastguard Worker void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors);
60*d57664e9SAndroid Build Coastguard Worker reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
61*d57664e9SAndroid Build Coastguard Worker return serialized;
62*d57664e9SAndroid Build Coastguard Worker }
63*d57664e9SAndroid Build Coastguard Worker
64*d57664e9SAndroid Build Coastguard Worker png_uint_32 width;
65*d57664e9SAndroid Build Coastguard Worker png_uint_32 height;
66*d57664e9SAndroid Build Coastguard Worker png_bytepp rows;
67*d57664e9SAndroid Build Coastguard Worker
68*d57664e9SAndroid Build Coastguard Worker // 9-patch info.
69*d57664e9SAndroid Build Coastguard Worker bool is9Patch;
70*d57664e9SAndroid Build Coastguard Worker Res_png_9patch info9Patch;
71*d57664e9SAndroid Build Coastguard Worker int32_t* xDivs;
72*d57664e9SAndroid Build Coastguard Worker int32_t* yDivs;
73*d57664e9SAndroid Build Coastguard Worker uint32_t* colors;
74*d57664e9SAndroid Build Coastguard Worker
75*d57664e9SAndroid Build Coastguard Worker // Layout padding, if relevant
76*d57664e9SAndroid Build Coastguard Worker bool haveLayoutBounds;
77*d57664e9SAndroid Build Coastguard Worker int32_t layoutBoundsLeft;
78*d57664e9SAndroid Build Coastguard Worker int32_t layoutBoundsTop;
79*d57664e9SAndroid Build Coastguard Worker int32_t layoutBoundsRight;
80*d57664e9SAndroid Build Coastguard Worker int32_t layoutBoundsBottom;
81*d57664e9SAndroid Build Coastguard Worker
82*d57664e9SAndroid Build Coastguard Worker // Round rect outline description
83*d57664e9SAndroid Build Coastguard Worker int32_t outlineInsetsLeft;
84*d57664e9SAndroid Build Coastguard Worker int32_t outlineInsetsTop;
85*d57664e9SAndroid Build Coastguard Worker int32_t outlineInsetsRight;
86*d57664e9SAndroid Build Coastguard Worker int32_t outlineInsetsBottom;
87*d57664e9SAndroid Build Coastguard Worker float outlineRadius;
88*d57664e9SAndroid Build Coastguard Worker uint8_t outlineAlpha;
89*d57664e9SAndroid Build Coastguard Worker
90*d57664e9SAndroid Build Coastguard Worker png_uint_32 allocHeight;
91*d57664e9SAndroid Build Coastguard Worker png_bytepp allocRows;
92*d57664e9SAndroid Build Coastguard Worker };
93*d57664e9SAndroid Build Coastguard Worker
log_warning(png_structp png_ptr,png_const_charp warning_message)94*d57664e9SAndroid Build Coastguard Worker static void log_warning(png_structp png_ptr, png_const_charp warning_message)
95*d57664e9SAndroid Build Coastguard Worker {
96*d57664e9SAndroid Build Coastguard Worker const char* imageName = (const char*) png_get_error_ptr(png_ptr);
97*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "%s: libpng warning: %s\n", imageName, warning_message);
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker
read_png(const char * imageName,png_structp read_ptr,png_infop read_info,image_info * outImageInfo)100*d57664e9SAndroid Build Coastguard Worker static void read_png(const char* imageName,
101*d57664e9SAndroid Build Coastguard Worker png_structp read_ptr, png_infop read_info,
102*d57664e9SAndroid Build Coastguard Worker image_info* outImageInfo)
103*d57664e9SAndroid Build Coastguard Worker {
104*d57664e9SAndroid Build Coastguard Worker int color_type;
105*d57664e9SAndroid Build Coastguard Worker int bit_depth, interlace_type, compression_type;
106*d57664e9SAndroid Build Coastguard Worker int i;
107*d57664e9SAndroid Build Coastguard Worker
108*d57664e9SAndroid Build Coastguard Worker png_set_error_fn(read_ptr, const_cast<char*>(imageName),
109*d57664e9SAndroid Build Coastguard Worker NULL /* use default errorfn */, log_warning);
110*d57664e9SAndroid Build Coastguard Worker png_read_info(read_ptr, read_info);
111*d57664e9SAndroid Build Coastguard Worker
112*d57664e9SAndroid Build Coastguard Worker png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
113*d57664e9SAndroid Build Coastguard Worker &outImageInfo->height, &bit_depth, &color_type,
114*d57664e9SAndroid Build Coastguard Worker &interlace_type, &compression_type, NULL);
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard Worker //printf("Image %s:\n", imageName);
117*d57664e9SAndroid Build Coastguard Worker //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
118*d57664e9SAndroid Build Coastguard Worker // color_type, bit_depth, interlace_type, compression_type);
119*d57664e9SAndroid Build Coastguard Worker
120*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_PALETTE)
121*d57664e9SAndroid Build Coastguard Worker png_set_palette_to_rgb(read_ptr);
122*d57664e9SAndroid Build Coastguard Worker
123*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
124*d57664e9SAndroid Build Coastguard Worker png_set_expand_gray_1_2_4_to_8(read_ptr);
125*d57664e9SAndroid Build Coastguard Worker
126*d57664e9SAndroid Build Coastguard Worker if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
127*d57664e9SAndroid Build Coastguard Worker //printf("Has PNG_INFO_tRNS!\n");
128*d57664e9SAndroid Build Coastguard Worker png_set_tRNS_to_alpha(read_ptr);
129*d57664e9SAndroid Build Coastguard Worker }
130*d57664e9SAndroid Build Coastguard Worker
131*d57664e9SAndroid Build Coastguard Worker if (bit_depth == 16)
132*d57664e9SAndroid Build Coastguard Worker png_set_strip_16(read_ptr);
133*d57664e9SAndroid Build Coastguard Worker
134*d57664e9SAndroid Build Coastguard Worker if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
135*d57664e9SAndroid Build Coastguard Worker png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
136*d57664e9SAndroid Build Coastguard Worker
137*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
138*d57664e9SAndroid Build Coastguard Worker png_set_gray_to_rgb(read_ptr);
139*d57664e9SAndroid Build Coastguard Worker
140*d57664e9SAndroid Build Coastguard Worker png_set_interlace_handling(read_ptr);
141*d57664e9SAndroid Build Coastguard Worker
142*d57664e9SAndroid Build Coastguard Worker png_read_update_info(read_ptr, read_info);
143*d57664e9SAndroid Build Coastguard Worker
144*d57664e9SAndroid Build Coastguard Worker outImageInfo->rows = (png_bytepp)malloc(
145*d57664e9SAndroid Build Coastguard Worker outImageInfo->height * sizeof(png_bytep));
146*d57664e9SAndroid Build Coastguard Worker outImageInfo->allocHeight = outImageInfo->height;
147*d57664e9SAndroid Build Coastguard Worker outImageInfo->allocRows = outImageInfo->rows;
148*d57664e9SAndroid Build Coastguard Worker
149*d57664e9SAndroid Build Coastguard Worker png_set_rows(read_ptr, read_info, outImageInfo->rows);
150*d57664e9SAndroid Build Coastguard Worker
151*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < (int)outImageInfo->height; i++)
152*d57664e9SAndroid Build Coastguard Worker {
153*d57664e9SAndroid Build Coastguard Worker outImageInfo->rows[i] = (png_bytep)
154*d57664e9SAndroid Build Coastguard Worker malloc(png_get_rowbytes(read_ptr, read_info));
155*d57664e9SAndroid Build Coastguard Worker }
156*d57664e9SAndroid Build Coastguard Worker
157*d57664e9SAndroid Build Coastguard Worker png_read_image(read_ptr, outImageInfo->rows);
158*d57664e9SAndroid Build Coastguard Worker
159*d57664e9SAndroid Build Coastguard Worker png_read_end(read_ptr, read_info);
160*d57664e9SAndroid Build Coastguard Worker
161*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
162*d57664e9SAndroid Build Coastguard Worker printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
163*d57664e9SAndroid Build Coastguard Worker imageName,
164*d57664e9SAndroid Build Coastguard Worker (int)outImageInfo->width, (int)outImageInfo->height,
165*d57664e9SAndroid Build Coastguard Worker bit_depth, color_type,
166*d57664e9SAndroid Build Coastguard Worker interlace_type, compression_type);
167*d57664e9SAndroid Build Coastguard Worker }
168*d57664e9SAndroid Build Coastguard Worker
169*d57664e9SAndroid Build Coastguard Worker png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
170*d57664e9SAndroid Build Coastguard Worker &outImageInfo->height, &bit_depth, &color_type,
171*d57664e9SAndroid Build Coastguard Worker &interlace_type, &compression_type, NULL);
172*d57664e9SAndroid Build Coastguard Worker }
173*d57664e9SAndroid Build Coastguard Worker
174*d57664e9SAndroid Build Coastguard Worker #define COLOR_TRANSPARENT 0
175*d57664e9SAndroid Build Coastguard Worker #define COLOR_WHITE 0xFFFFFFFF
176*d57664e9SAndroid Build Coastguard Worker #define COLOR_TICK 0xFF000000
177*d57664e9SAndroid Build Coastguard Worker #define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
178*d57664e9SAndroid Build Coastguard Worker
179*d57664e9SAndroid Build Coastguard Worker enum {
180*d57664e9SAndroid Build Coastguard Worker TICK_TYPE_NONE,
181*d57664e9SAndroid Build Coastguard Worker TICK_TYPE_TICK,
182*d57664e9SAndroid Build Coastguard Worker TICK_TYPE_LAYOUT_BOUNDS,
183*d57664e9SAndroid Build Coastguard Worker TICK_TYPE_BOTH
184*d57664e9SAndroid Build Coastguard Worker };
185*d57664e9SAndroid Build Coastguard Worker
tick_type(png_bytep p,bool transparent,const char ** outError)186*d57664e9SAndroid Build Coastguard Worker static int tick_type(png_bytep p, bool transparent, const char** outError)
187*d57664e9SAndroid Build Coastguard Worker {
188*d57664e9SAndroid Build Coastguard Worker png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
189*d57664e9SAndroid Build Coastguard Worker
190*d57664e9SAndroid Build Coastguard Worker if (transparent) {
191*d57664e9SAndroid Build Coastguard Worker if (p[3] == 0) {
192*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_NONE;
193*d57664e9SAndroid Build Coastguard Worker }
194*d57664e9SAndroid Build Coastguard Worker if (color == COLOR_LAYOUT_BOUNDS_TICK) {
195*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_LAYOUT_BOUNDS;
196*d57664e9SAndroid Build Coastguard Worker }
197*d57664e9SAndroid Build Coastguard Worker if (color == COLOR_TICK) {
198*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_TICK;
199*d57664e9SAndroid Build Coastguard Worker }
200*d57664e9SAndroid Build Coastguard Worker
201*d57664e9SAndroid Build Coastguard Worker // Error cases
202*d57664e9SAndroid Build Coastguard Worker if (p[3] != 0xff) {
203*d57664e9SAndroid Build Coastguard Worker *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
204*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_NONE;
205*d57664e9SAndroid Build Coastguard Worker }
206*d57664e9SAndroid Build Coastguard Worker if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
207*d57664e9SAndroid Build Coastguard Worker *outError = "Ticks in transparent frame must be black or red";
208*d57664e9SAndroid Build Coastguard Worker }
209*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_TICK;
210*d57664e9SAndroid Build Coastguard Worker }
211*d57664e9SAndroid Build Coastguard Worker
212*d57664e9SAndroid Build Coastguard Worker if (p[3] != 0xFF) {
213*d57664e9SAndroid Build Coastguard Worker *outError = "White frame must be a solid color (no alpha)";
214*d57664e9SAndroid Build Coastguard Worker }
215*d57664e9SAndroid Build Coastguard Worker if (color == COLOR_WHITE) {
216*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_NONE;
217*d57664e9SAndroid Build Coastguard Worker }
218*d57664e9SAndroid Build Coastguard Worker if (color == COLOR_TICK) {
219*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_TICK;
220*d57664e9SAndroid Build Coastguard Worker }
221*d57664e9SAndroid Build Coastguard Worker if (color == COLOR_LAYOUT_BOUNDS_TICK) {
222*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_LAYOUT_BOUNDS;
223*d57664e9SAndroid Build Coastguard Worker }
224*d57664e9SAndroid Build Coastguard Worker
225*d57664e9SAndroid Build Coastguard Worker if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
226*d57664e9SAndroid Build Coastguard Worker *outError = "Ticks in white frame must be black or red";
227*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_NONE;
228*d57664e9SAndroid Build Coastguard Worker }
229*d57664e9SAndroid Build Coastguard Worker return TICK_TYPE_TICK;
230*d57664e9SAndroid Build Coastguard Worker }
231*d57664e9SAndroid Build Coastguard Worker
232*d57664e9SAndroid Build Coastguard Worker enum {
233*d57664e9SAndroid Build Coastguard Worker TICK_START,
234*d57664e9SAndroid Build Coastguard Worker TICK_INSIDE_1,
235*d57664e9SAndroid Build Coastguard Worker TICK_OUTSIDE_1
236*d57664e9SAndroid Build Coastguard Worker };
237*d57664e9SAndroid Build Coastguard Worker
get_horizontal_ticks(png_bytep row,int width,bool transparent,bool required,int32_t * outLeft,int32_t * outRight,const char ** outError,uint8_t * outDivs,bool multipleAllowed)238*d57664e9SAndroid Build Coastguard Worker static status_t get_horizontal_ticks(
239*d57664e9SAndroid Build Coastguard Worker png_bytep row, int width, bool transparent, bool required,
240*d57664e9SAndroid Build Coastguard Worker int32_t* outLeft, int32_t* outRight, const char** outError,
241*d57664e9SAndroid Build Coastguard Worker uint8_t* outDivs, bool multipleAllowed)
242*d57664e9SAndroid Build Coastguard Worker {
243*d57664e9SAndroid Build Coastguard Worker int i;
244*d57664e9SAndroid Build Coastguard Worker *outLeft = *outRight = -1;
245*d57664e9SAndroid Build Coastguard Worker int state = TICK_START;
246*d57664e9SAndroid Build Coastguard Worker bool found = false;
247*d57664e9SAndroid Build Coastguard Worker
248*d57664e9SAndroid Build Coastguard Worker for (i=1; i<width-1; i++) {
249*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
250*d57664e9SAndroid Build Coastguard Worker if (state == TICK_START ||
251*d57664e9SAndroid Build Coastguard Worker (state == TICK_OUTSIDE_1 && multipleAllowed)) {
252*d57664e9SAndroid Build Coastguard Worker *outLeft = i-1;
253*d57664e9SAndroid Build Coastguard Worker *outRight = width-2;
254*d57664e9SAndroid Build Coastguard Worker found = true;
255*d57664e9SAndroid Build Coastguard Worker if (outDivs != NULL) {
256*d57664e9SAndroid Build Coastguard Worker *outDivs += 2;
257*d57664e9SAndroid Build Coastguard Worker }
258*d57664e9SAndroid Build Coastguard Worker state = TICK_INSIDE_1;
259*d57664e9SAndroid Build Coastguard Worker } else if (state == TICK_OUTSIDE_1) {
260*d57664e9SAndroid Build Coastguard Worker *outError = "Can't have more than one marked region along edge";
261*d57664e9SAndroid Build Coastguard Worker *outLeft = i;
262*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
263*d57664e9SAndroid Build Coastguard Worker }
264*d57664e9SAndroid Build Coastguard Worker } else if (*outError == NULL) {
265*d57664e9SAndroid Build Coastguard Worker if (state == TICK_INSIDE_1) {
266*d57664e9SAndroid Build Coastguard Worker // We're done with this div. Move on to the next.
267*d57664e9SAndroid Build Coastguard Worker *outRight = i-1;
268*d57664e9SAndroid Build Coastguard Worker outRight += 2;
269*d57664e9SAndroid Build Coastguard Worker outLeft += 2;
270*d57664e9SAndroid Build Coastguard Worker state = TICK_OUTSIDE_1;
271*d57664e9SAndroid Build Coastguard Worker }
272*d57664e9SAndroid Build Coastguard Worker } else {
273*d57664e9SAndroid Build Coastguard Worker *outLeft = i;
274*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
275*d57664e9SAndroid Build Coastguard Worker }
276*d57664e9SAndroid Build Coastguard Worker }
277*d57664e9SAndroid Build Coastguard Worker
278*d57664e9SAndroid Build Coastguard Worker if (required && !found) {
279*d57664e9SAndroid Build Coastguard Worker *outError = "No marked region found along edge";
280*d57664e9SAndroid Build Coastguard Worker *outLeft = -1;
281*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
282*d57664e9SAndroid Build Coastguard Worker }
283*d57664e9SAndroid Build Coastguard Worker
284*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
285*d57664e9SAndroid Build Coastguard Worker }
286*d57664e9SAndroid Build Coastguard Worker
get_vertical_ticks(png_bytepp rows,int offset,int height,bool transparent,bool required,int32_t * outTop,int32_t * outBottom,const char ** outError,uint8_t * outDivs,bool multipleAllowed)287*d57664e9SAndroid Build Coastguard Worker static status_t get_vertical_ticks(
288*d57664e9SAndroid Build Coastguard Worker png_bytepp rows, int offset, int height, bool transparent, bool required,
289*d57664e9SAndroid Build Coastguard Worker int32_t* outTop, int32_t* outBottom, const char** outError,
290*d57664e9SAndroid Build Coastguard Worker uint8_t* outDivs, bool multipleAllowed)
291*d57664e9SAndroid Build Coastguard Worker {
292*d57664e9SAndroid Build Coastguard Worker int i;
293*d57664e9SAndroid Build Coastguard Worker *outTop = *outBottom = -1;
294*d57664e9SAndroid Build Coastguard Worker int state = TICK_START;
295*d57664e9SAndroid Build Coastguard Worker bool found = false;
296*d57664e9SAndroid Build Coastguard Worker
297*d57664e9SAndroid Build Coastguard Worker for (i=1; i<height-1; i++) {
298*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
299*d57664e9SAndroid Build Coastguard Worker if (state == TICK_START ||
300*d57664e9SAndroid Build Coastguard Worker (state == TICK_OUTSIDE_1 && multipleAllowed)) {
301*d57664e9SAndroid Build Coastguard Worker *outTop = i-1;
302*d57664e9SAndroid Build Coastguard Worker *outBottom = height-2;
303*d57664e9SAndroid Build Coastguard Worker found = true;
304*d57664e9SAndroid Build Coastguard Worker if (outDivs != NULL) {
305*d57664e9SAndroid Build Coastguard Worker *outDivs += 2;
306*d57664e9SAndroid Build Coastguard Worker }
307*d57664e9SAndroid Build Coastguard Worker state = TICK_INSIDE_1;
308*d57664e9SAndroid Build Coastguard Worker } else if (state == TICK_OUTSIDE_1) {
309*d57664e9SAndroid Build Coastguard Worker *outError = "Can't have more than one marked region along edge";
310*d57664e9SAndroid Build Coastguard Worker *outTop = i;
311*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
312*d57664e9SAndroid Build Coastguard Worker }
313*d57664e9SAndroid Build Coastguard Worker } else if (*outError == NULL) {
314*d57664e9SAndroid Build Coastguard Worker if (state == TICK_INSIDE_1) {
315*d57664e9SAndroid Build Coastguard Worker // We're done with this div. Move on to the next.
316*d57664e9SAndroid Build Coastguard Worker *outBottom = i-1;
317*d57664e9SAndroid Build Coastguard Worker outTop += 2;
318*d57664e9SAndroid Build Coastguard Worker outBottom += 2;
319*d57664e9SAndroid Build Coastguard Worker state = TICK_OUTSIDE_1;
320*d57664e9SAndroid Build Coastguard Worker }
321*d57664e9SAndroid Build Coastguard Worker } else {
322*d57664e9SAndroid Build Coastguard Worker *outTop = i;
323*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
324*d57664e9SAndroid Build Coastguard Worker }
325*d57664e9SAndroid Build Coastguard Worker }
326*d57664e9SAndroid Build Coastguard Worker
327*d57664e9SAndroid Build Coastguard Worker if (required && !found) {
328*d57664e9SAndroid Build Coastguard Worker *outError = "No marked region found along edge";
329*d57664e9SAndroid Build Coastguard Worker *outTop = -1;
330*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
331*d57664e9SAndroid Build Coastguard Worker }
332*d57664e9SAndroid Build Coastguard Worker
333*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
334*d57664e9SAndroid Build Coastguard Worker }
335*d57664e9SAndroid Build Coastguard Worker
get_horizontal_layout_bounds_ticks(png_bytep row,int width,bool transparent,bool,int32_t * outLeft,int32_t * outRight,const char ** outError)336*d57664e9SAndroid Build Coastguard Worker static status_t get_horizontal_layout_bounds_ticks(
337*d57664e9SAndroid Build Coastguard Worker png_bytep row, int width, bool transparent, bool /* required */,
338*d57664e9SAndroid Build Coastguard Worker int32_t* outLeft, int32_t* outRight, const char** outError)
339*d57664e9SAndroid Build Coastguard Worker {
340*d57664e9SAndroid Build Coastguard Worker int i;
341*d57664e9SAndroid Build Coastguard Worker *outLeft = *outRight = 0;
342*d57664e9SAndroid Build Coastguard Worker
343*d57664e9SAndroid Build Coastguard Worker // Look for left tick
344*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
345*d57664e9SAndroid Build Coastguard Worker // Starting with a layout padding tick
346*d57664e9SAndroid Build Coastguard Worker i = 1;
347*d57664e9SAndroid Build Coastguard Worker while (i < width - 1) {
348*d57664e9SAndroid Build Coastguard Worker (*outLeft)++;
349*d57664e9SAndroid Build Coastguard Worker i++;
350*d57664e9SAndroid Build Coastguard Worker int tick = tick_type(row + i * 4, transparent, outError);
351*d57664e9SAndroid Build Coastguard Worker if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
352*d57664e9SAndroid Build Coastguard Worker break;
353*d57664e9SAndroid Build Coastguard Worker }
354*d57664e9SAndroid Build Coastguard Worker }
355*d57664e9SAndroid Build Coastguard Worker }
356*d57664e9SAndroid Build Coastguard Worker
357*d57664e9SAndroid Build Coastguard Worker // Look for right tick
358*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
359*d57664e9SAndroid Build Coastguard Worker // Ending with a layout padding tick
360*d57664e9SAndroid Build Coastguard Worker i = width - 2;
361*d57664e9SAndroid Build Coastguard Worker while (i > 1) {
362*d57664e9SAndroid Build Coastguard Worker (*outRight)++;
363*d57664e9SAndroid Build Coastguard Worker i--;
364*d57664e9SAndroid Build Coastguard Worker int tick = tick_type(row+i*4, transparent, outError);
365*d57664e9SAndroid Build Coastguard Worker if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
366*d57664e9SAndroid Build Coastguard Worker break;
367*d57664e9SAndroid Build Coastguard Worker }
368*d57664e9SAndroid Build Coastguard Worker }
369*d57664e9SAndroid Build Coastguard Worker }
370*d57664e9SAndroid Build Coastguard Worker
371*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
372*d57664e9SAndroid Build Coastguard Worker }
373*d57664e9SAndroid Build Coastguard Worker
get_vertical_layout_bounds_ticks(png_bytepp rows,int offset,int height,bool transparent,bool,int32_t * outTop,int32_t * outBottom,const char ** outError)374*d57664e9SAndroid Build Coastguard Worker static status_t get_vertical_layout_bounds_ticks(
375*d57664e9SAndroid Build Coastguard Worker png_bytepp rows, int offset, int height, bool transparent, bool /* required */,
376*d57664e9SAndroid Build Coastguard Worker int32_t* outTop, int32_t* outBottom, const char** outError)
377*d57664e9SAndroid Build Coastguard Worker {
378*d57664e9SAndroid Build Coastguard Worker int i;
379*d57664e9SAndroid Build Coastguard Worker *outTop = *outBottom = 0;
380*d57664e9SAndroid Build Coastguard Worker
381*d57664e9SAndroid Build Coastguard Worker // Look for top tick
382*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
383*d57664e9SAndroid Build Coastguard Worker // Starting with a layout padding tick
384*d57664e9SAndroid Build Coastguard Worker i = 1;
385*d57664e9SAndroid Build Coastguard Worker while (i < height - 1) {
386*d57664e9SAndroid Build Coastguard Worker (*outTop)++;
387*d57664e9SAndroid Build Coastguard Worker i++;
388*d57664e9SAndroid Build Coastguard Worker int tick = tick_type(rows[i] + offset, transparent, outError);
389*d57664e9SAndroid Build Coastguard Worker if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
390*d57664e9SAndroid Build Coastguard Worker break;
391*d57664e9SAndroid Build Coastguard Worker }
392*d57664e9SAndroid Build Coastguard Worker }
393*d57664e9SAndroid Build Coastguard Worker }
394*d57664e9SAndroid Build Coastguard Worker
395*d57664e9SAndroid Build Coastguard Worker // Look for bottom tick
396*d57664e9SAndroid Build Coastguard Worker if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
397*d57664e9SAndroid Build Coastguard Worker // Ending with a layout padding tick
398*d57664e9SAndroid Build Coastguard Worker i = height - 2;
399*d57664e9SAndroid Build Coastguard Worker while (i > 1) {
400*d57664e9SAndroid Build Coastguard Worker (*outBottom)++;
401*d57664e9SAndroid Build Coastguard Worker i--;
402*d57664e9SAndroid Build Coastguard Worker int tick = tick_type(rows[i] + offset, transparent, outError);
403*d57664e9SAndroid Build Coastguard Worker if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
404*d57664e9SAndroid Build Coastguard Worker break;
405*d57664e9SAndroid Build Coastguard Worker }
406*d57664e9SAndroid Build Coastguard Worker }
407*d57664e9SAndroid Build Coastguard Worker }
408*d57664e9SAndroid Build Coastguard Worker
409*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
410*d57664e9SAndroid Build Coastguard Worker }
411*d57664e9SAndroid Build Coastguard Worker
find_max_opacity(png_byte ** rows,int startX,int startY,int endX,int endY,int dX,int dY,int * out_inset)412*d57664e9SAndroid Build Coastguard Worker static void find_max_opacity(png_byte** rows,
413*d57664e9SAndroid Build Coastguard Worker int startX, int startY, int endX, int endY, int dX, int dY,
414*d57664e9SAndroid Build Coastguard Worker int* out_inset)
415*d57664e9SAndroid Build Coastguard Worker {
416*d57664e9SAndroid Build Coastguard Worker uint8_t max_opacity = 0;
417*d57664e9SAndroid Build Coastguard Worker int inset = 0;
418*d57664e9SAndroid Build Coastguard Worker *out_inset = 0;
419*d57664e9SAndroid Build Coastguard Worker for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
420*d57664e9SAndroid Build Coastguard Worker png_byte* color = rows[y] + x * 4;
421*d57664e9SAndroid Build Coastguard Worker uint8_t opacity = color[3];
422*d57664e9SAndroid Build Coastguard Worker if (opacity > max_opacity) {
423*d57664e9SAndroid Build Coastguard Worker max_opacity = opacity;
424*d57664e9SAndroid Build Coastguard Worker *out_inset = inset;
425*d57664e9SAndroid Build Coastguard Worker }
426*d57664e9SAndroid Build Coastguard Worker if (opacity == 0xff) return;
427*d57664e9SAndroid Build Coastguard Worker }
428*d57664e9SAndroid Build Coastguard Worker }
429*d57664e9SAndroid Build Coastguard Worker
max_alpha_over_row(png_byte * row,int startX,int endX)430*d57664e9SAndroid Build Coastguard Worker static uint8_t max_alpha_over_row(png_byte* row, int startX, int endX)
431*d57664e9SAndroid Build Coastguard Worker {
432*d57664e9SAndroid Build Coastguard Worker uint8_t max_alpha = 0;
433*d57664e9SAndroid Build Coastguard Worker for (int x = startX; x < endX; x++) {
434*d57664e9SAndroid Build Coastguard Worker uint8_t alpha = (row + x * 4)[3];
435*d57664e9SAndroid Build Coastguard Worker if (alpha > max_alpha) max_alpha = alpha;
436*d57664e9SAndroid Build Coastguard Worker }
437*d57664e9SAndroid Build Coastguard Worker return max_alpha;
438*d57664e9SAndroid Build Coastguard Worker }
439*d57664e9SAndroid Build Coastguard Worker
max_alpha_over_col(png_byte ** rows,int offsetX,int startY,int endY)440*d57664e9SAndroid Build Coastguard Worker static uint8_t max_alpha_over_col(png_byte** rows, int offsetX, int startY, int endY)
441*d57664e9SAndroid Build Coastguard Worker {
442*d57664e9SAndroid Build Coastguard Worker uint8_t max_alpha = 0;
443*d57664e9SAndroid Build Coastguard Worker for (int y = startY; y < endY; y++) {
444*d57664e9SAndroid Build Coastguard Worker uint8_t alpha = (rows[y] + offsetX * 4)[3];
445*d57664e9SAndroid Build Coastguard Worker if (alpha > max_alpha) max_alpha = alpha;
446*d57664e9SAndroid Build Coastguard Worker }
447*d57664e9SAndroid Build Coastguard Worker return max_alpha;
448*d57664e9SAndroid Build Coastguard Worker }
449*d57664e9SAndroid Build Coastguard Worker
get_outline(image_info * image)450*d57664e9SAndroid Build Coastguard Worker static void get_outline(image_info* image)
451*d57664e9SAndroid Build Coastguard Worker {
452*d57664e9SAndroid Build Coastguard Worker int midX = image->width / 2;
453*d57664e9SAndroid Build Coastguard Worker int midY = image->height / 2;
454*d57664e9SAndroid Build Coastguard Worker int endX = image->width - 2;
455*d57664e9SAndroid Build Coastguard Worker int endY = image->height - 2;
456*d57664e9SAndroid Build Coastguard Worker
457*d57664e9SAndroid Build Coastguard Worker // find left and right extent of nine patch content on center row
458*d57664e9SAndroid Build Coastguard Worker if (image->width > 4) {
459*d57664e9SAndroid Build Coastguard Worker find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
460*d57664e9SAndroid Build Coastguard Worker find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
461*d57664e9SAndroid Build Coastguard Worker } else {
462*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsLeft = 0;
463*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsRight = 0;
464*d57664e9SAndroid Build Coastguard Worker }
465*d57664e9SAndroid Build Coastguard Worker
466*d57664e9SAndroid Build Coastguard Worker // find top and bottom extent of nine patch content on center column
467*d57664e9SAndroid Build Coastguard Worker if (image->height > 4) {
468*d57664e9SAndroid Build Coastguard Worker find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
469*d57664e9SAndroid Build Coastguard Worker find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
470*d57664e9SAndroid Build Coastguard Worker } else {
471*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsTop = 0;
472*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsBottom = 0;
473*d57664e9SAndroid Build Coastguard Worker }
474*d57664e9SAndroid Build Coastguard Worker
475*d57664e9SAndroid Build Coastguard Worker int innerStartX = 1 + image->outlineInsetsLeft;
476*d57664e9SAndroid Build Coastguard Worker int innerStartY = 1 + image->outlineInsetsTop;
477*d57664e9SAndroid Build Coastguard Worker int innerEndX = endX - image->outlineInsetsRight;
478*d57664e9SAndroid Build Coastguard Worker int innerEndY = endY - image->outlineInsetsBottom;
479*d57664e9SAndroid Build Coastguard Worker int innerMidX = (innerEndX + innerStartX) / 2;
480*d57664e9SAndroid Build Coastguard Worker int innerMidY = (innerEndY + innerStartY) / 2;
481*d57664e9SAndroid Build Coastguard Worker
482*d57664e9SAndroid Build Coastguard Worker // assuming the image is a round rect, compute the radius by marching
483*d57664e9SAndroid Build Coastguard Worker // diagonally from the top left corner towards the center
484*d57664e9SAndroid Build Coastguard Worker image->outlineAlpha = std::max(
485*d57664e9SAndroid Build Coastguard Worker max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX),
486*d57664e9SAndroid Build Coastguard Worker max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY));
487*d57664e9SAndroid Build Coastguard Worker
488*d57664e9SAndroid Build Coastguard Worker int diagonalInset = 0;
489*d57664e9SAndroid Build Coastguard Worker find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
490*d57664e9SAndroid Build Coastguard Worker &diagonalInset);
491*d57664e9SAndroid Build Coastguard Worker
492*d57664e9SAndroid Build Coastguard Worker /* Determine source radius based upon inset:
493*d57664e9SAndroid Build Coastguard Worker * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
494*d57664e9SAndroid Build Coastguard Worker * sqrt(2) * r = sqrt(2) * i + r
495*d57664e9SAndroid Build Coastguard Worker * (sqrt(2) - 1) * r = sqrt(2) * i
496*d57664e9SAndroid Build Coastguard Worker * r = sqrt(2) / (sqrt(2) - 1) * i
497*d57664e9SAndroid Build Coastguard Worker */
498*d57664e9SAndroid Build Coastguard Worker image->outlineRadius = 3.4142f * diagonalInset;
499*d57664e9SAndroid Build Coastguard Worker
500*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
501*d57664e9SAndroid Build Coastguard Worker printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
502*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsLeft,
503*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsTop,
504*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsRight,
505*d57664e9SAndroid Build Coastguard Worker image->outlineInsetsBottom,
506*d57664e9SAndroid Build Coastguard Worker image->outlineRadius,
507*d57664e9SAndroid Build Coastguard Worker image->outlineAlpha);
508*d57664e9SAndroid Build Coastguard Worker }
509*d57664e9SAndroid Build Coastguard Worker }
510*d57664e9SAndroid Build Coastguard Worker
511*d57664e9SAndroid Build Coastguard Worker
get_color(png_bytepp rows,int left,int top,int right,int bottom)512*d57664e9SAndroid Build Coastguard Worker static uint32_t get_color(
513*d57664e9SAndroid Build Coastguard Worker png_bytepp rows, int left, int top, int right, int bottom)
514*d57664e9SAndroid Build Coastguard Worker {
515*d57664e9SAndroid Build Coastguard Worker png_bytep color = rows[top] + left*4;
516*d57664e9SAndroid Build Coastguard Worker
517*d57664e9SAndroid Build Coastguard Worker if (left > right || top > bottom) {
518*d57664e9SAndroid Build Coastguard Worker return Res_png_9patch::TRANSPARENT_COLOR;
519*d57664e9SAndroid Build Coastguard Worker }
520*d57664e9SAndroid Build Coastguard Worker
521*d57664e9SAndroid Build Coastguard Worker while (top <= bottom) {
522*d57664e9SAndroid Build Coastguard Worker for (int i = left; i <= right; i++) {
523*d57664e9SAndroid Build Coastguard Worker png_bytep p = rows[top]+i*4;
524*d57664e9SAndroid Build Coastguard Worker if (color[3] == 0) {
525*d57664e9SAndroid Build Coastguard Worker if (p[3] != 0) {
526*d57664e9SAndroid Build Coastguard Worker return Res_png_9patch::NO_COLOR;
527*d57664e9SAndroid Build Coastguard Worker }
528*d57664e9SAndroid Build Coastguard Worker } else if (p[0] != color[0] || p[1] != color[1]
529*d57664e9SAndroid Build Coastguard Worker || p[2] != color[2] || p[3] != color[3]) {
530*d57664e9SAndroid Build Coastguard Worker return Res_png_9patch::NO_COLOR;
531*d57664e9SAndroid Build Coastguard Worker }
532*d57664e9SAndroid Build Coastguard Worker }
533*d57664e9SAndroid Build Coastguard Worker top++;
534*d57664e9SAndroid Build Coastguard Worker }
535*d57664e9SAndroid Build Coastguard Worker
536*d57664e9SAndroid Build Coastguard Worker if (color[3] == 0) {
537*d57664e9SAndroid Build Coastguard Worker return Res_png_9patch::TRANSPARENT_COLOR;
538*d57664e9SAndroid Build Coastguard Worker }
539*d57664e9SAndroid Build Coastguard Worker return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
540*d57664e9SAndroid Build Coastguard Worker }
541*d57664e9SAndroid Build Coastguard Worker
do_9patch(const char * imageName,image_info * image)542*d57664e9SAndroid Build Coastguard Worker static status_t do_9patch(const char* imageName, image_info* image)
543*d57664e9SAndroid Build Coastguard Worker {
544*d57664e9SAndroid Build Coastguard Worker image->is9Patch = true;
545*d57664e9SAndroid Build Coastguard Worker
546*d57664e9SAndroid Build Coastguard Worker int W = image->width;
547*d57664e9SAndroid Build Coastguard Worker int H = image->height;
548*d57664e9SAndroid Build Coastguard Worker int i, j;
549*d57664e9SAndroid Build Coastguard Worker
550*d57664e9SAndroid Build Coastguard Worker int maxSizeXDivs = W * sizeof(int32_t);
551*d57664e9SAndroid Build Coastguard Worker int maxSizeYDivs = H * sizeof(int32_t);
552*d57664e9SAndroid Build Coastguard Worker int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs);
553*d57664e9SAndroid Build Coastguard Worker int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs);
554*d57664e9SAndroid Build Coastguard Worker uint8_t numXDivs = 0;
555*d57664e9SAndroid Build Coastguard Worker uint8_t numYDivs = 0;
556*d57664e9SAndroid Build Coastguard Worker
557*d57664e9SAndroid Build Coastguard Worker int8_t numColors;
558*d57664e9SAndroid Build Coastguard Worker int numRows;
559*d57664e9SAndroid Build Coastguard Worker int numCols;
560*d57664e9SAndroid Build Coastguard Worker int top;
561*d57664e9SAndroid Build Coastguard Worker int left;
562*d57664e9SAndroid Build Coastguard Worker int right;
563*d57664e9SAndroid Build Coastguard Worker int bottom;
564*d57664e9SAndroid Build Coastguard Worker memset(xDivs, -1, maxSizeXDivs);
565*d57664e9SAndroid Build Coastguard Worker memset(yDivs, -1, maxSizeYDivs);
566*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
567*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
568*d57664e9SAndroid Build Coastguard Worker
569*d57664e9SAndroid Build Coastguard Worker image->layoutBoundsLeft = image->layoutBoundsRight =
570*d57664e9SAndroid Build Coastguard Worker image->layoutBoundsTop = image->layoutBoundsBottom = 0;
571*d57664e9SAndroid Build Coastguard Worker
572*d57664e9SAndroid Build Coastguard Worker png_bytep p = image->rows[0];
573*d57664e9SAndroid Build Coastguard Worker bool transparent = p[3] == 0;
574*d57664e9SAndroid Build Coastguard Worker bool hasColor = false;
575*d57664e9SAndroid Build Coastguard Worker
576*d57664e9SAndroid Build Coastguard Worker const char* errorMsg = NULL;
577*d57664e9SAndroid Build Coastguard Worker int errorPixel = -1;
578*d57664e9SAndroid Build Coastguard Worker const char* errorEdge = NULL;
579*d57664e9SAndroid Build Coastguard Worker
580*d57664e9SAndroid Build Coastguard Worker int colorIndex = 0;
581*d57664e9SAndroid Build Coastguard Worker
582*d57664e9SAndroid Build Coastguard Worker // Validate size...
583*d57664e9SAndroid Build Coastguard Worker if (W < 3 || H < 3) {
584*d57664e9SAndroid Build Coastguard Worker errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
585*d57664e9SAndroid Build Coastguard Worker goto getout;
586*d57664e9SAndroid Build Coastguard Worker }
587*d57664e9SAndroid Build Coastguard Worker
588*d57664e9SAndroid Build Coastguard Worker // Validate frame...
589*d57664e9SAndroid Build Coastguard Worker if (!transparent &&
590*d57664e9SAndroid Build Coastguard Worker (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
591*d57664e9SAndroid Build Coastguard Worker errorMsg = "Must have one-pixel frame that is either transparent or white";
592*d57664e9SAndroid Build Coastguard Worker goto getout;
593*d57664e9SAndroid Build Coastguard Worker }
594*d57664e9SAndroid Build Coastguard Worker
595*d57664e9SAndroid Build Coastguard Worker // Find left and right of sizing areas...
596*d57664e9SAndroid Build Coastguard Worker if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
597*d57664e9SAndroid Build Coastguard Worker &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
598*d57664e9SAndroid Build Coastguard Worker errorPixel = xDivs[0];
599*d57664e9SAndroid Build Coastguard Worker errorEdge = "top";
600*d57664e9SAndroid Build Coastguard Worker goto getout;
601*d57664e9SAndroid Build Coastguard Worker }
602*d57664e9SAndroid Build Coastguard Worker
603*d57664e9SAndroid Build Coastguard Worker // Find top and bottom of sizing areas...
604*d57664e9SAndroid Build Coastguard Worker if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
605*d57664e9SAndroid Build Coastguard Worker &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
606*d57664e9SAndroid Build Coastguard Worker errorPixel = yDivs[0];
607*d57664e9SAndroid Build Coastguard Worker errorEdge = "left";
608*d57664e9SAndroid Build Coastguard Worker goto getout;
609*d57664e9SAndroid Build Coastguard Worker }
610*d57664e9SAndroid Build Coastguard Worker
611*d57664e9SAndroid Build Coastguard Worker // Copy patch size data into image...
612*d57664e9SAndroid Build Coastguard Worker image->info9Patch.numXDivs = numXDivs;
613*d57664e9SAndroid Build Coastguard Worker image->info9Patch.numYDivs = numYDivs;
614*d57664e9SAndroid Build Coastguard Worker
615*d57664e9SAndroid Build Coastguard Worker // Find left and right of padding area...
616*d57664e9SAndroid Build Coastguard Worker if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
617*d57664e9SAndroid Build Coastguard Worker &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
618*d57664e9SAndroid Build Coastguard Worker errorPixel = image->info9Patch.paddingLeft;
619*d57664e9SAndroid Build Coastguard Worker errorEdge = "bottom";
620*d57664e9SAndroid Build Coastguard Worker goto getout;
621*d57664e9SAndroid Build Coastguard Worker }
622*d57664e9SAndroid Build Coastguard Worker
623*d57664e9SAndroid Build Coastguard Worker // Find top and bottom of padding area...
624*d57664e9SAndroid Build Coastguard Worker if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
625*d57664e9SAndroid Build Coastguard Worker &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
626*d57664e9SAndroid Build Coastguard Worker errorPixel = image->info9Patch.paddingTop;
627*d57664e9SAndroid Build Coastguard Worker errorEdge = "right";
628*d57664e9SAndroid Build Coastguard Worker goto getout;
629*d57664e9SAndroid Build Coastguard Worker }
630*d57664e9SAndroid Build Coastguard Worker
631*d57664e9SAndroid Build Coastguard Worker // Find left and right of layout padding...
632*d57664e9SAndroid Build Coastguard Worker get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
633*d57664e9SAndroid Build Coastguard Worker &image->layoutBoundsLeft,
634*d57664e9SAndroid Build Coastguard Worker &image->layoutBoundsRight, &errorMsg);
635*d57664e9SAndroid Build Coastguard Worker
636*d57664e9SAndroid Build Coastguard Worker get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
637*d57664e9SAndroid Build Coastguard Worker &image->layoutBoundsTop,
638*d57664e9SAndroid Build Coastguard Worker &image->layoutBoundsBottom, &errorMsg);
639*d57664e9SAndroid Build Coastguard Worker
640*d57664e9SAndroid Build Coastguard Worker image->haveLayoutBounds = image->layoutBoundsLeft != 0
641*d57664e9SAndroid Build Coastguard Worker || image->layoutBoundsRight != 0
642*d57664e9SAndroid Build Coastguard Worker || image->layoutBoundsTop != 0
643*d57664e9SAndroid Build Coastguard Worker || image->layoutBoundsBottom != 0;
644*d57664e9SAndroid Build Coastguard Worker
645*d57664e9SAndroid Build Coastguard Worker if (image->haveLayoutBounds) {
646*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
647*d57664e9SAndroid Build Coastguard Worker printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
648*d57664e9SAndroid Build Coastguard Worker image->layoutBoundsRight, image->layoutBoundsBottom);
649*d57664e9SAndroid Build Coastguard Worker }
650*d57664e9SAndroid Build Coastguard Worker }
651*d57664e9SAndroid Build Coastguard Worker
652*d57664e9SAndroid Build Coastguard Worker // use opacity of pixels to estimate the round rect outline
653*d57664e9SAndroid Build Coastguard Worker get_outline(image);
654*d57664e9SAndroid Build Coastguard Worker
655*d57664e9SAndroid Build Coastguard Worker // If padding is not yet specified, take values from size.
656*d57664e9SAndroid Build Coastguard Worker if (image->info9Patch.paddingLeft < 0) {
657*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingLeft = xDivs[0];
658*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingRight = W - 2 - xDivs[1];
659*d57664e9SAndroid Build Coastguard Worker } else {
660*d57664e9SAndroid Build Coastguard Worker // Adjust value to be correct!
661*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
662*d57664e9SAndroid Build Coastguard Worker }
663*d57664e9SAndroid Build Coastguard Worker if (image->info9Patch.paddingTop < 0) {
664*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingTop = yDivs[0];
665*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingBottom = H - 2 - yDivs[1];
666*d57664e9SAndroid Build Coastguard Worker } else {
667*d57664e9SAndroid Build Coastguard Worker // Adjust value to be correct!
668*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
669*d57664e9SAndroid Build Coastguard Worker }
670*d57664e9SAndroid Build Coastguard Worker
671*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
672*d57664e9SAndroid Build Coastguard Worker printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
673*d57664e9SAndroid Build Coastguard Worker xDivs[0], xDivs[1],
674*d57664e9SAndroid Build Coastguard Worker yDivs[0], yDivs[1]);
675*d57664e9SAndroid Build Coastguard Worker printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
676*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
677*d57664e9SAndroid Build Coastguard Worker image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
678*d57664e9SAndroid Build Coastguard Worker }
679*d57664e9SAndroid Build Coastguard Worker
680*d57664e9SAndroid Build Coastguard Worker // Remove frame from image.
681*d57664e9SAndroid Build Coastguard Worker image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep));
682*d57664e9SAndroid Build Coastguard Worker for (i=0; i<(H-2); i++) {
683*d57664e9SAndroid Build Coastguard Worker image->rows[i] = image->allocRows[i+1];
684*d57664e9SAndroid Build Coastguard Worker memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
685*d57664e9SAndroid Build Coastguard Worker }
686*d57664e9SAndroid Build Coastguard Worker image->width -= 2;
687*d57664e9SAndroid Build Coastguard Worker W = image->width;
688*d57664e9SAndroid Build Coastguard Worker image->height -= 2;
689*d57664e9SAndroid Build Coastguard Worker H = image->height;
690*d57664e9SAndroid Build Coastguard Worker
691*d57664e9SAndroid Build Coastguard Worker // Figure out the number of rows and columns in the N-patch
692*d57664e9SAndroid Build Coastguard Worker numCols = numXDivs + 1;
693*d57664e9SAndroid Build Coastguard Worker if (xDivs[0] == 0) { // Column 1 is strechable
694*d57664e9SAndroid Build Coastguard Worker numCols--;
695*d57664e9SAndroid Build Coastguard Worker }
696*d57664e9SAndroid Build Coastguard Worker if (xDivs[numXDivs - 1] == W) {
697*d57664e9SAndroid Build Coastguard Worker numCols--;
698*d57664e9SAndroid Build Coastguard Worker }
699*d57664e9SAndroid Build Coastguard Worker numRows = numYDivs + 1;
700*d57664e9SAndroid Build Coastguard Worker if (yDivs[0] == 0) { // Row 1 is strechable
701*d57664e9SAndroid Build Coastguard Worker numRows--;
702*d57664e9SAndroid Build Coastguard Worker }
703*d57664e9SAndroid Build Coastguard Worker if (yDivs[numYDivs - 1] == H) {
704*d57664e9SAndroid Build Coastguard Worker numRows--;
705*d57664e9SAndroid Build Coastguard Worker }
706*d57664e9SAndroid Build Coastguard Worker
707*d57664e9SAndroid Build Coastguard Worker // Make sure the amount of rows and columns will fit in the number of
708*d57664e9SAndroid Build Coastguard Worker // colors we can use in the 9-patch format.
709*d57664e9SAndroid Build Coastguard Worker if (numRows * numCols > 0x7F) {
710*d57664e9SAndroid Build Coastguard Worker errorMsg = "Too many rows and columns in 9-patch perimeter";
711*d57664e9SAndroid Build Coastguard Worker goto getout;
712*d57664e9SAndroid Build Coastguard Worker }
713*d57664e9SAndroid Build Coastguard Worker
714*d57664e9SAndroid Build Coastguard Worker numColors = numRows * numCols;
715*d57664e9SAndroid Build Coastguard Worker image->info9Patch.numColors = numColors;
716*d57664e9SAndroid Build Coastguard Worker image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
717*d57664e9SAndroid Build Coastguard Worker
718*d57664e9SAndroid Build Coastguard Worker // Fill in color information for each patch.
719*d57664e9SAndroid Build Coastguard Worker
720*d57664e9SAndroid Build Coastguard Worker uint32_t c;
721*d57664e9SAndroid Build Coastguard Worker top = 0;
722*d57664e9SAndroid Build Coastguard Worker
723*d57664e9SAndroid Build Coastguard Worker // The first row always starts with the top being at y=0 and the bottom
724*d57664e9SAndroid Build Coastguard Worker // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
725*d57664e9SAndroid Build Coastguard Worker // the first row is stretchable along the Y axis, otherwise it is fixed.
726*d57664e9SAndroid Build Coastguard Worker // The last row always ends with the bottom being bitmap.height and the top
727*d57664e9SAndroid Build Coastguard Worker // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
728*d57664e9SAndroid Build Coastguard Worker // yDivs[numYDivs-1]. In the former case the last row is stretchable along
729*d57664e9SAndroid Build Coastguard Worker // the Y axis, otherwise it is fixed.
730*d57664e9SAndroid Build Coastguard Worker //
731*d57664e9SAndroid Build Coastguard Worker // The first and last columns are similarly treated with respect to the X
732*d57664e9SAndroid Build Coastguard Worker // axis.
733*d57664e9SAndroid Build Coastguard Worker //
734*d57664e9SAndroid Build Coastguard Worker // The above is to help explain some of the special casing that goes on the
735*d57664e9SAndroid Build Coastguard Worker // code below.
736*d57664e9SAndroid Build Coastguard Worker
737*d57664e9SAndroid Build Coastguard Worker // The initial yDiv and whether the first row is considered stretchable or
738*d57664e9SAndroid Build Coastguard Worker // not depends on whether yDiv[0] was zero or not.
739*d57664e9SAndroid Build Coastguard Worker for (j = (yDivs[0] == 0 ? 1 : 0);
740*d57664e9SAndroid Build Coastguard Worker j <= numYDivs && top < H;
741*d57664e9SAndroid Build Coastguard Worker j++) {
742*d57664e9SAndroid Build Coastguard Worker if (j == numYDivs) {
743*d57664e9SAndroid Build Coastguard Worker bottom = H;
744*d57664e9SAndroid Build Coastguard Worker } else {
745*d57664e9SAndroid Build Coastguard Worker bottom = yDivs[j];
746*d57664e9SAndroid Build Coastguard Worker }
747*d57664e9SAndroid Build Coastguard Worker left = 0;
748*d57664e9SAndroid Build Coastguard Worker // The initial xDiv and whether the first column is considered
749*d57664e9SAndroid Build Coastguard Worker // stretchable or not depends on whether xDiv[0] was zero or not.
750*d57664e9SAndroid Build Coastguard Worker for (i = xDivs[0] == 0 ? 1 : 0;
751*d57664e9SAndroid Build Coastguard Worker i <= numXDivs && left < W;
752*d57664e9SAndroid Build Coastguard Worker i++) {
753*d57664e9SAndroid Build Coastguard Worker if (i == numXDivs) {
754*d57664e9SAndroid Build Coastguard Worker right = W;
755*d57664e9SAndroid Build Coastguard Worker } else {
756*d57664e9SAndroid Build Coastguard Worker right = xDivs[i];
757*d57664e9SAndroid Build Coastguard Worker }
758*d57664e9SAndroid Build Coastguard Worker c = get_color(image->rows, left, top, right - 1, bottom - 1);
759*d57664e9SAndroid Build Coastguard Worker image->colors[colorIndex++] = c;
760*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
761*d57664e9SAndroid Build Coastguard Worker if (c != Res_png_9patch::NO_COLOR)
762*d57664e9SAndroid Build Coastguard Worker hasColor = true;
763*d57664e9SAndroid Build Coastguard Worker }
764*d57664e9SAndroid Build Coastguard Worker left = right;
765*d57664e9SAndroid Build Coastguard Worker }
766*d57664e9SAndroid Build Coastguard Worker top = bottom;
767*d57664e9SAndroid Build Coastguard Worker }
768*d57664e9SAndroid Build Coastguard Worker
769*d57664e9SAndroid Build Coastguard Worker assert(colorIndex == numColors);
770*d57664e9SAndroid Build Coastguard Worker
771*d57664e9SAndroid Build Coastguard Worker for (i=0; i<numColors; i++) {
772*d57664e9SAndroid Build Coastguard Worker if (hasColor) {
773*d57664e9SAndroid Build Coastguard Worker if (i == 0) printf("Colors in %s:\n ", imageName);
774*d57664e9SAndroid Build Coastguard Worker printf(" #%08x", image->colors[i]);
775*d57664e9SAndroid Build Coastguard Worker if (i == numColors - 1) printf("\n");
776*d57664e9SAndroid Build Coastguard Worker }
777*d57664e9SAndroid Build Coastguard Worker }
778*d57664e9SAndroid Build Coastguard Worker getout:
779*d57664e9SAndroid Build Coastguard Worker if (errorMsg) {
780*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
781*d57664e9SAndroid Build Coastguard Worker "ERROR: 9-patch image %s malformed.\n"
782*d57664e9SAndroid Build Coastguard Worker " %s.\n", imageName, errorMsg);
783*d57664e9SAndroid Build Coastguard Worker if (errorEdge != NULL) {
784*d57664e9SAndroid Build Coastguard Worker if (errorPixel >= 0) {
785*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
786*d57664e9SAndroid Build Coastguard Worker " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
787*d57664e9SAndroid Build Coastguard Worker } else {
788*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
789*d57664e9SAndroid Build Coastguard Worker " Found along %s edge.\n", errorEdge);
790*d57664e9SAndroid Build Coastguard Worker }
791*d57664e9SAndroid Build Coastguard Worker }
792*d57664e9SAndroid Build Coastguard Worker return UNKNOWN_ERROR;
793*d57664e9SAndroid Build Coastguard Worker }
794*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
795*d57664e9SAndroid Build Coastguard Worker }
796*d57664e9SAndroid Build Coastguard Worker
checkNinePatchSerialization(Res_png_9patch * inPatch,void * data)797*d57664e9SAndroid Build Coastguard Worker static void checkNinePatchSerialization(Res_png_9patch* inPatch, void* data)
798*d57664e9SAndroid Build Coastguard Worker {
799*d57664e9SAndroid Build Coastguard Worker size_t patchSize = inPatch->serializedSize();
800*d57664e9SAndroid Build Coastguard Worker void* newData = malloc(patchSize);
801*d57664e9SAndroid Build Coastguard Worker memcpy(newData, data, patchSize);
802*d57664e9SAndroid Build Coastguard Worker Res_png_9patch* outPatch = inPatch->deserialize(newData);
803*d57664e9SAndroid Build Coastguard Worker // deserialization is done in place, so outPatch == newData
804*d57664e9SAndroid Build Coastguard Worker assert(outPatch == newData);
805*d57664e9SAndroid Build Coastguard Worker assert(outPatch->numXDivs == inPatch->numXDivs);
806*d57664e9SAndroid Build Coastguard Worker assert(outPatch->numYDivs == inPatch->numYDivs);
807*d57664e9SAndroid Build Coastguard Worker assert(outPatch->paddingLeft == inPatch->paddingLeft);
808*d57664e9SAndroid Build Coastguard Worker assert(outPatch->paddingRight == inPatch->paddingRight);
809*d57664e9SAndroid Build Coastguard Worker assert(outPatch->paddingTop == inPatch->paddingTop);
810*d57664e9SAndroid Build Coastguard Worker assert(outPatch->paddingBottom == inPatch->paddingBottom);
811*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < outPatch->numXDivs; i++) {
812*d57664e9SAndroid Build Coastguard Worker assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
813*d57664e9SAndroid Build Coastguard Worker }
814*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < outPatch->numYDivs; i++) {
815*d57664e9SAndroid Build Coastguard Worker assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
816*d57664e9SAndroid Build Coastguard Worker }
817*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < outPatch->numColors; i++) {
818*d57664e9SAndroid Build Coastguard Worker assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
819*d57664e9SAndroid Build Coastguard Worker }
820*d57664e9SAndroid Build Coastguard Worker free(newData);
821*d57664e9SAndroid Build Coastguard Worker }
822*d57664e9SAndroid Build Coastguard Worker
dump_image(int w,int h,png_bytepp rows,int color_type)823*d57664e9SAndroid Build Coastguard Worker static void dump_image(int w, int h, png_bytepp rows, int color_type)
824*d57664e9SAndroid Build Coastguard Worker {
825*d57664e9SAndroid Build Coastguard Worker int i, j, rr, gg, bb, aa;
826*d57664e9SAndroid Build Coastguard Worker
827*d57664e9SAndroid Build Coastguard Worker int bpp;
828*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
829*d57664e9SAndroid Build Coastguard Worker bpp = 1;
830*d57664e9SAndroid Build Coastguard Worker } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
831*d57664e9SAndroid Build Coastguard Worker bpp = 2;
832*d57664e9SAndroid Build Coastguard Worker } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
833*d57664e9SAndroid Build Coastguard Worker // We use a padding byte even when there is no alpha
834*d57664e9SAndroid Build Coastguard Worker bpp = 4;
835*d57664e9SAndroid Build Coastguard Worker } else {
836*d57664e9SAndroid Build Coastguard Worker printf("Unknown color type %d.\n", color_type);
837*d57664e9SAndroid Build Coastguard Worker return;
838*d57664e9SAndroid Build Coastguard Worker }
839*d57664e9SAndroid Build Coastguard Worker
840*d57664e9SAndroid Build Coastguard Worker for (j = 0; j < h; j++) {
841*d57664e9SAndroid Build Coastguard Worker png_bytep row = rows[j];
842*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < w; i++) {
843*d57664e9SAndroid Build Coastguard Worker rr = row[0];
844*d57664e9SAndroid Build Coastguard Worker gg = row[1];
845*d57664e9SAndroid Build Coastguard Worker bb = row[2];
846*d57664e9SAndroid Build Coastguard Worker aa = row[3];
847*d57664e9SAndroid Build Coastguard Worker row += bpp;
848*d57664e9SAndroid Build Coastguard Worker
849*d57664e9SAndroid Build Coastguard Worker if (i == 0) {
850*d57664e9SAndroid Build Coastguard Worker printf("Row %d:", j);
851*d57664e9SAndroid Build Coastguard Worker }
852*d57664e9SAndroid Build Coastguard Worker switch (bpp) {
853*d57664e9SAndroid Build Coastguard Worker case 1:
854*d57664e9SAndroid Build Coastguard Worker printf(" (%d)", rr);
855*d57664e9SAndroid Build Coastguard Worker break;
856*d57664e9SAndroid Build Coastguard Worker case 2:
857*d57664e9SAndroid Build Coastguard Worker printf(" (%d %d", rr, gg);
858*d57664e9SAndroid Build Coastguard Worker break;
859*d57664e9SAndroid Build Coastguard Worker case 3:
860*d57664e9SAndroid Build Coastguard Worker printf(" (%d %d %d)", rr, gg, bb);
861*d57664e9SAndroid Build Coastguard Worker break;
862*d57664e9SAndroid Build Coastguard Worker case 4:
863*d57664e9SAndroid Build Coastguard Worker printf(" (%d %d %d %d)", rr, gg, bb, aa);
864*d57664e9SAndroid Build Coastguard Worker break;
865*d57664e9SAndroid Build Coastguard Worker }
866*d57664e9SAndroid Build Coastguard Worker if (i == (w - 1)) {
867*d57664e9SAndroid Build Coastguard Worker printf("\n");
868*d57664e9SAndroid Build Coastguard Worker }
869*d57664e9SAndroid Build Coastguard Worker }
870*d57664e9SAndroid Build Coastguard Worker }
871*d57664e9SAndroid Build Coastguard Worker }
872*d57664e9SAndroid Build Coastguard Worker
873*d57664e9SAndroid Build Coastguard Worker #define MAX(a,b) ((a)>(b)?(a):(b))
874*d57664e9SAndroid Build Coastguard Worker #define ABS(a) ((a)<0?-(a):(a))
875*d57664e9SAndroid Build Coastguard Worker
analyze_image(const char * imageName,image_info & imageInfo,int grayscaleTolerance,png_colorp rgbPalette,png_bytep alphaPalette,int * paletteEntries,int * alphaPaletteEntries,bool * hasTransparency,int * colorType,png_bytepp outRows)876*d57664e9SAndroid Build Coastguard Worker static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
877*d57664e9SAndroid Build Coastguard Worker png_colorp rgbPalette, png_bytep alphaPalette,
878*d57664e9SAndroid Build Coastguard Worker int *paletteEntries, int *alphaPaletteEntries, bool *hasTransparency,
879*d57664e9SAndroid Build Coastguard Worker int *colorType, png_bytepp outRows)
880*d57664e9SAndroid Build Coastguard Worker {
881*d57664e9SAndroid Build Coastguard Worker int w = imageInfo.width;
882*d57664e9SAndroid Build Coastguard Worker int h = imageInfo.height;
883*d57664e9SAndroid Build Coastguard Worker int i, j, rr, gg, bb, aa, idx;;
884*d57664e9SAndroid Build Coastguard Worker uint32_t opaqueColors[256], alphaColors[256];
885*d57664e9SAndroid Build Coastguard Worker uint32_t col;
886*d57664e9SAndroid Build Coastguard Worker int numOpaqueColors = 0, numAlphaColors = 0;
887*d57664e9SAndroid Build Coastguard Worker int maxGrayDeviation = 0;
888*d57664e9SAndroid Build Coastguard Worker
889*d57664e9SAndroid Build Coastguard Worker bool isOpaque = true;
890*d57664e9SAndroid Build Coastguard Worker bool isPalette = true;
891*d57664e9SAndroid Build Coastguard Worker bool isGrayscale = true;
892*d57664e9SAndroid Build Coastguard Worker
893*d57664e9SAndroid Build Coastguard Worker // Scan the entire image and determine if:
894*d57664e9SAndroid Build Coastguard Worker // 1. Every pixel has R == G == B (grayscale)
895*d57664e9SAndroid Build Coastguard Worker // 2. Every pixel has A == 255 (opaque)
896*d57664e9SAndroid Build Coastguard Worker // 3. There are no more than 256 distinct RGBA colors
897*d57664e9SAndroid Build Coastguard Worker // We will track opaque colors separately from colors with
898*d57664e9SAndroid Build Coastguard Worker // alpha. This allows us to reencode the color table more
899*d57664e9SAndroid Build Coastguard Worker // efficiently (color tables entries without a corresponding
900*d57664e9SAndroid Build Coastguard Worker // alpha value are assumed to be opaque).
901*d57664e9SAndroid Build Coastguard Worker
902*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
903*d57664e9SAndroid Build Coastguard Worker printf("Initial image data:\n");
904*d57664e9SAndroid Build Coastguard Worker dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
905*d57664e9SAndroid Build Coastguard Worker }
906*d57664e9SAndroid Build Coastguard Worker
907*d57664e9SAndroid Build Coastguard Worker for (j = 0; j < h; j++) {
908*d57664e9SAndroid Build Coastguard Worker png_bytep row = imageInfo.rows[j];
909*d57664e9SAndroid Build Coastguard Worker png_bytep out = outRows[j];
910*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < w; i++) {
911*d57664e9SAndroid Build Coastguard Worker
912*d57664e9SAndroid Build Coastguard Worker // Make sure any zero alpha pixels are fully zeroed. On average,
913*d57664e9SAndroid Build Coastguard Worker // each of our PNG assets seem to have about four distinct pixels
914*d57664e9SAndroid Build Coastguard Worker // with zero alpha.
915*d57664e9SAndroid Build Coastguard Worker // There are several advantages to setting these to zero:
916*d57664e9SAndroid Build Coastguard Worker // (1) Images are more likely able to be encodable with a palette.
917*d57664e9SAndroid Build Coastguard Worker // (2) Image palettes will be smaller.
918*d57664e9SAndroid Build Coastguard Worker // (3) Premultiplied and unpremultiplied PNG decodes can skip
919*d57664e9SAndroid Build Coastguard Worker // writing zeros to memory, often saving significant numbers
920*d57664e9SAndroid Build Coastguard Worker // of memory pages.
921*d57664e9SAndroid Build Coastguard Worker aa = *(row + 3);
922*d57664e9SAndroid Build Coastguard Worker if (aa == 0) {
923*d57664e9SAndroid Build Coastguard Worker rr = 0;
924*d57664e9SAndroid Build Coastguard Worker gg = 0;
925*d57664e9SAndroid Build Coastguard Worker bb = 0;
926*d57664e9SAndroid Build Coastguard Worker
927*d57664e9SAndroid Build Coastguard Worker // Also set red, green, and blue to zero in "row". If we later
928*d57664e9SAndroid Build Coastguard Worker // decide to encode the PNG as RGB or RGBA, we will use the
929*d57664e9SAndroid Build Coastguard Worker // values stored there.
930*d57664e9SAndroid Build Coastguard Worker *(row) = 0;
931*d57664e9SAndroid Build Coastguard Worker *(row + 1) = 0;
932*d57664e9SAndroid Build Coastguard Worker *(row + 2) = 0;
933*d57664e9SAndroid Build Coastguard Worker } else {
934*d57664e9SAndroid Build Coastguard Worker rr = *(row);
935*d57664e9SAndroid Build Coastguard Worker gg = *(row + 1);
936*d57664e9SAndroid Build Coastguard Worker bb = *(row + 2);
937*d57664e9SAndroid Build Coastguard Worker }
938*d57664e9SAndroid Build Coastguard Worker row += 4;
939*d57664e9SAndroid Build Coastguard Worker
940*d57664e9SAndroid Build Coastguard Worker int odev = maxGrayDeviation;
941*d57664e9SAndroid Build Coastguard Worker maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
942*d57664e9SAndroid Build Coastguard Worker maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
943*d57664e9SAndroid Build Coastguard Worker maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
944*d57664e9SAndroid Build Coastguard Worker if (maxGrayDeviation > odev) {
945*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
946*d57664e9SAndroid Build Coastguard Worker printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
947*d57664e9SAndroid Build Coastguard Worker maxGrayDeviation, i, j, rr, gg, bb, aa);
948*d57664e9SAndroid Build Coastguard Worker }
949*d57664e9SAndroid Build Coastguard Worker }
950*d57664e9SAndroid Build Coastguard Worker
951*d57664e9SAndroid Build Coastguard Worker // Check if image is really grayscale
952*d57664e9SAndroid Build Coastguard Worker if (isGrayscale) {
953*d57664e9SAndroid Build Coastguard Worker if (rr != gg || rr != bb) {
954*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
955*d57664e9SAndroid Build Coastguard Worker printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
956*d57664e9SAndroid Build Coastguard Worker i, j, rr, gg, bb, aa);
957*d57664e9SAndroid Build Coastguard Worker }
958*d57664e9SAndroid Build Coastguard Worker isGrayscale = false;
959*d57664e9SAndroid Build Coastguard Worker }
960*d57664e9SAndroid Build Coastguard Worker }
961*d57664e9SAndroid Build Coastguard Worker
962*d57664e9SAndroid Build Coastguard Worker // Check if image is really opaque
963*d57664e9SAndroid Build Coastguard Worker if (isOpaque) {
964*d57664e9SAndroid Build Coastguard Worker if (aa != 0xff) {
965*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
966*d57664e9SAndroid Build Coastguard Worker printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
967*d57664e9SAndroid Build Coastguard Worker i, j, rr, gg, bb, aa);
968*d57664e9SAndroid Build Coastguard Worker }
969*d57664e9SAndroid Build Coastguard Worker isOpaque = false;
970*d57664e9SAndroid Build Coastguard Worker }
971*d57664e9SAndroid Build Coastguard Worker }
972*d57664e9SAndroid Build Coastguard Worker
973*d57664e9SAndroid Build Coastguard Worker // Check if image is really <= 256 colors
974*d57664e9SAndroid Build Coastguard Worker if (isPalette) {
975*d57664e9SAndroid Build Coastguard Worker col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
976*d57664e9SAndroid Build Coastguard Worker bool match = false;
977*d57664e9SAndroid Build Coastguard Worker
978*d57664e9SAndroid Build Coastguard Worker if (aa == 0xff) {
979*d57664e9SAndroid Build Coastguard Worker for (idx = 0; idx < numOpaqueColors; idx++) {
980*d57664e9SAndroid Build Coastguard Worker if (opaqueColors[idx] == col) {
981*d57664e9SAndroid Build Coastguard Worker match = true;
982*d57664e9SAndroid Build Coastguard Worker break;
983*d57664e9SAndroid Build Coastguard Worker }
984*d57664e9SAndroid Build Coastguard Worker }
985*d57664e9SAndroid Build Coastguard Worker
986*d57664e9SAndroid Build Coastguard Worker if (!match) {
987*d57664e9SAndroid Build Coastguard Worker if (numOpaqueColors < 256) {
988*d57664e9SAndroid Build Coastguard Worker opaqueColors[numOpaqueColors] = col;
989*d57664e9SAndroid Build Coastguard Worker }
990*d57664e9SAndroid Build Coastguard Worker numOpaqueColors++;
991*d57664e9SAndroid Build Coastguard Worker }
992*d57664e9SAndroid Build Coastguard Worker
993*d57664e9SAndroid Build Coastguard Worker // Write the palette index for the pixel to outRows optimistically.
994*d57664e9SAndroid Build Coastguard Worker // We might overwrite it later if we decide to encode as gray or
995*d57664e9SAndroid Build Coastguard Worker // gray + alpha. We may also need to overwrite it when we combine
996*d57664e9SAndroid Build Coastguard Worker // into a single palette.
997*d57664e9SAndroid Build Coastguard Worker *out++ = idx;
998*d57664e9SAndroid Build Coastguard Worker } else {
999*d57664e9SAndroid Build Coastguard Worker for (idx = 0; idx < numAlphaColors; idx++) {
1000*d57664e9SAndroid Build Coastguard Worker if (alphaColors[idx] == col) {
1001*d57664e9SAndroid Build Coastguard Worker match = true;
1002*d57664e9SAndroid Build Coastguard Worker break;
1003*d57664e9SAndroid Build Coastguard Worker }
1004*d57664e9SAndroid Build Coastguard Worker }
1005*d57664e9SAndroid Build Coastguard Worker
1006*d57664e9SAndroid Build Coastguard Worker if (!match) {
1007*d57664e9SAndroid Build Coastguard Worker if (numAlphaColors < 256) {
1008*d57664e9SAndroid Build Coastguard Worker alphaColors[numAlphaColors] = col;
1009*d57664e9SAndroid Build Coastguard Worker }
1010*d57664e9SAndroid Build Coastguard Worker numAlphaColors++;
1011*d57664e9SAndroid Build Coastguard Worker }
1012*d57664e9SAndroid Build Coastguard Worker
1013*d57664e9SAndroid Build Coastguard Worker // Write the palette index for the pixel to outRows optimistically.
1014*d57664e9SAndroid Build Coastguard Worker // We might overwrite it later if we decide to encode as gray or
1015*d57664e9SAndroid Build Coastguard Worker // gray + alpha.
1016*d57664e9SAndroid Build Coastguard Worker *out++ = idx;
1017*d57664e9SAndroid Build Coastguard Worker }
1018*d57664e9SAndroid Build Coastguard Worker
1019*d57664e9SAndroid Build Coastguard Worker if (numOpaqueColors + numAlphaColors > 256) {
1020*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1021*d57664e9SAndroid Build Coastguard Worker printf("Found 257th color at %d, %d\n", i, j);
1022*d57664e9SAndroid Build Coastguard Worker }
1023*d57664e9SAndroid Build Coastguard Worker isPalette = false;
1024*d57664e9SAndroid Build Coastguard Worker }
1025*d57664e9SAndroid Build Coastguard Worker }
1026*d57664e9SAndroid Build Coastguard Worker }
1027*d57664e9SAndroid Build Coastguard Worker }
1028*d57664e9SAndroid Build Coastguard Worker
1029*d57664e9SAndroid Build Coastguard Worker // If we decide to encode the image using a palette, we will reset these counts
1030*d57664e9SAndroid Build Coastguard Worker // to the appropriate values later. Initializing them here avoids compiler
1031*d57664e9SAndroid Build Coastguard Worker // complaints about uses of possibly uninitialized variables.
1032*d57664e9SAndroid Build Coastguard Worker *paletteEntries = 0;
1033*d57664e9SAndroid Build Coastguard Worker *alphaPaletteEntries = 0;
1034*d57664e9SAndroid Build Coastguard Worker
1035*d57664e9SAndroid Build Coastguard Worker *hasTransparency = !isOpaque;
1036*d57664e9SAndroid Build Coastguard Worker int paletteSize = w * h + 3 * numOpaqueColors + 4 * numAlphaColors;
1037*d57664e9SAndroid Build Coastguard Worker
1038*d57664e9SAndroid Build Coastguard Worker int bpp = isOpaque ? 3 : 4;
1039*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1040*d57664e9SAndroid Build Coastguard Worker printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
1041*d57664e9SAndroid Build Coastguard Worker printf("isOpaque = %s\n", isOpaque ? "true" : "false");
1042*d57664e9SAndroid Build Coastguard Worker printf("isPalette = %s\n", isPalette ? "true" : "false");
1043*d57664e9SAndroid Build Coastguard Worker printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
1044*d57664e9SAndroid Build Coastguard Worker paletteSize, 2 * w * h, bpp * w * h);
1045*d57664e9SAndroid Build Coastguard Worker printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
1046*d57664e9SAndroid Build Coastguard Worker }
1047*d57664e9SAndroid Build Coastguard Worker
1048*d57664e9SAndroid Build Coastguard Worker // Choose the best color type for the image.
1049*d57664e9SAndroid Build Coastguard Worker // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
1050*d57664e9SAndroid Build Coastguard Worker // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
1051*d57664e9SAndroid Build Coastguard Worker // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
1052*d57664e9SAndroid Build Coastguard Worker // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
1053*d57664e9SAndroid Build Coastguard Worker // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
1054*d57664e9SAndroid Build Coastguard Worker if (isGrayscale) {
1055*d57664e9SAndroid Build Coastguard Worker if (isOpaque) {
1056*d57664e9SAndroid Build Coastguard Worker *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
1057*d57664e9SAndroid Build Coastguard Worker } else {
1058*d57664e9SAndroid Build Coastguard Worker // Use a simple heuristic to determine whether using a palette will
1059*d57664e9SAndroid Build Coastguard Worker // save space versus using gray + alpha for each pixel.
1060*d57664e9SAndroid Build Coastguard Worker // This doesn't take into account chunk overhead, filtering, LZ
1061*d57664e9SAndroid Build Coastguard Worker // compression, etc.
1062*d57664e9SAndroid Build Coastguard Worker if (isPalette && (paletteSize < 2 * w * h)) {
1063*d57664e9SAndroid Build Coastguard Worker *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
1064*d57664e9SAndroid Build Coastguard Worker } else {
1065*d57664e9SAndroid Build Coastguard Worker *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
1066*d57664e9SAndroid Build Coastguard Worker }
1067*d57664e9SAndroid Build Coastguard Worker }
1068*d57664e9SAndroid Build Coastguard Worker } else if (isPalette && (paletteSize < bpp * w * h)) {
1069*d57664e9SAndroid Build Coastguard Worker *colorType = PNG_COLOR_TYPE_PALETTE;
1070*d57664e9SAndroid Build Coastguard Worker } else {
1071*d57664e9SAndroid Build Coastguard Worker if (maxGrayDeviation <= grayscaleTolerance) {
1072*d57664e9SAndroid Build Coastguard Worker printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
1073*d57664e9SAndroid Build Coastguard Worker *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
1074*d57664e9SAndroid Build Coastguard Worker } else {
1075*d57664e9SAndroid Build Coastguard Worker *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
1076*d57664e9SAndroid Build Coastguard Worker }
1077*d57664e9SAndroid Build Coastguard Worker }
1078*d57664e9SAndroid Build Coastguard Worker
1079*d57664e9SAndroid Build Coastguard Worker // Perform postprocessing of the image or palette data based on the final
1080*d57664e9SAndroid Build Coastguard Worker // color type chosen
1081*d57664e9SAndroid Build Coastguard Worker
1082*d57664e9SAndroid Build Coastguard Worker if (*colorType == PNG_COLOR_TYPE_PALETTE) {
1083*d57664e9SAndroid Build Coastguard Worker // Combine the alphaColors and the opaqueColors into a single palette.
1084*d57664e9SAndroid Build Coastguard Worker // The alphaColors must be at the start of the palette.
1085*d57664e9SAndroid Build Coastguard Worker uint32_t* colors = alphaColors;
1086*d57664e9SAndroid Build Coastguard Worker memcpy(colors + numAlphaColors, opaqueColors, 4 * numOpaqueColors);
1087*d57664e9SAndroid Build Coastguard Worker
1088*d57664e9SAndroid Build Coastguard Worker // Fix the indices of the opaque colors in the image.
1089*d57664e9SAndroid Build Coastguard Worker for (j = 0; j < h; j++) {
1090*d57664e9SAndroid Build Coastguard Worker png_bytep row = imageInfo.rows[j];
1091*d57664e9SAndroid Build Coastguard Worker png_bytep out = outRows[j];
1092*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < w; i++) {
1093*d57664e9SAndroid Build Coastguard Worker uint32_t pixel = ((uint32_t*) row)[i];
1094*d57664e9SAndroid Build Coastguard Worker if (pixel >> 24 == 0xFF) {
1095*d57664e9SAndroid Build Coastguard Worker out[i] += numAlphaColors;
1096*d57664e9SAndroid Build Coastguard Worker }
1097*d57664e9SAndroid Build Coastguard Worker }
1098*d57664e9SAndroid Build Coastguard Worker }
1099*d57664e9SAndroid Build Coastguard Worker
1100*d57664e9SAndroid Build Coastguard Worker // Create separate RGB and Alpha palettes and set the number of colors
1101*d57664e9SAndroid Build Coastguard Worker int numColors = numOpaqueColors + numAlphaColors;
1102*d57664e9SAndroid Build Coastguard Worker *paletteEntries = numColors;
1103*d57664e9SAndroid Build Coastguard Worker *alphaPaletteEntries = numAlphaColors;
1104*d57664e9SAndroid Build Coastguard Worker
1105*d57664e9SAndroid Build Coastguard Worker // Create the RGB and alpha palettes
1106*d57664e9SAndroid Build Coastguard Worker for (int idx = 0; idx < numColors; idx++) {
1107*d57664e9SAndroid Build Coastguard Worker col = colors[idx];
1108*d57664e9SAndroid Build Coastguard Worker rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
1109*d57664e9SAndroid Build Coastguard Worker rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
1110*d57664e9SAndroid Build Coastguard Worker rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
1111*d57664e9SAndroid Build Coastguard Worker if (idx < numAlphaColors) {
1112*d57664e9SAndroid Build Coastguard Worker alphaPalette[idx] = (png_byte) (col & 0xff);
1113*d57664e9SAndroid Build Coastguard Worker }
1114*d57664e9SAndroid Build Coastguard Worker }
1115*d57664e9SAndroid Build Coastguard Worker } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
1116*d57664e9SAndroid Build Coastguard Worker // If the image is gray or gray + alpha, compact the pixels into outRows
1117*d57664e9SAndroid Build Coastguard Worker for (j = 0; j < h; j++) {
1118*d57664e9SAndroid Build Coastguard Worker png_bytep row = imageInfo.rows[j];
1119*d57664e9SAndroid Build Coastguard Worker png_bytep out = outRows[j];
1120*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < w; i++) {
1121*d57664e9SAndroid Build Coastguard Worker rr = *row++;
1122*d57664e9SAndroid Build Coastguard Worker gg = *row++;
1123*d57664e9SAndroid Build Coastguard Worker bb = *row++;
1124*d57664e9SAndroid Build Coastguard Worker aa = *row++;
1125*d57664e9SAndroid Build Coastguard Worker
1126*d57664e9SAndroid Build Coastguard Worker if (isGrayscale) {
1127*d57664e9SAndroid Build Coastguard Worker *out++ = rr;
1128*d57664e9SAndroid Build Coastguard Worker } else {
1129*d57664e9SAndroid Build Coastguard Worker *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
1130*d57664e9SAndroid Build Coastguard Worker }
1131*d57664e9SAndroid Build Coastguard Worker if (!isOpaque) {
1132*d57664e9SAndroid Build Coastguard Worker *out++ = aa;
1133*d57664e9SAndroid Build Coastguard Worker }
1134*d57664e9SAndroid Build Coastguard Worker }
1135*d57664e9SAndroid Build Coastguard Worker }
1136*d57664e9SAndroid Build Coastguard Worker }
1137*d57664e9SAndroid Build Coastguard Worker }
1138*d57664e9SAndroid Build Coastguard Worker
write_png(const char * imageName,png_structp write_ptr,png_infop write_info,image_info & imageInfo,const Bundle * bundle)1139*d57664e9SAndroid Build Coastguard Worker static void write_png(const char* imageName,
1140*d57664e9SAndroid Build Coastguard Worker png_structp write_ptr, png_infop write_info,
1141*d57664e9SAndroid Build Coastguard Worker image_info& imageInfo, const Bundle* bundle)
1142*d57664e9SAndroid Build Coastguard Worker {
1143*d57664e9SAndroid Build Coastguard Worker png_uint_32 width, height;
1144*d57664e9SAndroid Build Coastguard Worker int color_type;
1145*d57664e9SAndroid Build Coastguard Worker int bit_depth, interlace_type, compression_type;
1146*d57664e9SAndroid Build Coastguard Worker int i;
1147*d57664e9SAndroid Build Coastguard Worker
1148*d57664e9SAndroid Build Coastguard Worker png_unknown_chunk unknowns[3];
1149*d57664e9SAndroid Build Coastguard Worker unknowns[0].data = NULL;
1150*d57664e9SAndroid Build Coastguard Worker unknowns[1].data = NULL;
1151*d57664e9SAndroid Build Coastguard Worker unknowns[2].data = NULL;
1152*d57664e9SAndroid Build Coastguard Worker
1153*d57664e9SAndroid Build Coastguard Worker png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
1154*d57664e9SAndroid Build Coastguard Worker if (outRows == (png_bytepp) 0) {
1155*d57664e9SAndroid Build Coastguard Worker printf("Can't allocate output buffer!\n");
1156*d57664e9SAndroid Build Coastguard Worker exit(1);
1157*d57664e9SAndroid Build Coastguard Worker }
1158*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < (int) imageInfo.height; i++) {
1159*d57664e9SAndroid Build Coastguard Worker outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
1160*d57664e9SAndroid Build Coastguard Worker if (outRows[i] == (png_bytep) 0) {
1161*d57664e9SAndroid Build Coastguard Worker printf("Can't allocate output buffer!\n");
1162*d57664e9SAndroid Build Coastguard Worker exit(1);
1163*d57664e9SAndroid Build Coastguard Worker }
1164*d57664e9SAndroid Build Coastguard Worker }
1165*d57664e9SAndroid Build Coastguard Worker
1166*d57664e9SAndroid Build Coastguard Worker png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
1167*d57664e9SAndroid Build Coastguard Worker
1168*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1169*d57664e9SAndroid Build Coastguard Worker printf("Writing image %s: w = %d, h = %d\n", imageName,
1170*d57664e9SAndroid Build Coastguard Worker (int) imageInfo.width, (int) imageInfo.height);
1171*d57664e9SAndroid Build Coastguard Worker }
1172*d57664e9SAndroid Build Coastguard Worker
1173*d57664e9SAndroid Build Coastguard Worker png_color rgbPalette[256];
1174*d57664e9SAndroid Build Coastguard Worker png_byte alphaPalette[256];
1175*d57664e9SAndroid Build Coastguard Worker bool hasTransparency;
1176*d57664e9SAndroid Build Coastguard Worker int paletteEntries, alphaPaletteEntries;
1177*d57664e9SAndroid Build Coastguard Worker
1178*d57664e9SAndroid Build Coastguard Worker int grayscaleTolerance = bundle->getGrayscaleTolerance();
1179*d57664e9SAndroid Build Coastguard Worker analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
1180*d57664e9SAndroid Build Coastguard Worker &paletteEntries, &alphaPaletteEntries, &hasTransparency, &color_type, outRows);
1181*d57664e9SAndroid Build Coastguard Worker
1182*d57664e9SAndroid Build Coastguard Worker // Legacy versions of aapt would always encode 9patch PNGs as RGBA. This had the unintended
1183*d57664e9SAndroid Build Coastguard Worker // benefit of working around a bug decoding paletted images in Android 4.1.
1184*d57664e9SAndroid Build Coastguard Worker // https://code.google.com/p/android/issues/detail?id=34619
1185*d57664e9SAndroid Build Coastguard Worker //
1186*d57664e9SAndroid Build Coastguard Worker // If SDK_JELLY_BEAN is supported, we need to avoid a paletted encoding in order to not expose
1187*d57664e9SAndroid Build Coastguard Worker // this bug.
1188*d57664e9SAndroid Build Coastguard Worker if (!bundle->isMinSdkAtLeast(SDK_JELLY_BEAN_MR1)) {
1189*d57664e9SAndroid Build Coastguard Worker if (imageInfo.is9Patch && PNG_COLOR_TYPE_PALETTE == color_type) {
1190*d57664e9SAndroid Build Coastguard Worker if (hasTransparency) {
1191*d57664e9SAndroid Build Coastguard Worker color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1192*d57664e9SAndroid Build Coastguard Worker } else {
1193*d57664e9SAndroid Build Coastguard Worker color_type = PNG_COLOR_TYPE_RGB;
1194*d57664e9SAndroid Build Coastguard Worker }
1195*d57664e9SAndroid Build Coastguard Worker }
1196*d57664e9SAndroid Build Coastguard Worker }
1197*d57664e9SAndroid Build Coastguard Worker
1198*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1199*d57664e9SAndroid Build Coastguard Worker switch (color_type) {
1200*d57664e9SAndroid Build Coastguard Worker case PNG_COLOR_TYPE_PALETTE:
1201*d57664e9SAndroid Build Coastguard Worker printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
1202*d57664e9SAndroid Build Coastguard Worker imageName, paletteEntries,
1203*d57664e9SAndroid Build Coastguard Worker hasTransparency ? " (with alpha)" : "");
1204*d57664e9SAndroid Build Coastguard Worker break;
1205*d57664e9SAndroid Build Coastguard Worker case PNG_COLOR_TYPE_GRAY:
1206*d57664e9SAndroid Build Coastguard Worker printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName);
1207*d57664e9SAndroid Build Coastguard Worker break;
1208*d57664e9SAndroid Build Coastguard Worker case PNG_COLOR_TYPE_GRAY_ALPHA:
1209*d57664e9SAndroid Build Coastguard Worker printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName);
1210*d57664e9SAndroid Build Coastguard Worker break;
1211*d57664e9SAndroid Build Coastguard Worker case PNG_COLOR_TYPE_RGB:
1212*d57664e9SAndroid Build Coastguard Worker printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName);
1213*d57664e9SAndroid Build Coastguard Worker break;
1214*d57664e9SAndroid Build Coastguard Worker case PNG_COLOR_TYPE_RGB_ALPHA:
1215*d57664e9SAndroid Build Coastguard Worker printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName);
1216*d57664e9SAndroid Build Coastguard Worker break;
1217*d57664e9SAndroid Build Coastguard Worker }
1218*d57664e9SAndroid Build Coastguard Worker }
1219*d57664e9SAndroid Build Coastguard Worker
1220*d57664e9SAndroid Build Coastguard Worker png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
1221*d57664e9SAndroid Build Coastguard Worker 8, color_type, PNG_INTERLACE_NONE,
1222*d57664e9SAndroid Build Coastguard Worker PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1223*d57664e9SAndroid Build Coastguard Worker
1224*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_PALETTE) {
1225*d57664e9SAndroid Build Coastguard Worker png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
1226*d57664e9SAndroid Build Coastguard Worker if (hasTransparency) {
1227*d57664e9SAndroid Build Coastguard Worker png_set_tRNS(write_ptr, write_info, alphaPalette, alphaPaletteEntries,
1228*d57664e9SAndroid Build Coastguard Worker (png_color_16p) 0);
1229*d57664e9SAndroid Build Coastguard Worker }
1230*d57664e9SAndroid Build Coastguard Worker png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
1231*d57664e9SAndroid Build Coastguard Worker } else {
1232*d57664e9SAndroid Build Coastguard Worker png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
1233*d57664e9SAndroid Build Coastguard Worker }
1234*d57664e9SAndroid Build Coastguard Worker
1235*d57664e9SAndroid Build Coastguard Worker if (imageInfo.is9Patch) {
1236*d57664e9SAndroid Build Coastguard Worker int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0);
1237*d57664e9SAndroid Build Coastguard Worker int p_index = imageInfo.haveLayoutBounds ? 2 : 1;
1238*d57664e9SAndroid Build Coastguard Worker int b_index = 1;
1239*d57664e9SAndroid Build Coastguard Worker int o_index = 0;
1240*d57664e9SAndroid Build Coastguard Worker
1241*d57664e9SAndroid Build Coastguard Worker // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
1242*d57664e9SAndroid Build Coastguard Worker png_byte *chunk_names = imageInfo.haveLayoutBounds
1243*d57664e9SAndroid Build Coastguard Worker ? (png_byte*)"npOl\0npLb\0npTc\0"
1244*d57664e9SAndroid Build Coastguard Worker : (png_byte*)"npOl\0npTc";
1245*d57664e9SAndroid Build Coastguard Worker
1246*d57664e9SAndroid Build Coastguard Worker // base 9 patch data
1247*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1248*d57664e9SAndroid Build Coastguard Worker printf("Adding 9-patch info...\n");
1249*d57664e9SAndroid Build Coastguard Worker }
1250*d57664e9SAndroid Build Coastguard Worker memcpy((char*)unknowns[p_index].name, "npTc", 5);
1251*d57664e9SAndroid Build Coastguard Worker unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
1252*d57664e9SAndroid Build Coastguard Worker unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
1253*d57664e9SAndroid Build Coastguard Worker // TODO: remove the check below when everything works
1254*d57664e9SAndroid Build Coastguard Worker checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
1255*d57664e9SAndroid Build Coastguard Worker
1256*d57664e9SAndroid Build Coastguard Worker // automatically generated 9 patch outline data
1257*d57664e9SAndroid Build Coastguard Worker int chunk_size = sizeof(png_uint_32) * 6;
1258*d57664e9SAndroid Build Coastguard Worker memcpy((char*)unknowns[o_index].name, "npOl", 5);
1259*d57664e9SAndroid Build Coastguard Worker unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
1260*d57664e9SAndroid Build Coastguard Worker png_byte outputData[chunk_size];
1261*d57664e9SAndroid Build Coastguard Worker memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
1262*d57664e9SAndroid Build Coastguard Worker ((float*) outputData)[4] = imageInfo.outlineRadius;
1263*d57664e9SAndroid Build Coastguard Worker ((png_uint_32*) outputData)[5] = imageInfo.outlineAlpha;
1264*d57664e9SAndroid Build Coastguard Worker memcpy(unknowns[o_index].data, &outputData, chunk_size);
1265*d57664e9SAndroid Build Coastguard Worker unknowns[o_index].size = chunk_size;
1266*d57664e9SAndroid Build Coastguard Worker
1267*d57664e9SAndroid Build Coastguard Worker // optional optical inset / layout bounds data
1268*d57664e9SAndroid Build Coastguard Worker if (imageInfo.haveLayoutBounds) {
1269*d57664e9SAndroid Build Coastguard Worker int chunk_size = sizeof(png_uint_32) * 4;
1270*d57664e9SAndroid Build Coastguard Worker memcpy((char*)unknowns[b_index].name, "npLb", 5);
1271*d57664e9SAndroid Build Coastguard Worker unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
1272*d57664e9SAndroid Build Coastguard Worker memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
1273*d57664e9SAndroid Build Coastguard Worker unknowns[b_index].size = chunk_size;
1274*d57664e9SAndroid Build Coastguard Worker }
1275*d57664e9SAndroid Build Coastguard Worker
1276*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < chunk_count; i++) {
1277*d57664e9SAndroid Build Coastguard Worker unknowns[i].location = PNG_HAVE_IHDR;
1278*d57664e9SAndroid Build Coastguard Worker }
1279*d57664e9SAndroid Build Coastguard Worker png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1280*d57664e9SAndroid Build Coastguard Worker chunk_names, chunk_count);
1281*d57664e9SAndroid Build Coastguard Worker png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
1282*d57664e9SAndroid Build Coastguard Worker }
1283*d57664e9SAndroid Build Coastguard Worker
1284*d57664e9SAndroid Build Coastguard Worker
1285*d57664e9SAndroid Build Coastguard Worker png_write_info(write_ptr, write_info);
1286*d57664e9SAndroid Build Coastguard Worker
1287*d57664e9SAndroid Build Coastguard Worker png_bytepp rows;
1288*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
1289*d57664e9SAndroid Build Coastguard Worker if (color_type == PNG_COLOR_TYPE_RGB) {
1290*d57664e9SAndroid Build Coastguard Worker png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
1291*d57664e9SAndroid Build Coastguard Worker }
1292*d57664e9SAndroid Build Coastguard Worker rows = imageInfo.rows;
1293*d57664e9SAndroid Build Coastguard Worker } else {
1294*d57664e9SAndroid Build Coastguard Worker rows = outRows;
1295*d57664e9SAndroid Build Coastguard Worker }
1296*d57664e9SAndroid Build Coastguard Worker png_write_image(write_ptr, rows);
1297*d57664e9SAndroid Build Coastguard Worker
1298*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1299*d57664e9SAndroid Build Coastguard Worker printf("Final image data:\n");
1300*d57664e9SAndroid Build Coastguard Worker dump_image(imageInfo.width, imageInfo.height, rows, color_type);
1301*d57664e9SAndroid Build Coastguard Worker }
1302*d57664e9SAndroid Build Coastguard Worker
1303*d57664e9SAndroid Build Coastguard Worker png_write_end(write_ptr, write_info);
1304*d57664e9SAndroid Build Coastguard Worker
1305*d57664e9SAndroid Build Coastguard Worker for (i = 0; i < (int) imageInfo.height; i++) {
1306*d57664e9SAndroid Build Coastguard Worker free(outRows[i]);
1307*d57664e9SAndroid Build Coastguard Worker }
1308*d57664e9SAndroid Build Coastguard Worker free(outRows);
1309*d57664e9SAndroid Build Coastguard Worker free(unknowns[0].data);
1310*d57664e9SAndroid Build Coastguard Worker free(unknowns[1].data);
1311*d57664e9SAndroid Build Coastguard Worker free(unknowns[2].data);
1312*d57664e9SAndroid Build Coastguard Worker
1313*d57664e9SAndroid Build Coastguard Worker png_get_IHDR(write_ptr, write_info, &width, &height,
1314*d57664e9SAndroid Build Coastguard Worker &bit_depth, &color_type, &interlace_type,
1315*d57664e9SAndroid Build Coastguard Worker &compression_type, NULL);
1316*d57664e9SAndroid Build Coastguard Worker
1317*d57664e9SAndroid Build Coastguard Worker if (kIsDebug) {
1318*d57664e9SAndroid Build Coastguard Worker printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
1319*d57664e9SAndroid Build Coastguard Worker (int)width, (int)height, bit_depth, color_type, interlace_type,
1320*d57664e9SAndroid Build Coastguard Worker compression_type);
1321*d57664e9SAndroid Build Coastguard Worker }
1322*d57664e9SAndroid Build Coastguard Worker }
1323*d57664e9SAndroid Build Coastguard Worker
read_png_protected(png_structp read_ptr,String8 & printableName,png_infop read_info,const sp<AaptFile> & file,FILE * fp,image_info * imageInfo)1324*d57664e9SAndroid Build Coastguard Worker static bool read_png_protected(png_structp read_ptr, String8& printableName, png_infop read_info,
1325*d57664e9SAndroid Build Coastguard Worker const sp<AaptFile>& file, FILE* fp, image_info* imageInfo) {
1326*d57664e9SAndroid Build Coastguard Worker if (setjmp(png_jmpbuf(read_ptr))) {
1327*d57664e9SAndroid Build Coastguard Worker return false;
1328*d57664e9SAndroid Build Coastguard Worker }
1329*d57664e9SAndroid Build Coastguard Worker
1330*d57664e9SAndroid Build Coastguard Worker png_init_io(read_ptr, fp);
1331*d57664e9SAndroid Build Coastguard Worker
1332*d57664e9SAndroid Build Coastguard Worker read_png(printableName.c_str(), read_ptr, read_info, imageInfo);
1333*d57664e9SAndroid Build Coastguard Worker
1334*d57664e9SAndroid Build Coastguard Worker const size_t nameLen = file->getPath().length();
1335*d57664e9SAndroid Build Coastguard Worker if (nameLen > 6) {
1336*d57664e9SAndroid Build Coastguard Worker const char* name = file->getPath().c_str();
1337*d57664e9SAndroid Build Coastguard Worker if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1338*d57664e9SAndroid Build Coastguard Worker if (do_9patch(printableName.c_str(), imageInfo) != NO_ERROR) {
1339*d57664e9SAndroid Build Coastguard Worker return false;
1340*d57664e9SAndroid Build Coastguard Worker }
1341*d57664e9SAndroid Build Coastguard Worker }
1342*d57664e9SAndroid Build Coastguard Worker }
1343*d57664e9SAndroid Build Coastguard Worker
1344*d57664e9SAndroid Build Coastguard Worker return true;
1345*d57664e9SAndroid Build Coastguard Worker }
1346*d57664e9SAndroid Build Coastguard Worker
write_png_protected(png_structp write_ptr,String8 & printableName,png_infop write_info,image_info * imageInfo,const Bundle * bundle)1347*d57664e9SAndroid Build Coastguard Worker static bool write_png_protected(png_structp write_ptr, String8& printableName, png_infop write_info,
1348*d57664e9SAndroid Build Coastguard Worker image_info* imageInfo, const Bundle* bundle) {
1349*d57664e9SAndroid Build Coastguard Worker if (setjmp(png_jmpbuf(write_ptr))) {
1350*d57664e9SAndroid Build Coastguard Worker return false;
1351*d57664e9SAndroid Build Coastguard Worker }
1352*d57664e9SAndroid Build Coastguard Worker
1353*d57664e9SAndroid Build Coastguard Worker write_png(printableName.c_str(), write_ptr, write_info, *imageInfo, bundle);
1354*d57664e9SAndroid Build Coastguard Worker
1355*d57664e9SAndroid Build Coastguard Worker return true;
1356*d57664e9SAndroid Build Coastguard Worker }
1357*d57664e9SAndroid Build Coastguard Worker
preProcessImage(const Bundle * bundle,const sp<AaptAssets> &,const sp<AaptFile> & file,String8 *)1358*d57664e9SAndroid Build Coastguard Worker status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */,
1359*d57664e9SAndroid Build Coastguard Worker const sp<AaptFile>& file, String8* /* outNewLeafName */)
1360*d57664e9SAndroid Build Coastguard Worker {
1361*d57664e9SAndroid Build Coastguard Worker String8 ext(getPathExtension(file->getPath()));
1362*d57664e9SAndroid Build Coastguard Worker
1363*d57664e9SAndroid Build Coastguard Worker // We currently only process PNG images.
1364*d57664e9SAndroid Build Coastguard Worker if (strcmp(ext.c_str(), ".png") != 0) {
1365*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
1366*d57664e9SAndroid Build Coastguard Worker }
1367*d57664e9SAndroid Build Coastguard Worker
1368*d57664e9SAndroid Build Coastguard Worker // Example of renaming a file:
1369*d57664e9SAndroid Build Coastguard Worker //*outNewLeafName = file->getPath().getBasePath().getFileName();
1370*d57664e9SAndroid Build Coastguard Worker //outNewLeafName->append(".nupng");
1371*d57664e9SAndroid Build Coastguard Worker
1372*d57664e9SAndroid Build Coastguard Worker String8 printableName(file->getPrintableSource());
1373*d57664e9SAndroid Build Coastguard Worker
1374*d57664e9SAndroid Build Coastguard Worker if (bundle->getVerbose()) {
1375*d57664e9SAndroid Build Coastguard Worker printf("Processing image: %s\n", printableName.c_str());
1376*d57664e9SAndroid Build Coastguard Worker }
1377*d57664e9SAndroid Build Coastguard Worker
1378*d57664e9SAndroid Build Coastguard Worker png_structp read_ptr = NULL;
1379*d57664e9SAndroid Build Coastguard Worker png_infop read_info = NULL;
1380*d57664e9SAndroid Build Coastguard Worker FILE* fp;
1381*d57664e9SAndroid Build Coastguard Worker
1382*d57664e9SAndroid Build Coastguard Worker image_info imageInfo;
1383*d57664e9SAndroid Build Coastguard Worker
1384*d57664e9SAndroid Build Coastguard Worker png_structp write_ptr = NULL;
1385*d57664e9SAndroid Build Coastguard Worker png_infop write_info = NULL;
1386*d57664e9SAndroid Build Coastguard Worker
1387*d57664e9SAndroid Build Coastguard Worker status_t error = UNKNOWN_ERROR;
1388*d57664e9SAndroid Build Coastguard Worker
1389*d57664e9SAndroid Build Coastguard Worker fp = fopen(file->getSourceFile().c_str(), "rb");
1390*d57664e9SAndroid Build Coastguard Worker if (fp == NULL) {
1391*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.c_str());
1392*d57664e9SAndroid Build Coastguard Worker goto bail;
1393*d57664e9SAndroid Build Coastguard Worker }
1394*d57664e9SAndroid Build Coastguard Worker
1395*d57664e9SAndroid Build Coastguard Worker read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1396*d57664e9SAndroid Build Coastguard Worker (png_error_ptr)NULL);
1397*d57664e9SAndroid Build Coastguard Worker if (!read_ptr) {
1398*d57664e9SAndroid Build Coastguard Worker goto bail;
1399*d57664e9SAndroid Build Coastguard Worker }
1400*d57664e9SAndroid Build Coastguard Worker
1401*d57664e9SAndroid Build Coastguard Worker read_info = png_create_info_struct(read_ptr);
1402*d57664e9SAndroid Build Coastguard Worker if (!read_info) {
1403*d57664e9SAndroid Build Coastguard Worker goto bail;
1404*d57664e9SAndroid Build Coastguard Worker }
1405*d57664e9SAndroid Build Coastguard Worker
1406*d57664e9SAndroid Build Coastguard Worker if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) {
1407*d57664e9SAndroid Build Coastguard Worker goto bail;
1408*d57664e9SAndroid Build Coastguard Worker }
1409*d57664e9SAndroid Build Coastguard Worker
1410*d57664e9SAndroid Build Coastguard Worker write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1411*d57664e9SAndroid Build Coastguard Worker (png_error_ptr)NULL);
1412*d57664e9SAndroid Build Coastguard Worker if (!write_ptr)
1413*d57664e9SAndroid Build Coastguard Worker {
1414*d57664e9SAndroid Build Coastguard Worker goto bail;
1415*d57664e9SAndroid Build Coastguard Worker }
1416*d57664e9SAndroid Build Coastguard Worker
1417*d57664e9SAndroid Build Coastguard Worker write_info = png_create_info_struct(write_ptr);
1418*d57664e9SAndroid Build Coastguard Worker if (!write_info)
1419*d57664e9SAndroid Build Coastguard Worker {
1420*d57664e9SAndroid Build Coastguard Worker goto bail;
1421*d57664e9SAndroid Build Coastguard Worker }
1422*d57664e9SAndroid Build Coastguard Worker
1423*d57664e9SAndroid Build Coastguard Worker png_set_write_fn(write_ptr, (void*)file.get(),
1424*d57664e9SAndroid Build Coastguard Worker png_write_aapt_file, png_flush_aapt_file);
1425*d57664e9SAndroid Build Coastguard Worker
1426*d57664e9SAndroid Build Coastguard Worker if (!write_png_protected(write_ptr, printableName, write_info, &imageInfo, bundle)) {
1427*d57664e9SAndroid Build Coastguard Worker goto bail;
1428*d57664e9SAndroid Build Coastguard Worker }
1429*d57664e9SAndroid Build Coastguard Worker
1430*d57664e9SAndroid Build Coastguard Worker error = NO_ERROR;
1431*d57664e9SAndroid Build Coastguard Worker
1432*d57664e9SAndroid Build Coastguard Worker if (bundle->getVerbose()) {
1433*d57664e9SAndroid Build Coastguard Worker fseek(fp, 0, SEEK_END);
1434*d57664e9SAndroid Build Coastguard Worker size_t oldSize = (size_t)ftell(fp);
1435*d57664e9SAndroid Build Coastguard Worker size_t newSize = file->getSize();
1436*d57664e9SAndroid Build Coastguard Worker float factor = ((float)newSize)/oldSize;
1437*d57664e9SAndroid Build Coastguard Worker int percent = (int)(factor*100);
1438*d57664e9SAndroid Build Coastguard Worker printf(" (processed image %s: %d%% size of source)\n", printableName.c_str(), percent);
1439*d57664e9SAndroid Build Coastguard Worker }
1440*d57664e9SAndroid Build Coastguard Worker
1441*d57664e9SAndroid Build Coastguard Worker bail:
1442*d57664e9SAndroid Build Coastguard Worker if (read_ptr) {
1443*d57664e9SAndroid Build Coastguard Worker png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1444*d57664e9SAndroid Build Coastguard Worker }
1445*d57664e9SAndroid Build Coastguard Worker if (fp) {
1446*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1447*d57664e9SAndroid Build Coastguard Worker }
1448*d57664e9SAndroid Build Coastguard Worker if (write_ptr) {
1449*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1450*d57664e9SAndroid Build Coastguard Worker }
1451*d57664e9SAndroid Build Coastguard Worker
1452*d57664e9SAndroid Build Coastguard Worker if (error != NO_ERROR) {
1453*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1454*d57664e9SAndroid Build Coastguard Worker file->getPrintableSource().c_str());
1455*d57664e9SAndroid Build Coastguard Worker }
1456*d57664e9SAndroid Build Coastguard Worker return error;
1457*d57664e9SAndroid Build Coastguard Worker }
1458*d57664e9SAndroid Build Coastguard Worker
preProcessImageToCache(const Bundle * bundle,const String8 & source,const String8 & dest)1459*d57664e9SAndroid Build Coastguard Worker status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
1460*d57664e9SAndroid Build Coastguard Worker {
1461*d57664e9SAndroid Build Coastguard Worker png_structp read_ptr = NULL;
1462*d57664e9SAndroid Build Coastguard Worker png_infop read_info = NULL;
1463*d57664e9SAndroid Build Coastguard Worker
1464*d57664e9SAndroid Build Coastguard Worker FILE* fp;
1465*d57664e9SAndroid Build Coastguard Worker
1466*d57664e9SAndroid Build Coastguard Worker image_info imageInfo;
1467*d57664e9SAndroid Build Coastguard Worker
1468*d57664e9SAndroid Build Coastguard Worker png_structp write_ptr = NULL;
1469*d57664e9SAndroid Build Coastguard Worker png_infop write_info = NULL;
1470*d57664e9SAndroid Build Coastguard Worker
1471*d57664e9SAndroid Build Coastguard Worker status_t error = UNKNOWN_ERROR;
1472*d57664e9SAndroid Build Coastguard Worker
1473*d57664e9SAndroid Build Coastguard Worker if (bundle->getVerbose()) {
1474*d57664e9SAndroid Build Coastguard Worker printf("Processing image to cache: %s => %s\n", source.c_str(), dest.c_str());
1475*d57664e9SAndroid Build Coastguard Worker }
1476*d57664e9SAndroid Build Coastguard Worker
1477*d57664e9SAndroid Build Coastguard Worker // Get a file handler to read from
1478*d57664e9SAndroid Build Coastguard Worker fp = fopen(source.c_str(),"rb");
1479*d57664e9SAndroid Build Coastguard Worker if (fp == NULL) {
1480*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.c_str());
1481*d57664e9SAndroid Build Coastguard Worker return error;
1482*d57664e9SAndroid Build Coastguard Worker }
1483*d57664e9SAndroid Build Coastguard Worker
1484*d57664e9SAndroid Build Coastguard Worker // Call libpng to get a struct to read image data into
1485*d57664e9SAndroid Build Coastguard Worker read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1486*d57664e9SAndroid Build Coastguard Worker if (!read_ptr) {
1487*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1488*d57664e9SAndroid Build Coastguard Worker png_destroy_read_struct(&read_ptr, &read_info,NULL);
1489*d57664e9SAndroid Build Coastguard Worker return error;
1490*d57664e9SAndroid Build Coastguard Worker }
1491*d57664e9SAndroid Build Coastguard Worker
1492*d57664e9SAndroid Build Coastguard Worker // Call libpng to get a struct to read image info into
1493*d57664e9SAndroid Build Coastguard Worker read_info = png_create_info_struct(read_ptr);
1494*d57664e9SAndroid Build Coastguard Worker if (!read_info) {
1495*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1496*d57664e9SAndroid Build Coastguard Worker png_destroy_read_struct(&read_ptr, &read_info,NULL);
1497*d57664e9SAndroid Build Coastguard Worker return error;
1498*d57664e9SAndroid Build Coastguard Worker }
1499*d57664e9SAndroid Build Coastguard Worker
1500*d57664e9SAndroid Build Coastguard Worker // Set a jump point for libpng to long jump back to on error
1501*d57664e9SAndroid Build Coastguard Worker if (setjmp(png_jmpbuf(read_ptr))) {
1502*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1503*d57664e9SAndroid Build Coastguard Worker png_destroy_read_struct(&read_ptr, &read_info,NULL);
1504*d57664e9SAndroid Build Coastguard Worker return error;
1505*d57664e9SAndroid Build Coastguard Worker }
1506*d57664e9SAndroid Build Coastguard Worker
1507*d57664e9SAndroid Build Coastguard Worker // Set up libpng to read from our file.
1508*d57664e9SAndroid Build Coastguard Worker png_init_io(read_ptr,fp);
1509*d57664e9SAndroid Build Coastguard Worker
1510*d57664e9SAndroid Build Coastguard Worker // Actually read data from the file
1511*d57664e9SAndroid Build Coastguard Worker read_png(source.c_str(), read_ptr, read_info, &imageInfo);
1512*d57664e9SAndroid Build Coastguard Worker
1513*d57664e9SAndroid Build Coastguard Worker // We're done reading so we can clean up
1514*d57664e9SAndroid Build Coastguard Worker // Find old file size before releasing handle
1515*d57664e9SAndroid Build Coastguard Worker fseek(fp, 0, SEEK_END);
1516*d57664e9SAndroid Build Coastguard Worker size_t oldSize = (size_t)ftell(fp);
1517*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1518*d57664e9SAndroid Build Coastguard Worker png_destroy_read_struct(&read_ptr, &read_info,NULL);
1519*d57664e9SAndroid Build Coastguard Worker
1520*d57664e9SAndroid Build Coastguard Worker // Check to see if we're dealing with a 9-patch
1521*d57664e9SAndroid Build Coastguard Worker // If we are, process appropriately
1522*d57664e9SAndroid Build Coastguard Worker if (getPathExtension(getBasePath(source)) == ".9") {
1523*d57664e9SAndroid Build Coastguard Worker if (do_9patch(source.c_str(), &imageInfo) != NO_ERROR) {
1524*d57664e9SAndroid Build Coastguard Worker return error;
1525*d57664e9SAndroid Build Coastguard Worker }
1526*d57664e9SAndroid Build Coastguard Worker }
1527*d57664e9SAndroid Build Coastguard Worker
1528*d57664e9SAndroid Build Coastguard Worker // Call libpng to create a structure to hold the processed image data
1529*d57664e9SAndroid Build Coastguard Worker // that can be written to disk
1530*d57664e9SAndroid Build Coastguard Worker write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1531*d57664e9SAndroid Build Coastguard Worker if (!write_ptr) {
1532*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1533*d57664e9SAndroid Build Coastguard Worker return error;
1534*d57664e9SAndroid Build Coastguard Worker }
1535*d57664e9SAndroid Build Coastguard Worker
1536*d57664e9SAndroid Build Coastguard Worker // Call libpng to create a structure to hold processed image info that can
1537*d57664e9SAndroid Build Coastguard Worker // be written to disk
1538*d57664e9SAndroid Build Coastguard Worker write_info = png_create_info_struct(write_ptr);
1539*d57664e9SAndroid Build Coastguard Worker if (!write_info) {
1540*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1541*d57664e9SAndroid Build Coastguard Worker return error;
1542*d57664e9SAndroid Build Coastguard Worker }
1543*d57664e9SAndroid Build Coastguard Worker
1544*d57664e9SAndroid Build Coastguard Worker // Open up our destination file for writing
1545*d57664e9SAndroid Build Coastguard Worker fp = fopen(dest.c_str(), "wb");
1546*d57664e9SAndroid Build Coastguard Worker if (!fp) {
1547*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.c_str());
1548*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1549*d57664e9SAndroid Build Coastguard Worker return error;
1550*d57664e9SAndroid Build Coastguard Worker }
1551*d57664e9SAndroid Build Coastguard Worker
1552*d57664e9SAndroid Build Coastguard Worker // Set up libpng to write to our file
1553*d57664e9SAndroid Build Coastguard Worker png_init_io(write_ptr, fp);
1554*d57664e9SAndroid Build Coastguard Worker
1555*d57664e9SAndroid Build Coastguard Worker // Set up a jump for libpng to long jump back on on errors
1556*d57664e9SAndroid Build Coastguard Worker if (setjmp(png_jmpbuf(write_ptr))) {
1557*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1558*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1559*d57664e9SAndroid Build Coastguard Worker return error;
1560*d57664e9SAndroid Build Coastguard Worker }
1561*d57664e9SAndroid Build Coastguard Worker
1562*d57664e9SAndroid Build Coastguard Worker // Actually write out to the new png
1563*d57664e9SAndroid Build Coastguard Worker write_png(dest.c_str(), write_ptr, write_info, imageInfo, bundle);
1564*d57664e9SAndroid Build Coastguard Worker
1565*d57664e9SAndroid Build Coastguard Worker if (bundle->getVerbose()) {
1566*d57664e9SAndroid Build Coastguard Worker // Find the size of our new file
1567*d57664e9SAndroid Build Coastguard Worker FILE* reader = fopen(dest.c_str(), "rb");
1568*d57664e9SAndroid Build Coastguard Worker fseek(reader, 0, SEEK_END);
1569*d57664e9SAndroid Build Coastguard Worker size_t newSize = (size_t)ftell(reader);
1570*d57664e9SAndroid Build Coastguard Worker fclose(reader);
1571*d57664e9SAndroid Build Coastguard Worker
1572*d57664e9SAndroid Build Coastguard Worker float factor = ((float)newSize)/oldSize;
1573*d57664e9SAndroid Build Coastguard Worker int percent = (int)(factor*100);
1574*d57664e9SAndroid Build Coastguard Worker printf(" (processed image to cache entry %s: %d%% size of source)\n",
1575*d57664e9SAndroid Build Coastguard Worker dest.c_str(), percent);
1576*d57664e9SAndroid Build Coastguard Worker }
1577*d57664e9SAndroid Build Coastguard Worker
1578*d57664e9SAndroid Build Coastguard Worker //Clean up
1579*d57664e9SAndroid Build Coastguard Worker fclose(fp);
1580*d57664e9SAndroid Build Coastguard Worker png_destroy_write_struct(&write_ptr, &write_info);
1581*d57664e9SAndroid Build Coastguard Worker
1582*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
1583*d57664e9SAndroid Build Coastguard Worker }
1584*d57664e9SAndroid Build Coastguard Worker
postProcessImage(const Bundle * bundle,const sp<AaptAssets> & assets,ResourceTable * table,const sp<AaptFile> & file)1585*d57664e9SAndroid Build Coastguard Worker status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1586*d57664e9SAndroid Build Coastguard Worker ResourceTable* table, const sp<AaptFile>& file)
1587*d57664e9SAndroid Build Coastguard Worker {
1588*d57664e9SAndroid Build Coastguard Worker String8 ext(getPathExtension(file->getPath()));
1589*d57664e9SAndroid Build Coastguard Worker
1590*d57664e9SAndroid Build Coastguard Worker // At this point, now that we have all the resource data, all we need to
1591*d57664e9SAndroid Build Coastguard Worker // do is compile XML files.
1592*d57664e9SAndroid Build Coastguard Worker if (strcmp(ext.c_str(), ".xml") == 0) {
1593*d57664e9SAndroid Build Coastguard Worker String16 resourceName(parseResourceName(getPathLeaf(file->getSourceFile())));
1594*d57664e9SAndroid Build Coastguard Worker return compileXmlFile(bundle, assets, resourceName, file, table);
1595*d57664e9SAndroid Build Coastguard Worker }
1596*d57664e9SAndroid Build Coastguard Worker
1597*d57664e9SAndroid Build Coastguard Worker return NO_ERROR;
1598*d57664e9SAndroid Build Coastguard Worker }
1599