]> git.saurik.com Git - android/aapt.git/blob - Images.cpp
9d5937c7d6f063cd49988785427f1395c7982c59
[android/aapt.git] / Images.cpp
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
18 static void
19 png_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
28 static void
29 png_flush_aapt_file(png_structp png_ptr)
30 {
31 }
32
33 // This holds an image as 8bpp RGBA.
34 struct 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
63 static 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
131 static 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
160 enum {
161 TICK_START,
162 TICK_INSIDE_1,
163 TICK_OUTSIDE_1
164 };
165
166 static 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
215 static 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
264 static 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
294 static 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
313 static 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
329 static 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
345 static 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
546 getout:
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
563 static void checkNinePatchSerialization(Res_png_9patch* inPatch, void * data)
564 {
565 if (sizeof(void*) != sizeof(int32_t)) {
566 // can't deserialize on a non-32 bit system
567 return;
568 }
569 size_t patchSize = inPatch->serializedSize();
570 void * newData = malloc(patchSize);
571 memcpy(newData, data, patchSize);
572 Res_png_9patch* outPatch = inPatch->deserialize(newData);
573 // deserialization is done in place, so outPatch == newData
574 assert(outPatch == newData);
575 assert(outPatch->numXDivs == inPatch->numXDivs);
576 assert(outPatch->numYDivs == inPatch->numYDivs);
577 assert(outPatch->paddingLeft == inPatch->paddingLeft);
578 assert(outPatch->paddingRight == inPatch->paddingRight);
579 assert(outPatch->paddingTop == inPatch->paddingTop);
580 assert(outPatch->paddingBottom == inPatch->paddingBottom);
581 for (int i = 0; i < outPatch->numXDivs; i++) {
582 assert(outPatch->xDivs[i] == inPatch->xDivs[i]);
583 }
584 for (int i = 0; i < outPatch->numYDivs; i++) {
585 assert(outPatch->yDivs[i] == inPatch->yDivs[i]);
586 }
587 for (int i = 0; i < outPatch->numColors; i++) {
588 assert(outPatch->colors[i] == inPatch->colors[i]);
589 }
590 free(newData);
591 }
592
593 static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
594 if (!(patch1.numXDivs == patch2.numXDivs &&
595 patch1.numYDivs == patch2.numYDivs &&
596 patch1.numColors == patch2.numColors &&
597 patch1.paddingLeft == patch2.paddingLeft &&
598 patch1.paddingRight == patch2.paddingRight &&
599 patch1.paddingTop == patch2.paddingTop &&
600 patch1.paddingBottom == patch2.paddingBottom)) {
601 return false;
602 }
603 for (int i = 0; i < patch1.numColors; i++) {
604 if (patch1.colors[i] != patch2.colors[i]) {
605 return false;
606 }
607 }
608 for (int i = 0; i < patch1.numXDivs; i++) {
609 if (patch1.xDivs[i] != patch2.xDivs[i]) {
610 return false;
611 }
612 }
613 for (int i = 0; i < patch1.numYDivs; i++) {
614 if (patch1.yDivs[i] != patch2.yDivs[i]) {
615 return false;
616 }
617 }
618 return true;
619 }
620
621 static void write_png(const char* imageName,
622 png_structp write_ptr, png_infop write_info,
623 image_info& imageInfo)
624 {
625 png_uint_32 width, height;
626 int color_type;
627 int bit_depth, interlace_type, compression_type;
628 int i;
629
630 png_unknown_chunk unknowns[1];
631
632 png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
633
634 color_type = PNG_COLOR_MASK_COLOR;
635 if (imageInfo.hasTransparency) {
636 color_type |= PNG_COLOR_MASK_ALPHA;
637 }
638
639 png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
640 8, color_type, PNG_INTERLACE_NONE,
641 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
642
643 if (imageInfo.is9Patch) {
644 NOISY(printf("Adding 9-patch info...\n"));
645 strcpy((char*)unknowns[0].name, "npTc");
646 unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
647 unknowns[0].size = imageInfo.info9Patch.serializedSize();
648 // TODO: remove the check below when everything works
649 checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
650 png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
651 (png_byte*)"npTc", 1);
652 png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
653 // XXX I can't get this to work without forcibly changing
654 // the location to what I want... which apparently is supposed
655 // to be a private API, but everything else I have tried results
656 // in the location being set to what I -last- wrote so I never
657 // get written. :p
658 png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
659 }
660
661 png_write_info(write_ptr, write_info);
662
663 if (!imageInfo.hasTransparency) {
664 png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
665 }
666
667 png_write_image(write_ptr, imageInfo.rows);
668
669 png_write_end(write_ptr, write_info);
670
671 png_get_IHDR(write_ptr, write_info, &width, &height,
672 &bit_depth, &color_type, &interlace_type,
673 &compression_type, NULL);
674
675 NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
676 (int)width, (int)height, bit_depth, color_type, interlace_type,
677 compression_type));
678 }
679
680 status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
681 const sp<AaptFile>& file, String8* outNewLeafName)
682 {
683 String8 ext(file->getPath().getPathExtension());
684
685 // We currently only process PNG images.
686 if (strcmp(ext.string(), ".png") != 0) {
687 return NO_ERROR;
688 }
689
690 // Example of renaming a file:
691 //*outNewLeafName = file->getPath().getBasePath().getFileName();
692 //outNewLeafName->append(".nupng");
693
694 String8 printableName(file->getPrintableSource());
695
696 png_structp read_ptr = NULL;
697 png_infop read_info = NULL;
698 FILE* fp;
699
700 image_info imageInfo;
701
702 png_structp write_ptr = NULL;
703 png_infop write_info = NULL;
704
705 status_t error = UNKNOWN_ERROR;
706
707 const size_t nameLen = file->getPath().length();
708
709 fp = fopen(file->getSourceFile().string(), "rb");
710 if (fp == NULL) {
711 fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
712 goto bail;
713 }
714
715 read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
716 (png_error_ptr)NULL);
717 if (!read_ptr) {
718 goto bail;
719 }
720
721 read_info = png_create_info_struct(read_ptr);
722 if (!read_info) {
723 goto bail;
724 }
725
726 if (setjmp(png_jmpbuf(read_ptr))) {
727 goto bail;
728 }
729
730 png_init_io(read_ptr, fp);
731
732 read_png(printableName.string(), read_ptr, read_info, &imageInfo);
733
734 examine_image(&imageInfo);
735
736 if (nameLen > 6) {
737 const char* name = file->getPath().string();
738 if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
739 if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
740 goto bail;
741 }
742 }
743 }
744
745 write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
746 (png_error_ptr)NULL);
747 if (!write_ptr)
748 {
749 goto bail;
750 }
751
752 write_info = png_create_info_struct(write_ptr);
753 if (!write_info)
754 {
755 goto bail;
756 }
757
758 png_set_write_fn(write_ptr, (void*)file.get(),
759 png_write_aapt_file, png_flush_aapt_file);
760
761 if (setjmp(png_jmpbuf(write_ptr)))
762 {
763 goto bail;
764 }
765
766 write_png(printableName.string(), write_ptr, write_info, imageInfo);
767
768 error = NO_ERROR;
769
770 if (bundle->getVerbose()) {
771 fseek(fp, 0, SEEK_END);
772 size_t oldSize = (size_t)ftell(fp);
773 size_t newSize = file->getSize();
774 float factor = ((float)newSize)/oldSize;
775 int percent = (int)(factor*100);
776 printf(" (processed image %s: %d%% size of source)\n", printableName.string(), percent);
777 }
778
779 bail:
780 if (read_ptr) {
781 png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
782 }
783 if (fp) {
784 fclose(fp);
785 }
786 if (write_ptr) {
787 png_destroy_write_struct(&write_ptr, &write_info);
788 }
789
790 if (error != NO_ERROR) {
791 fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
792 file->getPrintableSource().string());
793 }
794 return error;
795 }
796
797
798
799 status_t postProcessImage(const sp<AaptAssets>& assets,
800 ResourceTable* table, const sp<AaptFile>& file)
801 {
802 String8 ext(file->getPath().getPathExtension());
803
804 // At this point, now that we have all the resource data, all we need to
805 // do is compile XML files.
806 if (strcmp(ext.string(), ".xml") == 0) {
807 return compileXmlFile(assets, file, table);
808 }
809
810 return NO_ERROR;
811 }