diff options
author | Chris Craik <ccraik@google.com> | 2014-07-08 17:13:08 -0700 |
---|---|---|
committer | Chris Craik <ccraik@google.com> | 2014-07-14 15:01:27 -0700 |
commit | 47cd8e921db73e894f94ec4729ade90da50996f5 (patch) | |
tree | dc087c5a19fec4c564ef73f6d487f1d72cf1cdb4 /tools/aapt/Images.cpp | |
parent | 5028fb035794c207698e52b276c54de109dd5022 (diff) |
Implement outline support for nine patches
b/15856895
Nine patches now have outline round rect metadata stored as optional
png tags. aapt generates these automatically by inspecting the bitmap
pixels to estimate outline bounds and round rect radius, based on
opacity.
Change-Id: I226e328a97873010d9e1adb797ac48f93a31183c
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, |