diff options
Diffstat (limited to 'tools/aapt/Images.cpp')
-rw-r--r-- | tools/aapt/Images.cpp | 134 |
1 files changed, 128 insertions, 6 deletions
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 12f5b92ab3fa..28de933a315e 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -77,6 +77,14 @@ struct image_info int32_t layoutBoundsRight; int32_t layoutBoundsBottom; + // Round rect outline description + int32_t outlineInsetsLeft; + int32_t outlineInsetsTop; + int32_t outlineInsetsRight; + int32_t outlineInsetsBottom; + float outlineRadius; + bool outlineFilled; + png_uint_32 allocHeight; png_bytepp allocRows; }; @@ -397,6 +405,98 @@ static status_t get_vertical_layout_bounds_ticks( return NO_ERROR; } +static void find_max_opacity(png_byte** rows, + int startX, int startY, int endX, int endY, int dX, int dY, + int* out_inset) +{ + bool opaque_within_inset = true; + unsigned char max_opacity = 0; + int inset = 0; + *out_inset = 0; + for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) { + png_byte* color = rows[y] + x * 4; + unsigned char opacity = color[3]; + if (opacity > max_opacity) { + max_opacity = opacity; + *out_inset = inset; + } + if (opacity == 0xff) return; + } +} + +static bool is_opaque_over_row(png_byte* row, int startX, int endX) +{ + for (int x = startX; x < endX; x++) { + png_byte* color = row + x * 4; + if (color[3] != 0xff) return false; + } + return true; +} + +static bool is_opaque_over_col(png_byte** rows, int offsetX, int startY, int endY) +{ + for (int y = startY; y < endY; y++) { + png_byte* color = rows[y] + offsetX * 4; + if (color[3] != 0xff) return false; + } + return true; +} + +static void get_outline(image_info* image) +{ + int midX = image->width / 2; + int midY = image->height / 2; + int endX = image->width - 2; + int endY = image->height - 2; + + // find left and right extent of nine patch content on center row + if (image->width > 4) { + find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft); + find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight); + } else { + image->outlineInsetsLeft = 0; + image->outlineInsetsRight = 0; + } + + // find top and bottom extent of nine patch content on center column + if (image->height > 4) { + find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop); + find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom); + } else { + image->outlineInsetsTop = 0; + image->outlineInsetsBottom = 0; + } + + int innerStartX = 1 + image->outlineInsetsLeft; + int innerStartY = 1 + image->outlineInsetsTop; + int innerEndX = endX - image->outlineInsetsRight; + int innerEndY = endY - image->outlineInsetsBottom; + int innerMidX = (innerEndX + innerStartX) / 2; + int innerMidY = (innerEndY + innerStartY) / 2; + + // assuming the image is a round rect, compute the radius by marching + // diagonally from the top left corner towards the center + image->outlineFilled = is_opaque_over_row(image->rows[innerMidY], innerStartX, innerEndX) + && is_opaque_over_col(image->rows, innerMidX, innerStartY, innerStartY); + + int diagonalInset = 0; + find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1, + &diagonalInset); + + // Determine source radius based upon inset + // radius = 1 / (sqrt(2) - 1) * inset + image->outlineRadius = 2.4142f * diagonalInset; + + NOISY(printf("outline insets %d %d %d %d, rad %f, filled %d\n", + image->outlineFilled, + image->outlineInsetsLeft, + image->outlineInsetsTop, + image->outlineInsetsRight, + image->outlineInsetsBottom, + image->outlineRadius, + image->outlineFilled)); +} + static uint32_t get_color( png_bytepp rows, int left, int top, int right, int bottom) @@ -571,6 +671,9 @@ static status_t do_9patch(const char* imageName, image_info* image) image->layoutBoundsRight, image->layoutBoundsBottom)); } + // use opacity of pixels to estimate the round rect outline + get_outline(image); + // If padding is not yet specified, take values from size. if (image->info9Patch.paddingLeft < 0) { image->info9Patch.paddingLeft = xDivs[0]; @@ -966,9 +1069,10 @@ static void write_png(const char* imageName, int bit_depth, interlace_type, compression_type; int i; - png_unknown_chunk unknowns[2]; + png_unknown_chunk unknowns[3]; unknowns[0].data = NULL; unknowns[1].data = NULL; + unknowns[2].data = NULL; png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep)); if (outRows == (png_bytepp) 0) { @@ -1038,12 +1142,17 @@ 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; + int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0); + int p_index = imageInfo.haveLayoutBounds ? 2 : 1; + int b_index = 1; + int o_index = 0; + + // Chunks ordered thusly because older platforms depend on the base 9 patch data being last png_byte *chunk_names = imageInfo.haveLayoutBounds - ? (png_byte*)"npLb\0npTc\0" - : (png_byte*)"npTc"; + ? (png_byte*)"npOl\0npLb\0npTc\0" + : (png_byte*)"npOl\0npTc"; + + // base 9 patch data NOISY(printf("Adding 9-patch info...\n")); strcpy((char*)unknowns[p_index].name, "npTc"); unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch(); @@ -1051,6 +1160,18 @@ static void write_png(const char* imageName, // TODO: remove the check below when everything works checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data); + // automatically generated 9 patch outline data + int chunk_size = sizeof(png_uint_32) * 6; + strcpy((char*)unknowns[o_index].name, "npOl"); + unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1); + png_byte outputData[chunk_size]; + memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32)); + ((float*) outputData)[4] = imageInfo.outlineRadius; + ((png_uint_32*) outputData)[5] = imageInfo.outlineFilled ? 1 : 0; + memcpy(unknowns[o_index].data, &outputData, chunk_size); + unknowns[o_index].size = chunk_size; + + // optional optical inset / layout bounds data if (imageInfo.haveLayoutBounds) { int chunk_size = sizeof(png_uint_32) * 4; strcpy((char*)unknowns[b_index].name, "npLb"); @@ -1099,6 +1220,7 @@ static void write_png(const char* imageName, free(outRows); free(unknowns[0].data); free(unknowns[1].data); + free(unknowns[2].data); png_get_IHDR(write_ptr, write_info, &width, &height, &bit_depth, &color_type, &interlace_type, |