2 // Copyright 2006 The Android Open Source Project 
   4 // Build resource files from raw assets. 
  11 #include <utils/ResourceTypes.h> 
  12 #include <utils/ByteOrder.h> 
  19 png_write_aapt_file(png_structp png_ptr
, png_bytep data
, png_size_t length
) 
  21     status_t err 
= ((AaptFile
*)png_ptr
->io_ptr
)->writeData(data
, length
); 
  22     if (err 
!= NO_ERROR
) { 
  23         png_error(png_ptr
, "Write Error"); 
  29 png_flush_aapt_file(png_structp png_ptr
) 
  33 // This holds an image as 8bpp RGBA. 
  36     image_info() : rows(NULL
), is9Patch(false), allocRows(NULL
) { } 
  38         if (rows 
&& rows 
!= allocRows
) { 
  42             for (int i
=0; i
<(int)allocHeight
; i
++) { 
  47         free(info9Patch
.xDivs
); 
  48         free(info9Patch
.yDivs
); 
  49         free(info9Patch
.colors
); 
  58     Res_png_9patch info9Patch
; 
  60     png_uint_32 allocHeight
; 
  64 static void read_png(const char* imageName
, 
  65                      png_structp read_ptr
, png_infop read_info
, 
  66                      image_info
* outImageInfo
) 
  69     int bit_depth
, interlace_type
, compression_type
; 
  72     png_read_info(read_ptr
, read_info
); 
  74     png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
, 
  75        &outImageInfo
->height
, &bit_depth
, &color_type
, 
  76        &interlace_type
, &compression_type
, NULL
); 
  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); 
  82     if (color_type 
== PNG_COLOR_TYPE_PALETTE
) 
  83         png_set_palette_to_rgb(read_ptr
); 
  85     if (color_type 
== PNG_COLOR_TYPE_GRAY 
&& bit_depth 
< 8) 
  86         png_set_gray_1_2_4_to_8(read_ptr
); 
  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
); 
  94         png_set_strip_16(read_ptr
); 
  96     if ((color_type
&PNG_COLOR_MASK_ALPHA
) == 0) 
  97         png_set_add_alpha(read_ptr
, 0xFF, PNG_FILLER_AFTER
); 
  99     if (color_type 
== PNG_COLOR_TYPE_GRAY 
|| color_type 
== PNG_COLOR_TYPE_GRAY_ALPHA
) 
 100         png_set_gray_to_rgb(read_ptr
); 
 102     png_read_update_info(read_ptr
, read_info
); 
 104     outImageInfo
->rows 
= (png_bytepp
)malloc( 
 105         outImageInfo
->height 
* png_sizeof(png_bytep
)); 
 106     outImageInfo
->allocHeight 
= outImageInfo
->height
; 
 107     outImageInfo
->allocRows 
= outImageInfo
->rows
; 
 109     png_set_rows(read_ptr
, read_info
, outImageInfo
->rows
); 
 111     for (i 
= 0; i 
< (int)outImageInfo
->height
; i
++) 
 113         outImageInfo
->rows
[i
] = (png_bytep
) 
 114             malloc(png_get_rowbytes(read_ptr
, read_info
)); 
 117     png_read_image(read_ptr
, outImageInfo
->rows
); 
 119     png_read_end(read_ptr
, read_info
); 
 121     NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n", 
 123                  (int)outImageInfo
->width
, (int)outImageInfo
->height
, 
 124                  bit_depth
, color_type
, 
 125                  interlace_type
, compression_type
)); 
 127     png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
, 
 128        &outImageInfo
->height
, &bit_depth
, &color_type
, 
 129        &interlace_type
, &compression_type
, NULL
); 
 132 static bool is_tick(png_bytep p
, bool transparent
, const char** outError
) 
 139             *outError 
