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 
= ""; 
 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
) { 
 466     numColors 
= numRows 
* numCols
; 
 467     image
->info9Patch
.numColors 
= numColors
; 
 468     image
->info9Patch
.colors 
= (uint32_t*)malloc(numColors 
* sizeof(uint32_t)); 
 470     // Fill in color information for each patch. 
 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. 
 483     // The first and last columns are similarly treated with respect to the X 
 486     // The above is to help explain some of the special casing that goes on the 
 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
; 
 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
; 
 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); 
 518     assert(colorIndex 
== numColors
); 
 520     for (i
=0; i
<numColors
; i
++) { 
 522             if (i 
== 0) printf("Colors in %s:\n ", imageName
); 
 523             printf(" #%08x", image
->info9Patch
.colors
[i
]); 
 524             if (i 
== numColors 
- 1) printf("\n"); 
 528     image
->is9Patch 
= true; 
 529     image
->info9Patch
.deviceToFile(); 
 534             "ERROR: 9-patch image %s malformed.\n" 
 535             "       %s.\n", imageName
, errorMsg
); 
 536         if (errorPixel 
>= 0) { 
 538             "       Found at pixel #%d along %s edge.\n", errorPixel
, errorEdge
); 
 541             "       Found along %s edge.\n", errorEdge
); 
 543         return UNKNOWN_ERROR
