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/PathUtils.h>
12 #include <androidfw/ResourceTypes.h>
13 #include <utils/ByteOrder.h>
14
15 #include <png.h>
16 #include <zlib.h>
17
18 // Change this to true for noisy debug output.
19 static const bool kIsDebug = false;
20
21 static void
png_write_aapt_file(png_structp png_ptr,png_bytep data,png_size_t length)22 png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
23 {
24 AaptFile* aaptfile = (AaptFile*) png_get_io_ptr(png_ptr);
25 status_t err = aaptfile->writeData(data, length);
26 if (err != NO_ERROR) {
27 png_error(png_ptr, "Write Error");
28 }
29 }
30
31
32 static void
png_flush_aapt_file(png_structp)33 png_flush_aapt_file(png_structp /* png_ptr */)
34 {
35 }
36
37 // This holds an image as 8bpp RGBA.
38 struct image_info
39 {
image_infoimage_info40 image_info() : rows(NULL), is9Patch(false),
41 xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { }
42
~image_infoimage_info43 ~image_info() {
44 if (rows && rows != allocRows) {
45 free(rows);
46 }
47 if (allocRows) {
48 for (int i=0; i<(int)allocHeight; i++) {
49 free(allocRows[i]);
50 }
51 free(allocRows);
52 }
53 free(xDivs);
54 free(yDivs);
55 free(colors);
56 }
57
serialize9patchimage_info58 void* serialize9patch() {
59 void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors);
60 reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
61 return serialized;
62 }
63
64 png_uint_32 width;
65 png_uint_32 height;
66 png_bytepp rows;
67
68 // 9-patch info.
69 bool is9Patch;
70 Res_png_9patch info9Patch;
71 int32_t* xDivs;
72 int32_t* yDivs;
73 uint32_t* colors;
74
75 // Layout padding, if relevant
76 bool haveLayoutBounds;
77 int32_t layoutBoundsLeft;
78 int32_t layoutBoundsTop;
79 int32_t layoutBoundsRight;
80 int32_t layoutBoundsBottom;
81
82 // Round rect outline description
83 int32_t outlineInsetsLeft;
84 int32_t outlineInsetsTop;
85 int32_t outlineInsetsRight;
86 int32_t outlineInsetsBottom;
87 float outlineRadius;
88 uint8_t outlineAlpha;
89
90 png_uint_32 allocHeight;
91 png_bytepp allocRows;
92 };
93
log_warning(png_structp png_ptr,png_const_charp warning_message)94 static void log_warning(png_structp png_ptr, png_const_charp warning_message)
95 {
96 const char* imageName = (const char*) png_get_error_ptr(png_ptr);
97 fprintf(stderr, "%s: libpng warning: %s\n", imageName, warning_message);
98 }
99
read_png(const char * imageName,png_structp read_ptr,png_infop read_info,image_info * outImageInfo)100 static void read_png(const char* imageName,
101 png_structp read_ptr, png_infop read_info,
102 image_info* outImageInfo)
103 {
104 int color_type;
105 int bit_depth, interlace_type, compression_type;
106 int i;
107
108 png_set_error_fn(read_ptr, const_cast<char*>(imageName),
109 NULL /* use default errorfn */, log_warning);
110 png_read_info(read_ptr, read_info);
111
112 png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
113 &outImageInfo->height, &bit_depth, &color_type,
114 &interlace_type, &compression_type, NULL);
115
116 //printf("Image %s:\n", imageName);
117 //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
118 // color_type, bit_depth, interlace_type, compression_type);
119
120 if (color_type == PNG_COLOR_TYPE_PALETTE)
121 png_set_palette_to_rgb(read_ptr);
122
123 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
124 png_set_expand_gray_1_2_4_to_8(read_ptr);
125
126 if (png_get_valid(read_ptr, read_info, PNG_INFO_tRNS)) {
127 //printf("Has PNG_INFO_tRNS!\n");
128 png_set_tRNS_to_alpha(read_ptr);
129 }
130
131 if (bit_depth == 16)
132 png_set_strip_16(read_ptr);
133
134 if ((color_type&PNG_COLOR_MASK_ALPHA) == 0)
135 png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
136
137 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
138 png_set_gray_to_rgb(read_ptr);
139
140 png_set_interlace_handling(read_ptr);
141
142 png_read_update_info(read_ptr, read_info);
143
144 outImageInfo->rows = (png_bytepp)malloc(
145 outImageInfo->height * sizeof(png_bytep));
146 outImageInfo->allocHeight = outImageInfo->height;
147 outImageInfo->allocRows = outImageInfo->rows;
148
149 png_set_rows(read_ptr, read_info, outImageInfo->rows);
150
151 for (i = 0; i < (int)outImageInfo->height; i++)
152 {
153 outImageInfo->rows[i] = (png_bytep)
154 malloc(png_get_rowbytes(read_ptr, read_info));
155 }
156
157 png_read_image(read_ptr, outImageInfo->rows);
158
159 png_read_end(read_ptr, read_info);
160
161 if (kIsDebug) {
162 printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
163 imageName,
164 (int)outImageInfo->width, (int)outImageInfo->height,
165 bit_depth, color_type,
166 interlace_type, compression_type);
167 }
168
169 png_get_IHDR(read_ptr, read_info, &outImageInfo->width,
170 &outImageInfo->height, &bit_depth, &color_type,
171 &interlace_type, &compression_type, NULL);
172 }
173
174 #define COLOR_TRANSPARENT 0
175 #define COLOR_WHITE 0xFFFFFFFF
176 #define COLOR_TICK 0xFF000000
177 #define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
178
179 enum {
180 TICK_TYPE_NONE,
181 TICK_TYPE_TICK,
182 TICK_TYPE_LAYOUT_BOUNDS,
183 TICK_TYPE_BOTH
184 };
185
tick_type(png_bytep p,bool transparent,const char ** outError)186 static int tick_type(png_bytep p, bool transparent, const char** outError)
187 {
188 png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
189
190 if (transparent) {
191 if (p[3] == 0) {
192 return TICK_TYPE_NONE;
193 }
194 if (color == COLOR_LAYOUT_BOUNDS_TICK) {
195 return TICK_TYPE_LAYOUT_BOUNDS;
196 }
197 if (color == COLOR_TICK) {
198 return TICK_TYPE_TICK;
199 }
200
201 // Error cases
202 if (p[3] != 0xff) {
203 *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
204 return TICK_TYPE_NONE;
205 }
206 if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
207 *outError = "Ticks in transparent frame must be black or red";
208 }
209 return TICK_TYPE_TICK;
210 }
211
212 if (p[3] != 0xFF) {
213 *outError = "White frame must be a solid color (no alpha)";
214 }
215 if (color == COLOR_WHITE) {
216 return TICK_TYPE_NONE;
217 }
218 if (color == COLOR_TICK) {
219 return TICK_TYPE_TICK;
220 }
221 if (color == COLOR_LAYOUT_BOUNDS_TICK) {
222 return TICK_TYPE_LAYOUT_BOUNDS;
223 }
224
225 if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
226 *outError = "Ticks in white frame must be black or red";
227 return TICK_TYPE_NONE;
228 }
229 return TICK_TYPE_TICK;
230 }
231
232 enum {
233 TICK_START,
234 TICK_INSIDE_1,
235 TICK_OUTSIDE_1
236 };
237
get_horizontal_ticks(png_bytep row,int width,bool transparent,bool required,int32_t * outLeft,int32_t * outRight,const char ** outError,uint8_t * outDivs,bool multipleAllowed)238 static status_t get_horizontal_ticks(
239 png_bytep row, int width, bool transparent, bool required,
240 int32_t* outLeft, int32_t* outRight, const char** outError,
241 uint8_t* outDivs, bool multipleAllowed)
242 {
243 int i;
244 *outLeft = *outRight = -1;
245 int state = TICK_START;
246 bool found = false;
247
248 for (i=1; i<width-1; i++) {
249 if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
250 if (state == TICK_START ||
251 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
252 *outLeft = i-1;
253 *outRight = width-2;
254 found = true;
255 if (outDivs != NULL) {
256 *outDivs += 2;
257 }
258 state = TICK_INSIDE_1;
259 } else if (state == TICK_OUTSIDE_1) {
260 *outError = "Can't have more than one marked region along edge";
261 *outLeft = i;
262 return UNKNOWN_ERROR;
263 }
264 } else if (*outError == NULL) {
265 if (state == TICK_INSIDE_1) {
266 // We're done with this div. Move on to the next.
267 *outRight = i-1;
268 outRight += 2;
269 outLeft += 2;
270 state = TICK_OUTSIDE_1;
271 }
272 } else {
273 *outLeft = i;
274 return UNKNOWN_ERROR;
275 }
276 }
277
278 if (required && !found) {
279 *outError = "No marked region found along edge";
280 *outLeft = -1;
281 return UNKNOWN_ERROR;
282 }
283
284 return NO_ERROR;
285 }
286
get_vertical_ticks(png_bytepp rows,int offset,int height,bool transparent,bool required,int32_t * outTop,int32_t * outBottom,const char ** outError,uint8_t * outDivs,bool multipleAllowed)287 static status_t get_vertical_ticks(
288 png_bytepp rows, int offset, int height, bool transparent, bool required,
289 int32_t* outTop, int32_t* outBottom, const char** outError,
290 uint8_t* outDivs, bool multipleAllowed)
291 {
292 int i;
293 *outTop = *outBottom = -1;
294 int state = TICK_START;
295 bool found = false;
296
297 for (i=1; i<height-1; i++) {
298 if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
299 if (state == TICK_START ||
300 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
301 *outTop = i-1;
302 *outBottom = height-2;
303 found = true;
304 if (outDivs != NULL) {
305 *outDivs += 2;
306 }
307 state = TICK_INSIDE_1;
308 } else if (state == TICK_OUTSIDE_1) {
309 *outError = "Can't have more than one marked region along edge";
310 *outTop = i;
311 return UNKNOWN_ERROR;
312 }
313 } else if (*outError == NULL) {
314 if (state == TICK_INSIDE_1) {
315 // We're done with this div. Move on to the next.
316 *outBottom = i-1;
317 outTop += 2;
318 outBottom += 2;
319 state = TICK_OUTSIDE_1;
320 }
321 } else {
322 *outTop = i;
323 return UNKNOWN_ERROR;
324 }
325 }
326
327 if (required && !found) {
328 *outError = "No marked region found along edge";
329 *outTop = -1;
330 return UNKNOWN_ERROR;
331 }
332
333 return NO_ERROR;
334 }
335
get_horizontal_layout_bounds_ticks(png_bytep row,int width,bool transparent,bool,int32_t * outLeft,int32_t * outRight,const char ** outError)336 static status_t get_horizontal_layout_bounds_ticks(
337 png_bytep row, int width, bool transparent, bool /* required */,
338 int32_t* outLeft, int32_t* outRight, const char** outError)
339 {
340 int i;
341 *outLeft = *outRight = 0;
342
343 // Look for left tick
344 if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
345 // Starting with a layout padding tick
346 i = 1;
347 while (i < width - 1) {
348 (*outLeft)++;
349 i++;
350 int tick = tick_type(row + i * 4, transparent, outError);
351 if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
352 break;
353 }
354 }
355 }
356
357 // Look for right tick
358 if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
359 // Ending with a layout padding tick
360 i = width - 2;
361 while (i > 1) {
362 (*outRight)++;
363 i--;
364 int tick = tick_type(row+i*4, transparent, outError);
365 if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
366 break;
367 }
368 }
369 }
370
371 return NO_ERROR;
372 }
373
get_vertical_layout_bounds_ticks(png_bytepp rows,int offset,int height,bool transparent,bool,int32_t * outTop,int32_t * outBottom,const char ** outError)374 static status_t get_vertical_layout_bounds_ticks(
375 png_bytepp rows, int offset, int height, bool transparent, bool /* required */,
376 int32_t* outTop, int32_t* outBottom, const char** outError)
377 {
378 int i;
379 *outTop = *outBottom = 0;
380
381 // Look for top tick
382 if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
383 // Starting with a layout padding tick
384 i = 1;
385 while (i < height - 1) {
386 (*outTop)++;
387 i++;
388 int tick = tick_type(rows[i] + offset, transparent, outError);
389 if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
390 break;
391 }
392 }
393 }
394
395 // Look for bottom tick
396 if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
397 // Ending with a layout padding tick
398 i = height - 2;
399 while (i > 1) {
400 (*outBottom)++;
401 i--;
402 int tick = tick_type(rows[i] + offset, transparent, outError);
403 if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
404 break;
405 }
406 }
407 }
408
409 return NO_ERROR;
410 }
411
find_max_opacity(png_byte ** rows,int startX,int startY,int endX,int endY,int dX,int dY,int * out_inset)412 static void find_max_opacity(png_byte** rows,
413 int startX, int startY, int endX, int endY, int dX, int dY,
414 int* out_inset)
415 {
416 uint8_t max_opacity = 0;
417 int inset = 0;
418 *out_inset = 0;
419 for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
420 png_byte* color = rows[y] + x * 4;
421 uint8_t opacity = color[3];
422 if (opacity > max_opacity) {
423 max_opacity = opacity;
424 *out_inset = inset;
425 }
426 if (opacity == 0xff) return;
427 }
428 }
429
max_alpha_over_row(png_byte * row,int startX,int endX)430 static uint8_t max_alpha_over_row(png_byte* row, int startX, int endX)
431 {
432 uint8_t max_alpha = 0;
433 for (int x = startX; x < endX; x++) {
434 uint8_t alpha = (row + x * 4)[3];
435 if (alpha > max_alpha) max_alpha = alpha;
436 }
437 return max_alpha;
438 }
439
max_alpha_over_col(png_byte ** rows,int offsetX,int startY,int endY)440 static uint8_t max_alpha_over_col(png_byte** rows, int offsetX, int startY, int endY)
441 {
442 uint8_t max_alpha = 0;
443 for (int y = startY; y < endY; y++) {
444 uint8_t alpha = (rows[y] + offsetX * 4)[3];
445 if (alpha > max_alpha) max_alpha = alpha;
446 }
447 return max_alpha;
448 }
449
get_outline(image_info * image)450 static void get_outline(image_info* image)
451 {
452 int midX = image->width / 2;
453 int midY = image->height / 2;
454 int endX = image->width - 2;
455 int endY = image->height - 2;
456
457 // find left and right extent of nine patch content on center row
458 if (image->width > 4) {
459 find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
460 find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
461 } else {
462 image->outlineInsetsLeft = 0;
463 image->outlineInsetsRight = 0;
464 }
465
466 // find top and bottom extent of nine patch content on center column
467 if (image->height > 4) {
468 find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
469 find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
470 } else {
471 image->outlineInsetsTop = 0;
472 image->outlineInsetsBottom = 0;
473 }
474
475 int innerStartX = 1 + image->outlineInsetsLeft;
476 int innerStartY = 1 + image->outlineInsetsTop;
477 int innerEndX = endX - image->outlineInsetsRight;
478 int innerEndY = endY - image->outlineInsetsBottom;
479 int innerMidX = (innerEndX + innerStartX) / 2;
480 int innerMidY = (innerEndY + innerStartY) / 2;
481
482 // assuming the image is a round rect, compute the radius by marching
483 // diagonally from the top left corner towards the center
484 image->outlineAlpha = std::max(
485 max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX),
486 max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY));
487
488 int diagonalInset = 0;
489 find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
490 &diagonalInset);
491
492 /* Determine source radius based upon inset:
493 * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
494 * sqrt(2) * r = sqrt(2) * i + r
495 * (sqrt(2) - 1) * r = sqrt(2) * i
496 * r = sqrt(2) / (sqrt(2) - 1) * i
497 */
498 image->outlineRadius = 3.4142f * diagonalInset;
499
500 if (kIsDebug) {
501 printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
502 image->outlineInsetsLeft,
503 image->outlineInsetsTop,
504 image->outlineInsetsRight,
505 image->outlineInsetsBottom,
506 image->outlineRadius,
507 image->outlineAlpha);
508 }
509 }
510
511
get_color(png_bytepp rows,int left,int top,int right,int bottom)512 static uint32_t get_color(
513 png_bytepp rows, int left, int top, int right, int bottom)
514 {
515 png_bytep color = rows[top] + left*4;
516
517 if (left > right || top > bottom) {
518 return Res_png_9patch::TRANSPARENT_COLOR;
519 }
520
521 while (top <= bottom) {
522 for (int i = left; i <= right; i++) {
523 png_bytep p = rows[top]+i*4;
524 if (color[3] == 0) {
525 if (p[3] != 0) {
526 return Res_png_9patch::NO_COLOR;
527 }
528 } else if (p[0] != color[0] || p[1] != color[1]
529 || p[2] != color[2] || p[3] != color[3]) {
530 return Res_png_9patch::NO_COLOR;
531 }
532 }
533 top++;
534 }
535
536 if (color[3] == 0) {
537 return Res_png_9patch::TRANSPARENT_COLOR;
538 }
539 return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
540 }
541
do_9patch(const char * imageName,image_info * image)542 static status_t do_9patch(const char* imageName, image_info* image)
543 {
544 image->is9Patch = true;
545
546 int W = image->width;
547 int H = image->height;
548 int i, j;
549
550 int maxSizeXDivs = W * sizeof(int32_t);
551 int maxSizeYDivs = H * sizeof(int32_t);
552 int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs);
553 int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs);
554 uint8_t numXDivs = 0;
555 uint8_t numYDivs = 0;
556
557 int8_t numColors;
558 int numRows;
559 int numCols;
560 int top;
561 int left;
562 int right;
563 int bottom;
564 memset(xDivs, -1, maxSizeXDivs);
565 memset(yDivs, -1, maxSizeYDivs);
566 image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
567 image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
568
569 image->layoutBoundsLeft = image->layoutBoundsRight =
570 image->layoutBoundsTop = image->layoutBoundsBottom = 0;
571
572 png_bytep p = image->rows[0];
573 bool transparent = p[3] == 0;
574 bool hasColor = false;
575
576 const char* errorMsg = NULL;
577 int errorPixel = -1;
578 const char* errorEdge = NULL;
579
580 int colorIndex = 0;
581
582 // Validate size...
583 if (W < 3 || H < 3) {
584 errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
585 goto getout;
586 }
587
588 // Validate frame...
589 if (!transparent &&
590 (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
591 errorMsg = "Must have one-pixel frame that is either transparent or white";
592 goto getout;
593 }
594
595 // Find left and right of sizing areas...
596 if (get_horizontal_ticks(p, W, transparent, true, &xDivs[0],
597 &xDivs[1], &errorMsg, &numXDivs, true) != NO_ERROR) {
598 errorPixel = xDivs[0];
599 errorEdge = "top";
600 goto getout;
601 }
602
603 // Find top and bottom of sizing areas...
604 if (get_vertical_ticks(image->rows, 0, H, transparent, true, &yDivs[0],
605 &yDivs[1], &errorMsg, &numYDivs, true) != NO_ERROR) {
606 errorPixel = yDivs[0];
607 errorEdge = "left";
608 goto getout;
609 }
610
611 // Copy patch size data into image...
612 image->info9Patch.numXDivs = numXDivs;
613 image->info9Patch.numYDivs = numYDivs;
614
615 // Find left and right of padding area...
616 if (get_horizontal_ticks(image->rows[H-1], W, transparent, false, &image->info9Patch.paddingLeft,
617 &image->info9Patch.paddingRight, &errorMsg, NULL, false) != NO_ERROR) {
618 errorPixel = image->info9Patch.paddingLeft;
619 errorEdge = "bottom";
620 goto getout;
621 }
622
623 // Find top and bottom of padding area...
624 if (get_vertical_ticks(image->rows, (W-1)*4, H, transparent, false, &image->info9Patch.paddingTop,
625 &image->info9Patch.paddingBottom, &errorMsg, NULL, false) != NO_ERROR) {
626 errorPixel = image->info9Patch.paddingTop;
627 errorEdge = "right";
628 goto getout;
629 }
630
631 // Find left and right of layout padding...
632 get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
633 &image->layoutBoundsLeft,
634 &image->layoutBoundsRight, &errorMsg);
635
636 get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
637 &image->layoutBoundsTop,
638 &image->layoutBoundsBottom, &errorMsg);
639
640 image->haveLayoutBounds = image->layoutBoundsLeft != 0
641 || image->layoutBoundsRight != 0
642 || image->layoutBoundsTop != 0
643 || image->layoutBoundsBottom != 0;
644
645 if (image->haveLayoutBounds) {
646 if (kIsDebug) {
647 printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
648 image->layoutBoundsRight, image->layoutBoundsBottom);
649 }
650 }
651
652 // use opacity of pixels to estimate the round rect outline
653 get_outline(image);
654
655 // If padding is not yet specified, take values from size.
656 if (image->info9Patch.paddingLeft < 0) {
657 image->info9Patch.paddingLeft = xDivs[0];
658 image->info9Patch.paddingRight = W - 2 - xDivs[1];
659 } else {
660 // Adjust value to be correct!
661 image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
662 }
663 if (image->info9Patch.paddingTop < 0) {
664 image->info9Patch.paddingTop = yDivs[0];
665 image->info9Patch.paddingBottom = H - 2 - yDivs[1];
666 } else {
667 // Adjust value to be correct!
668 image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
669 }
670
671 if (kIsDebug) {
672 printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
673 xDivs[0], xDivs[1],
674 yDivs[0], yDivs[1]);
675 printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
676 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
677 image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
678 }
679
680 // Remove frame from image.
681 image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep));
682 for (i=0; i<(H-2); i++) {
683 image->rows[i] = image->allocRows[i+1];
684 memmove(image->rows[i], image->rows[i]+4, (W-2)*4);
685 }
686 image->width -= 2;
687 W = image->width;
688 image->height -= 2;
689 H = image->height;
690
691 // Figure out the number of rows and columns in the N-patch
692 numCols = numXDivs + 1;
693 if (xDivs[0] == 0) { // Column 1 is strechable
694 numCols--;
695 }
696 if (xDivs[numXDivs - 1] == W) {
697 numCols--;
698 }
699 numRows = numYDivs + 1;
700 if (yDivs[0] == 0) { // Row 1 is strechable
701 numRows--;
702 }
703 if (yDivs[numYDivs - 1] == H) {
704 numRows--;
705 }
706
707 // Make sure the amount of rows and columns will fit in the number of
708 // colors we can use in the 9-patch format.
709 if (numRows * numCols > 0x7F) {
710 errorMsg = "Too many rows and columns in 9-patch perimeter";
711 goto getout;
712 }
713
714 numColors = numRows * numCols;
715 image->info9Patch.numColors = numColors;
716 image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
717
718 // Fill in color information for each patch.
719
720 uint32_t c;
721 top = 0;
722
723 // The first row always starts with the top being at y=0 and the bottom
724 // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
725 // the first row is stretchable along the Y axis, otherwise it is fixed.
726 // The last row always ends with the bottom being bitmap.height and the top
727 // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
728 // yDivs[numYDivs-1]. In the former case the last row is stretchable along
729 // the Y axis, otherwise it is fixed.
730 //
731 // The first and last columns are similarly treated with respect to the X
732 // axis.
733 //
734 // The above is to help explain some of the special casing that goes on the
735 // code below.
736
737 // The initial yDiv and whether the first row is considered stretchable or
738 // not depends on whether yDiv[0] was zero or not.
739 for (j = (yDivs[0] == 0 ? 1 : 0);
740 j <= numYDivs && top < H;
741 j++) {
742 if (j == numYDivs) {
743 bottom = H;
744 } else {
745 bottom = yDivs[j];
746 }
747 left = 0;
748 // The initial xDiv and whether the first column is considered
749 // stretchable or not depends on whether xDiv[0] was zero or not.
750 for (i = xDivs[0] == 0 ? 1 : 0;
751 i <= numXDivs && left < W;
752 i++) {
753 if (i == numXDivs) {
754 right = W;
755 } else {
756 right = xDivs[i];
757 }
758 c = get_color(image->rows, left, top, right - 1, bottom - 1);
759 image->colors[colorIndex++] = c;
760 if (kIsDebug) {
761 if (c != Res_png_9patch::NO_COLOR)
762 hasColor = true;
763 }
764 left = right;
765 }
766 top = bottom;
767 }
768
769 assert(colorIndex == numColors);
770
771 for (i=0; i<numColors; i++) {
772 if (hasColor) {
773 if (i == 0) printf("Colors in %s:\n ", imageName);
774 printf(" #%08x", image->colors[i]);
775 if (i == numColors - 1) printf("\n");
776 }
777 }
778 getout:
779 if (errorMsg) {
780 fprintf(stderr,
781 "ERROR: 9-patch image %s malformed.\n"
782 " %s.\n", imageName, errorMsg);
783 if (errorEdge != NULL) {
784 if (errorPixel >= 0) {
785 fprintf(stderr,
786 " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge);
787 } else {
788 fprintf(stderr,
789 " Found along %s edge.\n", errorEdge);
790 }
791 }
792 return UNKNOWN_ERROR;
793 }
794 return NO_ERROR;
795 }
796
checkNinePatchSerialization(Res_png_9patch * inPatch,void * data)797 static void checkNinePatchSerialization(Res_png_9patch* inPatch, void* data)
798 {
799 size_t patchSize = inPatch->serializedSize();
800 void* newData = malloc(patchSize);
801 memcpy(newData, data, patchSize);
802 Res_png_9patch* outPatch = inPatch->deserialize(newData);
803 // deserialization is done in place, so outPatch == newData
804 assert(outPatch == newData);
805 assert(outPatch->numXDivs == inPatch->numXDivs);
806 assert(outPatch->numYDivs == inPatch->numYDivs);
807 assert(outPatch->paddingLeft == inPatch->paddingLeft);
808 assert(outPatch->paddingRight == inPatch->paddingRight);
809 assert(outPatch->paddingTop == inPatch->paddingTop);
810 assert(outPatch->paddingBottom == inPatch->paddingBottom);
811 for (int i = 0; i < outPatch->numXDivs; i++) {
812 assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
813 }
814 for (int i = 0; i < outPatch->numYDivs; i++) {
815 assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
816 }
817 for (int i = 0; i < outPatch->numColors; i++) {
818 assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
819 }
820 free(newData);
821 }
822
dump_image(int w,int h,png_bytepp rows,int color_type)823 static void dump_image(int w, int h, png_bytepp rows, int color_type)
824 {
825 int i, j, rr, gg, bb, aa;
826
827 int bpp;
828 if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
829 bpp = 1;
830 } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
831 bpp = 2;
832 } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
833 // We use a padding byte even when there is no alpha
834 bpp = 4;
835 } else {
836 printf("Unknown color type %d.\n", color_type);
837 return;
838 }
839
840 for (j = 0; j < h; j++) {
841 png_bytep row = rows[j];
842 for (i = 0; i < w; i++) {
843 rr = row[0];
844 gg = row[1];
845 bb = row[2];
846 aa = row[3];
847 row += bpp;
848
849 if (i == 0) {
850 printf("Row %d:", j);
851 }
852 switch (bpp) {
853 case 1:
854 printf(" (%d)", rr);
855 break;
856 case 2:
857 printf(" (%d %d", rr, gg);
858 break;
859 case 3:
860 printf(" (%d %d %d)", rr, gg, bb);
861 break;
862 case 4:
863 printf(" (%d %d %d %d)", rr, gg, bb, aa);
864 break;
865 }
866 if (i == (w - 1)) {
867 printf("\n");
868 }
869 }
870 }
871 }
872
873 #define MAX(a,b) ((a)>(b)?(a):(b))
874 #define ABS(a) ((a)<0?-(a):(a))
875
analyze_image(const char * imageName,image_info & imageInfo,int grayscaleTolerance,png_colorp rgbPalette,png_bytep alphaPalette,int * paletteEntries,int * alphaPaletteEntries,bool * hasTransparency,int * colorType,png_bytepp outRows)876 static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
877 png_colorp rgbPalette, png_bytep alphaPalette,
878 int *paletteEntries, int *alphaPaletteEntries, bool *hasTransparency,
879 int *colorType, png_bytepp outRows)
880 {
881 int w = imageInfo.width;
882 int h = imageInfo.height;
883 int i, j, rr, gg, bb, aa, idx;;
884 uint32_t opaqueColors[256], alphaColors[256];
885 uint32_t col;
886 int numOpaqueColors = 0, numAlphaColors = 0;
887 int maxGrayDeviation = 0;
888
889 bool isOpaque = true;
890 bool isPalette = true;
891 bool isGrayscale = true;
892
893 // Scan the entire image and determine if:
894 // 1. Every pixel has R == G == B (grayscale)
895 // 2. Every pixel has A == 255 (opaque)
896 // 3. There are no more than 256 distinct RGBA colors
897 // We will track opaque colors separately from colors with
898 // alpha. This allows us to reencode the color table more
899 // efficiently (color tables entries without a corresponding
900 // alpha value are assumed to be opaque).
901
902 if (kIsDebug) {
903 printf("Initial image data:\n");
904 dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
905 }
906
907 for (j = 0; j < h; j++) {
908 png_bytep row = imageInfo.rows[j];
909 png_bytep out = outRows[j];
910 for (i = 0; i < w; i++) {
911
912 // Make sure any zero alpha pixels are fully zeroed. On average,
913 // each of our PNG assets seem to have about four distinct pixels
914 // with zero alpha.
915 // There are several advantages to setting these to zero:
916 // (1) Images are more likely able to be encodable with a palette.
917 // (2) Image palettes will be smaller.
918 // (3) Premultiplied and unpremultiplied PNG decodes can skip
919 // writing zeros to memory, often saving significant numbers
920 // of memory pages.
921 aa = *(row + 3);
922 if (aa == 0) {
923 rr = 0;
924 gg = 0;
925 bb = 0;
926
927 // Also set red, green, and blue to zero in "row". If we later
928 // decide to encode the PNG as RGB or RGBA, we will use the
929 // values stored there.
930 *(row) = 0;
931 *(row + 1) = 0;
932 *(row + 2) = 0;
933 } else {
934 rr = *(row);
935 gg = *(row + 1);
936 bb = *(row + 2);
937 }
938 row += 4;
939
940 int odev = maxGrayDeviation;
941 maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
942 maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
943 maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
944 if (maxGrayDeviation > odev) {
945 if (kIsDebug) {
946 printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
947 maxGrayDeviation, i, j, rr, gg, bb, aa);
948 }
949 }
950
951 // Check if image is really grayscale
952 if (isGrayscale) {
953 if (rr != gg || rr != bb) {
954 if (kIsDebug) {
955 printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
956 i, j, rr, gg, bb, aa);
957 }
958 isGrayscale = false;
959 }
960 }
961
962 // Check if image is really opaque
963 if (isOpaque) {
964 if (aa != 0xff) {
965 if (kIsDebug) {
966 printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
967 i, j, rr, gg, bb, aa);
968 }
969 isOpaque = false;
970 }
971 }
972
973 // Check if image is really <= 256 colors
974 if (isPalette) {
975 col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
976 bool match = false;
977
978 if (aa == 0xff) {
979 for (idx = 0; idx < numOpaqueColors; idx++) {
980 if (opaqueColors[idx] == col) {
981 match = true;
982 break;
983 }
984 }
985
986 if (!match) {
987 if (numOpaqueColors < 256) {
988 opaqueColors[numOpaqueColors] = col;
989 }
990 numOpaqueColors++;
991 }
992
993 // Write the palette index for the pixel to outRows optimistically.
994 // We might overwrite it later if we decide to encode as gray or
995 // gray + alpha. We may also need to overwrite it when we combine
996 // into a single palette.
997 *out++ = idx;
998 } else {
999 for (idx = 0; idx < numAlphaColors; idx++) {
1000 if (alphaColors[idx] == col) {
1001 match = true;
1002 break;
1003 }
1004 }
1005
1006 if (!match) {
1007 if (numAlphaColors < 256) {
1008 alphaColors[numAlphaColors] = col;
1009 }
1010 numAlphaColors++;
1011 }
1012
1013 // Write the palette index for the pixel to outRows optimistically.
1014 // We might overwrite it later if we decide to encode as gray or
1015 // gray + alpha.
1016 *out++ = idx;
1017 }
1018
1019 if (numOpaqueColors + numAlphaColors > 256) {
1020 if (kIsDebug) {
1021 printf("Found 257th color at %d, %d\n", i, j);
1022 }
1023 isPalette = false;
1024 }
1025 }
1026 }
1027 }
1028
1029 // If we decide to encode the image using a palette, we will reset these counts
1030 // to the appropriate values later. Initializing them here avoids compiler
1031 // complaints about uses of possibly uninitialized variables.
1032 *paletteEntries = 0;
1033 *alphaPaletteEntries = 0;
1034
1035 *hasTransparency = !isOpaque;
1036 int paletteSize = w * h + 3 * numOpaqueColors + 4 * numAlphaColors;
1037
1038 int bpp = isOpaque ? 3 : 4;
1039 if (kIsDebug) {
1040 printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
1041 printf("isOpaque = %s\n", isOpaque ? "true" : "false");
1042 printf("isPalette = %s\n", isPalette ? "true" : "false");
1043 printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
1044 paletteSize, 2 * w * h, bpp * w * h);
1045 printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
1046 }
1047
1048 // Choose the best color type for the image.
1049 // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
1050 // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
1051 // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
1052 // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
1053 // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
1054 if (isGrayscale) {
1055 if (isOpaque) {
1056 *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
1057 } else {
1058 // Use a simple heuristic to determine whether using a palette will
1059 // save space versus using gray + alpha for each pixel.
1060 // This doesn't take into account chunk overhead, filtering, LZ
1061 // compression, etc.
1062 if (isPalette && (paletteSize < 2 * w * h)) {
1063 *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
1064 } else {
1065 *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
1066 }
1067 }
1068 } else if (isPalette && (paletteSize < bpp * w * h)) {
1069 *colorType = PNG_COLOR_TYPE_PALETTE;
1070 } else {
1071 if (maxGrayDeviation <= grayscaleTolerance) {
1072 printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
1073 *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
1074 } else {
1075 *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
1076 }
1077 }
1078
1079 // Perform postprocessing of the image or palette data based on the final
1080 // color type chosen
1081
1082 if (*colorType == PNG_COLOR_TYPE_PALETTE) {
1083 // Combine the alphaColors and the opaqueColors into a single palette.
1084 // The alphaColors must be at the start of the palette.
1085 uint32_t* colors = alphaColors;
1086 memcpy(colors + numAlphaColors, opaqueColors, 4 * numOpaqueColors);
1087
1088 // Fix the indices of the opaque colors in the image.
1089 for (j = 0; j < h; j++) {
1090 png_bytep row = imageInfo.rows[j];
1091 png_bytep out = outRows[j];
1092 for (i = 0; i < w; i++) {
1093 uint32_t pixel = ((uint32_t*) row)[i];
1094 if (pixel >> 24 == 0xFF) {
1095 out[i] += numAlphaColors;
1096 }
1097 }
1098 }
1099
1100 // Create separate RGB and Alpha palettes and set the number of colors
1101 int numColors = numOpaqueColors + numAlphaColors;
1102 *paletteEntries = numColors;
1103 *alphaPaletteEntries = numAlphaColors;
1104
1105 // Create the RGB and alpha palettes
1106 for (int idx = 0; idx < numColors; idx++) {
1107 col = colors[idx];
1108 rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
1109 rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
1110 rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
1111 if (idx < numAlphaColors) {
1112 alphaPalette[idx] = (png_byte) (col & 0xff);
1113 }
1114 }
1115 } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
1116 // If the image is gray or gray + alpha, compact the pixels into outRows
1117 for (j = 0; j < h; j++) {
1118 png_bytep row = imageInfo.rows[j];
1119 png_bytep out = outRows[j];
1120 for (i = 0; i < w; i++) {
1121 rr = *row++;
1122 gg = *row++;
1123 bb = *row++;
1124 aa = *row++;
1125
1126 if (isGrayscale) {
1127 *out++ = rr;
1128 } else {
1129 *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
1130 }
1131 if (!isOpaque) {
1132 *out++ = aa;
1133 }
1134 }
1135 }
1136 }
1137 }
1138
write_png(const char * imageName,png_structp write_ptr,png_infop write_info,image_info & imageInfo,const Bundle * bundle)1139 static void write_png(const char* imageName,
1140 png_structp write_ptr, png_infop write_info,
1141 image_info& imageInfo, const Bundle* bundle)
1142 {
1143 png_uint_32 width, height;
1144 int color_type;
1145 int bit_depth, interlace_type, compression_type;
1146 int i;
1147
1148 png_unknown_chunk unknowns[3];
1149 unknowns[0].data = NULL;
1150 unknowns[1].data = NULL;
1151 unknowns[2].data = NULL;
1152
1153 png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
1154 if (outRows == (png_bytepp) 0) {
1155 printf("Can't allocate output buffer!\n");
1156 exit(1);
1157 }
1158 for (i = 0; i < (int) imageInfo.height; i++) {
1159 outRows[i] = (png_bytep) malloc(2 * (int) imageInfo.width);
1160 if (outRows[i] == (png_bytep) 0) {
1161 printf("Can't allocate output buffer!\n");
1162 exit(1);
1163 }
1164 }
1165
1166 png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
1167
1168 if (kIsDebug) {
1169 printf("Writing image %s: w = %d, h = %d\n", imageName,
1170 (int) imageInfo.width, (int) imageInfo.height);
1171 }
1172
1173 png_color rgbPalette[256];
1174 png_byte alphaPalette[256];
1175 bool hasTransparency;
1176 int paletteEntries, alphaPaletteEntries;
1177
1178 int grayscaleTolerance = bundle->getGrayscaleTolerance();
1179 analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
1180 &paletteEntries, &alphaPaletteEntries, &hasTransparency, &color_type, outRows);
1181
1182 // Legacy versions of aapt would always encode 9patch PNGs as RGBA. This had the unintended
1183 // benefit of working around a bug decoding paletted images in Android 4.1.
1184 // https://code.google.com/p/android/issues/detail?id=34619
1185 //
1186 // If SDK_JELLY_BEAN is supported, we need to avoid a paletted encoding in order to not expose
1187 // this bug.
1188 if (!bundle->isMinSdkAtLeast(SDK_JELLY_BEAN_MR1)) {
1189 if (imageInfo.is9Patch && PNG_COLOR_TYPE_PALETTE == color_type) {
1190 if (hasTransparency) {
1191 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1192 } else {
1193 color_type = PNG_COLOR_TYPE_RGB;
1194 }
1195 }
1196 }
1197
1198 if (kIsDebug) {
1199 switch (color_type) {
1200 case PNG_COLOR_TYPE_PALETTE:
1201 printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
1202 imageName, paletteEntries,
1203 hasTransparency ? " (with alpha)" : "");
1204 break;
1205 case PNG_COLOR_TYPE_GRAY:
1206 printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName);
1207 break;
1208 case PNG_COLOR_TYPE_GRAY_ALPHA:
1209 printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName);
1210 break;
1211 case PNG_COLOR_TYPE_RGB:
1212 printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName);
1213 break;
1214 case PNG_COLOR_TYPE_RGB_ALPHA:
1215 printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName);
1216 break;
1217 }
1218 }
1219
1220 png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height,
1221 8, color_type, PNG_INTERLACE_NONE,
1222 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1223
1224 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1225 png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
1226 if (hasTransparency) {
1227 png_set_tRNS(write_ptr, write_info, alphaPalette, alphaPaletteEntries,
1228 (png_color_16p) 0);
1229 }
1230 png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
1231 } else {
1232 png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
1233 }
1234
1235 if (imageInfo.is9Patch) {
1236 int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0);
1237 int p_index = imageInfo.haveLayoutBounds ? 2 : 1;
1238 int b_index = 1;
1239 int o_index = 0;
1240
1241 // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
1242 png_byte *chunk_names = imageInfo.haveLayoutBounds
1243 ? (png_byte*)"npOl\0npLb\0npTc\0"
1244 : (png_byte*)"npOl\0npTc";
1245
1246 // base 9 patch data
1247 if (kIsDebug) {
1248 printf("Adding 9-patch info...\n");
1249 }
1250 memcpy((char*)unknowns[p_index].name, "npTc", 5);
1251 unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
1252 unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
1253 // TODO: remove the check below when everything works
1254 checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
1255
1256 // automatically generated 9 patch outline data
1257 int chunk_size = sizeof(png_uint_32) * 6;
1258 memcpy((char*)unknowns[o_index].name, "npOl", 5);
1259 unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
1260 png_byte outputData[chunk_size];
1261 memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
1262 ((float*) outputData)[4] = imageInfo.outlineRadius;
1263 ((png_uint_32*) outputData)[5] = imageInfo.outlineAlpha;
1264 memcpy(unknowns[o_index].data, &outputData, chunk_size);
1265 unknowns[o_index].size = chunk_size;
1266
1267 // optional optical inset / layout bounds data
1268 if (imageInfo.haveLayoutBounds) {
1269 int chunk_size = sizeof(png_uint_32) * 4;
1270 memcpy((char*)unknowns[b_index].name, "npLb", 5);
1271 unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
1272 memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
1273 unknowns[b_index].size = chunk_size;
1274 }
1275
1276 for (int i = 0; i < chunk_count; i++) {
1277 unknowns[i].location = PNG_HAVE_IHDR;
1278 }
1279 png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
1280 chunk_names, chunk_count);
1281 png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
1282 }
1283
1284
1285 png_write_info(write_ptr, write_info);
1286
1287 png_bytepp rows;
1288 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
1289 if (color_type == PNG_COLOR_TYPE_RGB) {
1290 png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
1291 }
1292 rows = imageInfo.rows;
1293 } else {
1294 rows = outRows;
1295 }
1296 png_write_image(write_ptr, rows);
1297
1298 if (kIsDebug) {
1299 printf("Final image data:\n");
1300 dump_image(imageInfo.width, imageInfo.height, rows, color_type);
1301 }
1302
1303 png_write_end(write_ptr, write_info);
1304
1305 for (i = 0; i < (int) imageInfo.height; i++) {
1306 free(outRows[i]);
1307 }
1308 free(outRows);
1309 free(unknowns[0].data);
1310 free(unknowns[1].data);
1311 free(unknowns[2].data);
1312
1313 png_get_IHDR(write_ptr, write_info, &width, &height,
1314 &bit_depth, &color_type, &interlace_type,
1315 &compression_type, NULL);
1316
1317 if (kIsDebug) {
1318 printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
1319 (int)width, (int)height, bit_depth, color_type, interlace_type,
1320 compression_type);
1321 }
1322 }
1323
read_png_protected(png_structp read_ptr,String8 & printableName,png_infop read_info,const sp<AaptFile> & file,FILE * fp,image_info * imageInfo)1324 static bool read_png_protected(png_structp read_ptr, String8& printableName, png_infop read_info,
1325 const sp<AaptFile>& file, FILE* fp, image_info* imageInfo) {
1326 if (setjmp(png_jmpbuf(read_ptr))) {
1327 return false;
1328 }
1329
1330 png_init_io(read_ptr, fp);
1331
1332 read_png(printableName.c_str(), read_ptr, read_info, imageInfo);
1333
1334 const size_t nameLen = file->getPath().length();
1335 if (nameLen > 6) {
1336 const char* name = file->getPath().c_str();
1337 if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
1338 if (do_9patch(printableName.c_str(), imageInfo) != NO_ERROR) {
1339 return false;
1340 }
1341 }
1342 }
1343
1344 return true;
1345 }
1346
write_png_protected(png_structp write_ptr,String8 & printableName,png_infop write_info,image_info * imageInfo,const Bundle * bundle)1347 static bool write_png_protected(png_structp write_ptr, String8& printableName, png_infop write_info,
1348 image_info* imageInfo, const Bundle* bundle) {
1349 if (setjmp(png_jmpbuf(write_ptr))) {
1350 return false;
1351 }
1352
1353 write_png(printableName.c_str(), write_ptr, write_info, *imageInfo, bundle);
1354
1355 return true;
1356 }
1357
preProcessImage(const Bundle * bundle,const sp<AaptAssets> &,const sp<AaptFile> & file,String8 *)1358 status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */,
1359 const sp<AaptFile>& file, String8* /* outNewLeafName */)
1360 {
1361 String8 ext(getPathExtension(file->getPath()));
1362
1363 // We currently only process PNG images.
1364 if (strcmp(ext.c_str(), ".png") != 0) {
1365 return NO_ERROR;
1366 }
1367
1368 // Example of renaming a file:
1369 //*outNewLeafName = file->getPath().getBasePath().getFileName();
1370 //outNewLeafName->append(".nupng");
1371
1372 String8 printableName(file->getPrintableSource());
1373
1374 if (bundle->getVerbose()) {
1375 printf("Processing image: %s\n", printableName.c_str());
1376 }
1377
1378 png_structp read_ptr = NULL;
1379 png_infop read_info = NULL;
1380 FILE* fp;
1381
1382 image_info imageInfo;
1383
1384 png_structp write_ptr = NULL;
1385 png_infop write_info = NULL;
1386
1387 status_t error = UNKNOWN_ERROR;
1388
1389 fp = fopen(file->getSourceFile().c_str(), "rb");
1390 if (fp == NULL) {
1391 fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.c_str());
1392 goto bail;
1393 }
1394
1395 read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1396 (png_error_ptr)NULL);
1397 if (!read_ptr) {
1398 goto bail;
1399 }
1400
1401 read_info = png_create_info_struct(read_ptr);
1402 if (!read_info) {
1403 goto bail;
1404 }
1405
1406 if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) {
1407 goto bail;
1408 }
1409
1410 write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
1411 (png_error_ptr)NULL);
1412 if (!write_ptr)
1413 {
1414 goto bail;
1415 }
1416
1417 write_info = png_create_info_struct(write_ptr);
1418 if (!write_info)
1419 {
1420 goto bail;
1421 }
1422
1423 png_set_write_fn(write_ptr, (void*)file.get(),
1424 png_write_aapt_file, png_flush_aapt_file);
1425
1426 if (!write_png_protected(write_ptr, printableName, write_info, &imageInfo, bundle)) {
1427 goto bail;
1428 }
1429
1430 error = NO_ERROR;
1431
1432 if (bundle->getVerbose()) {
1433 fseek(fp, 0, SEEK_END);
1434 size_t oldSize = (size_t)ftell(fp);
1435 size_t newSize = file->getSize();
1436 float factor = ((float)newSize)/oldSize;
1437 int percent = (int)(factor*100);
1438 printf(" (processed image %s: %d%% size of source)\n", printableName.c_str(), percent);
1439 }
1440
1441 bail:
1442 if (read_ptr) {
1443 png_destroy_read_struct(&read_ptr, &read_info, (png_infopp)NULL);
1444 }
1445 if (fp) {
1446 fclose(fp);
1447 }
1448 if (write_ptr) {
1449 png_destroy_write_struct(&write_ptr, &write_info);
1450 }
1451
1452 if (error != NO_ERROR) {
1453 fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
1454 file->getPrintableSource().c_str());
1455 }
1456 return error;
1457 }
1458
preProcessImageToCache(const Bundle * bundle,const String8 & source,const String8 & dest)1459 status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
1460 {
1461 png_structp read_ptr = NULL;
1462 png_infop read_info = NULL;
1463
1464 FILE* fp;
1465
1466 image_info imageInfo;
1467
1468 png_structp write_ptr = NULL;
1469 png_infop write_info = NULL;
1470
1471 status_t error = UNKNOWN_ERROR;
1472
1473 if (bundle->getVerbose()) {
1474 printf("Processing image to cache: %s => %s\n", source.c_str(), dest.c_str());
1475 }
1476
1477 // Get a file handler to read from
1478 fp = fopen(source.c_str(),"rb");
1479 if (fp == NULL) {
1480 fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.c_str());
1481 return error;
1482 }
1483
1484 // Call libpng to get a struct to read image data into
1485 read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1486 if (!read_ptr) {
1487 fclose(fp);
1488 png_destroy_read_struct(&read_ptr, &read_info,NULL);
1489 return error;
1490 }
1491
1492 // Call libpng to get a struct to read image info into
1493 read_info = png_create_info_struct(read_ptr);
1494 if (!read_info) {
1495 fclose(fp);
1496 png_destroy_read_struct(&read_ptr, &read_info,NULL);
1497 return error;
1498 }
1499
1500 // Set a jump point for libpng to long jump back to on error
1501 if (setjmp(png_jmpbuf(read_ptr))) {
1502 fclose(fp);
1503 png_destroy_read_struct(&read_ptr, &read_info,NULL);
1504 return error;
1505 }
1506
1507 // Set up libpng to read from our file.
1508 png_init_io(read_ptr,fp);
1509
1510 // Actually read data from the file
1511 read_png(source.c_str(), read_ptr, read_info, &imageInfo);
1512
1513 // We're done reading so we can clean up
1514 // Find old file size before releasing handle
1515 fseek(fp, 0, SEEK_END);
1516 size_t oldSize = (size_t)ftell(fp);
1517 fclose(fp);
1518 png_destroy_read_struct(&read_ptr, &read_info,NULL);
1519
1520 // Check to see if we're dealing with a 9-patch
1521 // If we are, process appropriately
1522 if (getPathExtension(getBasePath(source)) == ".9") {
1523 if (do_9patch(source.c_str(), &imageInfo) != NO_ERROR) {
1524 return error;
1525 }
1526 }
1527
1528 // Call libpng to create a structure to hold the processed image data
1529 // that can be written to disk
1530 write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1531 if (!write_ptr) {
1532 png_destroy_write_struct(&write_ptr, &write_info);
1533 return error;
1534 }
1535
1536 // Call libpng to create a structure to hold processed image info that can
1537 // be written to disk
1538 write_info = png_create_info_struct(write_ptr);
1539 if (!write_info) {
1540 png_destroy_write_struct(&write_ptr, &write_info);
1541 return error;
1542 }
1543
1544 // Open up our destination file for writing
1545 fp = fopen(dest.c_str(), "wb");
1546 if (!fp) {
1547 fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.c_str());
1548 png_destroy_write_struct(&write_ptr, &write_info);
1549 return error;
1550 }
1551
1552 // Set up libpng to write to our file
1553 png_init_io(write_ptr, fp);
1554
1555 // Set up a jump for libpng to long jump back on on errors
1556 if (setjmp(png_jmpbuf(write_ptr))) {
1557 fclose(fp);
1558 png_destroy_write_struct(&write_ptr, &write_info);
1559 return error;
1560 }
1561
1562 // Actually write out to the new png
1563 write_png(dest.c_str(), write_ptr, write_info, imageInfo, bundle);
1564
1565 if (bundle->getVerbose()) {
1566 // Find the size of our new file
1567 FILE* reader = fopen(dest.c_str(), "rb");
1568 fseek(reader, 0, SEEK_END);
1569 size_t newSize = (size_t)ftell(reader);
1570 fclose(reader);
1571
1572 float factor = ((float)newSize)/oldSize;
1573 int percent = (int)(factor*100);
1574 printf(" (processed image to cache entry %s: %d%% size of source)\n",
1575 dest.c_str(), percent);
1576 }
1577
1578 //Clean up
1579 fclose(fp);
1580 png_destroy_write_struct(&write_ptr, &write_info);
1581
1582 return NO_ERROR;
1583 }
1584
postProcessImage(const Bundle * bundle,const sp<AaptAssets> & assets,ResourceTable * table,const sp<AaptFile> & file)1585 status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
1586 ResourceTable* table, const sp<AaptFile>& file)
1587 {
1588 String8 ext(getPathExtension(file->getPath()));
1589
1590 // At this point, now that we have all the resource data, all we need to
1591 // do is compile XML files.
1592 if (strcmp(ext.c_str(), ".xml") == 0) {
1593 String16 resourceName(parseResourceName(getPathLeaf(file->getSourceFile())));
1594 return compileXmlFile(bundle, assets, resourceName, file, table);
1595 }
1596
1597 return NO_ERROR;
1598 }
1599