2010. október 26., kedd

Draw a filled circle using ScanLine


Problem/Question/Abstract:

I am looking for some code to draw a filled circle on a bitmap or change colors of pixels within a circle on it, using Bitmap.Scanline. Any suggestions or ideas on how to do this, the edges need to be perfect and it has to be fast.

Answer:

Perfect edges mean you will have to work with an alpha channel and do anti-aliasing. This means, that you either have to use a 32-bit bitmap (see e.g. Graphics32) or you have to first draw the background image in the bitmap and directly blend it when rendering the circle. Next question: do you want to use integer precision or floating point precision for the circle properties like center point and diameter? If you use integer, you only have to draw 1/8 of the circle and the rest can be copied/mirrored/flipped around. Assuming floating point, and a grayscale bitmap, here's an approach:

CX, CY: center of circle (single)
R: radius of circle (single)
F: feather size (the number of pixels used as blend area, usually 1 pix) (single)

Determine bounds in Y (integers):
LX := floor(CX - R - F * 0.5);
RX := ceil(CX + R + F * 0.5);
LY := floor(CY - R - F * 0.5);
RY := ceil(CY + R + F * 0.5);

Determine some helpful values (singles)
RPF2 = sqr(R + F/2);
RMF2 = sqr(R - F/2);

{ ... }
var
  P: PByteArray
  sqdist: single;
  { ... }
    {Loop through Y values}
    {for y := LY to RY do begin -> not very safe}
  for y := max(LY, 0) to Min(RY, Bitmap.Height - 1) do
    P := Bitmap.Scanline[y];
  {Loop through X values}
  for x := Max(LX, 0) to Min(RX, Bitmap.Width - 1) do
  begin
    {Determine squared distance from center for this pixel}
    sqdist := sqr(y - CY) + sqr(x - CX); {Or use hypot() function}
    {Inside outer circle?}
    if sqdist < RPF2 then
    begin
      {Inside inner circle?}
      if sqdist < RMF1 then
        {Inside the inner circle.. just give the scanline the new color}
        P[x] := 255
      else
      begin
        {We are inbetween the inner and outer bound, now mix the color}
        Fact := Max(0, Min(255, round(((R - sqrt(sqdist)) * 2 / F) * 128 + 128)));
        P[x] := (255 - Fact) * P[x] + Fact;
      end;
    end;
    { ... }

This algorithm is optimized a bit but could be made faster probably. Untested!

Nincsenek megjegyzések:

Megjegyzés küldése