Images.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 <utils/ResourceTypes.h>
12#include <utils/ByteOrder.h>
13
14#include <png.h>
15
16#define NOISY(x) //x
17
18static void
19png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
20{
21    status_t err = ((AaptFile*)png_ptr->io_ptr)->writeData(data, length);
22    if (err != NO_ERROR) {
23        png_error(png_ptr, "Write Error");
24    }
25}
26
27
28static void
29png_flush_aapt_file(png_structp png_ptr)
30{
31}
32
33// This holds an image as 8bpp RGBA.
34struct image_info
35{
36    image_info() : rows(NULL), hasTransparency(true), is9Patch(false), allocRows(NULL) { }
37    ~image_info() {
38        if (rows && rows != allocRows) {
39            free(rows);
40        }
41        if (allocRows) {
42            for (int i=0; i<(int)allocHeight; i++) {
43                free(allocRows[i]);
44            }
45            free(allocRows);
46        }
47    }
48
49    png_uint_32 width;
50    png_uint_32 height;
51    png_bytepp rows;
52
53    bool hasTransparency;
54
55    // 9-patch info.
56    bool is9Patch;
57    Res_png_9patch info9Patch;
58
59    png_uint_32 allocHeight;
60    png_bytepp allocRows;
61};
62
63static void read_png(const char* imageName,
64                     png_structp read_ptr, png_infop read_info,
65                     image_info* outImageInfo)
66{
67    int color_type;
68    int bit_depth, interlace_type, compression_type;
69    int i;
70
71    png_read_info(read_ptr, read_info);
72
73    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
74       &outImageInfo->height, &bit_depth, &color_type,
75       &interlace_type, &compression_type, NULL);
76
77    //printf("Image %s:\n", imageName);
78    //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
79    //       color_type, bit_depth, interlace_type, compression_type);
80
81    if (color_type == PNG_COLOR_TYPE_PALETTE)
82        png_set_palette_to_rgb(read_ptr);
83
84    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
85        png_set_gray_1_2_4_to_8(read_ptr);
86
87    if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
88        //printf("Has PNG_INFO_tRNS!\n");
89        png_set_tRNS_to_alpha(read_ptr);
90    }
91
92    if (bit_depth == 16)
93        png_set_strip_16(read_ptr);
94
95    if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
96        png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
97
98    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
99        png_set_gray_to_rgb(read_ptr);
100
101    png_read_update_info(read_ptr, read_info);
102
103    outImageInfo->rows = (png_bytepp)malloc(
104        outImageInfo->height * png_sizeof(png_bytep));
105    outImageInfo->allocHeight = outImageInfo->height;
106    outImageInfo->allocRows = outImageInfo->rows;
107
108    png_set_rows(read_ptr, read_info, outImageInfo->rows);
109
110    for (i = 0; i < (int)outImageInfo->height; i++)
111    {
112        outImageInfo->rows[i] = (png_bytep)
113            malloc(png_get_rowbytes(read_ptr, read_info));
114    }
115
116    png_read_image(read_ptr, outImageInfo->rows);
117
118    png_read_end(read_ptr, read_info);
119
120    NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
121                 imageName,
122                 (int)outImageInfo->width, (int)outImageInfo->height,
123                 bit_depth, color_type,
124                 interlace_type, compression_type));
125
126    png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
127       &outImageInfo->height, &bit_depth, &color_type,
128       &interlace_type, &compression_type, NULL);
129}
130
131static bool is_tick(png_bytep p, bool transparent, const char** outError)
132{
133    if (transparent) {
134        if (p[3] == 0) {
135            return false;
136        }
137        if (p[3] != 0xff) {
138            *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
139            return false;
140        }
141        if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
142            *outError = "Ticks in transparent frame must be black";
143        }
144        return true;
145    }
146
147    if (p[3] != 0xFF) {
148        *outError = "White frame must be a solid color (no alpha)";
149    }
150    if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
151        return false;
152    }
153    if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
154        *outError = "Ticks in white frame must be black";
155        return false;
156    }
157    return true;
158}
159
160enum {
161    TICK_START,
162    TICK_INSIDE_1,
163    TICK_OUTSIDE_1
164};
165
166static status_t get_horizontal_ticks(
167        png_bytep row, int width, bool transparent, bool required,
168        int32_t* outLeft, int32_t* outRight, const char** outError,
169        uint8_t* outDivs, bool multipleAllowed)
170{
171    int i;
172    *outLeft = *outRight = -1;
173    int state = TICK_START;
174    bool found = false;
175
176    for (i=1; i<width-1; i++) {
177        if (is_tick(row+i*4, transparent, outError)) {
178            if (state == TICK_START ||
179                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
180                *outLeft = i-1;
181                *outRight = width-2;
182                found = true;
183                if (outDivs != NULL) {
184                    *outDivs += 2;
185                }
186                state = TICK_INSIDE_1;
187            } else if (state == TICK_OUTSIDE_1) {
188                *outError = "Can't have more than one marked region along edge";
189                *outLeft = i;
190                return UNKNOWN_ERROR;
191            }
192        } else if (*outError == NULL) {
193            if (state == TICK_INSIDE_1) {
194                // We're done with this div.  Move on to the next.
195                *outRight = i-1;
196                outRight += 2;
197                outLeft += 2;
198                state = TICK_OUTSIDE_1;
199            }
200        } else {
201            *outLeft = i;
202            return UNKNOWN_ERROR;
203        }
204    }
205
206    if (required && !found) {
207        *outError = "No marked region found along edge";
208        *outLeft = -1;
209        return UNKNOWN_ERROR;
210    }
211
212    return NO_ERROR;
213}
214
215static status_t get_vertical_ticks(
216        png_bytepp rows, int offset, int height, bool transparent, bool required,
217        int32_t* outTop, int32_t* outBottom, const char** outError,
218        uint8_t* outDivs, bool multipleAllowed)
219{
220    int i;
221    *outTop = *outBottom = -1;
222    int state = TICK_START;
223    bool found = false;
224
225    for (i=1; i<height-1; i++) {
226        if (is_tick(rows[i]+offset, transparent, outError)) {
227            if (state == TICK_START ||
228                (state == TICK_OUTSIDE_1 && multipleAllowed)) {
229                *outTop = i-1;
230                *outBottom = height-2;
231                found = true;
232                if (outDivs != NULL) {
233                    *outDivs += 2;
234                }
235                state = TICK_INSIDE_1;
236            } else if (state == TICK_OUTSIDE_1) {
237                *outError = "Can't have more than one marked region along edge";
238                *outTop = i;
239                return UNKNOWN_ERROR;
240            }
241        } else if (*outError == NULL) {
242            if (state == TICK_INSIDE_1) {
243                // We're done with this div.  Move on to the next.
244                *outBottom = i-1;
245                outTop += 2;
246                outBottom += 2;
247                state = TICK_OUTSIDE_1;
248            }
249        } else {
250            *outTop = i;
251            return UNKNOWN_ERROR;
252        }
253    }
254
255    if (required && !found) {
256        *outError = "No marked region found along edge";
257        *outTop = -1;
258        return UNKNOWN_ERROR;
259    }
260
261    return NO_ERROR;
262}
263
264static uint32_t get_color(
265    png_bytepp rows, int left, int top, int right, int bottom)
266{
267    png_bytep color = rows[top] + left*4;
268
269    if (left > right || top > bottom) {
270        return Res_png_9patch::TRANSPARENT_COLOR;
271    }
272
273    while (top <= bottom) {
274        for (int i = left; i <= right; i++) {
275            png_bytep p = rows[top]+i*4;
276            if (color[3] == 0) {
277                if (p[3] != 0) {
278                    return Res_png_9patch::NO_COLOR;
279                }
280            } else if (p[0] != color[0] || p[1] != color[1]
281                       || p[2] != color[2] || p[3] != color[3]) {
282                return Res_png_9patch::NO_COLOR;
283            }
284        }
285        top++;
286    }
287
288    if (color[3] == 0) {
289        return Res_png_9patch::TRANSPARENT_COLOR;
290    }
291    return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
292}
293
294static void select_patch(
295    int which, int front, int back, int size, int* start, int* end)
296{
297    switch (which) {
298    case 0:
299        *start = 0;
300        *end = front-1;
301        break;
302    case 1:
303        *start = front;
304        *end = back-1;
305        break;
306    case 2:
307        *start = back;
308        *end = size-1;
309        break;
310    }
311}
312
313static uint32_t get_color(image_info* image, int hpatch, int vpatch)
314{
315    int left, right, top, bottom;
316    select_patch(
317        hpatch, image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
318        image->width, &left, &right);
319    select_patch(
320        vpatch, image->info9Patch.yDivs[0], image->info9Patch.yDivs[1],
321        image->height, &top, &bottom);
322    //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
323    //       hpatch, vpatch, left, top, right, bottom);
324    const uint32_t c = get_color(image->rows, left, top, right, bottom);
325    NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left, top, right, bottom, c));
326    return c;
327}
328
329static void examine_image(image_info* image)
330{
331    bool hasTrans = false;
332    for (int i=0; i<(int)image->height && !hasTrans; i++) {
333        png_bytep p = image->rows[i];
334        for (int j=0; j<(int)image->width; j++) {
335            if (p[(j*4)+3] != 0xFF) {
336                hasTrans = true;
337                break;
338            }
339        }
340    }
341
342    image->hasTransparency = hasTrans;
343}
344
345static status_t do_9patch(const char* imageName, image_info* image)
346{
347    image->is9Patch = true;
348
349    int W = image->width;
350    int H = image->height;
351    int i, j;
352
353    int maxSizeXDivs = (W / 2 + 1) * sizeof(int32_t);
354    int maxSizeYDivs = (H / 2 + 1) * sizeof(int32_t);
355    int32_t* xDivs = (int32_t*) malloc(maxSizeXDivs);
356    int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs);
357    uint8_t  numXDivs = 0;
358    uint8_t  numYDivs = 0;
359    int8_t numColors;
360    int numRows;
361    int numCols;
362    int top;
363    int left;
364    int right;
365    int bottom;
366    memset(xDivs, -1, maxSizeXDivs);
367    memset(yDivs, -1, maxSizeYDivs);
368    image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
369        image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
370
371    png_bytep p = image->rows[0];
372    bool transparent = p[3] == 0;
373    bool hasColor = false;
374
375    const char* errorMsg = NULL;
376    int errorPixel = -1;
377    const char* errorEdge = "";
378
379    int colorIndex = 0;
380
381    // Validate size...
382    if (W < 3 || H < 3) {
383        errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
384        goto getout;
385    }
386
387    // Validate frame...
388    if (!transparent &&
389        (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
390        errorMsg = "Must have one-pixel frame that is either transparent or white";
391        goto getout;
392    }
393
394    // Find left and right of sizing areas...
395    if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
396                             &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
397        errorPixel = xDivs[0];
398        errorEdge = "top";
399        goto getout;
400    }
401
402    // Find top and bottom of sizing areas...
403    if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
404                           &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
405        errorPixel = yDivs[0];
406        errorEdge = "left";
407        goto getout;
408    }
409
410    // Find left and right of padding area...
411    if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
412                             &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
413        errorPixel = image->info9Patch.paddingLeft;
414        errorEdge = "bottom";
415        goto getout;
416    }
417
418    // Find top and bottom of padding area...
419    if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
420                           &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
421        errorPixel = image->info9Patch.paddingTop;
422        errorEdge = "right";
423        goto getout;
424    }
425
426    // Copy patch data into image
427    image->info9Patch.numXDivs = numXDivs;
428    image->info9Patch.numYDivs = numYDivs;
429    image->info9Patch.xDivs = xDivs;
430    image->info9Patch.yDivs = yDivs;
431
432    // If padding is not yet specified, take values from size.
433    if (image->info9Patch.paddingLeft < 0) {
434        image->info9Patch.paddingLeft = xDivs[0];
435        image->info9Patch.paddingRight = W - 2 - xDivs[1];
436    } else {
437        // Adjust value to be correct!
438        image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
439    }
440    if (image->info9Patch.paddingTop < 0) {
441        image->info9Patch.paddingTop = yDivs[0];
442        image->info9Patch.paddingBottom = H - 2 - yDivs[1];
443    } else {
444        // Adjust value to be correct!
445        image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
446    }
447
448    NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
449                 image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
450                 image->info9Patch.yDivs[0], image->info9Patch.yDivs[1]));
451    NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
452                 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
453                 image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
454
455    // Remove frame from image.
456    image->rows = (png_bytepp)malloc((H-2) * png_sizeof(png_bytep));
457    for (i=0; i<(H-2); i++) {
458        image->rows[i] = image->allocRows[i+1];
459        memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
460    }
461    image->width -= 2;
462    W = image->width;
463    image->height -= 2;
464    H = image->height;
465
466    // Figure out the number of rows and columns in the N-patch
467    numCols = numXDivs + 1;
468    if (xDivs[0] == 0) {  // Column 1 is strechable
469        numCols--;
470    }
471    if (xDivs[numXDivs - 1] == W) {
472        numCols--;
473    }
474    numRows = numYDivs + 1;
475    if (yDivs[0] == 0) {  // Row 1 is strechable
476        numRows--;
477    }
478    if (yDivs[numYDivs - 1] == H) {
479        numRows--;
480    }
481    numColors = numRows * numCols;
482    image->info9Patch.numColors = numColors;
483    image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
484
485    // Fill in color information for each patch.
486
487    uint32_t c;
488    top = 0;
489
490    // The first row always starts with the top being at y=0 and the bottom
491    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
492    // the first row is stretchable along the Y axis, otherwise it is fixed.
493    // The last row always ends with the bottom being bitmap.height and the top
494    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
495    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
496    // the Y axis, otherwise it is fixed.
497    //
498    // The first and last columns are similarly treated with respect to the X
499    // axis.
500    //
501    // The above is to help explain some of the special casing that goes on the
502    // code below.
503
504    // The initial yDiv and whether the first row is considered stretchable or
505    // not depends on whether yDiv[0] was zero or not.
506    for (j = (yDivs[0] == 0 ? 1 : 0);
507          j <= numYDivs && top < H;
508          j++) {
509        if (j == numYDivs) {
510            bottom = H;
511        } else {
512            bottom = yDivs[j];
513        }
514        left = 0;
515        // The initial xDiv and whether the first column is considered
516        // stretchable or not depends on whether xDiv[0] was zero or not.
517        for (i = xDivs[0] == 0 ? 1 : 0;
518              i <= numXDivs && left < W;
519              i++) {
520            if (i == numXDivs) {
521                right = W;
522            } else {
523                right = xDivs[i];
524            }
525            c = get_color(image->rows, left, top, right - 1, bottom - 1);
526            image->info9Patch.colors[colorIndex++] = c;
527            NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
528            left = right;
529        }
530        top = bottom;
531    }
532
533    assert(colorIndex == numColors);
534
535    for (i=0; i<numColors; i++) {
536        if (hasColor) {
537            if (i == 0) printf("Colors in %s:\n ", imageName);
538            printf(" #%08x", image->info9Patch.colors[i]);
539            if (i == numColors - 1) printf("\n");
540        }
541    }
542
543    image->is9Patch = true;
544    image->info9Patch.deviceToFile();
545
546getout:
547    if (errorMsg) {
548        fprintf(stderr,
549            "ERROR: 9-patch image %s malformed.\n"
550            "       %s.\n", imageName, errorMsg);
551        if (errorPixel >= 0) {
552            fprintf(stderr,
553            "       Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
554        } else {
555            fprintf(stderr,
556            "       Found along %s edge.\n", errorEdge);
557        }
558        return UNKNOWN_ERROR;
559    }
560    return NO_ERROR;
561}
562
563static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
564{
565    size_t patchSize = inPatch->serializedSize();
566    void * newData = malloc(patchSize);
567    memcpy(newData, data, patchSize);
568    Res_png_9patch* outPatch = inPatch->deserialize(newData);
569    assert(outPatch->numXDivs == inPatch->numXDivs);
570    assert(outPatch->numYDivs == inPatch->numYDivs);
571    assert(outPatch->paddingLeft == inPatch->paddingLeft);
572    assert(outPatch->paddingRight == inPatch->paddingRight);
573    assert(outPatch->paddingTop == inPatch->paddingTop);
574    assert(outPatch->paddingBottom == inPatch->paddingBottom);
575    for (int i = 0; i < outPatch->numXDivs; i++) {
576        assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
577    }
578    for (int i = 0; i < outPatch->numYDivs; i++) {
579        assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
580    }
581    for (int i = 0; i < outPatch->numColors; i++) {
582        assert(outPatch->colors[i] == inPatch->colors[i]);
583    }
584}
585
586static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
587    if (!(patch1.numXDivs == patch2.numXDivs &&
588          patch1.numYDivs == patch2.numYDivs &&
589          patch1.numColors == patch2.numColors &&
590          patch1.paddingLeft == patch2.paddingLeft &&
591          patch1.paddingRight == patch2.paddingRight &&
592          patch1.paddingTop == patch2.paddingTop &&
593          patch1.paddingBottom == patch2.paddingBottom)) {
594            return false;
595    }
596    for (int i = 0; i < patch1.numColors; i++) {
597        if (patch1.colors[i] != patch2.colors[i]) {
598            return false;
599        }
600    }
601    for (int i = 0; i < patch1.numXDivs; i++) {
602        if (patch1.xDivs[i] != patch2.xDivs[i]) {
603            return false;
604        }
605    }
606    for (int i = 0; i < patch1.numYDivs; i++) {
607        if (patch1.yDivs[i] != patch2.yDivs[i]) {
608            return false;
609        }
610    }
611    return true;
612}
613
614static void write_png(const char* imageName,
615                      png_structp write_ptr, png_infop write_info,
616                      image_info& imageInfo)
617{
618    png_uint_32 width, height;
619    int color_type;
620    int bit_depth, interlace_type, compression_type;
621    int i;
622
623    png_unknown_chunk unknowns[1];
624
625    png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
626
627    color_type = PNG_COLOR_MASK_COLOR;
628    if (imageInfo.hasTransparency) {
629        color_type |= PNG_COLOR_MASK_ALPHA;
630    }
631
632    png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
633                 8, color_type, PNG_INTERLACE_NONE,
634                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
635
636    if (imageInfo.is9Patch) {
637        NOISY(printf("Adding 9-patch info...\n"));
638        strcpy((char*)unknowns[0].name, "npTc");
639        unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
640        unknowns[0].size = imageInfo.info9Patch.serializedSize();
641        // TODO: remove the check below when everything works
642        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
643        png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
644                                    (png_byte*)"npTc", 1);
645        png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
646        // XXX I can't get this to work without forcibly changing
647        // the location to what I want...  which apparently is supposed
648        // to be a private API, but everything else I have tried results
649        // in the location being set to what I -last- wrote so I never
650        // get written. :p
651        png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
652    }
653
654    png_write_info(write_ptr, write_info);
655
656    if (!imageInfo.hasTransparency) {
657        png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
658    }
659
660    png_write_image(write_ptr, imageInfo.rows);
661
662    png_write_end(write_ptr, write_info);
663
664    png_get_IHDR(write_ptr, write_info, &width, &height,
665       &bit_depth, &color_type, &interlace_type,
666       &compression_type, NULL);
667
668    NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
669                 (int)width, (int)height, bit_depth, color_type, interlace_type,
670                 compression_type));
671}
672
673status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
674                         const sp<AaptFile>& file, String8* outNewLeafName)
675{
676    String8 ext(file->getPath().getPathExtension());
677
678    // We currently only process PNG images.
679    if (strcmp(ext.string(), ".png") != 0) {
680        return NO_ERROR;
681    }
682
683    // Example of renaming a file:
684    //*outNewLeafName = file->getPath().getBasePath().getFileName();
685    //outNewLeafName->append(".nupng");
686
687    String8 printableName(file->getPrintableSource());
688
689    png_structp read_ptr = NULL;
690    png_infop read_info = NULL;
691    FILE* fp;
692
693    image_info imageInfo;
694
695    png_structp write_ptr = NULL;
696    png_infop write_info = NULL;
697
698    status_t error = UNKNOWN_ERROR;
699
700    const size_t nameLen = file->getPath().length();
701
702    fp = fopen(file->getSourceFile().string(), "rb");
703    if (fp == NULL) {
704        fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
705        goto bail;
706    }
707
708    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
709                                        (png_error_ptr)NULL);
710    if (!read_ptr) {
711        goto bail;
712    }
713
714    read_info = png_create_info_struct(read_ptr);
715    if (!read_info) {
716        goto bail;
717    }
718
719    if (setjmp(png_jmpbuf(read_ptr))) {
720        goto bail;
721    }
722
723    png_init_io(read_ptr, fp);
724
725    read_png(printableName.string(), read_ptr, read_info, &imageInfo);
726
727    examine_image(&imageInfo);
728
729    if (nameLen > 6) {
730        const char* name = file->getPath().string();
731        if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
732            if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
733                goto bail;
734            }
735        }
736    }
737
738    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
739                                        (png_error_ptr)NULL);
740    if (!write_ptr)
741    {
742        goto bail;
743    }
744
745    write_info = png_create_info_struct(write_ptr);
746    if (!write_info)
747    {
748        goto bail;
749    }
750
751    png_set_write_fn(write_ptr, (void*)file.get(),
752                     png_write_aapt_file, png_flush_aapt_file);
753
754    if (setjmp(png_jmpbuf(write_ptr)))
755    {
756        goto bail;
757    }
758
759    write_png(printableName.string(), write_ptr, write_info, imageInfo);
760
761    error = NO_ERROR;
762
763    if (bundle->getVerbose()) {
764        fseek(fp, 0, SEEK_END);
765        size_t oldSize = (size_t)ftell(fp);
766        size_t newSize = file->getSize();
767        float factor = ((float)newSize)/oldSize;
768        int percent = (int)(factor*100);
769        printf("    (processed image %s: %d%% size of source)\n", printableName.string(), percent);
770    }
771
772bail:
773    if (read_ptr) {
774        png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
775    }
776    if (fp) {
777        fclose(fp);
778    }
779    if (write_ptr) {
780        png_destroy_write_struct(&write_ptr, &write_info);
781    }
782
783    if (error != NO_ERROR) {
784        fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
785                file->getPrintableSource().string());
786    }
787    return error;
788}
789
790
791
792status_t postProcessImage(const sp<AaptAssets>& assets,
793                          ResourceTable* table, const sp<AaptFile>& file)
794{
795    String8 ext(file->getPath().getPathExtension());
796
797    // At this point, now that we have all the resource data, all we need to
798    // do is compile XML files.
799    if (strcmp(ext.string(), ".xml") == 0) {
800        return compileXmlFile(assets, file, table);
801    }
802
803    return NO_ERROR;
804}
805