There are several versions of a ScanRegion procedure to be found on the internet in a variety of languages, but they all (at least the ones I can find) use pretty much the same method, traversing through the bitmap with a pointer and excluding transparent pixels from the region as it finds them. This works well, but it's very slow. The image I'm providing with this version,
http://www.lloydsplace.com/girlonchair.bmp
using the straight StartDrawing() and Point() method, took over 3000 ms to complete. Switching to a structured pointer traversing a BITMAP object speeded it up a whole 500 ms, leaving 2500+ milliseconds it was taking to scan the region. I wanted to use the SetWindowRgn API to create a transparent imagegadget, but at these speeds it's just unworkable. Some tests showed that the bottleneck was located in the large number of CombineRgn calls, these things are slow. So I realized that most transparent pixels have another transparent pixel beside them, usually quite a few. What if we just save them up until there's no more in this group and do one CombineRgn with all of them? So I came up with the following procedure. It brought the time taken to scan and set the window region from 2500+ ms down to 78 ms on my machine. At this level of performance, a transparent imagegadget is possible using this method. I haven't seen any scan procs this fast anywhere, so I'm sharing it:
Code: Select all
Procedure ScanRegion(ImageID, transcolor)
Structure RGBTRIPLEC
rgbtBlue.c
rgbtGreen.c
rgbtRed.c
EndStructure
GetObject_(ImageID, SizeOf(BITMAP), @bmp.BITMAP)
Protected width = bmp\bmWidth
Protected height = bmp\bmHeight
Protected hRgn = CreateRectRgn_(0, 0, width, height)
Protected tred = Red(transcolor)
Protected tgreen = Green(transcolor)
Protected tblue = Blue(transcolor)
BmiInfo.BITMAPINFOHEADER
With BmiInfo
\biSize = SizeOf(BITMAPINFOHEADER)
\biWidth = width
\biHeight = -height
\biPlanes = 1
\biBitCount = 24
\biCompression = #BI_RGB
EndWith
bytesperrow = 4*((3*width+3)/4)
*bits = AllocateMemory(bytesperrow*height)
hDC = GetWindowDC_(#Null)
iRes = GetDIBits_(hDC, ImageID, 0, height, *bits, @bmiInfo, #DIB_RGB_COLORS)
ReleaseDC_(#Null, hDC)
For y=0 To height-1
pxcount=0
For x=0 To bytesperrow-1 Step 3
*px.RGBTRIPLEC = *bits + bytesperrow * y + x
If *px\rgbtRed=tred And *px\rgbtGreen=tgreen And *px\rgbtBlue=tblue
transcount = 1
firsttrans = pxcount
While *px\rgbtRed=tred And *px\rgbtGreen=tgreen And *px\rgbtBlue=tblue And x <= bytesperrow-4
transcount+1
x+3
pxcount+1
*px = *bits + bytesperrow * y + x
Wend
hTmpRgn = CreateRectRgn_(firsttrans,y,firsttrans+transcount,y+1)
CombineRgn_(hRgn, hRgn, hTmpRgn, #RGN_XOR)
DeleteObject_(hTmpRgn)
EndIf
pxcount+1
Next
Next
FreeMemory(*bits)
ProcedureReturn hRgn
EndProcedure
LoadImage(0, "girl.bmp")
OpenWindow(0,0,0,ImageWidth(0),ImageHeight(0),"", #PB_Window_ScreenCentered|#PB_Window_BorderLess)
CreateGadgetList(WindowID(0))
ImageGadget(0,0,0,0,0,ImageID(0))
start = ElapsedMilliseconds()
SetWindowRgn_(WindowID(0), ScanRegion(ImageID(image), #White), #True)
ending = ElapsedMilliseconds()
MessageRequester("Created Region in:", Str(ending-start)+" milliseconds",$C0)
Repeat
WaitWindowEvent()
Until GetAsyncKeyState_(#VK_SPACE) & 32768
Can you speed it up more yet? If so, please post the code. Or, if you know where a faster one already is, if you could post a link it would be appreciated. Thanks!