Page 1 of 2

The awesome speed of TransparentBlt()

Posted: Tue Jul 25, 2006 11:12 pm
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

Posted: Tue Jul 25, 2006 11:20 pm
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 :?:

Posted: Tue Jul 25, 2006 11:23 pm
by netmaestro
A double is better for the division to produce images/sec. What numbers are you getting?

Posted: Tue Jul 25, 2006 11:25 pm
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! :)

Posted: Tue Jul 25, 2006 11:28 pm
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.

Posted: Tue Jul 25, 2006 11:36 pm
by Joakim Christiansen
Holy cow! :D

Posted: Tue Jul 25, 2006 11:40 pm
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.

Posted: Wed Jul 26, 2006 7:56 am
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

Posted: Wed Jul 26, 2006 8:22 am
by blueznl

Posted: Wed Jul 26, 2006 10:02 am
by Num3
:shock:

80555 images per second with debuger on !!!
75684 images per second with debuger off !!!

Posted: Wed Jul 26, 2006 11:54 am
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?

Posted: Wed Jul 26, 2006 12:15 pm
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.

Posted: Wed Jul 26, 2006 12:20 pm
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.

Posted: Wed Jul 26, 2006 8:28 pm
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

Posted: Wed Jul 26, 2006 9:28 pm
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.