in native-filters/src/main/jni/filters/rounding_filter.c [72:155]
static void toAntiAliasedCircle(
JNIEnv* env,
pixel_t* pixelPtr,
const int w,
const int h) {
// Distance from border of real circle
const int blend_distance = ANTI_ALIASING_PIXELS / 2.0;
// Radius of the circle with margin for antialiasing.
const float radius = (min(w,h) / 2.0) - blend_distance;
// Imaginary center of the circle.
const float centerX = (w - 1.0L) / 2.0;
const float centerY = (h - 1.0L) / 2.0;
if (radius < 1) {
safe_throw_exception(env, "Circle radius too small!");
return;
}
if (w <= 0 || h <= 0 || w > BITMAP_MAX_DIMENSION || h > BITMAP_MAX_DIMENSION) {
safe_throw_exception(env, "Invalid bitmap dimensions!");
return;
}
if (centerX < 0 || centerY < 0 || centerX >= w || centerY >= h) {
safe_throw_exception(env, "Invalid circle center coordinates!");
return;
}
// Clear top/bottom if height > width
const size_t line_bytes = sizeof(pixel_t) * w;
const int top_boundary = max(centerY - (radius + blend_distance), 0);
const int bottom_boundary = min(centerY + (radius + blend_distance), h);
for (int i = top_boundary; i >= 0; i--) {
memset(pixelPtr + i * w, TRANSPARENT_PIXEL_COLOR, line_bytes);
}
for (int i = bottom_boundary; i < h; i++) {
memset(pixelPtr + i * w, TRANSPARENT_PIXEL_COLOR, line_bytes);
}
int left_boundary = 0;
int right_boundary = 0;
float alpha = 0.0;
int x_offset = 0;
const float delta = 2 * blend_distance;
const float r_square = POW2(radius);
for (int y = top_boundary; y < bottom_boundary; y++) {
// Square formula: (x-cx)^2 + (y-cy)^2 = r^2 <=> (x-cx) = -cx^2 + 2cy*y - y^2 + r^2
x_offset = -POW2(centerY) + (2*centerY*y) - POW2(y) + r_square;
// Rows on top/bottom not in the actual circle but rather in the antialiasing 'ring'
// are traversed entirely for better looking images at top/bottom.
// There are ANTI_ALIASING_PIXELS lines that will be traversed entirely.
if (x_offset < 0) {
for (int x = 0; x < w; x++) {
alpha = getPixelAlpha(x, y, centerX, centerY, radius);
setPixelAlpha(alpha, &pixelPtr[(y * w) + x]);
alpha = getPixelAlpha(x, y, centerX, centerY, radius);
setPixelAlpha(alpha, &pixelPtr[(y * w) + x]);
}
} else {
// Compute the correct position
x_offset = sqrt(x_offset);
// Clear left part of the image keeping an delta margin with the circle
left_boundary = max(centerX - x_offset, 0);
left_boundary = max(left_boundary - delta, 0);
memset(pixelPtr + y * w, TRANSPARENT_PIXEL_COLOR, sizeof(pixel_t) * left_boundary);
// Clear right part of the image keeping an delta margin with the circle
right_boundary = min(centerX + x_offset, w);
right_boundary = min(right_boundary + delta , w);
memset(pixelPtr + y * w + right_boundary, TRANSPARENT_PIXEL_COLOR, sizeof(pixel_t) * (w - right_boundary));
// Visit the pixels at the left of the circle at row y to apply antialiasing
for (int x = left_boundary; x < left_boundary + (2 * delta); x++) {
alpha = getPixelAlpha(x, y, centerX, centerY, radius);
setPixelAlpha(alpha, &pixelPtr[(y * w) + x]);
}
// Visit the pixels at the right of the circle at row y to apply antialiasing
for (int x = right_boundary - (2*delta); x < right_boundary; x++) {
alpha = getPixelAlpha(x, y, centerX, centerY, radius);
setPixelAlpha(alpha, &pixelPtr[(y * w) + x]);
}
}
}
}