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