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
), is9Patch(false), allocRows(NULL
) { }
38 if (rows
&& rows
!= allocRows
) {
42 for (int i
=0; i
<(int)allocHeight
; i
++) {
47 free(info9Patch
.xDivs
);
48 free(info9Patch
.yDivs
);
49 free(info9Patch
.colors
);
58 Res_png_9patch info9Patch
;
60 png_uint_32 allocHeight
;
64 static void read_png(const char* imageName
,
65 png_structp read_ptr
, png_infop read_info
,
66 image_info
* outImageInfo
)
69 int bit_depth
, interlace_type
, compression_type
;
72 png_read_info(read_ptr
, read_info
);
74 png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
,
75 &outImageInfo
->height
, &bit_depth
, &color_type
,
76 &interlace_type
, &compression_type
, NULL
);
78 //printf("Image %s:\n", imageName);
79 //printf("color_type=%d, bit_depth=%d, interlace_type=%d, compression_type=%d\n",
80 // color_type, bit_depth, interlace_type, compression_type);
82 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
83 png_set_palette_to_rgb(read_ptr
);
85 if (color_type
== PNG_COLOR_TYPE_GRAY
&& bit_depth
< 8)
86 png_set_gray_1_2_4_to_8(read_ptr
);
88 if (png_get_valid(read_ptr
, read_info
, PNG_INFO_tRNS
)) {
89 //printf("Has PNG_INFO_tRNS!\n");
90 png_set_tRNS_to_alpha(read_ptr
);
94 png_set_strip_16(read_ptr
);
96 if ((color_type
&PNG_COLOR_MASK_ALPHA
) == 0)
97 png_set_add_alpha(read_ptr
, 0xFF, PNG_FILLER_AFTER
);
99 if (color_type
== PNG_COLOR_TYPE_GRAY
|| color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
)
100 png_set_gray_to_rgb(read_ptr
);
102 png_read_update_info(read_ptr
, read_info
);
104 outImageInfo
->rows
= (png_bytepp
)malloc(
105 outImageInfo
->height
* png_sizeof(png_bytep
));
106 outImageInfo
->allocHeight
= outImageInfo
->height
;
107 outImageInfo
->allocRows
= outImageInfo
->rows
;
109 png_set_rows(read_ptr
, read_info
, outImageInfo
->rows
);
111 for (i
= 0; i
< (int)outImageInfo
->height
; i
++)
113 outImageInfo
->rows
[i
] = (png_bytep
)
114 malloc(png_get_rowbytes(read_ptr
, read_info
));
117 png_read_image(read_ptr
, outImageInfo
->rows
);
119 png_read_end(read_ptr
, read_info
);
121 NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
123 (int)outImageInfo
->width
, (int)outImageInfo
->height
,
124 bit_depth
, color_type
,
125 interlace_type
, compression_type
));
127 png_get_IHDR(read_ptr
, read_info
, &outImageInfo
->width
,
128 &outImageInfo
->height
, &bit_depth
, &color_type
,
129 &interlace_type
, &compression_type
, NULL
);
132 static bool is_tick(png_bytep p
, bool transparent
, const char** outError
)
139 *outError
= "Frame pixels must be either solid or transparent (not intermediate alphas)";
142 if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) {
143 *outError
= "Ticks in transparent frame must be black";
149 *outError
= "White frame must be a solid color (no alpha)";
151 if (p
[0] == 0xFF && p
[1] == 0xFF && p
[2] == 0xFF) {
154 if (p
[0] != 0 || p
[1] != 0 || p
[2] != 0) {
155 *outError
= "Ticks in white frame must be black";
167 static status_t
get_horizontal_ticks(
168 png_bytep row
, int width
, bool transparent
, bool required
,
169 int32_t* outLeft
, int32_t* outRight
, const char** outError
,
170 uint8_t* outDivs
, bool multipleAllowed
)
173 *outLeft
= *outRight
= -1;
174 int state
= TICK_START
;
177 for (i
=1; i
<width
-1; i
++) {
178 if (is_tick(row
+i
*4, transparent
, outError
)) {
179 if (state
== TICK_START
||
180 (state
== TICK_OUTSIDE_1
&& multipleAllowed
)) {
184 if (outDivs
!= NULL
) {
187 state
= TICK_INSIDE_1
;
188 } else if (state
== TICK_OUTSIDE_1
) {
189 *outError
= "Can't have more than one marked region along edge";
191 return UNKNOWN_ERROR
;
193 } else if (*outError
== NULL
) {
194 if (state
== TICK_INSIDE_1
) {
195 // We're done with this div. Move on to the next.
199 state
= TICK_OUTSIDE_1
;
203 return UNKNOWN_ERROR
;
207 if (required
&& !found
) {
208 *outError
= "No marked region found along edge";
210 return UNKNOWN_ERROR
;
216 static status_t
get_vertical_ticks(
217 png_bytepp rows
, int offset
, int height
, bool transparent
, bool required
,
218 int32_t* outTop
, int32_t* outBottom
, const char** outError
,
219 uint8_t* outDivs
, bool multipleAllowed
)
222 *outTop
= *outBottom
= -1;
223 int state
= TICK_START
;
226 for (i
=1; i
<height
-1; i
++) {
227 if (is_tick(rows
[i
]+offset
, transparent
, outError
)) {
228 if (state
== TICK_START
||
229 (state
== TICK_OUTSIDE_1
&& multipleAllowed
)) {
231 *outBottom
= height
-2;
233 if (outDivs
!= NULL
) {
236 state
= TICK_INSIDE_1
;
237 } else if (state
== TICK_OUTSIDE_1
) {
238 *outError
= "Can't have more than one marked region along edge";
240 return UNKNOWN_ERROR
;
242 } else if (*outError
== NULL
) {
243 if (state
== TICK_INSIDE_1
) {
244 // We're done with this div. Move on to the next.
248 state
= TICK_OUTSIDE_1
;
252 return UNKNOWN_ERROR
;
256 if (required
&& !found
) {
257 *outError
= "No marked region found along edge";
259 return UNKNOWN_ERROR
;
265 static uint32_t get_color(
266 png_bytepp rows
, int left
, int top
, int right
, int bottom
)
268 png_bytep color
= rows
[top
] + left
*4;
270 if (left
> right
|| top
> bottom
) {
271 return Res_png_9patch::TRANSPARENT_COLOR
;
274 while (top
<= bottom
) {
275 for (int i
= left
; i
<= right
; i
++) {
276 png_bytep p
= rows
[top
]+i
*4;
279 return Res_png_9patch::NO_COLOR
;
281 } else if (p
[0] != color
[0] || p
[1] != color
[1]
282 || p
[2] != color
[2] || p
[3] != color
[3]) {
283 return Res_png_9patch::NO_COLOR
;
290 return Res_png_9patch::TRANSPARENT_COLOR
;
292 return (color
[3]<<24) | (color
[0]<<16) | (color
[1]<<8) | color
[2];
295 static void select_patch(
296 int which
, int front
, int back
, int size
, int* start
, int* end
)
314 static uint32_t get_color(image_info
* image
, int hpatch
, int vpatch
)
316 int left
, right
, top
, bottom
;
318 hpatch
, image
->info9Patch
.xDivs
[0], image
->info9Patch
.xDivs
[1],
319 image
->width
, &left
, &right
);
321 vpatch
, image
->info9Patch
.yDivs
[0], image
->info9Patch
.yDivs
[1],
322 image
->height
, &top
, &bottom
);
323 //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
324 // hpatch, vpatch, left, top, right, bottom);
325 const uint32_t c
= get_color(image
->rows
, left
, top
, right
, bottom
);
326 NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left
, top
, right
, bottom
, c
));
330 static status_t
do_9patch(const char* imageName
, image_info
* image
)
332 image
->is9Patch
= true;
334 int W
= image
->width
;
335 int H
= image
->height
;
338 int maxSizeXDivs
= W
* sizeof(int32_t);
339 int maxSizeYDivs
= H
* sizeof(int32_t);
340 int32_t* xDivs
= (int32_t*) malloc(maxSizeXDivs
);
341 int32_t* yDivs
= (int32_t*) malloc(maxSizeYDivs
);
342 uint8_t numXDivs
= 0;
343 uint8_t numYDivs
= 0;
351 memset(xDivs
, -1, maxSizeXDivs
);
352 memset(yDivs
, -1, maxSizeYDivs
);
353 image
->info9Patch
.paddingLeft
= image
->info9Patch
.paddingRight
=
354 image
->info9Patch
.paddingTop
= image
->info9Patch
.paddingBottom
= -1;
356 png_bytep p
= image
->rows
[0];
357 bool transparent
= p
[3] == 0;
358 bool hasColor
= false;
360 const char* errorMsg
= NULL
;
362 const char* errorEdge
= "";
367 if (W
< 3 || H
< 3) {
368 errorMsg
= "Image must be at least 3x3 (1x1 without frame) pixels";
374 (p
[0] != 0xFF || p
[1] != 0xFF || p
[2] != 0xFF || p
[3] != 0xFF)) {
375 errorMsg
= "Must have one-pixel frame that is either transparent or white";
379 // Find left and right of sizing areas...
380 if (get_horizontal_ticks(p
, W
, transparent
, true, &xDivs
[0],
381 &xDivs
[1], &errorMsg
, &numXDivs
, true) != NO_ERROR
) {
382 errorPixel
= xDivs
[0];
387 // Find top and bottom of sizing areas...
388 if (get_vertical_ticks(image
->rows
, 0, H
, transparent
, true, &yDivs
[0],
389 &yDivs
[1], &errorMsg
, &numYDivs
, true) != NO_ERROR
) {
390 errorPixel
= yDivs
[0];
395 // Find left and right of padding area...
396 if (get_horizontal_ticks(image
->rows
[H
-1], W
, transparent
, false, &image
->info9Patch
.paddingLeft
,
397 &image
->info9Patch
.paddingRight
, &errorMsg
, NULL
, false) != NO_ERROR
) {
398 errorPixel
= image
->info9Patch
.paddingLeft
;
399 errorEdge
= "bottom";
403 // Find top and bottom of padding area...
404 if (get_vertical_ticks(image
->rows
, (W
-1)*4, H
, transparent
, false, &image
->info9Patch
.paddingTop
,
405 &image
->info9Patch
.paddingBottom
, &errorMsg
, NULL
, false) != NO_ERROR
) {
406 errorPixel
= image
->info9Patch
.paddingTop
;
411 // Copy patch data into image
412 image
->info9Patch
.numXDivs
= numXDivs
;
413 image
->info9Patch
.numYDivs
= numYDivs
;
414 image
->info9Patch
.xDivs
= xDivs
;
415 image
->info9Patch
.yDivs
= yDivs
;
417 // If padding is not yet specified, take values from size.
418 if (image
->info9Patch
.paddingLeft
< 0) {
419 image
->info9Patch
.paddingLeft
= xDivs
[0];
420 image
->info9Patch
.paddingRight
= W
- 2 - xDivs
[1];
422 // Adjust value to be correct!
423 image
->info9Patch
.paddingRight
= W
- 2 - image
->info9Patch
.paddingRight
;
425 if (image
->info9Patch
.paddingTop
< 0) {
426 image
->info9Patch
.paddingTop
= yDivs
[0];
427 image
->info9Patch
.paddingBottom
= H
- 2 - yDivs
[1];
429 // Adjust value to be correct!
430 image
->info9Patch
.paddingBottom
= H
- 2 - image
->info9Patch
.paddingBottom
;
433 NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName
,
434 image
->info9Patch
.xDivs
[0], image
->info9Patch
.xDivs
[1],
435 image
->info9Patch
.yDivs
[0], image
->info9Patch
.yDivs
[1]));
436 NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName
,
437 image
->info9Patch
.paddingLeft
, image
->info9Patch
.paddingRight
,
438 image
->info9Patch
.paddingTop
, image
->info9Patch
.paddingBottom
));
440 // Remove frame from image.
441 image
->rows
= (png_bytepp
)malloc((H
-2) * png_sizeof(png_bytep
));
442 for (i
=0; i
<(H
-2); i
++) {
443 image
->rows
[i
] = image
->allocRows
[i
+1];
444 memmove(image
->rows
[i
], image
->rows
[i
]+4, (W
-2)*4);
451 // Figure out the number of rows and columns in the N-patch
452 numCols
= numXDivs
+ 1;
453 if (xDivs
[0] == 0) { // Column 1 is strechable
456 if (xDivs
[numXDivs
- 1] == W
) {
459 numRows
= numYDivs
+ 1;
460 if (yDivs
[0] == 0) { // Row 1 is strechable
463 if (yDivs
[numYDivs
- 1] == H
) {
466 numColors
= numRows
* numCols
;
467 image
->info9Patch
.numColors
= numColors
;
468 image
->info9Patch
.colors
= (uint32_t*)malloc(numColors
* sizeof(uint32_t));
470 // Fill in color information for each patch.
475 // The first row always starts with the top being at y=0 and the bottom
476 // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
477 // the first row is stretchable along the Y axis, otherwise it is fixed.
478 // The last row always ends with the bottom being bitmap.height and the top
479 // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
480 // yDivs[numYDivs-1]. In the former case the last row is stretchable along
481 // the Y axis, otherwise it is fixed.
483 // The first and last columns are similarly treated with respect to the X
486 // The above is to help explain some of the special casing that goes on the
489 // The initial yDiv and whether the first row is considered stretchable or
490 // not depends on whether yDiv[0] was zero or not.
491 for (j
= (yDivs
[0] == 0 ? 1 : 0);
492 j
<= numYDivs
&& top
< H
;
500 // The initial xDiv and whether the first column is considered
501 // stretchable or not depends on whether xDiv[0] was zero or not.
502 for (i
= xDivs
[0] == 0 ? 1 : 0;
503 i
<= numXDivs
&& left
< W
;
510 c
= get_color(image
->rows
, left
, top
, right
- 1, bottom
- 1);
511 image
->info9Patch
.colors
[colorIndex
++] = c
;
512 NOISY(if (c
!= Res_png_9patch::NO_COLOR
) hasColor
= true);
518 assert(colorIndex
== numColors
);
520 for (i
=0; i
<numColors
; i
++) {
522 if (i
== 0) printf("Colors in %s:\n ", imageName
);
523 printf(" #%08x", image
->info9Patch
.colors
[i
]);
524 if (i
== numColors
- 1) printf("\n");
528 image
->is9Patch
= true;
529 image
->info9Patch
.deviceToFile();
534 "ERROR: 9-patch image %s malformed.\n"
535 " %s.\n", imageName
, errorMsg
);
536 if (errorPixel
>= 0) {
538 " Found at pixel #%d along %s edge.\n", errorPixel
, errorEdge
);
541 " Found along %s edge.\n", errorEdge
);
543 return UNKNOWN_ERROR
;
548 static void checkNinePatchSerialization(Res_png_9patch
* inPatch
, void * data
)
550 if (sizeof(void*) != sizeof(int32_t)) {
551 // can't deserialize on a non-32 bit system
554 size_t patchSize
= inPatch
->serializedSize();
555 void * newData
= malloc(patchSize
);
556 memcpy(newData
, data
, patchSize
);
557 Res_png_9patch
* outPatch
= inPatch
->deserialize(newData
);
558 // deserialization is done in place, so outPatch == newData
559 assert(outPatch
== newData
);
560 assert(outPatch
->numXDivs
== inPatch
->numXDivs
);
561 assert(outPatch
->numYDivs
== inPatch
->numYDivs
);
562 assert(outPatch
->paddingLeft
== inPatch
->paddingLeft
);
563 assert(outPatch
->paddingRight
== inPatch
->paddingRight
);
564 assert(outPatch
->paddingTop
== inPatch
->paddingTop
);
565 assert(outPatch
->paddingBottom
== inPatch
->paddingBottom
);
566 for (int i
= 0; i
< outPatch
->numXDivs
; i
++) {
567 assert(outPatch
->xDivs
[i
] == inPatch
->xDivs
[i
]);
569 for (int i
= 0; i
< outPatch
->numYDivs
; i
++) {
570 assert(outPatch
->yDivs
[i
] == inPatch
->yDivs
[i
]);
572 for (int i
= 0; i
< outPatch
->numColors
; i
++) {
573 assert(outPatch
->colors
[i
] == inPatch
->colors
[i
]);
578 static bool patch_equals(Res_png_9patch
& patch1
, Res_png_9patch
& patch2
) {
579 if (!(patch1
.numXDivs
== patch2
.numXDivs
&&
580 patch1
.numYDivs
== patch2
.numYDivs
&&
581 patch1
.numColors
== patch2
.numColors
&&
582 patch1
.paddingLeft
== patch2
.paddingLeft
&&
583 patch1
.paddingRight
== patch2
.paddingRight
&&
584 patch1
.paddingTop
== patch2
.paddingTop
&&
585 patch1
.paddingBottom
== patch2
.paddingBottom
)) {
588 for (int i
= 0; i
< patch1
.numColors
; i
++) {
589 if (patch1
.colors
[i
] != patch2
.colors
[i
]) {
593 for (int i
= 0; i
< patch1
.numXDivs
; i
++) {
594 if (patch1
.xDivs
[i
] != patch2
.xDivs
[i
]) {
598 for (int i
= 0; i
< patch1
.numYDivs
; i
++) {
599 if (patch1
.yDivs
[i
] != patch2
.yDivs
[i
]) {
606 static void dump_image(int w
, int h
, png_bytepp rows
, int color_type
)
608 int i
, j
, rr
, gg
, bb
, aa
;
611 if (color_type
== PNG_COLOR_TYPE_PALETTE
|| color_type
== PNG_COLOR_TYPE_GRAY
) {
613 } else if (color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
615 } else if (color_type
== PNG_COLOR_TYPE_RGB
|| color_type
== PNG_COLOR_TYPE_RGB_ALPHA
) {
616 // We use a padding byte even when there is no alpha
619 printf("Unknown color type %d.\n", color_type
);
622 for (j
= 0; j
< h
; j
++) {
623 png_bytep row
= rows
[j
];
624 for (i
= 0; i
< w
; i
++) {
632 printf("Row %d:", j
);
639 printf(" (%d %d", rr
, gg
);
642 printf(" (%d %d %d)", rr
, gg
, bb
);
645 printf(" (%d %d %d %d)", rr
, gg
, bb
, aa
);
655 #define MAX(a,b) ((a)>(b)?(a):(b))
656 #define ABS(a) ((a)<0?-(a):(a))
658 static void analyze_image(const char *imageName
, image_info
&imageInfo
, int grayscaleTolerance
,
659 png_colorp rgbPalette
, png_bytep alphaPalette
,
660 int *paletteEntries
, bool *hasTransparency
, int *colorType
,
663 int w
= imageInfo
.width
;
664 int h
= imageInfo
.height
;
665 int i
, j
, rr
, gg
, bb
, aa
, idx
;
666 uint32_t colors
[256], col
;
668 int maxGrayDeviation
= 0;
670 bool isOpaque
= true;
671 bool isPalette
= true;
672 bool isGrayscale
= true;
674 // Scan the entire image and determine if:
675 // 1. Every pixel has R == G == B (grayscale)
676 // 2. Every pixel has A == 255 (opaque)
677 // 3. There are no more than 256 distinct RGBA colors
679 // NOISY(printf("Initial image data:\n"));
680 // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
682 for (j
= 0; j
< h
; j
++) {
683 png_bytep row
= imageInfo
.rows
[j
];
684 png_bytep out
= outRows
[j
];
685 for (i
= 0; i
< w
; i
++) {
691 int odev
= maxGrayDeviation
;
692 maxGrayDeviation
= MAX(ABS(rr
- gg
), maxGrayDeviation
);
693 maxGrayDeviation
= MAX(ABS(gg
- bb
), maxGrayDeviation
);
694 maxGrayDeviation
= MAX(ABS(bb
- rr
), maxGrayDeviation
);
695 if (maxGrayDeviation
> odev
) {
696 NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
697 maxGrayDeviation
, i
, j
, rr
, gg
, bb
, aa
));
700 // Check if image is really grayscale
702 if (rr
!= gg
|| rr
!= bb
) {
703 NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
704 i
, j
, rr
, gg
, bb
, aa
));
709 // Check if image is really opaque
712 NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
713 i
, j
, rr
, gg
, bb
, aa
));
718 // Check if image is really <= 256 colors
720 col
= (uint32_t) ((rr
<< 24) | (gg
<< 16) | (bb
<< 8) | aa
);
722 for (idx
= 0; idx
< num_colors
; idx
++) {
723 if (colors
[idx
] == col
) {
729 // Write the palette index for the pixel to outRows optimistically
730 // We might overwrite it later if we decide to encode as gray or
734 if (num_colors
== 256) {
735 NOISY(printf("Found 257th color at %d, %d\n", i
, j
));
738 colors
[num_colors
++] = col
;
746 *hasTransparency
= !isOpaque
;
747 int bpp
= isOpaque
? 3 : 4;
748 int paletteSize
= w
* h
+ bpp
* num_colors
;
750 NOISY(printf("isGrayscale = %s\n", isGrayscale
? "true" : "false"));
751 NOISY(printf("isOpaque = %s\n", isOpaque
? "true" : "false"));
752 NOISY(printf("isPalette = %s\n", isPalette
? "true" : "false"));
753 NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
754 paletteSize
, 2 * w
* h
, bpp
* w
* h
));
755 NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation
, grayscaleTolerance
));
757 // Choose the best color type for the image.
758 // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
759 // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
760 // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
761 // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
762 // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
765 *colorType
= PNG_COLOR_TYPE_GRAY
; // 1 byte/pixel
767 // Use a simple heuristic to determine whether using a palette will
768 // save space versus using gray + alpha for each pixel.
769 // This doesn't take into account chunk overhead, filtering, LZ
771 if (isPalette
&& (paletteSize
< 2 * w
* h
)) {
772 *colorType
= PNG_COLOR_TYPE_PALETTE
; // 1 byte/pixel + 4 bytes/color
774 *colorType
= PNG_COLOR_TYPE_GRAY_ALPHA
; // 2 bytes per pixel
777 } else if (isPalette
&& (paletteSize
< bpp
* w
* h
)) {
778 *colorType
= PNG_COLOR_TYPE_PALETTE
;
780 if (maxGrayDeviation
<= grayscaleTolerance
) {
781 printf("%s: forcing image to gray (max deviation = %d)\n", imageName
, maxGrayDeviation
);
782 *colorType
= isOpaque
? PNG_COLOR_TYPE_GRAY
: PNG_COLOR_TYPE_GRAY_ALPHA
;
784 *colorType
= isOpaque
? PNG_COLOR_TYPE_RGB
: PNG_COLOR_TYPE_RGB_ALPHA
;
788 // Perform postprocessing of the image or palette data based on the final
791 if (*colorType
== PNG_COLOR_TYPE_PALETTE
) {
792 // Create separate RGB and Alpha palettes and set the number of colors
793 *paletteEntries
= num_colors
;
795 // Create the RGB and alpha palettes
796 for (int idx
= 0; idx
< num_colors
; idx
++) {
798 rgbPalette
[idx
].red
= (png_byte
) ((col
>> 24) & 0xff);
799 rgbPalette
[idx
].green
= (png_byte
) ((col
>> 16) & 0xff);
800 rgbPalette
[idx
].blue
= (png_byte
) ((col
>> 8) & 0xff);
801 alphaPalette
[idx
] = (png_byte
) (col
& 0xff);
803 } else if (*colorType
== PNG_COLOR_TYPE_GRAY
|| *colorType
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
804 // If the image is gray or gray + alpha, compact the pixels into outRows
805 for (j
= 0; j
< h
; j
++) {
806 png_bytep row
= imageInfo
.rows
[j
];
807 png_bytep out
= outRows
[j
];
808 for (i
= 0; i
< w
; i
++) {
817 *out
++ = (png_byte
) (rr
* 0.2126f
+ gg
* 0.7152f
+ bb
* 0.0722f
);
828 static void write_png(const char* imageName
,
829 png_structp write_ptr
, png_infop write_info
,
830 image_info
& imageInfo
, int grayscaleTolerance
)
832 bool optimize
= true;
833 png_uint_32 width
, height
;
835 int bit_depth
, interlace_type
, compression_type
;
838 png_unknown_chunk unknowns
[1];
839 unknowns
[0].data
= NULL
;
841 png_bytepp outRows
= (png_bytepp
) malloc((int) imageInfo
.height
* png_sizeof(png_bytep
));
842 if (outRows
== (png_bytepp
) 0) {
843 printf("Can't allocate output buffer!\n");
846 for (i
= 0; i
< (int) imageInfo
.height
; i
++) {
847 outRows
[i
] = (png_bytep
) malloc(2 * (int) imageInfo
.width
);
848 if (outRows
[i
] == (png_bytep
) 0) {
849 printf("Can't allocate output buffer!\n");
854 png_set_compression_level(write_ptr
, Z_BEST_COMPRESSION
);
856 NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName
,
857 (int) imageInfo
.width
, (int) imageInfo
.height
));
859 png_color rgbPalette
[256];
860 png_byte alphaPalette
[256];
861 bool hasTransparency
;
864 analyze_image(imageName
, imageInfo
, grayscaleTolerance
, rgbPalette
, alphaPalette
,
865 &paletteEntries
, &hasTransparency
, &color_type
, outRows
);
867 // If the image is a 9-patch, we need to preserve it as a ARGB file to make
868 // sure the pixels will not be pre-dithered/clamped until we decide they are
869 if (imageInfo
.is9Patch
&& (color_type
== PNG_COLOR_TYPE_RGB
||
870 color_type
== PNG_COLOR_TYPE_GRAY
|| color_type
== PNG_COLOR_TYPE_PALETTE
)) {
871 color_type
= PNG_COLOR_TYPE_RGB_ALPHA
;
874 switch (color_type
) {
875 case PNG_COLOR_TYPE_PALETTE
:
876 NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
877 imageName
, paletteEntries
,
878 hasTransparency
? " (with alpha)" : ""));
880 case PNG_COLOR_TYPE_GRAY
:
881 NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName
));
883 case PNG_COLOR_TYPE_GRAY_ALPHA
:
884 NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName
));
886 case PNG_COLOR_TYPE_RGB
:
887 NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName
));
889 case PNG_COLOR_TYPE_RGB_ALPHA
:
890 NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName
));
894 png_set_IHDR(write_ptr
, write_info
, imageInfo
.width
, imageInfo
.height
,
895 8, color_type
, PNG_INTERLACE_NONE
,
896 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
898 if (color_type
== PNG_COLOR_TYPE_PALETTE
) {
899 png_set_PLTE(write_ptr
, write_info
, rgbPalette
, paletteEntries
);
900 if (hasTransparency
) {
901 png_set_tRNS(write_ptr
, write_info
, alphaPalette
, paletteEntries
, (png_color_16p
) 0);
903 png_set_filter(write_ptr
, 0, PNG_NO_FILTERS
);
905 png_set_filter(write_ptr
, 0, PNG_ALL_FILTERS
);
908 if (imageInfo
.is9Patch
) {
909 NOISY(printf("Adding 9-patch info...\n"));
910 strcpy((char*)unknowns
[0].name
, "npTc");
911 unknowns
[0].data
= (png_byte
*)imageInfo
.info9Patch
.serialize();
912 unknowns
[0].size
= imageInfo
.info9Patch
.serializedSize();
913 // TODO: remove the check below when everything works
914 checkNinePatchSerialization(&imageInfo
.info9Patch
, unknowns
[0].data
);
915 png_set_keep_unknown_chunks(write_ptr
, PNG_HANDLE_CHUNK_ALWAYS
,
916 (png_byte
*)"npTc", 1);
917 png_set_unknown_chunks(write_ptr
, write_info
, unknowns
, 1);
918 // XXX I can't get this to work without forcibly changing
919 // the location to what I want... which apparently is supposed
920 // to be a private API, but everything else I have tried results
921 // in the location being set to what I -last- wrote so I never
923 png_set_unknown_chunk_location(write_ptr
, write_info
, 0, PNG_HAVE_PLTE
);
926 png_write_info(write_ptr
, write_info
);
929 if (color_type
== PNG_COLOR_TYPE_RGB
|| color_type
== PNG_COLOR_TYPE_RGB_ALPHA
) {
930 png_set_filler(write_ptr
, 0, PNG_FILLER_AFTER
);
931 rows
= imageInfo
.rows
;
935 png_write_image(write_ptr
, rows
);
937 // NOISY(printf("Final image data:\n"));
938 // dump_image(imageInfo.width, imageInfo.height, rows, color_type);
940 png_write_end(write_ptr
, write_info
);
942 for (i
= 0; i
< (int) imageInfo
.height
; i
++) {
946 free(unknowns
[0].data
);
948 png_get_IHDR(write_ptr
, write_info
, &width
, &height
,
949 &bit_depth
, &color_type
, &interlace_type
,
950 &compression_type
, NULL
);
952 NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n",
953 (int)width
, (int)height
, bit_depth
, color_type
, interlace_type
,
957 status_t
preProcessImage(Bundle
* bundle
, const sp
<AaptAssets
>& assets
,
958 const sp
<AaptFile
>& file
, String8
* outNewLeafName
)
960 String8
ext(file
->getPath().getPathExtension());
962 // We currently only process PNG images.
963 if (strcmp(ext
.string(), ".png") != 0) {
967 // Example of renaming a file:
968 //*outNewLeafName = file->getPath().getBasePath().getFileName();
969 //outNewLeafName->append(".nupng");
971 String8
printableName(file
->getPrintableSource());
973 png_structp read_ptr
= NULL
;
974 png_infop read_info
= NULL
;
977 image_info imageInfo
;
979 png_structp write_ptr
= NULL
;
980 png_infop write_info
= NULL
;
982 status_t error
= UNKNOWN_ERROR
;
984 const size_t nameLen
= file
->getPath().length();
986 fp
= fopen(file
->getSourceFile().string(), "rb");
988 fprintf(stderr
, "%s: ERROR: Unable to open PNG file\n", printableName
.string());
992 read_ptr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
,
993 (png_error_ptr
)NULL
);
998 read_info
= png_create_info_struct(read_ptr
);
1003 if (setjmp(png_jmpbuf(read_ptr
))) {
1007 png_init_io(read_ptr
, fp
);
1009 read_png(printableName
.string(), read_ptr
, read_info
, &imageInfo
);
1012 const char* name
= file
->getPath().string();
1013 if (name
[nameLen
-5] == '9' && name
[nameLen
-6] == '.') {
1014 if (do_9patch(printableName
.string(), &imageInfo
) != NO_ERROR
) {
1020 write_ptr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, 0, (png_error_ptr
)NULL
,
1021 (png_error_ptr
)NULL
);
1027 write_info
= png_create_info_struct(write_ptr
);
1033 png_set_write_fn(write_ptr
, (void*)file
.get(),
1034 png_write_aapt_file
, png_flush_aapt_file
);
1036 if (setjmp(png_jmpbuf(write_ptr
)))
1041 write_png(printableName
.string(), write_ptr
, write_info
, imageInfo
,
1042 bundle
->getGrayscaleTolerance());
1046 if (bundle
->getVerbose()) {
1047 fseek(fp
, 0, SEEK_END
);
1048 size_t oldSize
= (size_t)ftell(fp
);
1049 size_t newSize
= file
->getSize();
1050 float factor
= ((float)newSize
)/oldSize
;
1051 int percent
= (int)(factor
*100);
1052 printf(" (processed image %s: %d%% size of source)\n", printableName
.string(), percent
);
1057 png_destroy_read_struct(&read_ptr
, &read_info
, (png_infopp
)NULL
);
1063 png_destroy_write_struct(&write_ptr
, &write_info
);
1066 if (error
!= NO_ERROR
) {
1067 fprintf(stderr
, "ERROR: Failure processing PNG image %s\n",
1068 file
->getPrintableSource().string());
1075 status_t
postProcessImage(const sp
<AaptAssets
>& assets
,
1076 ResourceTable
* table
, const sp
<AaptFile
>& file
)
1078 String8
ext(file
->getPath().getPathExtension());
1080 // At this point, now that we have all the resource data, all we need to
1081 // do is compile XML files.
1082 if (strcmp(ext
.string(), ".xml") == 0) {
1083 return compileXmlFile(assets
, file
, table
);