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