2 // Copyright 2006 The Android Open Source Project
4 // Build resource files from raw assets.
11 #include <utils/ResourceTypes.h>
12 #include <utils/ByteOrder.h>
19 png_write_aapt_file(png_structp png_ptr
, png_bytep data
, png_size_t length
)
21 status_t err
= ((AaptFile
*)png_ptr
->io_ptr
)->writeData(data
, length
);
22 if (err
!= NO_ERROR
) {
23 png_error(png_ptr
, "Write Error");
29 png_flush_aapt_file(png_structp png_ptr
)
33 // This holds an image as 8bpp RGBA.
36 image_info() : rows(NULL
), hasTransparency(true), is9Patch(false), allocRows(NULL
) { }
38 if (rows
&& rows
!= allocRows
) {
42 for (int i
=0; i
<(int)allocHeight
; i
++) {
57 Res_png_9patch info9Patch
;
59 png_uint_32 allocHeight
;
63 static void read_png(const char* imageName
,
64 png_structp read_ptr
, png_infop read_info
,
65 image_info
* outImageInfo
)
68 int bit_depth
, interlace_type
, compression_type
;
71 png_read_info(read_ptr
, read_info
);
73 png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
,
74 &outImageInfo
->height
, &bit_depth
, &color_type
,
75 &interlace_type
, &compression_type
, NULL
);
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);
81 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
82 png_set_palette_to_rgb(read_ptr
);
84 if (color_type
== PNG_COLOR_TYPE_GRAY
&& bit_depth
< 8)
85 png_set_gray_1_2_4_to_8(read_ptr
);
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
);
93 png_set_strip_16(read_ptr
);
95 if ((color_type
&PNG_COLOR_MASK_ALPHA
) == 0)
96 png_set_add_alpha(read_ptr
, 0xFF, PNG_FILLER_AFTER
);
98 if (color_type
== PNG_COLOR_TYPE_GRAY
|| color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
)
99 png_set_gray_to_rgb(read_ptr
);
101 png_read_update_info(read_ptr
, read_info
);
103 outImageInfo
->rows
= (png_bytepp
)malloc(
104 outImageInfo
->height
* png_sizeof(png_bytep
));
105 outImageInfo
->allocHeight
= outImageInfo
->height
;
106 outImageInfo
->allocRows
= outImageInfo
->rows
;
108 png_set_rows(read_ptr
, read_info
, outImageInfo
->rows
);
110 for (i
= 0; i
< (int)outImageInfo
->height
; i
++)
112 outImageInfo
->rows
[i
] = (png_bytep
)
113 malloc(png_get_rowbytes(read_ptr
, read_info
));
116 png_read_image(read_ptr
, outImageInfo
->rows
);
118 png_read_end(read_ptr
, read_info
);
120 NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
122 (int)outImageInfo
->width
, (int)outImageInfo
->height
,
123 bit_depth
, color_type
,
124 interlace_type
, compression_type
));
126 png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
,
127 &outImageInfo
->height
, &bit_depth
, &color_type
,
128 &interlace_type
, &compression_type
, NULL
);
131 static bool is_tick(png_bytep p
, bool transparent
, const char** outError
)
138 *outError
= "Frame pixels must be either solid or transparent (not intermediate alphas)";
141 if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) {
142 *outError
= "Ticks in transparent frame must be black";
148 *outError
= "White frame must be a solid color (no alpha)";
150 if (p
[0] == 0xFF && p
[1] == 0xFF && p
[2] == 0xFF) {
153 if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) {
154 *outError
= "Ticks in white frame must be black";
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
)
172 *outLeft
= *outRight
= -1;
173 int state
= TICK_START
;
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
)) {
183 if (outDivs
!= NULL
) {
186 state
= TICK_INSIDE_1
;
187 } else if (state
== TICK_OUTSIDE_1
) {
188 *outError
= "Can't have more than one marked region along edge";
190 return UNKNOWN_ERROR
;
192 } else if (*outError
== NULL
) {
193 if (state
== TICK_INSIDE_1
) {
194 // We're done with this div. Move on to the next.
198 state
= TICK_OUTSIDE_1
;
202 return UNKNOWN_ERROR
;
206 if (required
&& !found
) {
207 *outError
= "No marked region found along edge";
209 return UNKNOWN_ERROR
;
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
)
221 *outTop
= *outBottom
= -1;
222 int state
= TICK_START
;
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
)) {
230 *outBottom
= height
-2;
232 if (outDivs
!= NULL
) {
235 state
= TICK_INSIDE_1
;
236 } else if (state
== TICK_OUTSIDE_1
) {
237 *outError
= "Can't have more than one marked region along edge";
239 return UNKNOWN_ERROR
;
241 } else if (*outError
== NULL
) {
242 if (state
== TICK_INSIDE_1
) {
243 // We're done with this div. Move on to the next.
247 state
= TICK_OUTSIDE_1
;
251 return UNKNOWN_ERROR
;
255 if (required
&& !found
) {
256 *outError
= "No marked region found along edge";
258 return UNKNOWN_ERROR
;
264 static uint32_t get_color(
265 png_bytepp rows
, int left
, int top
, int right
, int bottom
)
267 png_bytep color
= rows
[top
] + left
*4;
269 if (left
> right
|| top
> bottom
) {
270 return Res_png_9patch::TRANSPARENT_COLOR
;
273 while (top
<= bottom
) {
274 for (int i
= left
; i
<= right
; i
++) {
275 png_bytep p
= rows
[top
]+i
*4;
278 return Res_png_9patch::NO_COLOR
;
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
;
289 return Res_png_9patch::TRANSPARENT_COLOR
;
291 return (color
[3]<<24) | (color
[0]<<16) | (color
[1]<<8) | color
[2];
294 static void select_patch(
295 int which
, int front
, int back
, int size
, int* start
, int* end
)
313 static uint32_t get_color(image_info
* image
, int hpatch
, int vpatch
)
315 int left
, right
, top
, bottom
;
317 hpatch
, image
->info9Patch
.xDivs
[0], image
->info9Patch
.xDivs
[1],
318 image
->width
, &left
, &right
);
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
));
329 static void examine_image(image_info
* image
)
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) {
342 image
->hasTransparency
= hasTrans
;
345 static status_t
do_9patch(const char* imageName
, image_info
* image
)
347 image
->is9Patch
= true;
349 int W
= image
->width
;
350 int H
= image
->height
;
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;
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;
371 png_bytep p
= image
->rows
[0];
372 bool transparent
= p
[3] == 0;
373 bool hasColor
= false;
375 const char* errorMsg
= NULL
;
377 const char* errorEdge
= "";
382 if (W
< 3 || H
< 3) {
383 errorMsg
= "Image must be at least 3x3 (1x1 without frame) pixels";
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";
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];
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];
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";
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
;
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
;
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];
437 // Adjust value to be correct!
438 image
->info9Patch
.paddingRight
= W
- 2 - image
->info9Patch
.paddingRight
;
440 if (image
->info9Patch
.paddingTop
< 0) {
441 image
->info9Patch
.paddingTop
= yDivs
[0];
442 image
->info9Patch
.paddingBottom
= H
- 2 - yDivs
[1];
444 // Adjust value to be correct!
445 image
->info9Patch
.paddingBottom
= H
- 2 - image
->info9Patch
.paddingBottom
;
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
));
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);
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
471 if (xDivs
[numXDivs
- 1] == W
) {
474 numRows
= numYDivs
+ 1;
475 if (yDivs
[0] == 0) { // Row 1 is strechable
478 if (yDivs
[numYDivs
- 1] == H
) {
481 numColors
= numRows
* numCols
;
482 image
->info9Patch
.numColors
= numColors
;
483 image
->info9Patch
.colors
= (uint32_t*)malloc(numColors
* sizeof(uint32_t));
485 // Fill in color information for each patch.
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.
498 // The first and last columns are similarly treated with respect to the X
501 // The above is to help explain some of the special casing that goes on the
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
;
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
;
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);
533 assert(colorIndex
== numColors
);
535 for (i
=0; i
<numColors
; i
++) {
537 if (i
== 0) printf("Colors in %s:\n ", imageName
);
538 printf(" #%08x", image
->info9Patch
.colors
[i
]);
539 if (i
== numColors
- 1) printf("\n");
543 image
->is9Patch
= true;
544 image
->info9Patch
.deviceToFile();
549 "ERROR: 9-patch image %s malformed.\n"
550 " %s.\n", imageName
, errorMsg
);
551 if (errorPixel
>= 0) {
553 " Found at pixel #%d along %s edge.\n", errorPixel
, errorEdge
);
556 " Found along %s edge.\n", errorEdge
);
558 return UNKNOWN_ERROR
;
563 static void checkNinePatchSerialization(Res_png_9patch
* inPatch
, void * data
)
565 if (sizeof(void*) != sizeof(int32_t)) {
566 // can't deserialize on a non-32 bit system
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
]);
584 for (int i
= 0; i
< outPatch
->numYDivs
; i
++) {
585 assert(outPatch
->yDivs
[i
] == inPatch
->yDivs
[i
]);
587 for (int i
= 0; i
< outPatch
->numColors
; i
++) {
588 assert(outPatch
->colors
[i
] == inPatch
->colors
[i
]);
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
)) {
603 for (int i
= 0; i
< patch1
.numColors
; i
++) {
604 if (patch1
.colors
[i
] != patch2
.colors
[i
]) {
608 for (int i
= 0; i
< patch1
.numXDivs
; i
++) {
609 if (patch1
.xDivs
[i
] != patch2
.xDivs
[i
]) {
613 for (int i
= 0; i
< patch1
.numYDivs
; i
++) {
614 if (patch1
.yDivs
[i
] != patch2
.yDivs
[i
]) {
622 static void analyze_image(image_info
&imageInfo
, png_colorp rgbPalette
, png_bytep alphaPalette
,
623 int *paletteEntries
, bool *hasTransparency
, int *colorType
,
626 int w
= imageInfo
.width
;
627 int h
= imageInfo
.height
;
628 bool trans
= imageInfo
.hasTransparency
;
629 int i
, j
, rr
, gg
, bb
, aa
, idx
;
630 uint32_t colors
[256], col
;
633 bool isOpaque
= true;
634 bool isPalette
= true;
635 bool isGrayscale
= true;
637 // Scan the entire image and determine if:
638 // 1. Every pixel has R == G == B (grayscale)
639 // 2. Every pixel has A == 255 (opaque)
640 // 3. There are no more than 256 distinct RGBA colors
641 for (j
= 0; j
< h
; j
++) {
642 png_bytep row
= imageInfo
.rows
[j
];
643 png_bytep out
= outRows
[j
];
644 for (i
= 0; i
< w
; i
++) {
650 // Ignore the actually byte and assume alpha == 255
654 // Check if image is really grayscale
656 if (rr
!= gg
|| rr
!= bb
) {
661 // Check if image is really opaque
668 // Check if image is really <= 256 colors
670 col
= (uint32_t) ((rr
<< 24) | (gg
<< 16) | (bb
<< 8) | aa
);
672 for (idx
= 0; idx
< num_colors
; idx
++) {
673 if (colors
[idx
] == col
) {
679 // Write the palette index for the pixel to outRows optimistically
680 // We might overwrite it later if we decide to encode as gray or
684 if (num_colors
== 256) {
687 colors
[num_colors
++] = col
;
695 *hasTransparency
= !isOpaque
;
696 int bpp
= isOpaque
? 3 : 4;
697 int paletteSize
= w
* h
+ bpp
* num_colors
;
699 // Choose the best color type for the image.
700 // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
701 // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
702 // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
703 // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
704 // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
707 *colorType
= PNG_COLOR_TYPE_GRAY
; // 1 byte/pixel
709 // Use a simple heuristic to determine whether using a palette will
710 // save space versus using gray + alpha for each pixel.
711 // This doesn't take into account chunk overhead, filtering, LZ
713 if (isPalette
&& (paletteSize
< 2 * w
* h
)) {
714 *colorType
= PNG_COLOR_TYPE_PALETTE
; // 1 byte/pixel + 4 bytes/color
716 *colorType
= PNG_COLOR_TYPE_GRAY_ALPHA
; // 2 bytes per pixel
719 } else if (isPalette
&& (paletteSize
< bpp
* w
* h
)) {
720 *colorType
= PNG_COLOR_TYPE_PALETTE
;
722 *colorType
= isOpaque
? PNG_COLOR_TYPE_RGB
: PNG_COLOR_TYPE_RGB_ALPHA
;
725 // Perform postprocessing of the image or palette data based on the final
728 if (*colorType
== PNG_COLOR_TYPE_PALETTE
) {
729 // Create separate RGB and Alpha palettes and set the number of colors
730 *paletteEntries
= num_colors
;
732 // Create the RGB and alpha palettes
733 for (int idx
= 0; idx
< num_colors
; idx
++) {
735 rgbPalette
[idx
].red
= (png_byte
) ((col
>> 24) & 0xff);
736 rgbPalette
[idx
].green
= (png_byte
) ((col
>> 16) & 0xff);
737 rgbPalette
[idx
].blue
= (png_byte
) ((col
>> 8) & 0xff);
738 alphaPalette
[idx
] = (png_byte
) (col
& 0xff);
740 } else if (*colorType
== PNG_COLOR_TYPE_GRAY
|| *colorType
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
741 // If the image is gray or gray + alpha, compact the pixels into outRows
742 for (j
= 0; j
< h
; j
++) {
743 png_bytep row
= imageInfo
.rows
[j
];
744 png_bytep out
= outRows
[j
];
745 for (i
= 0; i
< w
; i
++) {
761 static void write_png(const char* imageName
,
762 png_structp write_ptr
, png_infop write_info
,
763 image_info
& imageInfo
)
765 bool optimize
= true;
766 png_uint_32 width
, height
;
768 int bit_depth
, interlace_type
, compression_type
;
771 png_unknown_chunk unknowns
[1];
773 png_bytepp outRows
= (png_bytepp
) malloc((int) imageInfo
.height
* png_sizeof(png_bytep
));
774 if (outRows
== (png_bytepp
) 0) {
775 printf("Can't allocate output buffer!\n");
778 for (i
= 0; i
< (int) imageInfo
.height
; i
++) {
779 outRows
[i
] = (png_bytep
) malloc(2 * (int) imageInfo
.width
);
780 if (outRows
[i
] == (png_bytep
) 0) {
781 printf("Can't allocate output buffer!\n");
786 png_set_compression_level(write_ptr
, Z_BEST_COMPRESSION
);
788 NOISY(printf("Writing image %s: w = %d, h = %d, trans = %s\n", imageName
,
789 (int) imageInfo
.width
, (int) imageInfo
.height
,
790 imageInfo
.hasTransparency
? "true" : "false"));
792 png_color rgbPalette
[256];
793 png_byte alphaPalette
[256];
794 bool hasTransparency
;
798 analyze_image(imageInfo
, rgbPalette
, alphaPalette
, &paletteEntries
, &hasTransparency
,
799 &color_type
, outRows
);
800 switch (color_type
) {
801 case PNG_COLOR_TYPE_PALETTE
:
802 NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
803 imageName
, paletteEntries
,
804 hasTransparency
? " (with alpha)" : ""));
806 case PNG_COLOR_TYPE_GRAY
:
807 NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName
));
809 case PNG_COLOR_TYPE_GRAY_ALPHA
:
810 NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName
));
812 case PNG_COLOR_TYPE_RGB
:
813 NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName
));
815 case PNG_COLOR_TYPE_RGB_ALPHA
:
816 NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName
));
820 // Force RGB or RGB_ALPHA color type, copy transparency from input
822 hasTransparency
= imageInfo
.hasTransparency
;
823 color_type
= hasTransparency
? PNG_COLOR_TYPE_RGB_ALPHA
: PNG_COLOR_TYPE_RGB
;
826 png_set_IHDR(write_ptr
, write_info
, imageInfo
.width
, imageInfo
.height
,
827 8, color_type
, PNG_INTERLACE_NONE
,
828 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
830 if (color_type
== PNG_COLOR_TYPE_PALETTE
) {
831 png_set_PLTE(write_ptr
, write_info
, rgbPalette
, paletteEntries
);
832 if (hasTransparency
) {
833 png_set_tRNS(write_ptr
, write_info
, alphaPalette
, paletteEntries
, (png_color_16p
) 0);
835 png_set_filter(write_ptr
, 0, PNG_NO_FILTERS
);
837 png_set_filter(write_ptr
, 0, PNG_ALL_FILTERS
);
840 if (imageInfo
.is9Patch
) {
841 NOISY(printf("Adding 9-patch info...\n"));
842 strcpy((char*)unknowns
[0].name
, "npTc");
843 unknowns
[0].data
= (png_byte
*)imageInfo
.info9Patch
.serialize();
844 unknowns
[0].size
= imageInfo
.info9Patch
.serializedSize();
845 // TODO: remove the check below when everything works
846 checkNinePatchSerialization(&imageInfo
.info9Patch
, unknowns
[0].data
);
847 png_set_keep_unknown_chunks(write_ptr
, PNG_HANDLE_CHUNK_ALWAYS
,
848 (png_byte
*)"npTc", 1);
849 png_set_unknown_chunks(write_ptr
, write_info
, unknowns
, 1);
850 // XXX I can't get this to work without forcibly changing
851 // the location to what I want... which apparently is supposed
852 // to be a private API, but everything else I have tried results
853 // in the location being set to what I -last- wrote so I never
855 png_set_unknown_chunk_location(write_ptr
, write_info
, 0, PNG_HAVE_PLTE
);
858 png_write_info(write_ptr
, write_info
);
860 if (!imageInfo
.hasTransparency
) {
861 png_set_filler(write_ptr
, 0, PNG_FILLER_AFTER
);
864 if (color_type
== PNG_COLOR_TYPE_RGB
|| color_type
== PNG_COLOR_TYPE_RGB_ALPHA
) {
865 png_write_image(write_ptr
, imageInfo
.rows
);
867 png_write_image(write_ptr
, outRows
);
870 png_write_end(write_ptr
, write_info
);
872 for (i
= 0; i
< (int) imageInfo
.height
; i
++) {
877 png_get_IHDR(write_ptr
, write_info
, &width
, &height
,
878 &bit_depth
, &color_type
, &interlace_type
,
879 &compression_type
, NULL
);
881 NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
882 (int)width
, (int)height
, bit_depth
, color_type
, interlace_type
,
886 status_t
preProcessImage(Bundle
* bundle
, const sp
<AaptAssets
>& assets
,
887 const sp
<AaptFile
>& file
, String8
* outNewLeafName
)
889 String8
ext(file
->getPath().getPathExtension());
891 // We currently only process PNG images.
892 if (strcmp(ext
.string(), ".png") != 0) {
896 // Example of renaming a file:
897 //*outNewLeafName = file->getPath().getBasePath().getFileName();
898 //outNewLeafName->append(".nupng");
900 String8
printableName(file
->getPrintableSource());
902 png_structp read_ptr
= NULL
;
903 png_infop read_info
= NULL
;
906 image_info imageInfo
;
908 png_structp write_ptr
= NULL
;
909 png_infop write_info
= NULL
;
911 status_t error
= UNKNOWN_ERROR
;
913 const size_t nameLen
= file
->getPath().length();
915 fp
= fopen(file
->getSourceFile().string(), "rb");
917 fprintf(stderr
, "%s: ERROR: Unable to open PNG file\n", printableName
.string());
921 read_ptr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
,
922 (png_error_ptr
)NULL
);
927 read_info
= png_create_info_struct(read_ptr
);
932 if (setjmp(png_jmpbuf(read_ptr
))) {
936 png_init_io(read_ptr
, fp
);
938 read_png(printableName
.string(), read_ptr
, read_info
, &imageInfo
);
940 examine_image(&imageInfo
);
943 const char* name
= file
->getPath().string();
944 if (name
[nameLen
-5] == '9' && name
[nameLen
-6] == '.') {
945 if (do_9patch(printableName
.string(), &imageInfo
) != NO_ERROR
) {
951 write_ptr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
,
952 (png_error_ptr
)NULL
);
958 write_info
= png_create_info_struct(write_ptr
);
964 png_set_write_fn(write_ptr
, (void*)file
.get(),
965 png_write_aapt_file
, png_flush_aapt_file
);
967 if (setjmp(png_jmpbuf(write_ptr
)))
972 write_png(printableName
.string(), write_ptr
, write_info
, imageInfo
);
976 if (bundle
->getVerbose()) {
977 fseek(fp
, 0, SEEK_END
);
978 size_t oldSize
= (size_t)ftell(fp
);
979 size_t newSize
= file
->getSize();
980 float factor
= ((float)newSize
)/oldSize
;
981 int percent
= (int)(factor
*100);
982 printf(" (processed image %s: %d%% size of source)\n", printableName
.string(), percent
);
987 png_destroy_read_struct(&read_ptr
, &read_info
, (png_infopp
)NULL
);
993 png_destroy_write_struct(&write_ptr
, &write_info
);
996 if (error
!= NO_ERROR
) {
997 fprintf(stderr
, "ERROR: Failure processing PNG image %s\n",
998 file
->getPrintableSource().string());
1005 status_t
postProcessImage(const sp
<AaptAssets
>& assets
,
1006 ResourceTable
* table
, const sp
<AaptFile
>& file
)
1008 String8
ext(file
->getPath().getPathExtension());
1010 // At this point, now that we have all the resource data, all we need to
1011 // do is compile XML files.
1012 if (strcmp(ext
.string(), ".xml") == 0) {
1013 return compileXmlFile(assets
, file
, table
);