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