]> git.saurik.com Git - android/aapt.git/blame - Images.cpp
Initial Contribution
[android/aapt.git] / Images.cpp
CommitLineData
dadd9c1f
TAOSP
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}