Blur Optimisations

Just starting out? Need help? Post your questions and find answers here.
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Blur Optimisations

Post by Foz »

I'm hoping that some speed demons here will be able to help :)

I've implemented a square radius blurring algorithm (I just made that up, but that is what it's doing ;) ) but I'm finding it... slow.

I'm fairly certain that it'll have to be shifted into pure memory management, but I'm not certain how to go about that from PB images - hints please!

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected rv.i = CreateImage(#PB_Any, ImageWidth(pImageIn), ImageHeight(pImageIn))
  
  Average = Pow( ( pRadius * 2 ) + 1, 2)
  
  Dim out.i(ImageWidth(rv) - 1, ImageHeight(rv) - 1)

  Protected r.i, g.i, b.i
  
 StartDrawing(ImageOutput(pImageIn))
  For ImageOutX = 0 To ImageWidth(rv) - 1
    For ImageOutY = 0 To ImageHeight(rv) - 1
      
      r = 0
      g = 0
      b = 0
      colour = 0
      
      For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
        For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
          x = ImageInX
          If x < 0
            x = 0
          EndIf
          If x > ImageWidth(rv) - 1
            x = ImageWidth(rv) - 1
          EndIf
          
          y = ImageInY
          If y < 0
            y = 0
          EndIf
          If y > ImageHeight(rv) - 1
            y = ImageHeight(rv) - 1
          EndIf
          
          colour = Point(x, y)
          r + Red(colour)
          g + Green(colour)
          b + Blue(colour)
          
        Next
      Next
      
      out(ImageOutX, ImageOutY) = RGB(r / Average, g / Average, b / Average)
      
    Next
  Next
  StopDrawing()
  
   StartDrawing(ImageOutput(rv))
  For ImageOutX = 0 To ImageWidth(rv) - 1
    For ImageOutY = 0 To ImageHeight(rv) - 1
      Plot(ImageOutX, ImageOutY, out(ImageOutX, ImageOutY))
    Next
  Next
    StopDrawing()
  
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l, Blur)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC() 
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
  
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
  
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
  
  rv = Blur(RawImage, Blur)
  FreeImage(RawImage)
  
  ProcedureReturn rv
EndProcedure 

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