; 
 548 static void checkNinePatchSerialization(Res_png_9patch
* inPatch
,  void * data
) 
 550     if (sizeof(void*) != sizeof(int32_t)) { 
 551         // can't deserialize on a non-32 bit system 
 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
]); 
 569     for (int i 
= 0; i 
< outPatch
->numYDivs
; i
++) { 
 570         assert(outPatch
->yDivs
[i
] == inPatch
->yDivs
[i
]); 
 572     for (int i 
= 0; i 
< outPatch
->numColors
; i
++) { 
 573         assert(outPatch
->colors
[i
] == inPatch
->colors
[i
]); 
 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
)) { 
 588     for (int i 
= 0; i 
< patch1
.numColors
; i
++) { 
 589         if (patch1
.colors
[i
] != patch2
.colors
[i
]) { 
 593     for (int i 
= 0; i 
< patch1
.numXDivs
; i
++) { 
 594         if (patch1
.xDivs
[i
] != patch2
.xDivs
[i
]) { 
 598     for (int i 
= 0; i 
< patch1
.numYDivs
; i
++) { 
 599         if (patch1
.yDivs
[i
] != patch2
.yDivs
[i
]) { 
 606 static void dump_image(int w
, int h
, png_bytepp rows
, int color_type
) 
 608     int i
, j
, rr
, gg
, bb
, aa
; 
 611     if (color_type 
== PNG_COLOR_TYPE_PALETTE 
|| color_type 
== PNG_COLOR_TYPE_GRAY
) { 
 613     } else if (color_type 
== PNG_COLOR_TYPE_GRAY_ALPHA
) { 
 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 
 619         printf("Unknown color type %d.\n", color_type
); 
 622     for (j 
= 0; j 
< h
; j
++) { 
 623         png_bytep row 
= rows
[j
]; 
 624         for (i 
= 0; i 
< w
; i
++) { 
 632                 printf("Row %d:", j
); 
 639                 printf(" (%d %d", rr
, gg
); 
 642                 printf(" (%d %d %d)", rr
, gg
, bb
); 
 645                 printf(" (%d %d %d %d)", rr
, gg
, bb
, aa
); 
 655 #define MAX(a,b) ((a)>(b)?(a):(b)) 
 656 #define ABS(a)   ((a)<0?-(a):(a)) 
 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
, 
 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
; 
 668     int maxGrayDeviation 
= 0; 
 670     bool isOpaque 
= true; 
 671     bool isPalette 
= true; 
 672     bool isGrayscale 
= true; 
 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 
 679     // NOISY(printf("Initial image data:\n")); 
 680     // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA); 
 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
++) { 
 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
)); 
 700             // Check if image is really grayscale 
 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
)); 
 709             // Check if image is really opaque 
 712                     NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", 
 713                                  i
, j
, rr
, gg
, bb
, aa
)); 
 718             // Check if image is really <= 256 colors 
 720                 col 
= (uint32_t) ((rr 
<< 24) | (gg 
<< 16) | (bb 
<< 8) | aa
); 
 722                 for (idx 
= 0; idx 
< num_colors
; idx
++) { 
 723                     if (colors
[idx
] == col
) { 
 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 
 734                     if (num_colors 
== 256) { 
 735                         NOISY(printf("Found 257th color at %d, %d\n", i
, j
)); 
 738                         colors
[num_colors
++] = col
; 
 746     *hasTransparency 
= !isOpaque
; 
 747     int bpp 
= isOpaque 
? 3 : 4; 
 748     int paletteSize 
= w 
* h 
+ bpp 
* num_colors
; 
 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
)); 
 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} 
 765             *colorType 
= PNG_COLOR_TYPE_GRAY
; // 1 byte/pixel 
 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 
 771             if (isPalette 
&& (paletteSize 
< 2 * w 
* h
)) { 
 772                 *colorType 
= PNG_COLOR_TYPE_PALETTE
; // 1 byte/pixel + 4 bytes/color 
 774                 *colorType 
= PNG_COLOR_TYPE_GRAY_ALPHA
; // 2 bytes per pixel 
 777     } else if (isPalette 
&& (paletteSize 
< bpp 
* w 
* h
)) { 
 778         *colorType 
= PNG_COLOR_TYPE_PALETTE
; 
 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
; 
 784             *colorType 
= isOpaque 
? PNG_COLOR_TYPE_RGB 
: PNG_COLOR_TYPE_RGB_ALPHA
; 
 788     // Perform postprocessing of the image or palette data based on the final 
 791     if (*colorType 
== PNG_COLOR_TYPE_PALETTE
) { 
 792         // Create separate RGB and Alpha palettes and set the number of colors 
 793         *paletteEntries 
= num_colors
; 
 795         // Create the RGB and alpha palettes 
 796         for (int idx 
= 0; idx 
< num_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); 
 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
++) { 
 817                     *out
++ = (png_byte
) (rr 
* 0.2126f 
+ gg 
* 0.7152f 
+ bb 
* 0.0722f
); 
 828 static void write_png(const char* imageName
, 
 829                       png_structp write_ptr
, png_infop write_info
, 
 830                       image_info
& imageInfo
, int grayscaleTolerance
) 
 832     bool optimize 
= true; 
 833     png_uint_32 width
, height
; 
 835     int bit_depth
, interlace_type
, compression_type
; 
 838     png_unknown_chunk unknowns
[1]; 
 839     unknowns
[0].data 
= NULL
; 
 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"); 
 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"); 
 854     png_set_compression_level(write_ptr
, Z_BEST_COMPRESSION
); 
 856     NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName
, 
 857           (int) imageInfo
.width
, (int) imageInfo
.height
)); 
 859     png_color rgbPalette
[256]; 
 860     png_byte alphaPalette
[256]; 
 861     bool hasTransparency
; 
 864     analyze_image(imageName
, imageInfo
, grayscaleTolerance
, rgbPalette
, alphaPalette
, 
 865                   &paletteEntries
, &hasTransparency
, &color_type
, outRows
); 
 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
; 
 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)" : "")); 
 880     case PNG_COLOR_TYPE_GRAY
: 
 881         NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName
)); 
 883     case PNG_COLOR_TYPE_GRAY_ALPHA
: 
 884         NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName
)); 
 886     case PNG_COLOR_TYPE_RGB
: 
 887         NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName
)); 
 889     case PNG_COLOR_TYPE_RGB_ALPHA
