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