The awesome speed of TransparentBlt()

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

The awesome speed of TransparentBlt()

Post by netmaestro »

Code updated for 5.20+

There are a couple of tried-and-true methods kicking around the forum for drawing transparent images. They work well, one is reasonably fast and the other one is fairly slow. I wondered if the speed could be improved, and so I made some experiments, and from the results I made a new procedure. It works the same as the others but the speed - wow. I'm getting 68 thousand images rendered per second! Here's my test, see what numbers you get:

Code: Select all

Import "msimg32.lib"
  TransparentBlt(a,b,c,d,e,f,g,h,i,j,k)
EndImport

OpenWindow(0,0,0,640,480,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
    
CreateImage(0, 128,128)
StartDrawing(ImageOutput(0))
  Box(0,0,128,128,#Blue)
  Box(5,5,118,118,RGB(255,0,255))
StopDrawing()
ResizeImage(0,128,128)
CreateImage(1, 128,128)
StartDrawing(ImageOutput(1))
  Box(0,0,128,128,GetSysColor_(#COLOR_BTNFACE))
StopDrawing()

Procedure CommonMethod1(DC, Bitmap, x, y, Width, Height, TransparentColor) 
    maskDC = CreateCompatibleDC_(DC) 
    tempDC = CreateCompatibleDC_(DC) 
    SourceDC = CreateCompatibleDC_(DC) 
    SelectObject_(SourceDC, Bitmap) 
    hMaskBmp = CreateBitmap_(Width, Height, 1, 1, 0) 
    hTempBmp = CreateCompatibleBitmap_(DC, Width, Height) 
    SelectObject_(maskDC, hMaskBmp) 
    SelectObject_(tempDC, hTempBmp) 
    TransparentColor= SetBkColor_(SourceDC, TransparentColor) 
    BitBlt_ (maskDC, 0, 0, Width, Height, SourceDC, 0, 0, #SRCCOPY) 
    SetBkColor_(SourceDC, TransparentColor) 
    BitBlt_ (tempDC, 0, 0, Width, Height, maskDC, 0, 0, #SRCCOPY) 
    BitBlt_ (DC, X, Y, Width, Height, tempDC, 0, 0, #MERGEPAINT) 
    BitBlt_ (maskDC, 0, 0, Width, Height, maskDC, 0, 0, #NOTSRCCOPY) 
    BitBlt_ (tempDC, 0, 0, Width, Height, SourceDC, 0, 0, #SRCCOPY) 
    BitBlt_ (tempDC, 0, 0, Width, Height, maskDC, 0, 0, #MERGEPAINT) 
    BitBlt_ (DC, X, Y, Width, Height, tempDC, 0, 0, #SRCAND) 
    DeleteObject_ (hMaskBmp) 
    DeleteObject_ (hTempBmp) 
    DeleteDC_ (maskDC) 
    DeleteDC_ (tempDC) 
    DeleteDC_ (SourceDC) 
EndProcedure 

Procedure CommonMethod2(DC, ImageID, x, y, TransparentColor) 
  Protected ImageList, BM.BITMAP 
  GetObject_(ImageID,SizeOf(BITMAP),BM.BITMAP) 
  ImageID = CopyImage_(ImageID,#IMAGE_BITMAP,BM\bmWidth,BM\bmHeight,0) 
  ImageList = ImageList_Create_(BM\bmWidth,BM\bmHeight,#ILC_COLORDDB|#ILC_MASK,1,0) 
  ImageList_AddMasked_(ImageList,ImageID,TransparentColor) 
  ImageList_Draw_(ImageList,0,DC,x,y,#ILD_TRANSPARENT) 
  ImageList_Destroy_(ImageList) 
  DeleteObject_(ImageID) 
EndProcedure

Procedure MyNewFavouriteMethod(outputdc, image, x, y, transcolor)
  tmpdc=StartDrawing(ImageOutput(image))
    TransparentBlt(outputdc, x, y, ImageWidth(image), ImageHeight(image), tmpdc, 0, 0, ImageWidth(image), ImageHeight(image), transcolor)
  StopDrawing()
  ReleaseDC_(0, outputdc)
EndProcedure

Procedure cls()
  StartDrawing(WindowOutput(0))
    Box(0,0,640,480,GetSysColor_(#COLOR_BTNFACE))
  StopDrawing()
EndProcedure

cls()
Delay(300)
cc.d=0
s1=ElapsedMilliseconds()
While ElapsedMilliseconds()-s1 <= 2000
  CommonMethod2(GetDC_(WindowID(0)), ImageID(0), Random(600),Random(400), RGB(255,0,255))
  cc+1
Wend
str2.s = "Common Method 2: "+Str(Int(cc/(ElapsedMilliseconds()-s1)*1000))+" images rendered per second"

cls()
Delay(300)
cc.d=0
s1=ElapsedMilliseconds()
While ElapsedMilliseconds()-s1 <= 2000
  CommonMethod1(GetDC_(WindowID(0)), ImageID(0), Random(600),Random(400),ImageWidth(0), ImageHeight(0), RGB(255,0,255))
  cc+1
Wend
str1.s = "Common Method 1: "+Str(Int(cc/(ElapsedMilliseconds()-s1)*1000))+" images rendered per second"

cls()
Delay(300)
cc.d=0
s1=ElapsedMilliseconds()
While ElapsedMilliseconds()-s1 <= 2000
  MyNewFavouriteMethod(GetDC_(WindowID(0)), 0, Random(600),Random(400), RGB(255,0,255))
  cc+1
Wend
str0.s = "My New Favourite Method: "+Str(Int(cc/(ElapsedMilliseconds()-s1)*1000))+" images rendered per second!! "

; Debug str2
; Debug str1
; Debug str0
MessageRequester("Results", str2 + #LF$ + str1 + #LF$ + str0)

Repeat:Until WaitWindowEvent()=#WM_CLOSE
BERESHEIT
User avatar
Deeem2031
Enthusiast
Enthusiast
Posts: 216
Joined: Sat Sep 20, 2003 3:57 pm
Location: Germany
Contact:

Post by Deeem2031 »

Strange, with debugger enabled i get more images/sec than without. :shock:
And why you count the amouth of images with a double :?:
irc://irc.freenode.org/#purebasic
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

A double is better for the division to produce images/sec. What numbers are you getting?
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Interesting. :D I've never come across the TransparentBlt() function before.

Incidentally, you'll want to release all the dc's created in the above example. It played merry buggery with my display! :)
I may look like a mule, but I'm not a complete ass.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

I can't answer for the first two procedures as I didn't write them. The second one seems to generate some flickering. The one I wrote (all 4 lines of it!) is warranted not to have any leaks, as I ran it for 10 minutes and watched the program's memory usage in the CPU monitor. The others, I don't know.
BERESHEIT
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Post by Joakim Christiansen »

Holy cow! :D
I like logic, hence I dislike humans but love computers.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

There is a definite memory leak on my system (one I've seen before). On adjusting the code to just create one dc and then release it at the end, all leaks disappear and my display behaves itself. All methods also increase in speed although your new method is by far still the fastest.
I may look like a mule, but I'm not a complete ass.
User avatar
Mischa
Enthusiast
Enthusiast
Posts: 115
Joined: Fri Aug 15, 2003 7:43 pm

Post by Mischa »

Isn't the TransparentBlt() function buggy?
I thougth there was a memory leak.

I think this is one main reason, why the other 'common'-methods are made.
:roll:

A useful way to speedup transparent blitting is to create mask once and
don't delete it every time. So you need only two blt-operations for every
transparent display. If you have to draw the transparent pic once only, this tip is useless. :wink:


Regards,
Mischa
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Post by Num3 »

:shock:

80555 images per second with debuger on !!!
75684 images per second with debuger off !!!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

srod, are you using win98?
No, XP home.

The leak I'm seeing is simply due to not releasing the hdc's - not with the TransparentBlt() procedure, but with the other two. There's something about my system that always shows this problem very quickly - perhaps the relatively small amount of memory my computer has?
I may look like a mule, but I'm not a complete ass.
Edwin Knoppert
Addict
Addict
Posts: 1073
Joined: Fri Apr 25, 2003 11:13 pm
Location: Netherlands
Contact:

Post by Edwin Knoppert »

MSDN:
Windows 95/98: There are only 5 common DCs available per thread, thus failure to release a DC can prevent other applications from accessing one.

-------------

Also, if one uses a loop where you retrieve things over and over is poor programming.
If you can bring such parts outside the loop.
gnozal
PureBasic Expert
PureBasic Expert
Posts: 4229
Joined: Sat Apr 26, 2003 8:27 am
Location: Strasbourg / France
Contact:

Post by gnozal »

I also noticed that Win9x seems less tolerant to GDI leaks than WinNT/2k/XP.
Some codes you can find in the forum forget to ReleaseDC_() after a GetDC_(). While it does not seem to bother NT based OS, it definitely causes problems with Win9x.
For free libraries and tools, visit my web site (also home of jaPBe V3 and PureFORM).
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

I took a crack at making an image-to-image implementation. I'm the first to admit that when it comes to this type of thing I don't know what I'm doing, so improvements are eagerly encouraged.

This procedure takes as input two image#'s (not handles), an x,y location and a transcolor. It returns the handle to a third image which is composed of image1 drawn transparently on a background consisting of image2.

It is very fast, even though I'm sure it's poorly written. Here it's doing about 10000 images (256*256) per second. It is guaranteed not to have a memory leak, but you must remember that because of its construction, once you're finished with the image it makes you have to do:

DeleteObject_(<returned bmphandle>) or they will build up and eventually exhaust resources.

Here goes:

Code: Select all

Procedure SuperImpose(img1, img2, x, y, transcolor)
  srcDC = CreateDC_("DISPLAY", 0,0,0) 
  trgDC = CreateCompatibleDC_(srcDC) 
  BMPHandle = CreateCompatibleBitmap_(srcDC, ImageWidth(img2), ImageHeight(img2)) 
  SelectObject_( trgDC, BMPHandle)
  dcin=StartDrawing(ImageOutput(img2))
    BitBlt_(trgDC, 0, 0, ImageWidth(img2), ImageHeight(img2), dcin, 0, 0, #SRCCOPY)
  StopDrawing()
  dcin=StartDrawing(ImageOutput(img1))
    TransparentBlt(trgdc, x, y, ImageWidth(img1), ImageHeight(img1), dcin, 0, 0, ImageWidth(img1), ImageHeight(img1), transcolor) 
  StopDrawing()
  DeleteDC_(dcin)
  DeleteDC_(trgDC)
  ReleaseDC_(BMPHandle, srcDC)
  ProcedureReturn BMPHandle
EndProcedure
BERESHEIT
Edwin Knoppert
Addict
Addict
Posts: 1073
Joined: Fri Apr 25, 2003 11:13 pm
Location: Netherlands
Contact:

Post by Edwin Knoppert »

1) ReleaseDC expects an hWnd.

2) When you select a bitmap or any other gdi object you'll get a return value.
This IS a similar object type which was already present.
You should selecte it 'back' when done.

3) Deselect and destroy in reversed order like

CreateDC()
hOld... = Selectobject
Draw..
Selectobject( hOld.. )
Delete...

the compaible bitmap get's deselected and can be used with other GDI calls

When you create a DC, all gdi objects are already present like pen, brush bitmap and so on.
Usually useless and we select our own sized bitmap and brush whatever...
Still, you should select the old objects and then delete..

4) It might be easier to use GetDC( #HWND_DESKTOP ) (which is a value of 0 )

And then ReleaseDC( #HWND_DESKTOP , hDC ) to delete it again.
Post Reply