Circle edge blur

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Circle edge blur

Post by Keya »

Based on my previous general-purpose Edge Blur, this is a specific Edge Blur for circles (not ovals).
It has no tie to the background color like my general-purpose Edge Blur does.

To use it you simply need to know the location and diameter of the circle, as is the case if you ever use PB's Circle() command - its parameters are identical, making it very easy to blur the edges of those. It doesn't actually need a pre-drawn circle though - just location and diameter; you could for example apply it to a portrait where a face remains unblurred but everything outside the circle area of the face is blurred, if blurdistance is set large.

It only blurs the circle edges; all other content is left untouched.

As with the other Edge Blur, you're free to use any 3x3 convolution filter, there's a lot of gaussian levels to choose from as well as other models like 'smooth'; i've included 3 to choose from. It supports a variable blur distance, but generally this will only ever be set to 1 or 2, maybe 3 (remembering it's a 3x3 filter).

I'll be adapting it soon for rectangles and squares now that annoying circles are taken care of. Thanks again to MrMat for the trigonometry refresher! (tried my best to avoid it for this long, lol). Btw i think PB's excellent vector library produces circles with smoother edges than PB's Circle(), but obviously that isn't going to be the target or an option in all cases.
Windows/Linux/Mac, 24/32bit images, x86/x64, Uni/Ascii, Weekdays/Weekends.

Example:
Image

CircleEdgeBlur.pbi

Code: Select all

Global Dim TargetMatrix.a(0,0)
Global Dim ConvMatrix.f(2,2)
;### Gaussian Blur (weak)
;ConvMatrix(0,0) = 0.03125:  ConvMatrix(0,1) = 0.0625:  ConvMatrix(0,2) = 0.03125
;ConvMatrix(1,0) = 0.0625:   ConvMatrix(1,1) = 0.625:  ConvMatrix(1,2)  = 0.0625
;ConvMatrix(2,0) = 0.03125:  ConvMatrix(2,1) = 0.0625:  ConvMatrix(2,2) = 0.03125
;### Gaussian Blur (medium)
ConvMatrix(0,0) = 0.0625:  ConvMatrix(0,1) = 0.125:  ConvMatrix(0,2) = 0.0625
ConvMatrix(1,0) = 0.125:   ConvMatrix(1,1) = 0.25:  ConvMatrix(1,2)  = 0.125
ConvMatrix(2,0) = 0.0625:  ConvMatrix(2,1) = 0.125:  ConvMatrix(2,2) = 0.0625
;### Smooth
;ConvMatrix(0,0) = 0.11111:  ConvMatrix(0,1) = 0.11111:  ConvMatrix(0,2) = 0.11111
;ConvMatrix(1,0) = 0.11111:   ConvMatrix(1,1) = 0.11111:  ConvMatrix(1,2)  = 0.11111
;ConvMatrix(2,0) = 0.11111:  ConvMatrix(2,1) = 0.11111:  ConvMatrix(2,2) = 0.11111


Procedure.l ConvolutePixel(*imgbuf, X,Y, width,height, pitch, pixbytes)
  Protected r.f, g.f, b.f
  For yy = -1 To 1    ;\ Good candidates for unrolling
    For xx = -1 To 1  ;/
      filter.f = ConvMatrix(xx+1,yy+1)
      curx = x + xx: cury = y + yy
      If curx < 0: curx = 0: EndIf
      If curx => width: curx = width: EndIf
      If cury < 0: cury = 0: EndIf
      If cury => height: cury = height: EndIf        
      color.l = PeekL(*imgbuf + (cury * pitch) + (curx * pixbytes))
      r + (Red(color) * filter)
      g + (Green(color) * filter)
      b + (Blue(color) * filter)              
    Next xx
  Next yy
  ar.a = r: ab.a = b: ag.a = g
  color = (ab << 16 | ag << 8 | ar) | $FF000000
  ProcedureReturn color
EndProcedure


