• 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 = NULL;
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 
467     // Make sure the amount of rows and columns will fit in the number of
468     // colors we can use in the 9-patch format.
469     if (numRows * numCols > 0x7F) {
470         errorMsg = "Too many rows and columns in 9-patch perimeter";
471         goto getout;
472     }
473 
474     numColors = numRows * numCols;
475     image->info9Patch.numColors = numColors;
476     image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
477 
478     // Fill in color information for each patch.
479 
480     uint32_t c;
481     top = 0;
482 
483     // The first row always starts with the top being at y=0 and the bottom
484     // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
485     // the first row is stretchable along the Y axis, otherwise it is fixed.
486     // The last row always ends with the bottom being bitmap.height and the top
487     // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
488     // yDivs[numYDivs-1]. In the former case the last row is stretchable along
489     // the Y axis, otherwise it is fixed.
490     //
491     // The first and last columns are similarly treated with respect to the X
492     // axis.
493     //
494     // The above is to help explain some of the special casing that goes on the
495     // code below.
496 
497     // The initial yDiv and whether the first row is considered stretchable or
498     // not depends on whether yDiv[0] was zero or not.
499     for (j = (yDivs[0] == 0 ? 1 : 0);
500           j <= numYDivs && top < H;
501           j++) {
502         if (j == numYDivs) {
503             bottom = H;
504         } else {
505             bottom = yDivs[j];
506         }
507         left = 0;
508         // The initial xDiv and whether the first column is considered
509         // stretchable or not depends on whether xDiv[0] was zero or not.
510         for (i = xDivs[0] == 0 ? 1 : 0;
511               i <= numXDivs && left < W;
512               i++) {
513             if (i == numXDivs) {
514                 right = W;
515             } else {
516                 right = xDivs[i];
517             }
518             c = get_color(image->rows, left, top, right - 1, bottom - 1);
519             image->info9Patch.colors[colorIndex++] = c;
520             NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
521             left = right;
522         }
523         top = bottom;
524     }
525 
526     assert(colorIndex == numColors);
527 
528     for (i=0; i<numColors; i++) {
529         if (hasColor) {
530             if (i == 0) printf("Colors in %s:\n ", imageName);
531             printf(" #%08x", image->info9Patch.colors[i]);
532             if (i == numColors - 1) printf("\n");
533         }
534     }
535 
536     image->is9Patch = true;
537     image->info9Patch.deviceToFile();
538 
539 getout:
540     if (errorMsg) {
541         fprintf(stderr,
542             "ERROR: 9-patch image %s malformed.\n"
543             "       %s.\n", imageName, errorMsg);
544         if (errorEdge != NULL) {
545             if (errorPixel >= 0) {
546                 fprintf(stderr,
547                     "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
548             } else {
549                 fprintf(stderr,
550                     "       Found along %s edge.\n", errorEdge);
551             }
552         }
553         return UNKNOWN_ERROR;
554     }
555     return NO_ERROR;
556 }
557 
checkNinePatchSerialization(Res_png_9patch * inPatch,void * data)558 static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
559 {
560     if (sizeof(void*) != sizeof(int32_t)) {
561         // can't deserialize on a non-32 bit system
562         return;
563     }
564     size_t patchSize = inPatch->serializedSize();
565     void * newData = malloc(patchSize);
566     memcpy(newData, data, patchSize);
567     Res_png_9patch* outPatch = inPatch->deserialize(newData);
568     // deserialization is done in place, so outPatch == newData
569     assert(outPatch == newData);
570     assert(outPatch->numXDivs == inPatch->numXDivs);
571     assert(outPatch->numYDivs == inPatch->numYDivs);
572     assert(outPatch->paddingLeft == inPatch->paddingLeft);
573     assert(outPatch->paddingRight == inPatch->paddingRight);
574     assert(outPatch->paddingTop == inPatch->paddingTop);
575     assert(outPatch->paddingBottom == inPatch->paddingBottom);
576     for (int i = 0; i < outPatch->numXDivs; i++) {
577         assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
578     }
579     for (int i = 0; i < outPatch->numYDivs; i++) {
580         assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
581     }
582     for (int i = 0; i < outPatch->numColors; i++) {
583         assert(outPatch->colors[i] == inPatch->colors[i]);
584     }
585     free(newData);
586 }
587 
patch_equals(Res_png_9patch & patch1,Res_png_9patch & patch2)588 static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
589     if (!(patch1.numXDivs == patch2.numXDivs &&
590           patch1.numYDivs == patch2.numYDivs &&
591           patch1.numColors == patch2.numColors &&
592           patch1.paddingLeft == patch2.paddingLeft &&
593           patch1.paddingRight == patch2.paddingRight &&
594           patch1.paddingTop == patch2.paddingTop &&
595           patch1.paddingBottom == patch2.paddingBottom)) {
596             return false;
597     }
598     for (int i = 0; i < patch1.numColors; i++) {
599         if (patch1.colors[i] != patch2.colors[i]) {
600             return false;
601         }
602     }
603     for (int i = 0; i < patch1.numXDivs; i++) {
604         if (patch1.xDivs[i] != patch2.xDivs[i]) {
605             return false;
606         }
607     }
608     for (int i = 0; i < patch1.numYDivs; i++) {
609         if (patch1.yDivs[i] != patch2.yDivs[i]) {
610             return false;
611         }
612     }
613     return true;
614 }
615 
dump_image(int w,int h,png_bytepp rows,int color_type)616 static void dump_image(int w, int h, png_bytepp rows, int color_type)
617 {
618     int i, j, rr, gg, bb, aa;
619 
620     int bpp;
621     if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
622         bpp = 1;
623     } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
624         bpp = 2;
625     } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
626         // We use a padding byte even when there is no alpha
627         bpp = 4;
628     } else {
629         printf("Unknown color type %d.\n", color_type);
630     }
631 
632     for (j = 0; j < h; j++) {
633         png_bytep row = rows[j];
634         for (i = 0; i < w; i++) {
635             rr = row[0];
636             gg = row[1];
637             bb = row[2];
638             aa = row[3];
639             row += bpp;
640 
641             if (i == 0) {
642                 printf("Row %d:", j);
643             }
644             switch (bpp) {
645             case 1:
646                 printf(" (%d)", rr);
647                 break;
648             case 2:
649                 printf(" (%d %d", rr, gg);
650                 break;
651             case 3:
652                 printf(" (%d %d %d)", rr, gg, bb);
653                 break;
654             case 4:
655                 printf(" (%d %d %d %d)", rr, gg, bb, aa);
656                 break;
657             }
658             if (i == (w - 1)) {
659                 NOISY(printf("\n"));
660             }
661         }
662     }
663 }
664 
665 #define MAX(a,b) ((a)>(b)?(a):(b))
666 #define ABS(a)   ((a)<0?-(a):(a))
667 
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)668 static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
669                           png_colorp rgbPalette, png_bytep alphaPalette,
670                           int *paletteEntries, bool *hasTransparency, int *colorType,
671                           png_bytepp outRows)
672 {
673     int w = imageInfo.width;
674     int h = imageInfo.height;
675     int i, j, rr, gg, bb, aa, idx;
676     uint32_t colors[256], col;
677     int num_colors = 0;
678     int maxGrayDeviation = 0;
679 
680     bool isOpaque = true;
681     bool isPalette = true;
682     bool isGrayscale = true;
683 
684     // Scan the entire image and determine if:
685     // 1. Every pixel has R == G == B (grayscale)
686     // 2. Every pixel has A == 255 (opaque)
687     // 3. There are no more than 256 distinct RGBA colors
688 
689     // NOISY(printf("Initial image data:\n"));
690     // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
691 
692     for (j = 0; j < h; j++) {
693         png_bytep row = imageInfo.rows[j];
694         png_bytep out = outRows[j];
695         for (i = 0; i < w; i++) {
696             rr = *row++;
697             gg = *row++;
698             bb = *row++;
699             aa = *row++;
700 
701             int odev = maxGrayDeviation;
702             maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
703             maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
704             maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
705             if (maxGrayDeviation > odev) {
706                 NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
707                              maxGrayDeviation, i, j, rr, gg, bb, aa));
708             }
709 
710             // Check if image is really grayscale
711             if (isGrayscale) {
712                 if (rr != gg || rr != bb) {
713                      NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
714                                   i, j, rr, gg, bb, aa));
715                     isGrayscale = false;
716                 }
717             }
718 
719             // Check if image is really opaque
720             if (isOpaque) {
721                 if (aa != 0xff) {
722                     NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
723                                  i, j, rr, gg, bb, aa));
724                     isOpaque = false;
725                 }
726             }
727 
728             // Check if image is really <= 256 colors
729             if (isPalette) {
730                 col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
731                 bool match = false;
732                 for (idx = 0; idx < num_colors; idx++) {
733                     if (colors[idx] == col) {
734                         match = true;
735                         break;
736                     }
737                 }
738 
739                 // Write the palette index for the pixel to outRows optimistically
740                 // We might overwrite it later if we decide to encode as gray or
741                 // gray + alpha
742                 *out++ = idx;
743                 if (!match) {
744                     if (num_colors == 256) {
745                         NOISY(printf("Found 257th color at %d, %d\n", i, j));
746                         isPalette = false;
747                     } else {
748                         colors[num_colors++] = col;
749                     }
750                 }
751             }
752         }
753     }
754 
755     *paletteEntries = 0;
756     *hasTransparency = !isOpaque;
757     int bpp = isOpaque ? 3 : 4;
758     int paletteSize = w * h + bpp * num_colors;
759 
760     NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"));
761     NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false"));
762     NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false"));
763     NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
764                  paletteSize, 2 * w * h, bpp * w * h));
765     NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance));
766 
767     // Choose the best color type for the image.
768     // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
769     // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
770     //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
771     // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
772     //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
773     if (isGrayscale) {
774         if (isOpaque) {
775             *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
776         } else {
777             // Use a simple heuristic to determine whether using a palette will
778             // save space versus using gray + alpha for each pixel.
779             // This doesn't take into account chunk overhead, filtering, LZ
780             // compression, etc.
781             if (isPalette && (paletteSize < 2 * w * h)) {
782                 *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
783             } else {
784                 *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
785             }
786         }
787     } else if (isPalette && (paletteSize < bpp * w * h)) {
788         *colorType = PNG_COLOR_TYPE_PALETTE;
789     } else {
790         if (maxGrayDeviation <= grayscaleTolerance) {
791             printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
792             *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
793         } else {
794             *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
795         }
796     }
797 
798     // Perform postprocessing of the image or palette data based on the final
799     // color type chosen
800 
801     if (*colorType == PNG_COLOR_TYPE_PALETTE) {
802         // Create separate RGB and Alpha palettes and set the number of colors
803         *paletteEntries = num_colors;
804 
805         // Create the RGB and alpha palettes
806         for (int idx = 0; idx < num_colors; idx++) {
807             col = colors[idx];
808             rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
809             rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
810             rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
811             alphaPalette[idx]     = (png_byte)  (col        & 0xff);
812         }
813     } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
814         // If the image is gray or gray + alpha, compact the pixels into outRows
815         for (j = 0; j < h; j++) {
816             png_bytep row = imageInfo.rows[j];
817             png_bytep out = outRows[j];
818             for (i = 0; i < w; i++) {
819                 rr = *row++;
820                 gg = *row++;
821                 bb = *row++;
822                 aa = *row++;
823 
824                 if (isGrayscale) {
825                     *out++ = rr;
826                 } else {
827                     *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
828                 }
829                 if (!isOpaque) {
830                     *out++ = aa;
831                 }
832            }
833         }
834     }
835 }
836 
837 
write_png(const char * imageName,png_structp write_ptr,png_infop write_info,image_info & imageInfo,int grayscaleTolerance)838 static void write_png(const char* imageName,
839                       png_structp write_ptr, png_infop write_info,
840                       image_info& imageInfo, int grayscaleTolerance)
841 {
842     bool optimize = true;
843     png_uint_32 width, height;
844     int color_type;
845     int bit_depth, interlace_type, compression_type;
846     int i;
847 
848     png_unknown_chunk unknowns[1];
849     unknowns[0].data = NULL;
850 
851     png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
852     if (outRows == (png_bytepp) 0) {
853         printf("Can't allocate output buffer!\n");
854         exit(1);
855     }
856     for (i = 0; i < (int) imageInfo.height; i++) {
857         outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
858         if (outRows[i] == (png_bytep) 0) {
859             printf("Can't allocate output buffer!\n");
860             exit(1);
861         }
862     }
863 
864     png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
865 
866     NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName,
867           (int) imageInfo.width, (int) imageInfo.height));
868 
869     png_color rgbPalette[256];
870     png_byte alphaPalette[256];
871     bool hasTransparency;
872     int paletteEntries;
873 
874     analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
875                   &paletteEntries, &hasTransparency, &color_type, outRows);
876 
877     // If the image is a 9-patch, we need to preserve it as a ARGB file to make
878     // sure the pixels will not be pre-dithered/clamped until we decide they are
879     if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
880             color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
881         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
882     }
883 
884     switch (color_type) {
885     case PNG_COLOR_TYPE_PALETTE:
886         NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
887                      imageName, paletteEntries,
888                      hasTransparency ? " (with alpha)" : ""));
889         break;
890     case PNG_COLOR_TYPE_GRAY:
891         NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
892         break;
893     case PNG_COLOR_TYPE_GRAY_ALPHA:
894         NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
895         break;
896     case PNG_COLOR_TYPE_RGB:
897         NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
898         break;
899     case PNG_COLOR_TYPE_RGB_ALPHA:
900         NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
901         break;
902     }
903 
904     png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
905                  8, color_type, PNG_INTERLACE_NONE,
906                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
907 
908     if (color_type == PNG_COLOR_TYPE_PALETTE) {
909         png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
910         if (hasTransparency) {
911             png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
912         }
913        png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
914     } else {
915        png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
916     }
917 
918     if (imageInfo.is9Patch) {
919         NOISY(printf("Adding 9-patch info...\n"));
920         strcpy((char*)unknowns[0].name, "npTc");
921         unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
922         unknowns[0].size = imageInfo.info9Patch.serializedSize();
923         // TODO: remove the check below when everything works
924         checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
925         png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
926                                     (png_byte*)"npTc", 1);
927         png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
928         // XXX I can't get this to work without forcibly changing
929         // the location to what I want...  which apparently is supposed
930         // to be a private API, but everything else I have tried results
931         // in the location being set to what I -last- wrote so I never
932         // get written. :p
933         png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
934     }
935 
936     png_write_info(write_ptr, write_info);
937 
938     png_bytepp rows;
939     if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
940         png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
941         rows = imageInfo.rows;
942     } else {
943         rows = outRows;
944     }
945     png_write_image(write_ptr, rows);
946 
947 //     NOISY(printf("Final image data:\n"));
948 //     dump_image(imageInfo.width, imageInfo.height, rows, color_type);
949 
950     png_write_end(write_ptr, write_info);
951 
952     for (i = 0; i < (int) imageInfo.height; i++) {
953         free(outRows[i]);
954     }
955     free(outRows);
956     free(unknowns[0].data);
957 
958     png_get_IHDR(write_ptr, write_info, &width, &height,
959        &bit_depth, &color_type, &interlace_type,
960        &compression_type, NULL);
961 
962     NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
963                  (int)width, (int)height, bit_depth, color_type, interlace_type,
964                  compression_type));
965 }
966 
preProcessImage(Bundle * bundle,const sp<AaptAssets> & assets,const sp<AaptFile> & file,String8 * outNewLeafName)967 status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
968                          const sp<AaptFile>& file, String8* outNewLeafName)
969 {
970     String8 ext(file->getPath().getPathExtension());
971 
972     // We currently only process PNG images.
973     if (strcmp(ext.string(), ".png") != 0) {
974         return NO_ERROR;
975     }
976 
977     // Example of renaming a file:
978     //*outNewLeafName = file->getPath().getBasePath().getFileName();
979     //outNewLeafName->append(".nupng");
980 
981     String8 printableName(file->getPrintableSource());
982 
983     png_structp read_ptr = NULL;
984     png_infop read_info = NULL;
985     FILE* fp;
986 
987     image_info imageInfo;
988 
989     png_structp write_ptr = NULL;
990     png_infop write_info = NULL;
991 
992     status_t error = UNKNOWN_ERROR;
993 
994     const size_t nameLen = file->getPath().length();
995 
996     fp = fopen(file->getSourceFile().string(), "rb");
997     if (fp == NULL) {
998         fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
999         goto bail;
1000     }
1001 
1002     read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1003                                         (png_error_ptr)NULL);
1004     if (!read_ptr) {
1005         goto bail;
1006     }
1007 
1008     read_info = png_create_info_struct(read_ptr);
1009     if (!read_info) {
1010         goto bail;
1011     }
1012 
1013     if (setjmp(png_jmpbuf(read_ptr))) {
1014         goto bail;
1015     }
1016 
1017     png_init_io(read_ptr, fp);
1018 
1019     read_png(printableName.string(), read_ptr, read_info, &imageInfo);
1020 
1021     if (nameLen > 6) {
1022         const char* name = file->getPath().string();
1023         if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1024             if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
1025                 goto bail;
1026             }
1027         }
1028     }
1029 
1030     write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1031                                         (png_error_ptr)NULL);
1032     if (!write_ptr)
1033     {
1034         goto bail;
1035     }
1036 
1037     write_info = png_create_info_struct(write_ptr);
1038     if (!write_info)
1039     {
1040         goto bail;
1041     }
1042 
1043     png_set_write_fn(write_ptr, (void*)file.get(),
1044                      png_write_aapt_file, png_flush_aapt_file);
1045 
1046     if (setjmp(png_jmpbuf(write_ptr)))
1047     {
1048         goto bail;
1049     }
1050 
1051     write_png(printableName.string(), write_ptr, write_info, imageInfo,
1052               bundle->getGrayscaleTolerance());
1053 
1054     error = NO_ERROR;
1055 
1056     if (bundle->getVerbose()) {
1057         fseek(fp, 0, SEEK_END);
1058         size_t oldSize = (size_t)ftell(fp);
1059         size_t newSize = file->getSize();
1060         float factor = ((float)newSize)/oldSize;
1061         int percent = (int)(factor*100);
1062         printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
1063     }
1064 
1065 bail:
1066     if (read_ptr) {
1067         png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1068     }
1069     if (fp) {
1070         fclose(fp);
1071     }
1072     if (write_ptr) {
1073         png_destroy_write_struct(&write_ptr, &write_info);
1074     }
1075 
1076     if (error != NO_ERROR) {
1077         fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1078                 file->getPrintableSource().string());
1079     }
1080     return error;
1081 }
1082 
1083 
1084 
postProcessImage(const sp<AaptAssets> & assets,ResourceTable * table,const sp<AaptFile> & file)1085 status_t postProcessImage(const sp<AaptAssets>& assets,
1086                           ResourceTable* table, const sp<AaptFile>& file)
1087 {
1088     String8 ext(file->getPath().getPathExtension());
1089 
1090     // At this point, now that we have all the resource data, all we need to
1091     // do is compile XML files.
1092     if (strcmp(ext.string(), ".xml") == 0) {
1093         return compileXmlFile(assets, file, table);
1094     }
1095 
1096     return NO_ERROR;
1097 }
1098