static void toAntiAliasedCircle()

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]);
      }
    }
  }
}