= "Frame pixels must be either solid or transparent (not intermediate alphas)"; 
 142         if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) { 
 143             *outError 
= "Ticks in transparent frame must be black"; 
 149         *outError 
= "White frame must be a solid color (no alpha)"; 
 151     if (p
[0] == 0xFF && p
[1] == 0xFF && p
[2] == 0xFF) { 
 154     if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) { 
 155         *outError 
= "Ticks in white frame must be black"; 
 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
) 
 173     *outLeft 
= *outRight 
= -1; 
 174     int state 
= TICK_START
; 
 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
)) { 
 184                 if (outDivs 
!= NULL
) { 
 187                 state 
= TICK_INSIDE_1
; 
 188             } else if (state 
== TICK_OUTSIDE_1
) { 
 189                 *outError 
= "Can't have more than one marked region along edge"; 
 191                 return UNKNOWN_ERROR
; 
 193         } else if (*outError 
== NULL
) { 
 194             if (state 
== TICK_INSIDE_1
) { 
 195                 // We're done with this div.  Move on to the next. 
 199                 state 
= TICK_OUTSIDE_1
; 
 203             return UNKNOWN_ERROR
; 
 207     if (required 
&& !found
) { 
 208         *outError 
= "No marked region found along edge"; 
 210         return UNKNOWN_ERROR
; 
 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
) 
 222     *outTop 
= *outBottom 
= -1; 
 223     int state 
= TICK_START
; 
 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
)) { 
 231                 *outBottom 
= height
-2; 
 233                 if (outDivs 
!= NULL
) { 
 236                 state 
= TICK_INSIDE_1
; 
 237             } else if (state 
== TICK_OUTSIDE_1
) { 
 238                 *outError 
= "Can't have more than one marked region along edge"; 
 240                 return UNKNOWN_ERROR
; 
 242         } else if (*outError 
== NULL
) { 
 243             if (state 
== TICK_INSIDE_1
) { 
 244                 // We're done with this div.  Move on to the next. 
 248                 state 
= TICK_OUTSIDE_1
; 
 252             return UNKNOWN_ERROR
; 
 256     if (required 
&& !found
) { 
 257         *outError 
= "No marked region found along edge"; 
 259         return UNKNOWN_ERROR
; 
 265 static uint32_t get_color( 
 266     png_bytepp rows
, int left
, int top
, int right
, int bottom
) 
 268     png_bytep color 
= rows
[top
] + left
*4; 
 270     if (left 
> right 
|| top 
> bottom
) { 
 271         return Res_png_9patch::TRANSPARENT_COLOR
; 
 274     while (top 
<= bottom
) { 
 275         for (int i 
= left
; i 
<= right
; i
++) { 
 276             png_bytep p 
= rows
[top
]+i
*4; 
 279                     return Res_png_9patch::NO_COLOR
; 
 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
; 
 290         return Res_png_9patch::TRANSPARENT_COLOR
; 
 292     return (color
[3]<<24) | (color
[0]<<16) | (color
[1]<<8) | color
[2]; 
 295 static void select_patch( 
 296     int which
, int front
, int back
, int size
, int* start
, int* end
) 
 314 static uint32_t get_color(image_info
* image
, int hpatch
, int vpatch
) 
 316     int left
, right
, top
, bottom
; 
 318         hpatch
, image
->info9Patch
.xDivs
[0], image
->info9Patch
.xDivs
[1], 
 319         image
->width
, &left
, &right
); 
 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
)); 
 330 static status_t 
do_9patch(const char* imageName
, image_info
* image
) 
 332     image
->is9Patch 
= true; 
 334     int W 
= image
->width
; 
 335     int H 
= image
->height
; 
 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; 
 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; 
 356     png_bytep p 
= image
->rows
[0]; 
 357     bool transparent 
= p
[3] == 0; 
 358     bool hasColor 
= false; 
 360     const char* errorMsg 
= NULL
; 
 362     const char* errorEdge 
= NULL
; 
 367     if (W 
< 3 || H 
< 3) { 
 368         errorMsg 
= "Image must be at least 3x3 (1x1 without frame) pixels"; 
 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"; 
 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]; 
 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]; 
 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"; 
 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
; 
 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
; 
 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]; 
 422         // Adjust value to be correct! 
 423         image
->info9Patch
.paddingRight 
= W 
- 2 - image
->info9Patch
.paddingRight
; 
 425     if (image
->info9Patch
.paddingTop 
< 0) { 
 426         image
->info9Patch
.paddingTop 
= yDivs
[0]; 
 427         image
->info9Patch
.paddingBottom 
= H 
- 2 - yDivs
[1]; 
 429         // Adjust value to be correct! 
 430         image
->info9Patch
.paddingBottom 
= H 
- 2 - image
->info9Patch
.paddingBottom
; 
 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
)); 
 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); 
 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 
 456     if (xDivs
[numXDivs 
- 1] == W
) { 
 459     numRows 
= numYDivs 
+ 1; 
 460     if (yDivs
[0] == 0) {  // Row 1 is strechable 
 463     if (yDivs
[numYDivs 
- 1] == H
) { 
 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"; 
 474     numColors 
= numRows 
* numCols
; 
 475     image
->info9Patch
.numColors 
= numColors
; 
 476     image
->info9Patch
.colors 
= (uint32_t*)malloc(numColors 
* sizeof(uint32_t)); 
 478     // Fill in color information for each patch. 
 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. 
 491     // The first and last columns are similarly treated with respect to the X 
 494     // The above is to help explain some of the special casing that goes on the 
 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
; 
 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
; 
 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); 
 526     assert(colorIndex 
== numColors
); 
 528     for (i
=0; i
<numColors
; i
++) { 
 530             if (i 
== 0) printf("Colors in %s:\n ", imageName
); 
 531             printf(" #%08x", image
->info9Patch
.colors
[i
]); 
 532             if (i 
== numColors 
- 1) printf("\n"); 
 536     image
->is9Patch 
= true; 
 537     image
->info9Patch
.deviceToFile(); 
 542             "ERROR: 9-patch image %s malformed.\n" 
 543             "       %s.\n", imageName
, errorMsg
); 
 544         if (errorEdge 
!= NULL
) { 
 545             if (errorPixel 
>= 0) { 
 547                     "       Found at pixel #%d along %s edge.\n", errorPixel
, errorEdge
); 
 550                     "       Found along %s edge.\n", errorEdge
); 
 553         return UNKNOWN_ERROR
; 
 558 static void checkNinePatchSerialization(Res_png_9patch
* inPatch
,  void * data
) 
 560     if (sizeof(void*) != sizeof(int32_t)) { 
 561         // can't deserialize on a non-32 bit system 
 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
]); 
 579     for (int i 
= 0; i 
< outPatch
->numYDivs
; i
++) { 
 580         assert(outPatch
->yDivs
[i
] == inPatch
->yDivs
[i
]); 
 582     for (int i 
= 0; i 
< outPatch
->numColors
; i
++) { 
 583         assert(outPatch
->colors
[i
] == inPatch
->colors
[i
]); 
 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
)) { 
 598     for (int i 
= 0; i 
< patch1
.numColors
; i
++) { 
 599         if (patch1
.colors
[i
] != patch2
.colors
[i
]) { 
 603     for (int i 
= 0; i 
< patch1
.numXDivs
; i
++) { 
 604         if (patch1
.xDivs
[i
] != patch2
.xDivs
[i
]) { 
 608     for (int i 
= 0; i 
< patch1
.numYDivs
; i
++) { 
 609         if (patch1
.yDivs
[i
] != patch2
.yDivs
[i
]) { 
 616 static void dump_image(int w
, int h
, png_bytepp rows
, int color_type
) 
 618     int i
, j
, rr
, gg
, bb
, aa
; 
 621     if (color_type 
== PNG_COLOR_TYPE_PALETTE 
|| color_type 
== PNG_COLOR_TYPE_GRAY
) { 
 623     } else if (color_type 
== PNG_COLOR_TYPE_GRAY_ALPHA
) { 
 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 
 629         printf("Unknown color type %d.\n", color_type
); 
 632     for (j 
= 0; j 
< h
; j
++) { 
 633         png_bytep row 
= rows
[j
]; 
 634         for (i 
= 0; i 
< w
; i
++) { 
 642                 printf("Row %d:", j
); 
 649                 printf(" (%d %d", rr
, gg
); 
 652                 printf(" (%d %d %d)", rr
, gg
, bb
); 
 655                 printf(" (%d %d %d %d)", rr
, gg
, bb
, aa
); 
 665 #define MAX(a,b) ((a)>(b)?(a):(b)) 
 666 #define ABS(a)   ((a)<0?-(a):(a)) 
 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
, 
 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
; 
 678     int maxGrayDeviation 
= 0; 
 680     bool isOpaque 
= true; 
 681     bool isPalette 
= true; 
 682     bool isGrayscale 
= true; 
 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 
 689     // NOISY(printf("Initial image data:\n")); 
 690     // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA); 
 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
++) { 
 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
)); 
 710             // Check if image is really grayscale 
 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
)); 
 719             // Check if image is really opaque 
 722                     NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", 
 723                                  i
, j
, rr
, gg
, bb
, aa
)); 
 728             // Check if image is really <= 256 colors 
 730                 col 
= (uint32_t) ((rr 
<< 24) | (gg 
<< 16) | (bb 
<< 8) | aa
); 
 732                 for (idx 
= 0; idx 
< num_colors
; idx
++) { 
 733                     if (colors
[idx
] == col
) { 
 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 
 744                     if (num_colors 
== 256) { 
 745                         NOISY(printf("Found 257th color at %d, %d\n", i
, j
)); 
 748                         colors
[num_colors
++] = col
; 
 756     *hasTransparency 
= !isOpaque
; 
 757     int bpp 
= isOpaque 
? 3 : 4; 
 758     int paletteSize 
= w 
* h 
+ bpp 
* num_colors
; 
 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
)); 
 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} 
 775             *colorType 
= PNG_COLOR_TYPE_GRAY
; // 1 byte/pixel 
 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 
 781             if (isPalette 
&& (paletteSize 
< 2 * w 
* h
)) { 
 782                 *colorType 
= PNG_COLOR_TYPE_PALETTE
; // 1 byte/pixel + 4 bytes/color 
 784                 *colorType 
= PNG_COLOR_TYPE_GRAY_ALPHA
; // 2 bytes per pixel 
 787     } else if (isPalette 
&& (paletteSize 
< bpp 
* w 
* h
)) { 
 788         *colorType 
= PNG_COLOR_TYPE_PALETTE
; 
 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
; 
 794             *colorType 
= isOpaque 
? PNG_COLOR_TYPE_RGB 
: PNG_COLOR_TYPE_RGB_ALPHA
; 
 798     // Perform postprocessing of the image or palette data based on the final 
 801     if (*colorType 
== PNG_COLOR_TYPE_PALETTE
) { 
 802         // Create separate RGB and Alpha palettes and set the number of colors 
 803         *paletteEntries 
= num_colors
; 
 805         // Create the RGB and alpha palettes 
 806         for (int idx 
= 0; idx 
< num_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); 
 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
++) { 
 827                     *out
++ = (png_byte
) (rr 
* 0.2126f 
+ gg 
* 0.7152f 
+ bb 
* 0.0722f
); 
 838 static void write_png(const char* imageName
, 
 839                       png_structp write_ptr
, png_infop write_info
, 
 840                       image_info
& imageInfo
, int grayscaleTolerance
) 
 842     bool optimize 
= true; 
 843     png_uint_32 width
, height
; 
 845     int bit_depth
, interlace_type
, compression_type
; 
 848     png_unknown_chunk unknowns
[1]; 
 849     unknowns
[0].data 
= NULL
; 
 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"); 
 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"); 
 864     png_set_compression_level(write_ptr
, Z_BEST_COMPRESSION
); 
 866     NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName
, 
 867           (int) imageInfo
.width
, (int) imageInfo
.height
)); 
 869     png_color rgbPalette
[256]; 
 870     png_byte alphaPalette
[256]; 
 871     bool hasTransparency
; 
 874     analyze_image(imageName
, imageInfo
, grayscaleTolerance
, rgbPalette
, alphaPalette
, 
 875                   &paletteEntries
, &hasTransparency
, &color_type
, outRows
); 
 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
; 
 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)" : "")); 
 890     case PNG_COLOR_TYPE_GRAY
: 
 891         NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName
)); 
 893     case PNG_COLOR_TYPE_GRAY_ALPHA
: 
 894         NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName
)); 
 896     case PNG_COLOR_TYPE_RGB
: 
 897         NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName
)); 
 899     case PNG_COLOR_TYPE_RGB_ALPHA
