• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <utils/ResourceTypes.h>
12 #include <utils/ByteOrder.h>
13 
14 #include <png.h>
15 
16 #define NOISY(x) //x
17 
18 static void
png_write_aapt_file(png_structp png_ptr,png_bytep data,png_size_t length)19 png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
20 {
21     status_t err = ((AaptFile*)png_ptr->io_ptr)->writeData(data, length);
22     if (err != NO_ERROR) {
23         png_error(png_ptr, "Write Error");
24     }
25 }
26 
27 
28 static void
png_flush_aapt_file(png_structp png_ptr)29 png_flush_aapt_file(png_structp png_ptr)
30 {
31 }
32 
33 // This holds an image as 8bpp RGBA.
34 struct image_info
35 {
image_infoimage_info36     image_info() : rows(NULL), is9Patch(false), allocRows(NULL) { }
~image_infoimage_info37     ~image_info() {
38         if (rows && rows != allocRows) {
39             free(rows);
40         }
41         if (allocRows) {
42             for (int i=0; i<(int)allocHeight; i++) {
43                 free(allocRows[i]);
44             }
45             free(allocRows);
46         }
47         free(info9Patch.xDivs);
48         free(info9Patch.yDivs);
49         free(info9Patch.colors);
50     }
51 
52     png_uint_32 width;
53     png_uint_32 height;
54     png_bytepp rows;
55 
56     // 9-patch info.
57     bool is9Patch;
58     Res_png_9patch info9Patch;
59 
60     png_uint_32 allocHeight;
61     png_bytepp allocRows;
62 };
63 
read_png(const char * imageName,png_structp read_ptr,png_infop read_info,image_info * outImageInfo)64 static void read_png(const char* imageName,
65                      png_structp read_ptr, png_infop read_info,
66                      image_info* outImageInfo)
67 {
68     int color_type;
69     int bit_depth, interlace_type, compression_type;
70     int i;
71 
72     png_read_info(read_ptr, read_info);
73 
74     png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
75        &outImageInfo->height, &bit_depth, &color_type,
76        &interlace_type, &compression_type, NULL);
77 
78     //printf("Image %s:\n", imageName);
79     //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
80     //       color_type, bit_depth, interlace_type, compression_type);
81 
82     if (color_type == PNG_COLOR_TYPE_PALETTE)
83         png_set_palette_to_rgb(read_ptr);
84 
85     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
86         png_set_gray_1_2_4_to_8(read_ptr);
87 
88     if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
89         //printf("Has PNG_INFO_tRNS!\n");
90         png_set_tRNS_to_alpha(read_ptr);
91     }
92 
93     if (bit_depth == 16)
94         png_set_strip_16(read_ptr);
95 
96     if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
97         png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
98 
99     if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
100         png_set_gray_to_rgb(read_ptr);
101 
102     png_read_update_info(read_ptr, read_info);
103 
104     outImageInfo->rows = (png_bytepp)malloc(
105         outImageInfo->height * png_sizeof(png_bytep));
106     outImageInfo->allocHeight = outImageInfo->height;
107     outImageInfo->allocRows = outImageInfo->rows;
108 
109     png_set_rows(read_ptr, read_info, outImageInfo->rows);
110 
111     for (i = 0; i < (int)outImageInfo->height; i++)
112     {
113         outImageInfo->rows[i] = (png_bytep)
114             malloc(png_get_rowbytes(read_ptr, read_info));
115     }
116 
117     png_read_image(read_ptr, outImageInfo->rows);
118 
119     png_read_end(read_ptr, read_info);
120 
121     NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
122                  imageName,
123                  (int)outImageInfo->width, (int)outImageInfo->height,
124                  bit_depth, color_type,
125                  interlace_type, compression_type));
126 
127     png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
128        &outImageInfo->height, &bit_depth, &color_type,
129        &interlace_type, &compression_type, NULL);
130 }
131 
is_tick(png_bytep p,bool transparent,const char ** outError)132 static bool is_tick(png_bytep p, bool transparent, const char** outError)
133 {
134     if (transparent) {
135         if (p[3] == 0) {
136             return false;
137         }
138         if (p[3] != 0xff) {
139             *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
140             return false;
141         }
142         if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
143             *outError = "Ticks in transparent frame must be black";
144         }
145         return true;
146     }
147 
148     if (p[3] != 0xFF) {
149         *outError = "White frame must be a solid color (no alpha)";
150     }
151     if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
152         return false;
153     }
154     if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
155         *outError = "Ticks in white frame must be black";
156         return false;
157     }
158     return true;
159 }
160 
161 enum {
162     TICK_START,
163     TICK_INSIDE_1,
164     TICK_OUTSIDE_1
165 };
166 
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)167 static status_t get_horizontal_ticks(
168         png_bytep row, int width, bool transparent, bool required,
169         int32_t* outLeft, int32_t* outRight, const char** outError,
170         uint8_t* outDivs, bool multipleAllowed)
171 {
172     int i;
173     *outLeft = *outRight = -1;
174     int state = TICK_START;
175     bool found = false;
176 
177     for (i=1; i<width-1; i++) {
178         if (is_tick(row+i*4, transparent, outError)) {
179             if (state == TICK_START ||
180                 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
181                 *outLeft = i-1;
182                 *outRight = width-2;
183                 found = true;
184                 if (outDivs != NULL) {
185                     *outDivs += 2;
186                 }
187                 state = TICK_INSIDE_1;
188             } else if (state == TICK_OUTSIDE_1) {
189                 *outError = "Can't have more than one marked region along edge";
190                 *outLeft = i;
191                 return UNKNOWN_ERROR;
192             }
193         } else if (*outError == NULL) {
194             if (state == TICK_INSIDE_1) {
195                 // We're done with this div.  Move on to the next.
196                 *outRight = i-1;
197                 outRight += 2;
198                 outLeft += 2;
199                 state = TICK_OUTSIDE_1;
200             }
201         } else {
202             *outLeft = i;
203             return UNKNOWN_ERROR;
204         }
205     }
206 
207     if (required && !found) {
208         *outError = "No marked region found along edge";
209         *outLeft = -1;
210         return UNKNOWN_ERROR;
211     }
212 
213     return NO_ERROR;
214 }
215 
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)216 static status_t get_vertical_ticks(
217         png_bytepp rows, int offset, int height, bool transparent, bool required,
218         int32_t* outTop, int32_t* outBottom, const char** outError,
219         uint8_t* outDivs, bool multipleAllowed)
220 {
221     int i;
222     *outTop = *outBottom = -1;
223     int state = TICK_START;
224     bool found = false;
225 
226     for (i=1; i<height-1; i++) {
227         if (is_tick(rows[i]+offset, transparent, outError)) {
228             if (state == TICK_START ||
229                 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
230                 *outTop = i-1;
231                 *outBottom = height-2;
232                 found = true;
233                 if (outDivs != NULL) {
234                     *outDivs += 2;
235                 }
236                 state = TICK_INSIDE_1;
237             } else if (state == TICK_OUTSIDE_1) {
238                 *outError = "Can't have more than one marked region along edge";
239                 *outTop = i;
240                 return UNKNOWN_ERROR;
241             }
242         } else if (*outError == NULL) {
243             if (state == TICK_INSIDE_1) {
244                 // We're done with this div.  Move on to the next.
245                 *outBottom = i-1;
246                 outTop += 2;
247                 outBottom += 2;
248                 state = TICK_OUTSIDE_1;
249             }
250         } else {
251             *outTop = i;
252             return UNKNOWN_ERROR;
253         }
254     }
255 
256     if (required && !found) {
257         *outError = "No marked region found along edge";
258         *outTop = -1;
259         return UNKNOWN_ERROR;
260     }
261 
262     return NO_ERROR;
263 }
264 
get_color(png_bytepp rows,int left,int top,int right,int bottom)265 static uint32_t get_color(
266     png_bytepp rows, int left, int top, int right, int bottom)
267 {
268     png_bytep color = rows[top] + left*4;
269 
270     if (left > right || top > bottom) {
271         return Res_png_9patch::TRANSPARENT_COLOR;
272     }
273 
274     while (top <= bottom) {
275         for (int i = left; i <= right; i++) {
276             png_bytep p = rows[top]+i*4;
277             if (color[3] == 0) {
278                 if (p[3] != 0) {
279                     return Res_png_9patch::NO_COLOR;
280                 }
281             } else if (p[0] != color[0] || p[1] != color[1]
282                        || p[2] != color[2] || p[3] != color[3]) {
283                 return Res_png_9patch::NO_COLOR;
284             }
285         }
286         top++;
287     }
288 
289     if (color[3] == 0) {
290         return Res_png_9patch::TRANSPARENT_COLOR;
291     }
292     return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
293 }
294 
select_patch(int which,int front,int back,int size,int * start,int * end)295 static void select_patch(
296     int which, int front, int back, int size, int* start, int* end)
297 {
298     switch (which) {
299     case 0:
300         *start = 0;
301         *end = front-1;
302         break;
303     case 1:
304         *start = front;
305         *end = back-1;
306         break;
307     case 2:
308         *start = back;
309         *end = size-1;
310         break;
311     }
312 }
313 
get_color(image_info * image,int hpatch,int vpatch)314 static uint32_t get_color(image_info* image, int hpatch, int vpatch)
315 {
316     int left, right, top, bottom;
317     select_patch(
318         hpatch, image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
319         image->width, &left, &right);
320     select_patch(
321         vpatch, image->info9Patch.yDivs[0], image->info9Patch.yDivs[1],
322         image->height, &top, &bottom);
323     //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
324     //       hpatch, vpatch, left, top, right, bottom);
325     const uint32_t c = get_color(image->rows, left, top, right, bottom);
326     NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left, top, right, bottom, c));
327     return c;
328 }
329 
do_9patch(const char * imageName,image_info * image)330 static status_t do_9patch(const char* imageName, image_info* image)
331 {
332     image->is9Patch = true;
333 
334     int W = image->width;
335     int H = image->height;
336     int i, j;
337 
338     int maxSizeXDivs = W * sizeof(int32_t);
339     int maxSizeYDivs = H * sizeof(int32_t);
340     int32_t* xDivs = (int32_t*) malloc(maxSizeXDivs);
341     int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs);
342     uint8_t  numXDivs = 0;
343     uint8_t  numYDivs = 0;
344     int8_t numColors;
345     int numRows;
346     int numCols;
347     int top;
348     int left;
349     int right;
350     int bottom;
351     memset(xDivs, -1, maxSizeXDivs);
352     memset(yDivs, -1, maxSizeYDivs);
353     image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
354         image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
355 
356     png_bytep p = image->rows[0];
357     bool transparent = p[3] == 0;
358     bool hasColor = false;
359 
360     const char* errorMsg = NULL;
361     int errorPixel = -1;
362     const char* errorEdge = "";
363 
364     int colorIndex = 0;
365 
366     // Validate size...
367     if (W < 3 || H < 3) {
368         errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
369         goto getout;
370     }
371 
372     // Validate frame...
373     if (!transparent &&
374         (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
375         errorMsg = "Must have one-pixel frame that is either transparent or white";
376         goto getout;
377     }
378 
379     // Find left and right of sizing areas...
380     if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
381                              &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
382         errorPixel = xDivs[0];
383         errorEdge = "top";
384         goto getout;
385     }
386 
387     // Find top and bottom of sizing areas...
388     if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
389                            &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
390         errorPixel = yDivs[0];
391         errorEdge = "left";
392         goto getout;
393     }
394 
395     // Find left and right of padding area...
396     if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
397                              &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
398         errorPixel = image->info9Patch.paddingLeft;
399         errorEdge = "bottom";
400         goto getout;
401     }
402 
403     // Find top and bottom of padding area...
404     if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
405                            &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
406         errorPixel = image->info9Patch.paddingTop;
407         errorEdge = "right";
408         goto getout;
409     }
410 
411     // Copy patch data into image
412     image->info9Patch.numXDivs = numXDivs;
413     image->info9Patch.numYDivs = numYDivs;
414     image->info9Patch.xDivs = xDivs;
415     image->info9Patch.yDivs = yDivs;
416 
417     // If padding is not yet specified, take values from size.
418     if (image->info9Patch.paddingLeft < 0) {
419         image->info9Patch.paddingLeft = xDivs[0];
420         image->info9Patch.paddingRight = W - 2 - xDivs[1];
421     } else {
422         // Adjust value to be correct!
423         image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
424     }
425     if (image->info9Patch.paddingTop < 0) {
426         image->info9Patch.paddingTop = yDivs[0];
427         image->info9Patch.paddingBottom = H - 2 - yDivs[1];
428     } else {
429         // Adjust value to be correct!
430         image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
431     }
432 
433     NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
434                  image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
435                  image->info9Patch.yDivs[0], image->info9Patch.yDivs[1]));
436     NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
437                  image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
438                  image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
439 
440     // Remove frame from image.
441     image->rows = (png_bytepp)malloc((H-2) * png_sizeof(png_bytep));
442     for (i=0; i<(H-2); i++) {
443         image->rows[i] = image->allocRows[i+1];
444         memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
445     }
446     image->width -= 2;
447     W = image->width;
448     image->height -= 2;
449     H = image->height;
450 
451     // Figure out the number of rows and columns in the N-patch
452     numCols = numXDivs + 1;
453     if (xDivs[0] == 0) {  // Column 1 is strechable
454         numCols--;
455     }
456     if (xDivs[numXDivs - 1] == W) {
457         numCols--;
458     }
459     numRows = numYDivs + 1;
460     if (yDivs[0] == 0) {  // Row 1 is strechable
461         numRows--;
462     }
463     if (yDivs[numYDivs - 1] == H) {
464         numRows--;
465     }
466     numColors = numRows * numCols;
467     image->info9Patch.numColors = numColors;
468     image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
469 
470     // Fill in color information for each patch.
471 
472     uint32_t c;
473     top = 0;
474 
475     // The first row always starts with the top being at y=0 and the bottom
476     // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
477     // the first row is stretchable along the Y axis, otherwise it is fixed.
478     // The last row always ends with the bottom being bitmap.height and the top
479     // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
480     // yDivs[numYDivs-1]. In the former case the last row is stretchable along
481     // the Y axis, otherwise it is fixed.
482     //
483     // The first and last columns are similarly treated with respect to the X
484     // axis.
485     //
486     // The above is to help explain some of the special casing that goes on the
487     // code below.
488 
489     // The initial yDiv and whether the first row is considered stretchable or
490     // not depends on whether yDiv[0] was zero or not.
491     for (j = (yDivs[0] == 0 ? 1 : 0);
492           j <= numYDivs && top < H;
493           j++) {
494         if (j == numYDivs) {
495             bottom = H;
496         } else {
497             bottom = yDivs[j];
498         }
499         left = 0;
500         // The initial xDiv and whether the first column is considered
501         // stretchable or not depends on whether xDiv[0] was zero or not.
502         for (i = xDivs[0] == 0 ? 1 : 0;
503               i <= numXDivs && left < W;
504               i++) {
505             if (i == numXDivs) {
506                 right = W;
507             } else {
508                 right = xDivs[i];
509             }
510             c = get_color(image->rows, left, top, right - 1, bottom - 1);
511             image->info9Patch.colors[colorIndex++] = c;
512             NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
513             left = right;
514         }
515         top = bottom;
516     }
517 
518     assert(colorIndex == numColors);
519 
520     for (i=0; i<numColors; i++) {
521         if (hasColor) {
522             if (i == 0) printf("Colors in %s:\n ", imageName);
523             printf(" #%08x", image->info9Patch.colors[i]);
524             if (i == numColors - 1) printf("\n");
525         }
526     }
527 
528     image->is9Patch = true;
529     image->info9Patch.deviceToFile();
530 
531 getout:
532     if (errorMsg) {
533         fprintf(stderr,
534             "ERROR: 9-patch image %s malformed.\n"
535             "       %s.\n", imageName, errorMsg);
536         if (errorPixel >= 0) {
537             fprintf(stderr,
538             "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
539         } else {
540             fprintf(stderr,
541             "       Found along %s edge.\n", errorEdge);
542         }
543         return UNKNOWN_ERROR;
544     }
545     return NO_ERROR;
546 }
547 
checkNinePatchSerialization(Res_png_9patch * inPatch,void * data)548 static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
549 {
550     if (sizeof(void*) != sizeof(int32_t)) {
551         // can't deserialize on a non-32 bit system
552         return;
553     }
554     size_t patchSize = inPatch->serializedSize();
555     void * newData = malloc(patchSize);
556     memcpy(newData, data, patchSize);
557     Res_png_9patch* outPatch = inPatch->deserialize(newData);
558     // deserialization is done in place, so outPatch == newData
559     assert(outPatch == newData);
560     assert(outPatch->numXDivs == inPatch->numXDivs);
561     assert(outPatch->numYDivs == inPatch->numYDivs);
562     assert(outPatch->paddingLeft == inPatch->paddingLeft);
563     assert(outPatch->paddingRight == inPatch->paddingRight);
564     assert(outPatch->paddingTop == inPatch->paddingTop);
565     assert(outPatch->paddingBottom == inPatch->paddingBottom);
566     for (int i = 0; i < outPatch->numXDivs; i++) {
567         assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
568     }
569     for (int i = 0; i < outPatch->numYDivs; i++) {
570         assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
571     }
572     for (int i = 0; i < outPatch->numColors; i++) {
573         assert(outPatch->colors[i] == inPatch->colors[i]);
574     }
575     free(newData);
576 }
577 
patch_equals(Res_png_9patch & patch1,Res_png_9patch & patch2)578 static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
579     if (!(patch1.numXDivs == patch2.numXDivs &&
580           patch1.numYDivs == patch2.numYDivs &&
581           patch1.numColors == patch2.numColors &&
582           patch1.paddingLeft == patch2.paddingLeft &&
583           patch1.paddingRight == patch2.paddingRight &&
584           patch1.paddingTop == patch2.paddingTop &&
585           patch1.paddingBottom == patch2.paddingBottom)) {
586             return false;
587     }
588     for (int i = 0; i < patch1.numColors; i++) {
589         if (patch1.colors[i] != patch2.colors[i]) {
590             return false;
591         }
592     }
593     for (int i = 0; i < patch1.numXDivs; i++) {
594         if (patch1.xDivs[i] != patch2.xDivs[i]) {
595             return false;
596         }
597     }
598     for (int i = 0; i < patch1.numYDivs; i++) {
599         if (patch1.yDivs[i] != patch2.yDivs[i]) {
600             return false;
601         }
602     }
603     return true;
604 }
605 
dump_image(int w,int h,png_bytepp rows,int color_type)606 static void dump_image(int w, int h, png_bytepp rows, int color_type)
607 {
608     int i, j, rr, gg, bb, aa;
609 
610     int bpp;
611     if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
612         bpp = 1;
613     } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
614         bpp = 2;
615     } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
616 	    // We use a padding byte even when there is no alpha
617         bpp = 4;
618     } else {
619         printf("Unknown color type %d.\n", color_type);
620     }
621 
622     for (j = 0; j < h; j++) {
623         png_bytep row = rows[j];
624         for (i = 0; i < w; i++) {
625             rr = row[0];
626             gg = row[1];
627             bb = row[2];
628             aa = row[3];
629             row += bpp;
630 
631             if (i == 0) {
632                 printf("Row %d:", j);
633             }
634             switch (bpp) {
635             case 1:
636                 printf(" (%d)", rr);
637                 break;
638             case 2:
639                 printf(" (%d %d", rr, gg);
640                 break;
641             case 3:
642                 printf(" (%d %d %d)", rr, gg, bb);
643                 break;
644             case 4:
645                 printf(" (%d %d %d %d)", rr, gg, bb, aa);
646                 break;
647             }
648             if (i == (w - 1)) {
649                 NOISY(printf("\n"));
650             }
651         }
652     }
653 }
654 
655 #define MAX(a,b) ((a)>(b)?(a):(b))
656 #define ABS(a)   ((a)<0?-(a):(a))
657 
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)658 static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
659                           png_colorp rgbPalette, png_bytep alphaPalette,
660                           int *paletteEntries, bool *hasTransparency, int *colorType,
661                           png_bytepp outRows)
662 {
663     int w = imageInfo.width;
664     int h = imageInfo.height;
665     int i, j, rr, gg, bb, aa, idx;
666     uint32_t colors[256], col;
667     int num_colors = 0;
668     int maxGrayDeviation = 0;
669 
670     bool isOpaque = true;
671     bool isPalette = true;
672     bool isGrayscale = true;
673 
674     // Scan the entire image and determine if:
675     // 1. Every pixel has R == G == B (grayscale)
676     // 2. Every pixel has A == 255 (opaque)
677     // 3. There are no more than 256 distinct RGBA colors
678 
679     // NOISY(printf("Initial image data:\n"));
680     // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
681 
682     for (j = 0; j < h; j++) {
683         png_bytep row = imageInfo.rows[j];
684         png_bytep out = outRows[j];
685         for (i = 0; i < w; i++) {
686             rr = *row++;
687             gg = *row++;
688             bb = *row++;
689             aa = *row++;
690 
691             int odev = maxGrayDeviation;
692             maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
693             maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
694             maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
695             if (maxGrayDeviation > odev) {
696                 NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
697                              maxGrayDeviation, i, j, rr, gg, bb, aa));
698             }
699 
700             // Check if image is really grayscale
701             if (isGrayscale) {
702                 if (rr != gg || rr != bb) {
703                      NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
704                                   i, j, rr, gg, bb, aa));
705                     isGrayscale = false;
706                 }
707             }
708 
709             // Check if image is really opaque
710             if (isOpaque) {
711                 if (aa != 0xff) {
712                     NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
713                                  i, j, rr, gg, bb, aa));
714                     isOpaque = false;
715                 }
716             }
717 
718             // Check if image is really <= 256 colors
719             if (isPalette) {
720                 col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
721                 bool match = false;
722                 for (idx = 0; idx < num_colors; idx++) {
723                     if (colors[idx] == col) {
724                         match = true;
725                         break;
726                     }
727                 }
728 
729                 // Write the palette index for the pixel to outRows optimistically
730                 // We might overwrite it later if we decide to encode as gray or
731                 // gray + alpha
732                 *out++ = idx;
733                 if (!match) {
734                     if (num_colors == 256) {
735                         NOISY(printf("Found 257th color at %d, %d\n", i, j));
736                         isPalette = false;
737                     } else {
738                         colors[num_colors++] = col;
739                     }
740                 }
741             }
742         }
743     }
744 
745     *paletteEntries = 0;
746     *hasTransparency = !isOpaque;
747     int bpp = isOpaque ? 3 : 4;
748     int paletteSize = w * h + bpp * num_colors;
749 
750     NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"));
751     NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false"));
752     NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false"));
753     NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
754                  paletteSize, 2 * w * h, bpp * w * h));
755     NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance));
756 
757     // Choose the best color type for the image.
758     // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
759     // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
760     //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
761     // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
762     //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
763     if (isGrayscale) {
764         if (isOpaque) {
765             *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
766         } else {
767             // Use a simple heuristic to determine whether using a palette will
768             // save space versus using gray + alpha for each pixel.
769             // This doesn't take into account chunk overhead, filtering, LZ
770             // compression, etc.
771             if (isPalette && (paletteSize < 2 * w * h)) {
772                 *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
773             } else {
774                 *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
775             }
776         }
777     } else if (isPalette && (paletteSize < bpp * w * h)) {
778         *colorType = PNG_COLOR_TYPE_PALETTE;
779     } else {
780         if (maxGrayDeviation <= grayscaleTolerance) {
781             printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
782             *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
783         } else {
784             *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
785         }
786     }
787 
788     // Perform postprocessing of the image or palette data based on the final
789     // color type chosen
790 
791     if (*colorType == PNG_COLOR_TYPE_PALETTE) {
792         // Create separate RGB and Alpha palettes and set the number of colors
793         *paletteEntries = num_colors;
794 
795         // Create the RGB and alpha palettes
796         for (int idx = 0; idx < num_colors; idx++) {
797             col = colors[idx];
798             rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
799             rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
800             rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
801             alphaPalette[idx]     = (png_byte)  (col        & 0xff);
802         }
803     } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
804         // If the image is gray or gray + alpha, compact the pixels into outRows
805         for (j = 0; j < h; j++) {
806             png_bytep row = imageInfo.rows[j];
807             png_bytep out = outRows[j];
808             for (i = 0; i < w; i++) {
809                 rr = *row++;
810                 gg = *row++;
811                 bb = *row++;
812                 aa = *row++;
813 
814                 if (isGrayscale) {
815                     *out++ = rr;
816                 } else {
817                     *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
818                 }
819                 if (!isOpaque) {
820                     *out++ = aa;
821                 }
822            }
823         }
824     }
825 }
826 
827 
write_png(const char * imageName,png_structp write_ptr,png_infop write_info,image_info & imageInfo,int grayscaleTolerance)828 static void write_png(const char* imageName,
829                       png_structp write_ptr, png_infop write_info,
830                       image_info& imageInfo, int grayscaleTolerance)
831 {
832     bool optimize = true;
833     png_uint_32 width, height;
834     int color_type;
835     int bit_depth, interlace_type, compression_type;
836     int i;
837 
838     png_unknown_chunk unknowns[1];
839     unknowns[0].data = NULL;
840 
841     png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
842     if (outRows == (png_bytepp) 0) {
843         printf("Can't allocate output buffer!\n");
844         exit(1);
845     }
846     for (i = 0; i < (int) imageInfo.height; i++) {
847         outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
848         if (outRows[i] == (png_bytep) 0) {
849             printf("Can't allocate output buffer!\n");
850             exit(1);
851         }
852     }
853 
854     png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
855 
856     NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName,
857           (int) imageInfo.width, (int) imageInfo.height));
858 
859     png_color rgbPalette[256];
860     png_byte alphaPalette[256];
861     bool hasTransparency;
862     int paletteEntries;
863 
864     analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
865                   &paletteEntries, &hasTransparency, &color_type, outRows);
866 
867     // If the image is a 9-patch, we need to preserve it as a ARGB file to make
868     // sure the pixels will not be pre-dithered/clamped until we decide they are
869     if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
870             color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
871         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
872     }
873 
874     switch (color_type) {
875     case PNG_COLOR_TYPE_PALETTE:
876         NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
877                      imageName, paletteEntries,
878                      hasTransparency ? " (with alpha)" : ""));
879         break;
880     case PNG_COLOR_TYPE_GRAY:
881         NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
882         break;
883     case PNG_COLOR_TYPE_GRAY_ALPHA:
884         NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
885         break;
886     case PNG_COLOR_TYPE_RGB:
887         NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
888         break;
889     case PNG_COLOR_TYPE_RGB_ALPHA:
890         NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
891         break;
892     }
893 
894     png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
895                  8, color_type, PNG_INTERLACE_NONE,
896                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
897 
898     if (color_type == PNG_COLOR_TYPE_PALETTE) {
899         png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
900         if (hasTransparency) {
901             png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
902         }
903        png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
904     } else {
905        png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
906     }
907 
908     if (imageInfo.is9Patch) {
909         NOISY(printf("Adding 9-patch info...\n"));
910         strcpy((char*)unknowns[0].name, "npTc");
911         unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
912         unknowns[0].size = imageInfo.info9Patch.serializedSize();
913         // TODO: remove the check below when everything works
914         checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
915         png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
916                                     (png_byte*)"npTc", 1);
917         png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
918         // XXX I can't get this to work without forcibly changing
919         // the location to what I want...  which apparently is supposed
920         // to be a private API, but everything else I have tried results
921         // in the location being set to what I -last- wrote so I never
922         // get written. :p
923         png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
924     }
925 
926     png_write_info(write_ptr, write_info);
927 
928     png_bytepp rows;
929     if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
930         png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
931         rows = imageInfo.rows;
932     } else {
933         rows = outRows;
934     }
935     png_write_image(write_ptr, rows);
936 
937 //     NOISY(printf("Final image data:\n"));
938 //     dump_image(imageInfo.width, imageInfo.height, rows, color_type);
939 
940     png_write_end(write_ptr, write_info);
941 
942     for (i = 0; i < (int) imageInfo.height; i++) {
943         free(outRows[i]);
944     }
945     free(outRows);
946     free(unknowns[0].data);
947 
948     png_get_IHDR(write_ptr, write_info, &width, &height,
949        &bit_depth, &color_type, &interlace_type,
950        &compression_type, NULL);
951 
952     NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
953                  (int)width, (int)height, bit_depth, color_type, interlace_type,
954                  compression_type));
955 }
956 
preProcessImage(Bundle * bundle,const sp<AaptAssets> & assets,const sp<AaptFile> & file,String8 * outNewLeafName)957 status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
958                          const sp<AaptFile>& file, String8* outNewLeafName)
959 {
960     String8 ext(file->getPath().getPathExtension());
961 
962     // We currently only process PNG images.
963     if (strcmp(ext.string(), ".png") != 0) {
964         return NO_ERROR;
965     }
966 
967     // Example of renaming a file:
968     //*outNewLeafName = file->getPath().getBasePath().getFileName();
969     //outNewLeafName->append(".nupng");
970 
971     String8 printableName(file->getPrintableSource());
972 
973     png_structp read_ptr = NULL;
974     png_infop read_info = NULL;
975     FILE* fp;
976 
977     image_info imageInfo;
978 
979     png_structp write_ptr = NULL;
980     png_infop write_info = NULL;
981 
982     status_t error = UNKNOWN_ERROR;
983 
984     const size_t nameLen = file->getPath().length();
985 
986     fp = fopen(file->getSourceFile().string(), "rb");
987     if (fp == NULL) {
988         fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
989         goto bail;
990     }
991 
992     read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
993                                         (png_error_ptr)NULL);
994     if (!read_ptr) {
995         goto bail;
996     }
997 
998     read_info = png_create_info_struct(read_ptr);
999     if (!read_info) {
1000         goto bail;
1001     }
1002 
1003     if (setjmp(png_jmpbuf(read_ptr))) {
1004         goto bail;
1005     }
1006 
1007     png_init_io(read_ptr, fp);
1008 
1009     read_png(printableName.string(), read_ptr, read_info, &imageInfo);
1010 
1011     if (nameLen > 6) {
1012         const char* name = file->getPath().string();
1013         if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1014             if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
1015                 goto bail;
1016             }
1017         }
1018     }
1019 
1020     write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1021                                         (png_error_ptr)NULL);
1022     if (!write_ptr)
1023     {
1024         goto bail;
1025     }
1026 
1027     write_info = png_create_info_struct(write_ptr);
1028     if (!write_info)
1029     {
1030         goto bail;
1031     }
1032 
1033     png_set_write_fn(write_ptr, (void*)file.get(),
1034                      png_write_aapt_file, png_flush_aapt_file);
1035 
1036     if (setjmp(png_jmpbuf(write_ptr)))
1037     {
1038         goto bail;
1039     }
1040 
1041     write_png(printableName.string(), write_ptr, write_info, imageInfo,
1042               bundle->getGrayscaleTolerance());
1043 
1044     error = NO_ERROR;
1045 
1046     if (bundle->getVerbose()) {
1047         fseek(fp, 0, SEEK_END);
1048         size_t oldSize = (size_t)ftell(fp);
1049         size_t newSize = file->getSize();
1050         float factor = ((float)newSize)/oldSize;
1051         int percent = (int)(factor*100);
1052         printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
1053     }
1054 
1055 bail:
1056     if (read_ptr) {
1057         png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1058     }
1059     if (fp) {
1060         fclose(fp);
1061     }
1062     if (write_ptr) {
1063         png_destroy_write_struct(&write_ptr, &write_info);
1064     }
1065 
1066     if (error != NO_ERROR) {
1067         fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1068                 file->getPrintableSource().string());
1069     }
1070     return error;
1071 }
1072 
1073 
1074 
postProcessImage(const sp<AaptAssets> & assets,ResourceTable * table,const sp<AaptFile> & file)1075 status_t postProcessImage(const sp<AaptAssets>& assets,
1076                           ResourceTable* table, const sp<AaptFile>& file)
1077 {
1078     String8 ext(file->getPath().getPathExtension());
1079 
1080     // At this point, now that we have all the resource data, all we need to
1081     // do is compile XML files.
1082     if (strcmp(ext.string(), ".xml") == 0) {
1083         return compileXmlFile(assets, file, table);
1084     }
1085 
1086     return NO_ERROR;
1087 }
1088