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