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