t = ElapsedMilliseconds()
img = CaptureScreen(0, 0, FormWidth, FormHeight, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          Break
      EndSelect
      
  EndSelect
ForEver
flood
User
User
Posts: 32
Joined: Sun Aug 14, 2011 10:32 pm

Re: Blur Optimisations

Post by flood »

Here's my attempt to speed it up a bit, which I'm sure will be put to shame by someone else. This uses SSE, drawingbuffer() and static variables for pre-calculations. I'm still an amateur when it comes to ASM, so I wouldn't be surprised if there's a better way of structuring the SSE code.

Code: Select all

DisableDebugger
Structure sse
  val1.f
  val2.f
  val3.f
  val4.f
EndStructure
Macro RGB(Red, Green, Blue)
    (((Blue << 8 + Green) << 8 ) + Red) 
EndMacro
Macro Red(color_)
  (color_ & $FF)
EndMacro
Macro Green(color_)
  ((color_ & $FFFF) >> 8)
EndMacro
Macro Blue(color_)
  (color_ & 16777215) >> 16
EndMacro

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected image_width.i = ImageWidth(pImageIn)
  Protected image_height.i = ImageHeight(pImageIn)
  Protected sse_buffer1.sse
  Protected sse_buffer2.sse
  Protected *out_pointer.Long
  Protected *pointer.Long
  Protected Average.f
  Protected r.i, g.i, b.i
  Protected pitch.i,*buffer.i
  Static rv.i,old_image_width.i,old_image_height.i,old_radius.i,*out.i,out_pitch.i
  If image_width <> old_image_width And image_height <> old_image_height
    If rv
      FreeImage(rv)
    EndIf
    rv = CreateImage(#PB_Any, image_width, image_height)
    old_image_width = image_width
    old_image_height = image_height
    *out = AllocateMemory(image_width*image_height*3)
    out_pitch = image_width*3
  EndIf

  If pRadius <> old_radius
    Average = Pow( ( pRadius * 2 ) + 1, 2)
    old_radius = pRadius
  EndIf
  sse_buffer2\val1 = Average
  sse_buffer2\val2 = Average
  sse_buffer2\val3 = Average
  StartDrawing(ImageOutput(pImageIn))
  *buffer = DrawingBuffer()
  pitch.i = DrawingBufferPitch()
  !movups xmm1,[p.v_sse_buffer2]
  For ImageOutY = 0 To image_height - 1
    For ImageOutX = 0 To image_width - 1
     
      r = 0
      g = 0
      b = 0
   
      For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
        If ImageInY < 0
          ImageInY = 0
        ElseIf ImageInY > image_height - 1
          Break
        EndIf
        If ImageOutX-pRadius > 0
          *pointer = *buffer + ImageInY * pitch + (ImageOutX-pRadius)*3
          *out_pointer = *out + ImageInY * out_pitch + (ImageOutX-pRadius)*3
        Else
          *pointer = *buffer + ImageInY * pitch
          *out_pointer = *out + ImageInY * out_pitch
        EndIf
        For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
          If ImageInX < 0
            ImageInX = 0
          ElseIf ImageInX > image_width - 1
            Break
          EndIf
          colour = *pointer\l
          *pointer + 3
          r + Red(colour)
          g + Green(colour)
          b + Blue(colour)
        Next ImageInX
      Next ImageInY
      sse_buffer1\val1 = r
      sse_buffer1\val2 = g
      sse_buffer1\val3 = b
      !movups xmm0,[p.v_sse_buffer1]
      !divps xmm0,xmm1
      !movups [p.v_sse_buffer1],xmm0
      r = sse_buffer1\val1
      g = sse_buffer1\val2
      b = sse_buffer1\val3
      *out_pointer\l = RGB(r,g,b)
      *out_pointer + 3
    Next ImageOutX 
  Next ImageOutY
  StopDrawing()
 
  StartDrawing(ImageOutput(rv))
  *buffer = DrawingBuffer()
  pitch = DrawingBufferPitch()
  CopyMemory(*out,*buffer,pitch*image_height)
  StopDrawing()
 
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l, Blur)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC()
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
 
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
 
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
 
  rv = Blur(RawImage, Blur)
  FreeImage(RawImage)
 
  ProcedureReturn rv
EndProcedure

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

t = ElapsedMilliseconds()
img = CaptureScreen(0, 0, FormWidth, FormHeight, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
     
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          Break
      EndSelect
     
  EndSelect
ForEver
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

That is an incredible speed difference!

On my work dual screen (total of 2560x1024 pixels) it drops from 3500 milliseconds down to 1000!

Does anyone use computers that pre-date the Pentium 3 any more? :D
infratec
Always Here
Always Here
Posts: 7588
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Blur Optimisations

Post by infratec »

Maybe with this its's a bit faster:

Code: Select all

Structure ColorStr
  R.a
  G.a
  B.a
EndStructure

Code: Select all

;Protected *out_pointer.Long
;Protected *pointer.Long
Protected *out_pointer.ColorStr
Protected *pointer.ColorStr

Code: Select all

;colour = *pointer\l
;r + Red(colour)
;g + Green(colour)
;b + Blue(colour)
r + *pointer\R
g + *pointer\G
b + *pointer\B

*pointer + 3

Code: Select all

;r = sse_buffer1\val1
;g = sse_buffer1\val2
;b = sse_buffer1\val3
;*out_pointer\l = RGB(r,g,b)
*out_pointer\R = sse_buffer1\val1
*out_pointer\G = sse_buffer1\val2
*out_pointer\B = sse_buffer1\val3
Bernd

P.S.: Reduced from 1046 to 687ms
But the SSE version sometimes gives an IMA at DisableDebug !
flood
User
User
Posts: 32
Joined: Sun Aug 14, 2011 10:32 pm

Re: Blur Optimisations

Post by flood »

Here is infratec's additions with a few more optimizations added

Code: Select all

DisableDebugger
Structure ColorStr
  r.a
  g.a
  b.a
EndStructure
Structure sse
  val1.f
  val2.f
  val3.f
  val4.f
EndStructure
Macro RGB(Red, Green, Blue)
    (((Blue << 8 + Green) << 8 ) + Red) 
EndMacro
Macro Red(color_)
  (color_ & $FF)
EndMacro
Macro Green(color_)
  ((color_ & $FFFF) >> 8)
EndMacro
Macro Blue(color_)
  (color_ & 16777215) >> 16
EndMacro

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected image_width.i = ImageWidth(pImageIn)
  Protected image_height.i = ImageHeight(pImageIn)
  Protected sse_buffer1.sse
  Protected sse_buffer2.sse
  Protected *out_pointer.ColorStr
  Protected *pointer.ColorStr
  Protected Average.i
  Protected r.i, g.i, b.i
  Protected pitch.i,*buffer.i
  Static rv.i,old_image_width.i,old_image_height.i,old_radius.i,*out.i,out_pitch.i
  If image_width <> old_image_width And image_height <> old_image_height
    If rv
      FreeImage(rv)
    EndIf
    rv = CreateImage(#PB_Any, image_width, image_height)
    old_image_width = image_width
    old_image_height = image_height
    *out = AllocateMemory(image_width*image_height*3)
    out_pitch = image_width*3
  EndIf

  If pRadius <> old_radius
    !MOV eax,[p.v_pRadius]
    !SHL eax,1
    !ADD eax,1
    !IMUL eax,eax
    !MOV [p.v_Average],eax
    old_radius = pRadius
  EndIf
  sse_buffer2\val1 = Average
  sse_buffer2\val2 = Average
  sse_buffer2\val3 = Average
  StartDrawing(ImageOutput(pImageIn))
  *buffer = DrawingBuffer()
  pitch.i = DrawingBufferPitch()
  !movups xmm1,[p.v_sse_buffer2]
  For ImageOutY = 0 To image_height - 1
    For ImageOutX = 0 To image_width - 1
     
      r = 0
      g = 0
      b = 0
   
      For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
        If ImageInY < 0
          ImageInY = 0
        ElseIf ImageInY > image_height - 1
          Break
        EndIf
        If ImageOutX-pRadius > 0
          *pointer = *buffer + ImageInY * pitch + (ImageOutX-pRadius)*3
          *out_pointer = *out + ImageInY * out_pitch + (ImageOutX-pRadius)*3
        Else
          *pointer = *buffer + ImageInY * pitch
          *out_pointer = *out + ImageInY * out_pitch
        EndIf
        For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
          If ImageInX < 0
            ImageInX = 0
          ElseIf ImageInX > image_width - 1
            Break
          EndIf
          r + *pointer\r
          g + *pointer\g
          b + *pointer\b
          *pointer + 3
        Next ImageInX
      Next ImageInY
      sse_buffer1\val1 = r
      sse_buffer1\val2 = g
      sse_buffer1\val3 = b
      !movups xmm0,[p.v_sse_buffer1]
      !divps xmm0,xmm1
      !movups [p.v_sse_buffer1],xmm0
      *out_pointer\r = sse_buffer1\val1
      *out_pointer\g = sse_buffer1\val2
      *out_pointer\b = sse_buffer1\val3
      *out_pointer + 3
    Next ImageOutX 
  Next ImageOutY
  StopDrawing()
 
  StartDrawing(ImageOutput(rv))
  *buffer = DrawingBuffer()
  CopyMemory(*out,*buffer,out_pitch*image_height)
  StopDrawing()
 
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l, Blur)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC()
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
 
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
 
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
 
  rv = Blur(RawImage, Blur)
  FreeImage(RawImage)
 
  ProcedureReturn rv
EndProcedure

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

t = ElapsedMilliseconds()
img = CaptureScreen(0, 0, FormWidth, FormHeight, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
     
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          Break
      EndSelect
     
  EndSelect
ForEver
infratec
Always Here
Always Here
Posts: 7588
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Blur Optimisations

Post by infratec »

With my modifications only, it is arround 20ms faster.

Bernd
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

Wow! You guys are awesome! :mrgreen:

I'm going to have a go at changing the blur routine tonight (I'm at work at the mo) and see if I can reduce the number of calculations while maintaining a decent blur, as I'm thinking that the square radius is probably a brute force overkill, when I might be able to reduce it to a few random picks.

Thank you both for the optimisations though. I'll post my results later.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Blur Optimisations

Post by wilbert »

If you wouldn't mind using SSE2

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected image_width.i = ImageWidth(pImageIn)
  Protected image_height.i = ImageHeight(pImageIn)
  Protected width_minus_one = image_width - 1
  Protected height_minus_one = image_height - 1
  Protected *pointer, *out_pointer
  Protected pitch.i,*buffer.i
  Static rv.i,old_image_width.i,old_image_height.i,*out.i,out_pitch.i
  If image_width <> old_image_width And image_height <> old_image_height
    If rv
      FreeImage(rv)
    EndIf
    rv = CreateImage(#PB_Any, image_width, image_height)
    old_image_width = image_width
    old_image_height = image_height
    *out = AllocateMemory(image_width*image_height*3)
    out_pitch = image_width*3
  EndIf

  StartDrawing(ImageOutput(pImageIn))
  *buffer = DrawingBuffer()
  pitch.i = DrawingBufferPitch()
  !mov eax,[p.v_pRadius]
  !shl eax,1
  !add eax,1
  !cvtsi2ss xmm1, eax
  !mulss xmm1, xmm1
  !pshufd xmm1, xmm1, 0
  !pxor xmm3, xmm3
  For ImageOutY = 0 To height_minus_one
    For ImageOutX = 0 To width_minus_one
      !pxor xmm0, xmm0
      For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
        If ImageInY < 0
          ImageInY = 0
        ElseIf ImageInY > height_minus_one
          Break
        EndIf
        If ImageOutX-pRadius > 0
          *pointer = *buffer + ImageInY * pitch + (ImageOutX-pRadius)*3
          *out_pointer = *out + ImageInY * out_pitch + (ImageOutX-pRadius)*3
        Else
          *pointer = *buffer + ImageInY * pitch
          *out_pointer = *out + ImageInY * out_pitch
        EndIf
        For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
          If ImageInX < 0
            ImageInX = 0
          ElseIf ImageInX > width_minus_one
            Break
          EndIf
          !mov eax, [p.p_pointer]
          !movd xmm2, [eax]
          !punpcklbw xmm2, xmm3
          !punpcklwd xmm2, xmm3
          !paddd xmm0, xmm2
          !add eax, 3
          !mov [p.p_pointer], eax
        Next ImageInX
      Next ImageInY
      !mov eax, [p.p_out_pointer]
      !cvtdq2ps xmm0, xmm0
      !divps xmm0,xmm1
      !cvtps2dq xmm0, xmm0
      !packssdw xmm0, xmm0
      !packuswb xmm0, xmm0
      !movd [eax], xmm0
      !add eax, 3
      !mov [p.p_out_pointer], eax
    Next ImageOutX 
  Next ImageOutY
  StopDrawing()
 
  StartDrawing(ImageOutput(rv))
  *buffer = DrawingBuffer()
  pitch = DrawingBufferPitch()
  CopyMemory(*out,*buffer,pitch*image_height)
  StopDrawing()
 
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l, Blur)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC()
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
 
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
 
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
 
  rv = Blur(RawImage, Blur)
  FreeImage(RawImage)
 
  ProcedureReturn rv
EndProcedure

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

t = ElapsedMilliseconds()
img = CaptureScreen(0, 0, FormWidth, FormHeight, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
     
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          Break
      EndSelect
     
  EndSelect
ForEver
If you want it to work on older hardware and don't mind the radius being limited from 1 - 5, here's a MMX solution

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected image_width.i = ImageWidth(pImageIn)
  Protected image_height.i = ImageHeight(pImageIn)
  Protected width_minus_one = image_width - 1
  Protected height_minus_one = image_height - 1
  Protected *pointer, *out_pointer
  Protected pitch.i,*buffer.i
  Static rv.i,old_image_width.i,old_image_height.i,*out.i,out_pitch.i
  
  If image_width <> old_image_width And image_height <> old_image_height
    If rv
      FreeImage(rv)
    EndIf
    rv = CreateImage(#PB_Any, image_width, image_height)
    old_image_width = image_width
    old_image_height = image_height
    *out = AllocateMemory(image_width*image_height*3)
    out_pitch = image_width*3
  EndIf
  
  If pRadius < 1
    pRadius = 1
  ElseIf pRadius > 5
    pRadius = 5
  EndIf
  
  StartDrawing(ImageOutput(pImageIn))
  *buffer = DrawingBuffer()
  pitch.i = DrawingBufferPitch()
  !mov ecx,[p.v_pRadius]
  !shl ecx,1
  !add ecx,1
  !imul ecx, ecx
  !xor edx, edx
  !mov eax, 65536
  !idiv ecx
  !movd mm1, eax
  !punpcklwd mm1, mm1
  !punpcklwd mm1, mm1
  !pxor mm3, mm3
  For ImageOutY = 0 To height_minus_one
    For ImageOutX = 0 To width_minus_one
      !pxor mm0, mm0
      For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
        If ImageInY < 0
          ImageInY = 0
        ElseIf ImageInY > height_minus_one
          Break
        EndIf
        If ImageOutX-pRadius > 0
          *pointer = *buffer + ImageInY * pitch + (ImageOutX-pRadius)*3
          *out_pointer = *out + ImageInY * out_pitch + (ImageOutX-pRadius)*3
        Else
          *pointer = *buffer + ImageInY * pitch
          *out_pointer = *out + ImageInY * out_pitch
        EndIf
        For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
          If ImageInX < 0
            ImageInX = 0
          ElseIf ImageInX > width_minus_one
            Break
          EndIf
          !mov eax, [p.p_pointer]
          !movd mm2, [eax]
          !punpcklbw mm2, mm3
          !paddw mm0, mm2
          !add eax, 3
          !mov [p.p_pointer], eax
        Next ImageInX
      Next ImageInY
      !mov eax, [p.p_out_pointer]
      !pmulhw mm0, mm1
      !packuswb mm0, mm0
      !movd [eax], mm0
      !add eax, 3
      !mov [p.p_out_pointer], eax
    Next ImageOutX 
  Next ImageOutY
  !emms
  StopDrawing()
 
  StartDrawing(ImageOutput(rv))
  *buffer = DrawingBuffer()
  pitch = DrawingBufferPitch()
  CopyMemory(*out,*buffer,pitch*image_height)
  StopDrawing()
 
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l, Blur)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC()
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
 
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
 
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
 
  rv = Blur(RawImage, Blur)
  FreeImage(RawImage)
 
  ProcedureReturn rv
EndProcedure

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

t = ElapsedMilliseconds()
img = CaptureScreen(0, 0, FormWidth, FormHeight, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)
SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
     
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          Break
      EndSelect
     
  EndSelect
ForEver
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

Thanks Wilbert, but your examples just crashes with an Invalid Memory Exception - for both the SSE2 and the MMX.

I have had a go at my original code, and I've shrunk the time on my laptop from 1800ms down to 600ms per blur. One interface change - left click blurs again, right click exits.

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  Protected imgWidth.i = ImageWidth(pImageIn)
  Protected imgHeight.i = ImageHeight(pImageIn)
  
  Protected rv.i = CreateImage(#PB_Any, imgWidth, imgHeight)
  
  Protected Average = (( pRadius * 2 ) + 1) * 2
  
  Dim out.i(imgWidth - 1, imgWidth - 1)

  Protected r.i, g.i, b.i
  
 StartDrawing(ImageOutput(pImageIn))
  For ImageOutX = 0 To imgWidth - 1
    For ImageOutY = 0 To imgHeight - 1
      
      r = 0
      g = 0
      b = 0
      colour = 0
      
      For ImageInX = ImageOutX - pRadius To ImageOutX + pRadius
        
        x = ImageInX
          If x < 0
            x = 0
          EndIf
          If x > imgWidth - 1
            x = imgWidth - 1
          EndIf
          
          colour = Point(x, ImageOutY)
          r + Red(colour)
          g + Green(colour)
          b + Blue(colour)
          
          
        Next
              For ImageInY = ImageOutY - pRadius To ImageOutY + pRadius
          
          y = ImageInY
          If y < 0
            y = 0
          EndIf
          If y > ImageHeight(rv) - 1
            y = ImageHeight(rv) - 1
          EndIf
          
          colour = Point(ImageOutX, y)
          r + Red(colour)
          g + Green(colour)
          b + Blue(colour)
          
        Next
      
      out(ImageOutX, ImageOutY) = RGB(r / Average, g / Average, b / Average)
      
    Next
  Next

  StopDrawing()
  
   StartDrawing(ImageOutput(rv))
  For ImageOutX = 0 To ImageWidth(rv) - 1
    For ImageOutY = 0 To ImageHeight(rv) - 1
      Plot(ImageOutX, ImageOutY, out(ImageOutX, ImageOutY))
    Next
  Next
    StopDrawing()
  
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC() 
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
  
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
  
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
  
  ProcedureReturn RawImage
EndProcedure 

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

img = CaptureScreen(0, 0, FormWidth, FormHeight)

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)

t = ElapsedMilliseconds()

rv = Blur(img, 2)
FreeImage(img)
img = rv

t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick

t = ElapsedMilliseconds()

rv = Blur(img, 2)
FreeImage(img)
img = rv

t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

        Case #PB_EventType_RightClick
          Break
          
      EndSelect
      
  EndSelect
ForEver
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Blur Optimisations

Post by wilbert »

Here's a new MMX attempt based on your latest code.
Does this still give an error ?

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.l)
  
  If pRadius < 1
    pRadius = 1
  ElseIf pRadius > 31
    pRadius = 31
  EndIf
    
  Protected imgWidth.i = ImageWidth(pImageIn)
  Protected imgHeight.i = ImageHeight(pImageIn)
  Protected width_minus_one = imgWidth - 1
  Protected height_minus_one = imgHeight - 1
  
  Protected rv.i = CreateImage(#PB_Any, imgWidth, imgHeight, 32)
  Protected memSize.i = imgWidth * imgHeight * 4
  Protected *in_base, *out, *out_base = AllocateMemory(memSize)
  Protected.i ImageOutX, ImageOutY, r
  
  StartDrawing(ImageOutput(rv))
  DrawImage(ImageID(pImageIn), 0, 0)
  *in_base = DrawingBuffer()
  *out = *out_base
  
  !mov ecx,[p.v_pRadius]
  !shl ecx,1
  !add ecx,1
  !shl ecx, 1
  !xor edx, edx
  !mov eax, 65536
  !idiv ecx
  !movd mm1, eax
  !punpcklwd mm1, mm1
  !punpcklwd mm1, mm1
  !pxor mm3, mm3
  
  For ImageOutY = 0 To height_minus_one
    For ImageOutX = 0 To width_minus_one
      
      !pxor mm0, mm0
      
      For r = -pRadius To pRadius
        
        !xor edx, edx
        
        !mov ecx, [p.v_ImageOutX]
        !add ecx, [p.v_r]
        !cmp ecx, edx
        !cmovng ecx, edx
        !cmp ecx, [p.v_width_minus_one]
        !cmovg ecx, [p.v_width_minus_one]; ecx = limit(ImageOutX + r, 0, width - 1)

        !mov eax, [p.v_ImageOutY]
        !imul eax, [p.v_imgWidth]
        !add eax, ecx
        !shl eax, 2
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
          !add eax, [p.p_in_base]
          !movd mm2, [eax]
        CompilerElse
          !add rax, [p.p_in_base]
          !movd mm2, [rax]
        CompilerEndIf
        !punpcklbw mm2, mm3
        !paddw mm0, mm2
        
        !mov eax, [p.v_ImageOutY]
        !add eax, [p.v_r]
        !cmp eax, edx
        !cmovng eax, edx
        !cmp eax, [p.v_height_minus_one]
        !cmovg eax, [p.v_height_minus_one]; eax = limit(ImageOutY + r, 0, height - 1)
        
        !imul eax, [p.v_imgWidth]
        !add eax, [p.v_ImageOutX]
        !shl eax, 2
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
          !add eax, [p.p_in_base]
          !movd mm2, [eax]
        CompilerElse
          !add rax, [p.p_in_base]
          !movd mm2, [rax]
        CompilerEndIf
        !punpcklbw mm2, mm3
        !paddw mm0, mm2
        
      Next
      
      !pmulhw mm0, mm1
      !packuswb mm0, mm0
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        !mov eax, [p.p_out]
        !movd [eax], mm0
        !add eax, 4
        !mov [p.p_out], eax
      CompilerElse
        !mov rax, [p.p_out]
        !movd [rax], mm0
        !add rax, 4
        !mov [p.p_out], rax
      CompilerEndIf
      
    Next 
  Next
  
  !emms
  
  StopDrawing()
 
  StartDrawing(ImageOutput(rv))
  CopyMemory(*out_base, DrawingBuffer(), memSize)
  FreeMemory(*out_base)
  StopDrawing()
 
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC() 
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
  
  RawImage.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( RawImage ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
  
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
  
  ProcedureReturn RawImage
EndProcedure 

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

img = CaptureScreen(0, 0, FormWidth, FormHeight)

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)

t = ElapsedMilliseconds()

rv = Blur(img, 2)
FreeImage(img)
img = rv

t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick

t = ElapsedMilliseconds()

rv = Blur(img, 2)
FreeImage(img)
img = rv

t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

        Case #PB_EventType_RightClick
          Break
          
      EndSelect
      
  EndSelect
ForEver
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

I'm at work now, and Wilbert, I've tested your original posts, and they DO work on P4 machine at work, whereas it didn't work on my laptops Core Duo. Go figure.

I've also tested your latest MMX offering and that, again works at work, but I haven't gotten to trying it on my laptop yet.

As an idea for speed difference, my Works res of 2560x1024 drops from 1280ms down to 220ms! Astounding!

I had a couple more ideas for algorithm optimisation that I'm going to give a whirl tonight.

I'm beginning to think that possibly having a small radius blur that is very very fast and being called multiple times may be better than a single pass with a large (and slow) radius
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Blur Optimisations

Post by wilbert »

Foz wrote:I had a couple more ideas for algorithm optimisation that I'm going to give a whirl tonight.
A common one is a 3 x 3 matrix

1 2 1
2 4 2
1 2 1

The numbers indicate the weight.
So you multiply the pixels by these values and divide the result by 16.
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

I can see that would result in a high quality blur but my aim is for a very blurry result that is also very fast.

Your last MMX post was the speed I have been looking for, but not the blurriness.

With yours and the communities help, I'll get there I'm sure :)
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

Right I've had a try on my laptop now and your new MMX code works a treat - dropping from 520ms down to 70ms!

I've tried the matrix idea you suggested, and fixed up a number of issues with my first try, as a result, I now have a faster blur that looks better.

(I mean really, what was I thinking putting all the results in an Array and then plotting out all the pixels again! I must have been mad!)

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  imgWidth = ImageWidth(pImageIn)
  imgHeight = ImageHeight(pImageIn)
  
  imgWidthMinus1 = ImageWidth(pImageIn) - 1
  imgHeightMinus1 = ImageHeight(pImageIn) - 1
  
  Average.f
  For x = 0-pRadius To pRadius
    For y = 0-pRadius To pRadius
      Average + ( ( (pRadius * 2) + 1 ) - Abs( x + y ) )
    Next
  Next
  Average = 1 / Average
  
  
  RadiusTimes2Plus1 = (pRadius * 2) + 1
  
  Dim multiplier(pRadius * 2, pRadius * 2)
  
  For x = -pRadius To pRadius
    For y = -pRadius To pRadius
      multiplier(x + pRadius, y + pRadius) = ( RadiusTimes2Plus1 - Abs( x + y ) )
    Next
  Next
  
  
  Protected r.i, g.i, b.i
  
  StartDrawing(ImageOutput(pImageIn))
  For ImageOutX = 0 To imgWidthMinus1
    For ImageOutY = 0 To imgHeightMinus1
      
      r = 0
      g = 0
      b = 0
      colour = 0
      
      For RadiusX = -pRadius To pRadius
        For RadiusY = -pRadius To pRadius
          ImageInX = ImageOutX + RadiusX
          ImageInY = ImageOutY + RadiusY
          
          x = ImageInX
          If x < 0
            x = 0
          ElseIf x > imgWidthMinus1
            x = imgWidthMinus1
          EndIf
          
          y = ImageInY
          If y < 0
            y = 0
          ElseIf y > imgHeightMinus1
            y = imgHeightMinus1
          EndIf
          
          mult = multiplier(RadiusX + pRadius, RadiusY + pRadius)
          
          colour = Point(x, y)
          r + Red(colour)   * mult
          g + Green(colour) * mult
          b + Blue(colour)  * mult
          
        Next
      Next
      
      Plot(ImageOutX, ImageOutY, RGB(r * Average, g * Average, b * Average))
    Next
  Next
  StopDrawing()
  
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC() 
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
  
  rv.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( rv ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
  
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
  
  ProcedureReturn rv
EndProcedure 

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

img = CaptureScreen(0, 0, FormWidth, FormHeight)

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)

t = ElapsedMilliseconds()
Blur(img, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          
          t = ElapsedMilliseconds()
          Blur(img, 2) ; blurriness radius of 2
          t = ElapsedMilliseconds() - t
          
          SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))
          
          StartDrawing(CanvasOutput(0))
          DrawText(10, 10, Str(t))
          StopDrawing()
          
        Case #PB_EventType_RightClick
          Break
          
      EndSelect
      
  EndSelect
ForEver
Foz
Addict
Addict
Posts: 1359
Joined: Tue Nov 13, 2007 12:42 pm
Location: Manchester, UK

Re: Blur Optimisations

Post by Foz »

And adapting it to the second try results in only a slightly faster (about 10-50ms faster on my laptop), but again, resulting in a nicer blur:

Code: Select all

DisableDebugger

Procedure.i Blur(pImageIn.i, pRadius.i)
  imgWidth = ImageWidth(pImageIn)
  imgHeight = ImageHeight(pImageIn)
  
  imgWidthMinus1 = ImageWidth(pImageIn) - 1
  imgHeightMinus1 = ImageHeight(pImageIn) - 1
  
  Average.f
  For x = 0-pRadius To pRadius
    Average + ( ( (pRadius * 2) + 1 ) - Abs( x ) )
  Next
  For y = 0-pRadius To pRadius
    Average + ( ( (pRadius * 2) + 1 ) - Abs( y ) )
  Next
  Average = 1 / Average
  
  
  RadiusTimes2Plus1 = (pRadius * 2) + 1
  
  Dim multiplier(pRadius * 2, pRadius * 2)
  
  For x = -pRadius To pRadius
    For y = -pRadius To pRadius
      multiplier(x + pRadius, y + pRadius) = ( RadiusTimes2Plus1 - Abs( x + y ) )
    Next
  Next
  
  
  Protected r.i, g.i, b.i
  
  StartDrawing(ImageOutput(pImageIn))
  For ImageOutX = 0 To imgWidthMinus1
    For ImageOutY = 0 To imgHeightMinus1
      
      r = 0
      g = 0
      b = 0
      colour = 0
      
      For RadiusX = -pRadius To pRadius
        ImageInX = ImageOutX + RadiusX
        y = ImageOutY
        
        x = ImageInX
        If x < 0
          x = 0
        ElseIf x > imgWidthMinus1
          x = imgWidthMinus1
        EndIf
        
        mult = multiplier(RadiusX + pRadius, pRadius)
        
        colour = Point(x, y)
        r + Red(colour)   * mult
        g + Green(colour) * mult
        b + Blue(colour)  * mult
        
      Next
      For RadiusY = -pRadius To pRadius
        x = ImageOutX
        ImageInY = ImageOutY + RadiusY
        
        y = ImageInY
        If y < 0
          y = 0
        ElseIf y > imgHeightMinus1
          y = imgHeightMinus1
        EndIf
        
        mult = multiplier(pRadius, RadiusY + pRadius)
        
        colour = Point(x, y)
        r + Red(colour)   * mult
        g + Green(colour) * mult
        b + Blue(colour)  * mult
        
      Next
      
      Plot(ImageOutX, ImageOutY, RGB(r * Average, g * Average, b * Average))
    Next
  Next
  StopDrawing()
  
  ProcedureReturn rv
EndProcedure


Procedure.i CaptureScreen(left.l, top.l, Width.l, Height.l)
  #CAPTUREBLT = $40000000
  dm.DEVMODE ;structure for CreateDC() 
  srcDC.l
  trgDC.l
  BMPHandle.l
  srcDC = CreateDC_ ( "DISPLAY" , "" , "" , dm)
  trgDC = CreateCompatibleDC_ (srcDC)
  BMPHandle = CreateCompatibleBitmap_ (srcDC, Width, Height)
  SelectObject_ ( trgDC, BMPHandle)
  BitBlt_ ( trgDC, 0, 0, Width, Height, srcDC, left , top, #SRCCOPY|#CAPTUREBLT )
  
  rv.i = CreateImage ( #PB_Any ,Width,Height)
  StartDrawing ( ImageOutput ( rv ))
  DrawImage (BMPHandle,0,0)
  StopDrawing ()
  
  DeleteDC_ ( trgDC)
  ReleaseDC_ ( BMPHandle, srcDC)
  
  ProcedureReturn rv
EndProcedure 

FormWidth = 0
FormHeight = 0
DesktopCount = ExamineDesktops()
For i = 0 To DesktopCount-1
  If FormWidth < DesktopX(i) + DesktopWidth(i)
    FormWidth = DesktopX(i) + DesktopWidth(i)
  EndIf
  If FormHeight < DesktopY(i) + DesktopHeight(i)
    FormHeight = DesktopY(i) + DesktopHeight(i)
  EndIf
Next

img = CaptureScreen(0, 0, FormWidth, FormHeight)

OpenWindow(0, 0, 0, FormWidth, FormHeight, "...", #PB_Window_BorderLess )
StickyWindow(0, 1)
CanvasGadget(0, 0, 0, FormWidth, FormHeight)

t = ElapsedMilliseconds()
Blur(img, 2) ; blurriness radius of 2
t = ElapsedMilliseconds() - t

SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))

StartDrawing(CanvasOutput(0))
DrawText(10, 10, Str(t))
StopDrawing()

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_Gadget
      Select EventType()
        Case #PB_EventType_LeftClick
          
          t = ElapsedMilliseconds()
          Blur(img, 2) ; blurriness radius of 2
          t = ElapsedMilliseconds() - t
          
          SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(img))
          
          StartDrawing(CanvasOutput(0))
          DrawText(10, 10, Str(t))
          StopDrawing()
          
        Case #PB_EventType_RightClick
          Break
          
      EndSelect
      
  EndSelect
ForEver
Post Reply