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