: 
 900         NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName
)); 
 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
); 
 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); 
 913        png_set_filter(write_ptr
, 0, PNG_NO_FILTERS
); 
 915        png_set_filter(write_ptr
, 0, PNG_ALL_FILTERS
); 
 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 
 933         png_set_unknown_chunk_location(write_ptr
, write_info
, 0, PNG_HAVE_PLTE
); 
 936     png_write_info(write_ptr
, write_info
); 
 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
; 
 945     png_write_image(write_ptr
, rows
); 
 947 //     NOISY(printf("Final image data:\n")); 
 948 //     dump_image(imageInfo.width, imageInfo.height, rows, color_type); 
 950     png_write_end(write_ptr
, write_info
); 
 952     for (i 
= 0; i 
< (int) imageInfo
.height
; i
++) { 
 956     free(unknowns
[0].data
); 
 958     png_get_IHDR(write_ptr
, write_info
, &width
, &height
, 
 959        &bit_depth
, &color_type
, &interlace_type
, 
 960        &compression_type
, NULL
); 
 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
, 
 967 status_t 
preProcessImage(Bundle
* bundle
, const sp
<AaptAssets
>& assets
, 
 968                          const sp
<AaptFile
>& file
, String8
* outNewLeafName
) 
 970     String8 
ext(file
->getPath().getPathExtension()); 
 972     // We currently only process PNG images. 
 973     if (strcmp(ext
.string(), ".png") != 0) { 
 977     // Example of renaming a file: 
 978     //*outNewLeafName = file->getPath().getBasePath().getFileName(); 
 979     //outNewLeafName->append(".nupng"); 
 981     String8 
printableName(file
->getPrintableSource()); 
 983     png_structp read_ptr 
= NULL
; 
 984     png_infop read_info 
= NULL
; 
 987     image_info imageInfo
; 
 989     png_structp write_ptr 
= NULL
; 
 990     png_infop write_info 
= NULL
; 
 992     status_t error 
= UNKNOWN_ERROR
; 
 994     const size_t nameLen 
= file
->getPath().length(); 
 996     fp 
= fopen(file
->getSourceFile().string(), "rb"); 
 998         fprintf(stderr
, "%s: ERROR: Unable to open PNG file\n", printableName
.string()); 
1002     read_ptr 
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
, 
1003                                         (png_error_ptr
)NULL
); 
1008     read_info 
= png_create_info_struct(read_ptr
); 
1013     if (setjmp(png_jmpbuf(read_ptr
))) { 
1017     png_init_io(read_ptr
, fp
); 
1019     read_png(printableName
.string(), read_ptr
, read_info
, &imageInfo
); 
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
) { 
1030     write_ptr 
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
, 
1031                                         (png_error_ptr
)NULL
); 
1037     write_info 
= png_create_info_struct(write_ptr
); 
1043     png_set_write_fn(write_ptr
, (void*)file
.get(), 
1044                      png_write_aapt_file
, png_flush_aapt_file
); 
1046     if (setjmp(png_jmpbuf(write_ptr
))) 
1051     write_png(printableName
.string(), write_ptr
, write_info
, imageInfo
, 
1052               bundle
->getGrayscaleTolerance()); 
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
); 
1067         png_destroy_read_struct(&read_ptr
, &read_info
, (png_infopp
)NULL
); 
1073         png_destroy_write_struct(&write_ptr
, &write_info
); 
1076     if (error 
!= NO_ERROR
) { 
1077         fprintf(stderr
, "ERROR: Failure processing PNG image %s\n", 
1078                 file
->getPrintableSource().string()); 
1085 status_t 
postProcessImage(const sp
<AaptAssets
>& assets
, 
1086                           ResourceTable
* table
, const sp
<AaptFile
>& file
) 
1088     String8 
ext(file
->getPath().getPathExtension()); 
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
);