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