: 
 890         NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName
)); 
 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
); 
 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); 
 903        png_set_filter(write_ptr
, 0, PNG_NO_FILTERS
); 
 905        png_set_filter(write_ptr
, 0, PNG_ALL_FILTERS
); 
 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 
 923         png_set_unknown_chunk_location(write_ptr
, write_info
, 0, PNG_HAVE_PLTE
); 
 926     png_write_info(write_ptr
, write_info
); 
 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
; 
 935     png_write_image(write_ptr
, rows
); 
 937 //     NOISY(printf("Final image data:\n")); 
 938 //     dump_image(imageInfo.width, imageInfo.height, rows, color_type); 
 940     png_write_end(write_ptr
, write_info
); 
 942     for (i 
= 0; i 
< (int) imageInfo
.height
; i
++) { 
 946     free(unknowns
[0].data
); 
 948     png_get_IHDR(write_ptr
, write_info
, &width
, &height
, 
 949        &bit_depth
, &color_type
, &interlace_type
, 
 950        &compression_type
, NULL
); 
 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
, 
 957 status_t 
preProcessImage(Bundle
* bundle
, const sp
<AaptAssets
>& assets
, 
 958                          const sp
<AaptFile
>& file
, String8
* outNewLeafName
) 
 960     String8 
ext(file
->getPath().getPathExtension()); 
 962     // We currently only process PNG images. 
 963     if (strcmp(ext
.string(), ".png") != 0) { 
 967     // Example of renaming a file: 
 968     //*outNewLeafName = file->getPath().getBasePath().getFileName(); 
 969     //outNewLeafName->append(".nupng"); 
 971     String8 
printableName(file
->getPrintableSource()); 
 973     png_structp read_ptr 
= NULL
; 
 974     png_infop read_info 
= NULL
; 
 977     image_info imageInfo
; 
 979     png_structp write_ptr 
= NULL
; 
 980     png_infop write_info 
= NULL
; 
 982     status_t error 
= UNKNOWN_ERROR
; 
 984     const size_t nameLen 
= file
->getPath().length(); 
 986     fp 
= fopen(file
->getSourceFile().string(), "rb"); 
 988         fprintf(stderr
, "%s: ERROR: Unable to open PNG file\n", printableName
.string()); 
 992     read_ptr 
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
, 
 993                                         (png_error_ptr
)NULL
); 
 998     read_info 
= png_create_info_struct(read_ptr
); 
1003     if (setjmp(png_jmpbuf(read_ptr
))) { 
1007     png_init_io(read_ptr
, fp
); 
1009     read_png(printableName
.string(), read_ptr
, read_info
, &imageInfo
); 
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
) { 
1020     write_ptr 
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
, 
1021                                         (png_error_ptr
)NULL
); 
1027     write_info 
= png_create_info_struct(write_ptr
); 
1033     png_set_write_fn(write_ptr
, (void*)file
.get(), 
1034                      png_write_aapt_file
, png_flush_aapt_file
); 
1036     if (setjmp(png_jmpbuf(write_ptr
))) 
1041     write_png(printableName
.string(), write_ptr
, write_info
, imageInfo
, 
1042               bundle
->getGrayscaleTolerance()); 
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
); 
1057         png_destroy_read_struct(&read_ptr
, &read_info
, (png_infopp
)NULL
); 
1063         png_destroy_write_struct(&write_ptr
, &write_info
); 
1066     if (error 
!= NO_ERROR
) { 
1067         fprintf(stderr
, "ERROR: Failure processing PNG image %s\n", 
1068                 file
->getPrintableSource().string()); 
1075 status_t 
postProcessImage(const sp
<AaptAssets
>& assets
, 
1076                           ResourceTable
* table
, const sp
<AaptFile
>& file
) 
1078     String8 
ext(file
->getPath().getPathExtension()); 
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
);