X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/0dad19a601cb95d1c6fdaaa3e655a1687f557d66..13d17ba29811ac083084f1a2f0dc4c1510d04afb:/Images.cpp diff --git a/Images.cpp b/Images.cpp index 7c4963f..9de685a 100644 --- a/Images.cpp +++ b/Images.cpp @@ -8,7 +8,7 @@ #include "Images.h" -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> #include <png.h> @@ -33,7 +33,7 @@ png_flush_aapt_file(png_structp png_ptr) // This holds an image as 8bpp RGBA. struct image_info { - image_info() : rows(NULL), hasTransparency(true), is9Patch(false), allocRows(NULL) { } + image_info() : rows(NULL), is9Patch(false), allocRows(NULL) { } ~image_info() { if (rows && rows != allocRows) { free(rows); @@ -44,18 +44,26 @@ struct image_info } free(allocRows); } + free(info9Patch.xDivs); + free(info9Patch.yDivs); + free(info9Patch.colors); } png_uint_32 width; png_uint_32 height; png_bytepp rows; - bool hasTransparency; - // 9-patch info. bool is9Patch; Res_png_9patch info9Patch; + // Layout padding, if relevant + bool haveLayoutBounds; + int32_t layoutBoundsLeft; + int32_t layoutBoundsTop; + int32_t layoutBoundsRight; + int32_t layoutBoundsBottom; + png_uint_32 allocHeight; png_bytepp allocRows; }; @@ -128,33 +136,62 @@ static void read_png(const char* imageName, &interlace_type, &compression_type, NULL); } -static bool is_tick(png_bytep p, bool transparent, const char** outError) +#define COLOR_TRANSPARENT 0 +#define COLOR_WHITE 0xFFFFFFFF +#define COLOR_TICK 0xFF000000 +#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF + +enum { + TICK_TYPE_NONE, + TICK_TYPE_TICK, + TICK_TYPE_LAYOUT_BOUNDS, + TICK_TYPE_BOTH +}; + +static int tick_type(png_bytep p, bool transparent, const char** outError) { + png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + if (transparent) { if (p[3] == 0) { - return false; + return TICK_TYPE_NONE; } + if (color == COLOR_LAYOUT_BOUNDS_TICK) { + return TICK_TYPE_LAYOUT_BOUNDS; + } + if (color == COLOR_TICK) { + return TICK_TYPE_TICK; + } + + // Error cases if (p[3] != 0xff) { *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)"; - return false; + return TICK_TYPE_NONE; } if (p[0] != 0 || p[1] != 0 || p[2] != 0) { - *outError = "Ticks in transparent frame must be black"; + *outError = "Ticks in transparent frame must be black or red"; } - return true; + return TICK_TYPE_TICK; } if (p[3] != 0xFF) { *outError = "White frame must be a solid color (no alpha)"; } - if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) { - return false; + if (color == COLOR_WHITE) { + return TICK_TYPE_NONE; + } + if (color == COLOR_TICK) { + return TICK_TYPE_TICK; + } + if (color == COLOR_LAYOUT_BOUNDS_TICK) { + return TICK_TYPE_LAYOUT_BOUNDS; } + if (p[0] != 0 || p[1] != 0 || p[2] != 0) { - *outError = "Ticks in white frame must be black"; - return false; + *outError = "Ticks in white frame must be black or red"; + return TICK_TYPE_NONE; } - return true; + return TICK_TYPE_TICK; } enum { @@ -174,7 +211,7 @@ static status_t get_horizontal_ticks( bool found = false; for (i=1; i<width-1; i++) { - if (is_tick(row+i*4, transparent, outError)) { + if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) { if (state == TICK_START || (state == TICK_OUTSIDE_1 && multipleAllowed)) { *outLeft = i-1; @@ -223,7 +260,7 @@ static status_t get_vertical_ticks( bool found = false; for (i=1; i<height-1; i++) { - if (is_tick(rows[i]+offset, transparent, outError)) { + if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) { if (state == TICK_START || (state == TICK_OUTSIDE_1 && multipleAllowed)) { *outTop = i-1; @@ -261,6 +298,83 @@ static status_t get_vertical_ticks( return NO_ERROR; } +static status_t get_horizontal_layout_bounds_ticks( + png_bytep row, int width, bool transparent, bool required, + int32_t* outLeft, int32_t* outRight, const char** outError) +{ + int i; + *outLeft = *outRight = 0; + + // Look for left tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) { + // Starting with a layout padding tick + i = 1; + while (i < width - 1) { + (*outLeft)++; + i++; + int tick = tick_type(row + i * 4, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + // Look for right tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) { + // Ending with a layout padding tick + i = width - 2; + while (i > 1) { + (*outRight)++; + i--; + int tick = tick_type(row+i*4, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + return NO_ERROR; +} + +static status_t get_vertical_layout_bounds_ticks( + png_bytepp rows, int offset, int height, bool transparent, bool required, + int32_t* outTop, int32_t* outBottom, const char** outError) +{ + int i; + *outTop = *outBottom = 0; + + // Look for top tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) { + // Starting with a layout padding tick + i = 1; + while (i < height - 1) { + (*outTop)++; + i++; + int tick = tick_type(rows[i] + offset, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + // Look for bottom tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) { + // Ending with a layout padding tick + i = height - 2; + while (i > 1) { + (*outBottom)++; + i--; + int tick = tick_type(rows[i] + offset, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + return NO_ERROR; +} + + static uint32_t get_color( png_bytepp rows, int left, int top, int right, int bottom) { @@ -326,22 +440,6 @@ static uint32_t get_color(image_info* image, int hpatch, int vpatch) return c; } -static void examine_image(image_info* image) -{ - bool hasTrans = false; - for (int i=0; i<(int)image->height && !hasTrans; i++) { - png_bytep p = image->rows[i]; - for (int j=0; j<(int)image->width; j++) { - if (p[(j*4)+3] != 0xFF) { - hasTrans = true; - break; - } - } - } - - image->hasTransparency = hasTrans; -} - static status_t do_9patch(const char* imageName, image_info* image) { image->is9Patch = true; @@ -350,8 +448,8 @@ static status_t do_9patch(const char* imageName, image_info* image) int H = image->height; int i, j; - int maxSizeXDivs = (W / 2 + 1) * sizeof(int32_t); - int maxSizeYDivs = (H / 2 + 1) * sizeof(int32_t); + int maxSizeXDivs = W * sizeof(int32_t); + int maxSizeYDivs = H * sizeof(int32_t); int32_t* xDivs = (int32_t*) malloc(maxSizeXDivs); int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs); uint8_t numXDivs = 0; @@ -368,13 +466,16 @@ static status_t do_9patch(const char* imageName, image_info* image) image->info9Patch.paddingLeft = image->info9Patch.paddingRight = image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1; + image->layoutBoundsLeft = image->layoutBoundsRight = + image->layoutBoundsTop = image->layoutBoundsBottom = 0; + png_bytep p = image->rows[0]; bool transparent = p[3] == 0; bool hasColor = false; const char* errorMsg = NULL; int errorPixel = -1; - const char* errorEdge = ""; + const char* errorEdge = NULL; int colorIndex = 0; @@ -423,6 +524,25 @@ static status_t do_9patch(const char* imageName, image_info* image) goto getout; } + // Find left and right of layout padding... + get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false, + &image->layoutBoundsLeft, + &image->layoutBoundsRight, &errorMsg); + + get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false, + &image->layoutBoundsTop, + &image->layoutBoundsBottom, &errorMsg); + + image->haveLayoutBounds = image->layoutBoundsLeft != 0 + || image->layoutBoundsRight != 0 + || image->layoutBoundsTop != 0 + || image->layoutBoundsBottom != 0; + + if (image->haveLayoutBounds) { + NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop, + image->layoutBoundsRight, image->layoutBoundsBottom)); + } + // Copy patch data into image image->info9Patch.numXDivs = numXDivs; image->info9Patch.numYDivs = numYDivs; @@ -478,6 +598,14 @@ static status_t do_9patch(const char* imageName, image_info* image) if (yDivs[numYDivs - 1] == H) { numRows--; } + + // Make sure the amount of rows and columns will fit in the number of + // colors we can use in the 9-patch format. + if (numRows * numCols > 0x7F) { + errorMsg = "Too many rows and columns in 9-patch perimeter"; + goto getout; + } + numColors = numRows * numCols; image->info9Patch.numColors = numColors; image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t)); @@ -548,12 +676,14 @@ getout: fprintf(stderr, "ERROR: 9-patch image %s malformed.\n" " %s.\n", imageName, errorMsg); - if (errorPixel >= 0) { - fprintf(stderr, - " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge); - } else { - fprintf(stderr, - " Found along %s edge.\n", errorEdge); + if (errorEdge != NULL) { + if (errorPixel >= 0) { + fprintf(stderr, + " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge); + } else { + fprintf(stderr, + " Found along %s edge.\n", errorEdge); + } } return UNKNOWN_ERROR; } @@ -618,17 +748,69 @@ static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) { return true; } +static void dump_image(int w, int h, png_bytepp rows, int color_type) +{ + int i, j, rr, gg, bb, aa; + + int bpp; + if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) { + bpp = 1; + } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + bpp = 2; + } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + // We use a padding byte even when there is no alpha + bpp = 4; + } else { + printf("Unknown color type %d.\n", color_type); + } -static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_bytep alphaPalette, + for (j = 0; j < h; j++) { + png_bytep row = rows[j]; + for (i = 0; i < w; i++) { + rr = row[0]; + gg = row[1]; + bb = row[2]; + aa = row[3]; + row += bpp; + + if (i == 0) { + printf("Row %d:", j); + } + switch (bpp) { + case 1: + printf(" (%d)", rr); + break; + case 2: + printf(" (%d %d", rr, gg); + break; + case 3: + printf(" (%d %d %d)", rr, gg, bb); + break; + case 4: + printf(" (%d %d %d %d)", rr, gg, bb, aa); + break; + } + if (i == (w - 1)) { + NOISY(printf("\n")); + } + } + } +} + +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define ABS(a) ((a)<0?-(a):(a)) + +static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance, + png_colorp rgbPalette, png_bytep alphaPalette, int *paletteEntries, bool *hasTransparency, int *colorType, png_bytepp outRows) { int w = imageInfo.width; int h = imageInfo.height; - bool trans = imageInfo.hasTransparency; int i, j, rr, gg, bb, aa, idx; uint32_t colors[256], col; int num_colors = 0; + int maxGrayDeviation = 0; bool isOpaque = true; bool isPalette = true; @@ -638,6 +820,10 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte // 1. Every pixel has R == G == B (grayscale) // 2. Every pixel has A == 255 (opaque) // 3. There are no more than 256 distinct RGBA colors + + // NOISY(printf("Initial image data:\n")); + // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA); + for (j = 0; j < h; j++) { png_bytep row = imageInfo.rows[j]; png_bytep out = outRows[j]; @@ -646,23 +832,32 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte gg = *row++; bb = *row++; aa = *row++; - if (!trans) { - // Ignore the actually byte and assume alpha == 255 - aa = 0xff; + + int odev = maxGrayDeviation; + maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation); + maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation); + maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation); + if (maxGrayDeviation > odev) { + NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", + maxGrayDeviation, i, j, rr, gg, bb, aa)); } // Check if image is really grayscale if (isGrayscale) { - if (rr != gg || rr != bb) { - isGrayscale = false; - } + if (rr != gg || rr != bb) { + NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", + i, j, rr, gg, bb, aa)); + isGrayscale = false; + } } // Check if image is really opaque if (isOpaque) { - if (aa != 0xff) { - isOpaque = false; - } + if (aa != 0xff) { + NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", + i, j, rr, gg, bb, aa)); + isOpaque = false; + } } // Check if image is really <= 256 colors @@ -682,6 +877,7 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte *out++ = idx; if (!match) { if (num_colors == 256) { + NOISY(printf("Found 257th color at %d, %d\n", i, j)); isPalette = false; } else { colors[num_colors++] = col; @@ -696,6 +892,13 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte int bpp = isOpaque ? 3 : 4; int paletteSize = w * h + bpp * num_colors; + NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false")); + NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false")); + NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false")); + NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", + paletteSize, 2 * w * h, bpp * w * h)); + NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance)); + // Choose the best color type for the image. // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations @@ -719,7 +922,12 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte } else if (isPalette && (paletteSize < bpp * w * h)) { *colorType = PNG_COLOR_TYPE_PALETTE; } else { - *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; + if (maxGrayDeviation <= grayscaleTolerance) { + printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation); + *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA; + } else { + *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; + } } // Perform postprocessing of the image or palette data based on the final @@ -748,11 +956,15 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte bb = *row++; aa = *row++; - *out++ = rr; + if (isGrayscale) { + *out++ = rr; + } else { + *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f); + } if (!isOpaque) { *out++ = aa; } - } + } } } } @@ -760,7 +972,7 @@ static void analyze_image(image_info &imageInfo, png_colorp rgbPalette, png_byte static void write_png(const char* imageName, png_structp write_ptr, png_infop write_info, - image_info& imageInfo) + image_info& imageInfo, int grayscaleTolerance) { bool optimize = true; png_uint_32 width, height; @@ -768,7 +980,9 @@ static void write_png(const char* imageName, int bit_depth, interlace_type, compression_type; int i; - png_unknown_chunk unknowns[1]; + png_unknown_chunk unknowns[2]; + unknowns[0].data = NULL; + unknowns[1].data = NULL; png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep)); if (outRows == (png_bytepp) 0) { @@ -785,42 +999,42 @@ static void write_png(const char* imageName, png_set_compression_level(write_ptr, Z_BEST_COMPRESSION); - NOISY(printf("Writing image %s: w = %d, h = %d, trans = %s\n", imageName, - (int) imageInfo.width, (int) imageInfo.height, - imageInfo.hasTransparency ? "true" : "false")); + NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName, + (int) imageInfo.width, (int) imageInfo.height)); png_color rgbPalette[256]; png_byte alphaPalette[256]; bool hasTransparency; int paletteEntries; - if (optimize) { - analyze_image(imageInfo, rgbPalette, alphaPalette, &paletteEntries, &hasTransparency, - &color_type, outRows); - switch (color_type) { - case PNG_COLOR_TYPE_PALETTE: - NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n", - imageName, paletteEntries, - hasTransparency ? " (with alpha)" : "")); - break; - case PNG_COLOR_TYPE_GRAY: - NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName)); - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName)); - break; - case PNG_COLOR_TYPE_RGB: - NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName)); - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName)); - break; - } - } else { - // Force RGB or RGB_ALPHA color type, copy transparency from input - paletteEntries = 0; - hasTransparency = imageInfo.hasTransparency; - color_type = hasTransparency ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; + analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette, + &paletteEntries, &hasTransparency, &color_type, outRows); + + // If the image is a 9-patch, we need to preserve it as a ARGB file to make + // sure the pixels will not be pre-dithered/clamped until we decide they are + if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) { + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + + switch (color_type) { + case PNG_COLOR_TYPE_PALETTE: + NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n", + imageName, paletteEntries, + hasTransparency ? " (with alpha)" : "")); + break; + case PNG_COLOR_TYPE_GRAY: + NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName)); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName)); + break; + case PNG_COLOR_TYPE_RGB: + NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName)); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName)); + break; } png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height, @@ -838,34 +1052,55 @@ static void write_png(const char* imageName, } if (imageInfo.is9Patch) { + int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0); + int p_index = imageInfo.haveLayoutBounds ? 1 : 0; + int b_index = 0; + png_byte *chunk_names = imageInfo.haveLayoutBounds + ? (png_byte*)"npLb\0npTc\0" + : (png_byte*)"npTc"; NOISY(printf("Adding 9-patch info...\n")); - strcpy((char*)unknowns[0].name, "npTc"); - unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize(); - unknowns[0].size = imageInfo.info9Patch.serializedSize(); + strcpy((char*)unknowns[p_index].name, "npTc"); + unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize(); + unknowns[p_index].size = imageInfo.info9Patch.serializedSize(); // TODO: remove the check below when everything works - checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data); + checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data); + + if (imageInfo.haveLayoutBounds) { + int chunk_size = sizeof(png_uint_32) * 4; + strcpy((char*)unknowns[b_index].name, "npLb"); + unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1); + memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size); + unknowns[b_index].size = chunk_size; + } + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, - (png_byte*)"npTc", 1); - png_set_unknown_chunks(write_ptr, write_info, unknowns, 1); + chunk_names, chunk_count); + png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count); // XXX I can't get this to work without forcibly changing // the location to what I want... which apparently is supposed // to be a private API, but everything else I have tried results // in the location being set to what I -last- wrote so I never // get written. :p png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE); + if (imageInfo.haveLayoutBounds) { + png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE); + } } + png_write_info(write_ptr, write_info); - - if (!imageInfo.hasTransparency) { - png_set_filler(write_ptr, 0, PNG_FILLER_AFTER); - } + png_bytepp rows; if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_write_image(write_ptr, imageInfo.rows); + png_set_filler(write_ptr, 0, PNG_FILLER_AFTER); + rows = imageInfo.rows; } else { - png_write_image(write_ptr, outRows); + rows = outRows; } + png_write_image(write_ptr, rows); + +// NOISY(printf("Final image data:\n")); +// dump_image(imageInfo.width, imageInfo.height, rows, color_type); png_write_end(write_ptr, write_info); @@ -873,6 +1108,8 @@ static void write_png(const char* imageName, free(outRows[i]); } free(outRows); + free(unknowns[0].data); + free(unknowns[1].data); png_get_IHDR(write_ptr, write_info, &width, &height, &bit_depth, &color_type, &interlace_type, @@ -883,7 +1120,7 @@ static void write_png(const char* imageName, compression_type)); } -status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets, +status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptFile>& file, String8* outNewLeafName) { String8 ext(file->getPath().getPathExtension()); @@ -899,6 +1136,10 @@ status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets, String8 printableName(file->getPrintableSource()); + if (bundle->getVerbose()) { + printf("Processing image: %s\n", printableName.string()); + } + png_structp read_ptr = NULL; png_infop read_info = NULL; FILE* fp; @@ -937,8 +1178,6 @@ status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets, read_png(printableName.string(), read_ptr, read_info, &imageInfo); - examine_image(&imageInfo); - if (nameLen > 6) { const char* name = file->getPath().string(); if (name[nameLen-5] == '9' && name[nameLen-6] == '.') { @@ -969,7 +1208,8 @@ status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets, goto bail; } - write_png(printableName.string(), write_ptr, write_info, imageInfo); + write_png(printableName.string(), write_ptr, write_info, imageInfo, + bundle->getGrayscaleTolerance()); error = NO_ERROR; @@ -1000,7 +1240,132 @@ bail: return error; } +status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest) +{ + png_structp read_ptr = NULL; + png_infop read_info = NULL; + FILE* fp; + + image_info imageInfo; + + png_structp write_ptr = NULL; + png_infop write_info = NULL; + + status_t error = UNKNOWN_ERROR; + + if (bundle->getVerbose()) { + printf("Processing image to cache: %s => %s\n", source.string(), dest.string()); + } + + // Get a file handler to read from + fp = fopen(source.string(),"rb"); + if (fp == NULL) { + fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string()); + return error; + } + + // Call libpng to get a struct to read image data into + read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!read_ptr) { + fclose(fp); + png_destroy_read_struct(&read_ptr, &read_info,NULL); + return error; + } + + // Call libpng to get a struct to read image info into + read_info = png_create_info_struct(read_ptr); + if (!read_info) { + fclose(fp); + png_destroy_read_struct(&read_ptr, &read_info,NULL); + return error; + } + + // Set a jump point for libpng to long jump back to on error + if (setjmp(png_jmpbuf(read_ptr))) { + fclose(fp); + png_destroy_read_struct(&read_ptr, &read_info,NULL); + return error; + } + + // Set up libpng to read from our file. + png_init_io(read_ptr,fp); + + // Actually read data from the file + read_png(source.string(), read_ptr, read_info, &imageInfo); + + // We're done reading so we can clean up + // Find old file size before releasing handle + fseek(fp, 0, SEEK_END); + size_t oldSize = (size_t)ftell(fp); + fclose(fp); + png_destroy_read_struct(&read_ptr, &read_info,NULL); + + // Check to see if we're dealing with a 9-patch + // If we are, process appropriately + if (source.getBasePath().getPathExtension() == ".9") { + if (do_9patch(source.string(), &imageInfo) != NO_ERROR) { + return error; + } + } + + // Call libpng to create a structure to hold the processed image data + // that can be written to disk + write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!write_ptr) { + png_destroy_write_struct(&write_ptr, &write_info); + return error; + } + + // Call libpng to create a structure to hold processed image info that can + // be written to disk + write_info = png_create_info_struct(write_ptr); + if (!write_info) { + png_destroy_write_struct(&write_ptr, &write_info); + return error; + } + + // Open up our destination file for writing + fp = fopen(dest.string(), "wb"); + if (!fp) { + fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string()); + png_destroy_write_struct(&write_ptr, &write_info); + return error; + } + + // Set up libpng to write to our file + png_init_io(write_ptr, fp); + + // Set up a jump for libpng to long jump back on on errors + if (setjmp(png_jmpbuf(write_ptr))) { + fclose(fp); + png_destroy_write_struct(&write_ptr, &write_info); + return error; + } + + // Actually write out to the new png + write_png(dest.string(), write_ptr, write_info, imageInfo, + bundle->getGrayscaleTolerance()); + + if (bundle->getVerbose()) { + // Find the size of our new file + FILE* reader = fopen(dest.string(), "rb"); + fseek(reader, 0, SEEK_END); + size_t newSize = (size_t)ftell(reader); + fclose(reader); + + float factor = ((float)newSize)/oldSize; + int percent = (int)(factor*100); + printf(" (processed image to cache entry %s: %d%% size of source)\n", + dest.string(), percent); + } + + //Clean up + fclose(fp); + png_destroy_write_struct(&write_ptr, &write_info); + + return NO_ERROR; +} status_t postProcessImage(const sp<AaptAssets>& assets, ResourceTable* table, const sp<AaptFile>& file)