Images.cpp revision 6c16fff219e8f733ea14ed7df9edda9c037d4b71
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 = (int32_t*) malloc(maxSizeXDivs);
456    int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs);
457    uint8_t  numXDivs = 0;
458    uint8_t  numYDivs = 0;
459    int8_t numColors;
460    int numRows;
461    int numCols;
462    int top;
463    int left;
464    int right;
465    int bottom;
466    memset(xDivs, -1, maxSizeXDivs);
467    memset(yDivs, -1, maxSizeYDivs);
468    image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
469        image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
470
471    image->layoutBoundsLeft = image->layoutBoundsRight =
472        image->layoutBoundsTop = image->layoutBoundsBottom = 0;
473
474    png_bytep p = image->rows[0];
475    bool transparent = p[3] == 0;
476    bool hasColor = false;
477
478    const char* errorMsg = NULL;
479    int errorPixel = -1;
480    const char* errorEdge = NULL;
481
482    int colorIndex = 0;
483
484    // Validate size...
485    if (W < 3 || H < 3) {
486        errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
487        goto getout;
488    }
489
490    // Validate frame...
491    if (!transparent &&
492        (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
493        errorMsg = "Must have one-pixel frame that is either transparent or white";
494        goto getout;
495    }
496
497    // Find left and right of sizing areas...
498    if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
499                             &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
500        errorPixel = xDivs[0];
501        errorEdge = "top";
502        goto getout;
503    }
504
505    // Find top and bottom of sizing areas...
506    if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
507                           &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
508        errorPixel = yDivs[0];
509        errorEdge = "left";
510        goto getout;
511    }
512
513    // Find left and right of padding area...
514    if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
515                             &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
516        errorPixel = image->info9Patch.paddingLeft;
517        errorEdge = "bottom";
518        goto getout;
519    }
520
521    // Find top and bottom of padding area...
522    if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
523                           &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
524        errorPixel = image->info9Patch.paddingTop;
525        errorEdge = "right";
526        goto getout;
527    }
528
529    // Find left and right of layout padding...
530    get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
531                                        &image->layoutBoundsLeft,
532                                        &image->layoutBoundsRight, &errorMsg);
533
534    get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
535                                        &image->layoutBoundsTop,
536                                        &image->layoutBoundsBottom, &errorMsg);
537
538    image->haveLayoutBounds = image->layoutBoundsLeft != 0
539                               || image->layoutBoundsRight != 0
540                               || image->layoutBoundsTop != 0
541                               || image->layoutBoundsBottom != 0;
542
543    if (image->haveLayoutBounds) {
544        NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
545                image->layoutBoundsRight, image->layoutBoundsBottom));
546    }
547
548    // Copy patch data into image
549    image->info9Patch.numXDivs = numXDivs;
550    image->info9Patch.numYDivs = numYDivs;
551    image->info9Patch.xDivs = xDivs;
552    image->info9Patch.yDivs = yDivs;
553
554    // If padding is not yet specified, take values from size.
555    if (image->info9Patch.paddingLeft < 0) {
556        image->info9Patch.paddingLeft = xDivs[0];
557        image->info9Patch.paddingRight = W - 2 - xDivs[1];
558    } else {
559        // Adjust value to be correct!
560        image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
561    }
562    if (image->info9Patch.paddingTop < 0) {
563        image->info9Patch.paddingTop = yDivs[0];
564        image->info9Patch.paddingBottom = H - 2 - yDivs[1];
565    } else {
566        // Adjust value to be correct!
567        image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
568    }
569
570    NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
571                 image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
572                 image->info9Patch.yDivs[0], image->info9Patch.yDivs[1]));
573    NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
574                 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
575                 image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
576
577    // Remove frame from image.
578    image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep));
579    for (i=0; i<(H-2); i++) {
580        image->rows[i] = image->allocRows[i+1];
581        memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
582    }
583    image->width -= 2;
584    W = image->width;
585    image->height -= 2;
586    H = image->height;
587
588    // Figure out the number of rows and columns in the N-patch
589    numCols = numXDivs + 1;
590    if (xDivs[0] == 0) {  // Column 1 is strechable
591        numCols--;
592    }
593    if (xDivs[numXDivs - 1] == W) {
594        numCols--;
595    }
596    numRows = numYDivs + 1;
597    if (yDivs[0] == 0) {  // Row 1 is strechable
598        numRows--;
599    }
600    if (yDivs[numYDivs - 1] == H) {
601        numRows--;
602    }
603
604    // Make sure the amount of rows and columns will fit in the number of
605    // colors we can use in the 9-patch format.
606    if (numRows * numCols > 0x7F) {
607        errorMsg = "Too many rows and columns in 9-patch perimeter";
608        goto getout;
609    }
610
611    numColors = numRows * numCols;
612    image->info9Patch.numColors = numColors;
613    image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
614
615    // Fill in color information for each patch.
616
617    uint32_t c;
618    top = 0;
619
620    // The first row always starts with the top being at y=0 and the bottom
621    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
622    // the first row is stretchable along the Y axis, otherwise it is fixed.
623    // The last row always ends with the bottom being bitmap.height and the top
624    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
625    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
626    // the Y axis, otherwise it is fixed.
627    //
628    // The first and last columns are similarly treated with respect to the X
629    // axis.
630    //
631    // The above is to help explain some of the special casing that goes on the
632    // code below.
633
634    // The initial yDiv and whether the first row is considered stretchable or
635    // not depends on whether yDiv[0] was zero or not.
636    for (j = (yDivs[0] == 0 ? 1 : 0);
637          j <= numYDivs && top < H;
638          j++) {
639        if (j == numYDivs) {
640            bottom = H;
641        } else {
642            bottom = yDivs[j];
643        }
644        left = 0;
645        // The initial xDiv and whether the first column is considered
646        // stretchable or not depends on whether xDiv[0] was zero or not.
647        for (i = xDivs[0] == 0 ? 1 : 0;
648              i <= numXDivs && left < W;
649              i++) {
650            if (i == numXDivs) {
651                right = W;
652            } else {
653                right = xDivs[i];
654            }
655            c = get_color(image->rows, left, top, right - 1, bottom - 1);
656            image->info9Patch.colors[colorIndex++] = c;
657            NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
658            left = right;
659        }
660        top = bottom;
661    }
662
663    assert(colorIndex == numColors);
664
665    for (i=0; i<numColors; i++) {
666        if (hasColor) {
667            if (i == 0) printf("Colors in %s:\n ", imageName);
668            printf(" #%08x", image->info9Patch.colors[i]);
669            if (i == numColors - 1) printf("\n");
670        }
671    }
672
673    image->is9Patch = true;
674    image->info9Patch.deviceToFile();
675
676getout:
677    if (errorMsg) {
678        fprintf(stderr,
679            "ERROR: 9-patch image %s malformed.\n"
680            "       %s.\n", imageName, errorMsg);
681        if (errorEdge != NULL) {
682            if (errorPixel >= 0) {
683                fprintf(stderr,
684                    "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
685            } else {
686                fprintf(stderr,
687                    "       Found along %s edge.\n", errorEdge);
688            }
689        }
690        return UNKNOWN_ERROR;
691    }
692    return NO_ERROR;
693}
694
695static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
696{
697    if (sizeof(void*) != sizeof(int32_t)) {
698        // can't deserialize on a non-32 bit system
699        return;
700    }
701    size_t patchSize = inPatch->serializedSize();
702    void * newData = malloc(patchSize);
703    memcpy(newData, data, patchSize);
704    Res_png_9patch* outPatch = inPatch->deserialize(newData);
705    // deserialization is done in place, so outPatch == newData
706    assert(outPatch == newData);
707    assert(outPatch->numXDivs == inPatch->numXDivs);
708    assert(outPatch->numYDivs == inPatch->numYDivs);
709    assert(outPatch->paddingLeft == inPatch->paddingLeft);
710    assert(outPatch->paddingRight == inPatch->paddingRight);
711    assert(outPatch->paddingTop == inPatch->paddingTop);
712    assert(outPatch->paddingBottom == inPatch->paddingBottom);
713    for (int i = 0; i < outPatch->numXDivs; i++) {
714        assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
715    }
716    for (int i = 0; i < outPatch->numYDivs; i++) {
717        assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
718    }
719    for (int i = 0; i < outPatch->numColors; i++) {
720        assert(outPatch->colors[i] == inPatch->colors[i]);
721    }
722    free(newData);
723}
724
725static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
726    if (!(patch1.numXDivs == patch2.numXDivs &&
727          patch1.numYDivs == patch2.numYDivs &&
728          patch1.numColors == patch2.numColors &&
729          patch1.paddingLeft == patch2.paddingLeft &&
730          patch1.paddingRight == patch2.paddingRight &&
731          patch1.paddingTop == patch2.paddingTop &&
732          patch1.paddingBottom == patch2.paddingBottom)) {
733            return false;
734    }
735    for (int i = 0; i < patch1.numColors; i++) {
736        if (patch1.colors[i] != patch2.colors[i]) {
737            return false;
738        }
739    }
740    for (int i = 0; i < patch1.numXDivs; i++) {
741        if (patch1.xDivs[i] != patch2.xDivs[i]) {
742            return false;
743        }
744    }
745    for (int i = 0; i < patch1.numYDivs; i++) {
746        if (patch1.yDivs[i] != patch2.yDivs[i]) {
747            return false;
748        }
749    }
750    return true;
751}
752
753static void dump_image(int w, int h, png_bytepp rows, int color_type)
754{
755    int i, j, rr, gg, bb, aa;
756
757    int bpp;
758    if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
759        bpp = 1;
760    } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
761        bpp = 2;
762    } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
763        // We use a padding byte even when there is no alpha
764        bpp = 4;
765    } else {
766        printf("Unknown color type %d.\n", color_type);
767    }
768
769    for (j = 0; j < h; j++) {
770        png_bytep row = rows[j];
771        for (i = 0; i < w; i++) {
772            rr = row[0];
773            gg = row[1];
774            bb = row[2];
775            aa = row[3];
776            row += bpp;
777
778            if (i == 0) {
779                printf("Row %d:", j);
780            }
781            switch (bpp) {
782            case 1:
783                printf(" (%d)", rr);
784                break;
785            case 2:
786                printf(" (%d %d", rr, gg);
787                break;
788            case 3:
789                printf(" (%d %d %d)", rr, gg, bb);
790                break;
791            case 4:
792                printf(" (%d %d %d %d)", rr, gg, bb, aa);
793                break;
794            }
795            if (i == (w - 1)) {
796                NOISY(printf("\n"));
797            }
798        }
799    }
800}
801
802#define MAX(a,b) ((a)>(b)?(a):(b))
803#define ABS(a)   ((a)<0?-(a):(a))
804
805static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
806                          png_colorp rgbPalette, png_bytep alphaPalette,
807                          int *paletteEntries, bool *hasTransparency, int *colorType,
808                          png_bytepp outRows)
809{
810    int w = imageInfo.width;
811    int h = imageInfo.height;
812    int i, j, rr, gg, bb, aa, idx;
813    uint32_t colors[256], col;
814    int num_colors = 0;
815    int maxGrayDeviation = 0;
816
817    bool isOpaque = true;
818    bool isPalette = true;
819    bool isGrayscale = true;
820
821    // Scan the entire image and determine if:
822    // 1. Every pixel has R == G == B (grayscale)
823    // 2. Every pixel has A == 255 (opaque)
824    // 3. There are no more than 256 distinct RGBA colors
825
826    // NOISY(printf("Initial image data:\n"));
827    // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
828
829    for (j = 0; j < h; j++) {
830        png_bytep row = imageInfo.rows[j];
831        png_bytep out = outRows[j];
832        for (i = 0; i < w; i++) {
833            rr = *row++;
834            gg = *row++;
835            bb = *row++;
836            aa = *row++;
837
838            int odev = maxGrayDeviation;
839            maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
840            maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
841            maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
842            if (maxGrayDeviation > odev) {
843                NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
844                             maxGrayDeviation, i, j, rr, gg, bb, aa));
845            }
846
847            // Check if image is really grayscale
848            if (isGrayscale) {
849                if (rr != gg || rr != bb) {
850                     NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
851                                  i, j, rr, gg, bb, aa));
852                    isGrayscale = false;
853                }
854            }
855
856            // Check if image is really opaque
857            if (isOpaque) {
858                if (aa != 0xff) {
859                    NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
860                                 i, j, rr, gg, bb, aa));
861                    isOpaque = false;
862                }
863            }
864
865            // Check if image is really <= 256 colors
866            if (isPalette) {
867                col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
868                bool match = false;
869                for (idx = 0; idx < num_colors; idx++) {
870                    if (colors[idx] == col) {
871                        match = true;
872                        break;
873                    }
874                }
875
876                // Write the palette index for the pixel to outRows optimistically
877                // We might overwrite it later if we decide to encode as gray or
878                // gray + alpha
879                *out++ = idx;
880                if (!match) {
881                    if (num_colors == 256) {
882                        NOISY(printf("Found 257th color at %d, %d\n", i, j));
883                        isPalette = false;
884                    } else {
885                        colors[num_colors++] = col;
886                    }
887                }
888            }
889        }
890    }
891
892    *paletteEntries = 0;
893    *hasTransparency = !isOpaque;
894    int bpp = isOpaque ? 3 : 4;
895    int paletteSize = w * h + bpp * num_colors;
896
897    NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"));
898    NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false"));
899    NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false"));
900    NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
901                 paletteSize, 2 * w * h, bpp * w * h));
902    NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance));
903
904    // Choose the best color type for the image.
905    // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
906    // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
907    //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
908    // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
909    //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
910    if (isGrayscale) {
911        if (isOpaque) {
912            *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
913        } else {
914            // Use a simple heuristic to determine whether using a palette will
915            // save space versus using gray + alpha for each pixel.
916            // This doesn't take into account chunk overhead, filtering, LZ
917            // compression, etc.
918            if (isPalette && (paletteSize < 2 * w * h)) {
919                *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
920            } else {
921                *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
922            }
923        }
924    } else if (isPalette && (paletteSize < bpp * w * h)) {
925        *colorType = PNG_COLOR_TYPE_PALETTE;
926    } else {
927        if (maxGrayDeviation <= grayscaleTolerance) {
928            printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
929            *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
930        } else {
931            *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
932        }
933    }
934
935    // Perform postprocessing of the image or palette data based on the final
936    // color type chosen
937
938    if (*colorType == PNG_COLOR_TYPE_PALETTE) {
939        // Create separate RGB and Alpha palettes and set the number of colors
940        *paletteEntries = num_colors;
941
942        // Create the RGB and alpha palettes
943        for (int idx = 0; idx < num_colors; idx++) {
944            col = colors[idx];
945            rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
946            rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
947            rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
948            alphaPalette[idx]     = (png_byte)  (col        & 0xff);
949        }
950    } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
951        // If the image is gray or gray + alpha, compact the pixels into outRows
952        for (j = 0; j < h; j++) {
953            png_bytep row = imageInfo.rows[j];
954            png_bytep out = outRows[j];
955            for (i = 0; i < w; i++) {
956                rr = *row++;
957                gg = *row++;
958                bb = *row++;
959                aa = *row++;
960
961                if (isGrayscale) {
962                    *out++ = rr;
963                } else {
964                    *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
965                }
966                if (!isOpaque) {
967                    *out++ = aa;
968                }
969           }
970        }
971    }
972}
973
974
975static void write_png(const char* imageName,
976                      png_structp write_ptr, png_infop write_info,
977                      image_info& imageInfo, int grayscaleTolerance)
978{
979    bool optimize = true;
980    png_uint_32 width, height;
981    int color_type;
982    int bit_depth, interlace_type, compression_type;
983    int i;
984
985    png_unknown_chunk unknowns[2];
986    unknowns[0].data = NULL;
987    unknowns[1].data = NULL;
988
989    png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
990    if (outRows == (png_bytepp) 0) {
991        printf("Can't allocate output buffer!\n");
992        exit(1);
993    }
994    for (i = 0; i < (int) imageInfo.height; i++) {
995        outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
996        if (outRows[i] == (png_bytep) 0) {
997            printf("Can't allocate output buffer!\n");
998            exit(1);
999        }
1000    }
1001
1002    png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
1003
1004    NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName,
1005          (int) imageInfo.width, (int) imageInfo.height));
1006
1007    png_color rgbPalette[256];
1008    png_byte alphaPalette[256];
1009    bool hasTransparency;
1010    int paletteEntries;
1011
1012    analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
1013                  &paletteEntries, &hasTransparency, &color_type, outRows);
1014
1015    // If the image is a 9-patch, we need to preserve it as a ARGB file to make
1016    // sure the pixels will not be pre-dithered/clamped until we decide they are
1017    if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
1018            color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
1019        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1020    }
1021
1022    switch (color_type) {
1023    case PNG_COLOR_TYPE_PALETTE:
1024        NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
1025                     imageName, paletteEntries,
1026                     hasTransparency ? " (with alpha)" : ""));
1027        break;
1028    case PNG_COLOR_TYPE_GRAY:
1029        NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName));
1030        break;
1031    case PNG_COLOR_TYPE_GRAY_ALPHA:
1032        NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName));
1033        break;
1034    case PNG_COLOR_TYPE_RGB:
1035        NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName));
1036        break;
1037    case PNG_COLOR_TYPE_RGB_ALPHA:
1038        NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName));
1039        break;
1040    }
1041
1042    png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
1043                 8, color_type, PNG_INTERLACE_NONE,
1044                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1045
1046    if (color_type == PNG_COLOR_TYPE_PALETTE) {
1047        png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
1048        if (hasTransparency) {
1049            png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
1050        }
1051       png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
1052    } else {
1053       png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
1054    }
1055
1056    if (imageInfo.is9Patch) {
1057        int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0);
1058        int p_index = imageInfo.haveLayoutBounds ? 1 : 0;
1059        int b_index = 0;
1060        png_byte *chunk_names = imageInfo.haveLayoutBounds
1061                ? (png_byte*)"npLb\0npTc\0"
1062                : (png_byte*)"npTc";
1063        NOISY(printf("Adding 9-patch info...\n"));
1064        strcpy((char*)unknowns[p_index].name, "npTc");
1065        unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize();
1066        unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
1067        // TODO: remove the check below when everything works
1068        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
1069
1070        if (imageInfo.haveLayoutBounds) {
1071            int chunk_size = sizeof(png_uint_32) * 4;
1072            strcpy((char*)unknowns[b_index].name, "npLb");
1073            unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
1074            memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
1075            unknowns[b_index].size = chunk_size;
1076        }
1077
1078        for (int i = 0; i < chunk_count; i++) {
1079            unknowns[i].location = PNG_HAVE_PLTE;
1080        }
1081        png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1082                                    chunk_names, chunk_count);
1083        png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
1084#if PNG_LIBPNG_VER < 10600
1085        /* Deal with unknown chunk location bug in 1.5.x and earlier */
1086        png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
1087        if (imageInfo.haveLayoutBounds) {
1088            png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
1089        }
1090#endif
1091    }
1092
1093
1094    png_write_info(write_ptr, write_info);
1095
1096    png_bytepp rows;
1097    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
1098        if (color_type == PNG_COLOR_TYPE_RGB) {
1099            png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
1100        }
1101        rows = imageInfo.rows;
1102    } else {
1103        rows = outRows;
1104    }
1105    png_write_image(write_ptr, rows);
1106
1107//     NOISY(printf("Final image data:\n"));
1108//     dump_image(imageInfo.width, imageInfo.height, rows, color_type);
1109
1110    png_write_end(write_ptr, write_info);
1111
1112    for (i = 0; i < (int) imageInfo.height; i++) {
1113        free(outRows[i]);
1114    }
1115    free(outRows);
1116    free(unknowns[0].data);
1117    free(unknowns[1].data);
1118
1119    png_get_IHDR(write_ptr, write_info, &width, &height,
1120       &bit_depth, &color_type, &interlace_type,
1121       &compression_type, NULL);
1122
1123    NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
1124                 (int)width, (int)height, bit_depth, color_type, interlace_type,
1125                 compression_type));
1126}
1127
1128status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1129                         const sp<AaptFile>& file, String8* outNewLeafName)
1130{
1131    String8 ext(file->getPath().getPathExtension());
1132
1133    // We currently only process PNG images.
1134    if (strcmp(ext.string(), ".png") != 0) {
1135        return NO_ERROR;
1136    }
1137
1138    // Example of renaming a file:
1139    //*outNewLeafName = file->getPath().getBasePath().getFileName();
1140    //outNewLeafName->append(".nupng");
1141
1142    String8 printableName(file->getPrintableSource());
1143
1144    if (bundle->getVerbose()) {
1145        printf("Processing image: %s\n", printableName.string());
1146    }
1147
1148    png_structp read_ptr = NULL;
1149    png_infop read_info = NULL;
1150    FILE* fp;
1151
1152    image_info imageInfo;
1153
1154    png_structp write_ptr = NULL;
1155    png_infop write_info = NULL;
1156
1157    status_t error = UNKNOWN_ERROR;
1158
1159    const size_t nameLen = file->getPath().length();
1160
1161    fp = fopen(file->getSourceFile().string(), "rb");
1162    if (fp == NULL) {
1163        fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
1164        goto bail;
1165    }
1166
1167    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1168                                        (png_error_ptr)NULL);
1169    if (!read_ptr) {
1170        goto bail;
1171    }
1172
1173    read_info = png_create_info_struct(read_ptr);
1174    if (!read_info) {
1175        goto bail;
1176    }
1177
1178    if (setjmp(png_jmpbuf(read_ptr))) {
1179        goto bail;
1180    }
1181
1182    png_init_io(read_ptr, fp);
1183
1184    read_png(printableName.string(), read_ptr, read_info, &imageInfo);
1185
1186    if (nameLen > 6) {
1187        const char* name = file->getPath().string();
1188        if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1189            if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
1190                goto bail;
1191            }
1192        }
1193    }
1194
1195    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1196                                        (png_error_ptr)NULL);
1197    if (!write_ptr)
1198    {
1199        goto bail;
1200    }
1201
1202    write_info = png_create_info_struct(write_ptr);
1203    if (!write_info)
1204    {
1205        goto bail;
1206    }
1207
1208    png_set_write_fn(write_ptr, (void*)file.get(),
1209                     png_write_aapt_file, png_flush_aapt_file);
1210
1211    if (setjmp(png_jmpbuf(write_ptr)))
1212    {
1213        goto bail;
1214    }
1215
1216    write_png(printableName.string(), write_ptr, write_info, imageInfo,
1217              bundle->getGrayscaleTolerance());
1218
1219    error = NO_ERROR;
1220
1221    if (bundle->getVerbose()) {
1222        fseek(fp, 0, SEEK_END);
1223        size_t oldSize = (size_t)ftell(fp);
1224        size_t newSize = file->getSize();
1225        float factor = ((float)newSize)/oldSize;
1226        int percent = (int)(factor*100);
1227        printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
1228    }
1229
1230bail:
1231    if (read_ptr) {
1232        png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1233    }
1234    if (fp) {
1235        fclose(fp);
1236    }
1237    if (write_ptr) {
1238        png_destroy_write_struct(&write_ptr, &write_info);
1239    }
1240
1241    if (error != NO_ERROR) {
1242        fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1243                file->getPrintableSource().string());
1244    }
1245    return error;
1246}
1247
1248status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
1249{
1250    png_structp read_ptr = NULL;
1251    png_infop read_info = NULL;
1252
1253    FILE* fp;
1254
1255    image_info imageInfo;
1256
1257    png_structp write_ptr = NULL;
1258    png_infop write_info = NULL;
1259
1260    status_t error = UNKNOWN_ERROR;
1261
1262    if (bundle->getVerbose()) {
1263        printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
1264    }
1265
1266    // Get a file handler to read from
1267    fp = fopen(source.string(),"rb");
1268    if (fp == NULL) {
1269        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
1270        return error;
1271    }
1272
1273    // Call libpng to get a struct to read image data into
1274    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1275    if (!read_ptr) {
1276        fclose(fp);
1277        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1278        return error;
1279    }
1280
1281    // Call libpng to get a struct to read image info into
1282    read_info = png_create_info_struct(read_ptr);
1283    if (!read_info) {
1284        fclose(fp);
1285        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1286        return error;
1287    }
1288
1289    // Set a jump point for libpng to long jump back to on error
1290    if (setjmp(png_jmpbuf(read_ptr))) {
1291        fclose(fp);
1292        png_destroy_read_struct(&read_ptr, &read_info,NULL);
1293        return error;
1294    }
1295
1296    // Set up libpng to read from our file.
1297    png_init_io(read_ptr,fp);
1298
1299    // Actually read data from the file
1300    read_png(source.string(), read_ptr, read_info, &imageInfo);
1301
1302    // We're done reading so we can clean up
1303    // Find old file size before releasing handle
1304    fseek(fp, 0, SEEK_END);
1305    size_t oldSize = (size_t)ftell(fp);
1306    fclose(fp);
1307    png_destroy_read_struct(&read_ptr, &read_info,NULL);
1308
1309    // Check to see if we're dealing with a 9-patch
1310    // If we are, process appropriately
1311    if (source.getBasePath().getPathExtension() == ".9")  {
1312        if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
1313            return error;
1314        }
1315    }
1316
1317    // Call libpng to create a structure to hold the processed image data
1318    // that can be written to disk
1319    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1320    if (!write_ptr) {
1321        png_destroy_write_struct(&write_ptr, &write_info);
1322        return error;
1323    }
1324
1325    // Call libpng to create a structure to hold processed image info that can
1326    // be written to disk
1327    write_info = png_create_info_struct(write_ptr);
1328    if (!write_info) {
1329        png_destroy_write_struct(&write_ptr, &write_info);
1330        return error;
1331    }
1332
1333    // Open up our destination file for writing
1334    fp = fopen(dest.string(), "wb");
1335    if (!fp) {
1336        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
1337        png_destroy_write_struct(&write_ptr, &write_info);
1338        return error;
1339    }
1340
1341    // Set up libpng to write to our file
1342    png_init_io(write_ptr, fp);
1343
1344    // Set up a jump for libpng to long jump back on on errors
1345    if (setjmp(png_jmpbuf(write_ptr))) {
1346        fclose(fp);
1347        png_destroy_write_struct(&write_ptr, &write_info);
1348        return error;
1349    }
1350
1351    // Actually write out to the new png
1352    write_png(dest.string(), write_ptr, write_info, imageInfo,
1353              bundle->getGrayscaleTolerance());
1354
1355    if (bundle->getVerbose()) {
1356        // Find the size of our new file
1357        FILE* reader = fopen(dest.string(), "rb");
1358        fseek(reader, 0, SEEK_END);
1359        size_t newSize = (size_t)ftell(reader);
1360        fclose(reader);
1361
1362        float factor = ((float)newSize)/oldSize;
1363        int percent = (int)(factor*100);
1364        printf("  (processed image to cache entry %s: %d%% size of source)\n",
1365               dest.string(), percent);
1366    }
1367
1368    //Clean up
1369    fclose(fp);
1370    png_destroy_write_struct(&write_ptr, &write_info);
1371
1372    return NO_ERROR;
1373}
1374
1375status_t postProcessImage(const sp<AaptAssets>& assets,
1376                          ResourceTable* table, const sp<AaptFile>& file)
1377{
1378    String8 ext(file->getPath().getPathExtension());
1379
1380    // At this point, now that we have all the resource data, all we need to
1381    // do is compile XML files.
1382    if (strcmp(ext.string(), ".xml") == 0) {
1383        return compileXmlFile(assets, file, table);
1384    }
1385
1386    return NO_ERROR;
1387}
1388