Procedure GaussianBlurImage(*imgbuf, hImg, MiddleX, MiddleY, width, height, depth)
  If StartDrawing(ImageOutput(hImg))
    pitch = DrawingBufferPitch()
    *out = DrawingBuffer()    
    Pixbytes = depth/8
    w=width-1
    h=height-1
    For y = 0 To h
      *pixelin = *imgbuf + (y * pitch)
      *pixelout = *out + (y * pitch)
      For x = 0 To w
        CompilerIf #PB_Compiler_OS = #PB_OS_Windows
          mtx = TargetMatrix(X, h-Y)
        CompilerElse
          mtx = TargetMatrix(X, Y)
        CompilerEndIf
        If mtx
          color = ConvolutePixel(*imgbuf, X, Y, width,height, pitch, pixbytes)
        Else
          color = PeekL(*pixelin)
        EndIf
        PokeL(*pixelout, color)
        *pixelin + Pixbytes
        *pixelout + Pixbytes
      Next x
    Next y  
    StopDrawing()
  EndIf
EndProcedure


Procedure BuildCircleEdge(*buf, MiddleX, MiddleY, width,height, LineLen)
  Protected multiplier.f, degrees.f, CalcDegrees.f, LastX=$7FFFFFFF, LastY=$7FFFFFFF
  multiplier = Sqr(LineLen)
  If multiplier < 2: multiplier = 2: EndIf
  degmax = 360*multiplier
  For i = 0 To degmax
    degrees = i/multiplier    ;If we only did every 1.0 degrees we may end up missing pixels at 0.25, 0.5 etc
    CalcDegrees = 360 - Degrees + 180
    X = MiddleX + (Sin(CalcDegrees * (2 * #PI / 360)) * LineLen)
    Y = MiddleY + (Cos(CalcDegrees * (2 * #PI / 360)) * LineLen)
    If X <> LastX Or Y <> LastY
      If X => 0 And X < width And Y => 0 And Y < height
        TargetMatrix(X,Y)=1
        LastX = X: LastY = Y
      EndIf
      ;Plot(X,Y, RGB(255,0,0)) show marking
    EndIf 
  Next i  
EndProcedure


Procedure BlurCircleEdge(*buf, MiddleX, MiddleY, width,height, LineLen, depth, blurdistance=2)
  Dim TargetMatrix(width,height)
  If blurdistance < 1: blurdistance = 1: EndIf
  For i = 1 To blurdistance
    BuildCircleEdge(*buf, MiddleX, MiddleY, width,height, LineLen+(i-1))
  Next i
  hImg = CreateImage(#PB_Any, width, height, depth)
  If hImg
    GaussianBlurImage(*buf, hImg, MiddleX, MiddleY, width, height, depth)
  EndIf
  ProcedureReturn hImg
EndProcedure

Example.pb

Code: Select all

XIncludeFile("CircleEdgeBlur.pbi")

If OpenWindow(0, 0, 0, 300, 300, "Circle Edge Blur", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  width=300: height=300
  hImg1 = CreateImage(#PB_Any, width,height, 32) 
  If hImg1 And StartDrawing(ImageOutput(hImg1))
    Diameter = 50
    Radius = Diameter/2
    MiddleX = Radius+10
    MiddleY = Radius+10
    LineLen = Radius-5
    Circle(MiddleX, MiddleY, LineLen, $FFFF00)    
    *buf = DrawingBuffer()
    depth=ImageDepth(hImg1)
    StopDrawing()
    
    blurdistance=2
    hImg2 = BlurCircleEdge(*buf, MiddleX, MiddleY, width,height, LineLen, depth, blurdistance)  ;Note: Same params as Circle()
    FreeImage(hImg1)
    ImageGadget(0, 0, 0, width, height, ImageID(hImg2))
    
  EndIf  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 625
Joined: Mon May 09, 2011 9:36 am

Re: Circle edge blur

Post by VB6_to_PBx »

Keya ,

i wish i knew even a "tenth of your programming knowledge"

thank you very much for this example !
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Circle edge blur

Post by Keya »

VB6_to_PBx wrote:Keya ,
i wish i knew even a "tenth of your programming knowledge"
"programming knowledge" in Donald Trump quotes is well suited here lol, thankyou very much for your kind words but you should set your bar higher as i'm towards the bottom of this pile as pointed out by a fellow member. But i consider myself lucky to be able to learn from the code contributions and interesting A's to my Q's here from some super talented gun coders who've opened more doors than i can throw empty Red Bull cans at (which i drink in an attempt to make me feel like a legit coder eheh). I hope to post the square/rectangle version of this demo soon.
Last edited by Keya on Wed Mar 29, 2017 2:07 pm, edited 1 time in total.
Post Reply