static void drawBorder()

in native-filters/src/main/jni/filters/rounding_filter.c [564:632]


static void drawBorder(
    JNIEnv* env,
    pixel_t* pixelPtr,
    const int w,
    const int h,
    int32_t colorABGR,
    int borderWidth) {
  // Radius of the circle
  const float radius = min(w,h) / 2.0;
  //const float borderSize = radius * 0.038;
  const float borderSize = min(borderWidth, radius-1);
  const float innerRadius = radius - borderSize;
  // increase radius to avoid outer border on the sides

  // 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 int top_boundary = max(centerY - radius, 0);
  const int bottom_boundary = min(centerY + radius, h);
  const float outer_r_square = POW2(radius);
  const float inner_r_square = POW2(innerRadius);

  int inner_x_offset = 0;
  float inner_x_offset_sq = 0;
  int outer_x_offset = 0;
  float outer_x_offset_sq = 0;

  pixel_t borderColorPixel = colorABGRtoPixel(colorABGR);

  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
      outer_x_offset_sq = -POW2(centerY) + (2*centerY*y) - POW2(y) + outer_r_square;
      inner_x_offset_sq = -POW2(centerY) + (2*centerY*y) - POW2(y) + inner_r_square;

      if(outer_x_offset_sq > 0 && inner_x_offset_sq > 0) {
        outer_x_offset = ceil(sqrt(outer_x_offset_sq));
        inner_x_offset = ceil(sqrt(inner_x_offset_sq));
        int borderSizeInLine = outer_x_offset - inner_x_offset + 1;

        if (borderSizeInLine>0) {
          paintRowSegment(pixelPtr + y * w + (int) centerX - (int) outer_x_offset, borderSizeInLine, colorABGR);
          paintRowSegment(pixelPtr + y * w + (int) centerX + (int) inner_x_offset, borderSizeInLine, colorABGR);
        }
        // internal border antialiasing
        antialiasInternalBorder(pixelPtr, borderColorPixel, y, w, centerX, centerY, inner_x_offset, innerRadius);
      } else if (outer_x_offset >= 0) {
        outer_x_offset = sqrt(outer_x_offset_sq);
        paintRowSegment(pixelPtr + y * w + (int) (centerX - outer_x_offset), (int) outer_x_offset * 2, colorABGR);
      } else if (inner_x_offset >= 0) {
        inner_x_offset = sqrt(inner_x_offset_sq);
        paintRowSegment(pixelPtr + y * w + (int) (centerX - inner_x_offset), (int) outer_x_offset * 2, colorABGR);
      }
  }
}