Drawing a transparent image Win32

Just starting out? Need help? Post your questions and find answers here.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Drawing a transparent image Win32

Post by hagibaba »

I want to convert some code to PB that I wrote to load animated gifs in B3D but I have a problem. I don't understand how to use the win32 commands to draw a transparent image. I have some code by localmotion but I'm not sure if it's right, he doesn't say where he got it from. So could anyone tell me if this code is correct?

Code: Select all

Procedure DrawTransparentImage(DC, Bitmap, x, y, Width, Height, TransparentColor)
 
  ; First, create some DC's. These are our gateways To associated
  ; bitmaps in RAM
  maskDC = CreateCompatibleDC_(DC)
  tempDC = CreateCompatibleDC_(DC)
 
  SourceDC = CreateCompatibleDC_(DC)
  SelectObject_(SourceDC, Bitmap)
 
 
  ; Then, we need the bitmaps. Note that we create a monochrome
  ; bitmap here!
  ; This is a trick we use For creating a mask fast enough.
  hMaskBmp = CreateBitmap_(Width, Height, 1, 1, 0)
  hTempBmp = CreateCompatibleBitmap_(DC, Width, Height)
 
  ; Then we can assign the bitmaps to the DCs
  ;
  hMaskBmp = SelectObject_(maskDC, hMaskBmp)
  hTempBmp = SelectObject_(tempDC, hTempBmp)
 
  ; Now we can create a mask. First, we set the background color
  ; To the transparent color; then we copy the image into the
  ; monochrome bitmap.
  ; When we are done, we reset the background color of the
  ; original source.
  TransparentColor= SetBkColor_(SourceDC, TransparentColor)
  BitBlt_ (maskDC, 0, 0, Width, Height, SourceDC, 0, 0, #SRCCOPY)
  SetBkColor_(SourceDC, TransparentColor)
 
  ; The first we do with the mask is To MergePaint it into the
  ; destination.
  ; This will punch a WHITE hole in the background exactly were
  ; we want the graphics To be painted in.
  BitBlt_ (tempDC, 0, 0, Width, Height, maskDC, 0, 0, #SRCCOPY)
  BitBlt_ (DC, x, y, Width, Height, tempDC, 0, 0, #MERGEPAINT)
 
  ; Now we delete the transparent part of our source image. To do
  ; this, we must invert the mask And MergePaint it into the
  ; source image. The transparent area will now appear as WHITE.
  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)
 
  ; Both target And source are clean. All we have To do is To And
  ; them together!
  BitBlt_ (DC, x, y, Width, Height, tempDC, 0, 0, #SRCAND)
 
  ; Now all we have To do is To clean up after us And free system
  ; resources..
  DeleteObject_ (hMaskBmp)
  DeleteObject_ (hTempBmp)
  DeleteDC_ (maskDC)
  DeleteDC_ (tempDC)
  DeleteDC_ (SourceDC)
 
EndProcedure
Heathen
Enthusiast
Enthusiast
Posts: 498
Joined: Tue Sep 27, 2005 6:54 pm
Location: At my pc coding..

Post by Heathen »

For what it's worth, that's the same code i use in a program and have never had problems.. Not sure if that helps..
I love Purebasic.
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

Anthony Jordan
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 »

That code is flawed and it leaks. iirc it was JLC who discovered this. If you run this test program, you'll see it draw around 5000 images and then it stops drawing, even though the code keeps trying:

Code: Select all

OpenWindow(0,0,0,800,600,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CreateStatusBar(0,WindowID(0))
AddStatusBarField(100)
CreateImage(0,20,20)
StartDrawing(ImageOutput(0))
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(0,0,19,19,#Red)
StopDrawing()
Repeat
  ev=WindowEvent()
  hdc=StartDrawing(WindowOutput(0))
  drawtransparentimage(hdc,ImageID(0),Random(780),Random(580),20,20,0)
  StopDrawing()
  cc+1
  StatusBarText(0,0,Str(cc))
Until ev=#PB_Event_CloseWindow
I translated the following routine from the original c last year based on Microsoft's recommended method for drawing transparent images and have since tested and used it heavily. It is faster, performs much better and does not leak. It will continue to draw as long as the program runs.

Code: Select all

ProcedureDLL TransBlit(hdc, hBitmap, xStart, yStart, TransColor) 
  ; by netmaestro based on http://support.microsoft.com/kb/79212/EN-US/ 
  Define.POINT  ptSize 
  hdcTemp = CreateCompatibleDC_(hdc) 
  SelectObject_(hdcTemp, hBitmap) 
  GetObject_(hBitmap, SizeOf(BITMAP), bm.BITMAP) 
  ptSize\x = bm\bmWidth 
  ptSize\y = bm\bmHeight 
  DPtoLP_(hdcTemp, @ptSize, 1) 
  hdcBack   = CreateCompatibleDC_(hdc) 
  hdcObject = CreateCompatibleDC_(hdc) 
  hdcMem    = CreateCompatibleDC_(hdc) 
  hdcSave   = CreateCompatibleDC_(hdc) 
  bmAndBack   = CreateBitmap_(ptSize\x, ptSize\y, 1, 1, #Null) 
  bmAndObject = CreateBitmap_(ptSize\x, ptSize\y, 1, 1, #Null) 
  bmAndMem    = CreateCompatibleBitmap_(hdc, ptSize\x, ptSize\y) 
  bmSave      = CreateCompatibleBitmap_(hdc, ptSize\x, ptSize\y) 
  bmBackOld   = SelectObject_(hdcBack, bmAndBack) 
  bmObjectOld = SelectObject_(hdcObject, bmAndObject) 
  bmMemOld    = SelectObject_(hdcMem, bmAndMem) 
  bmSaveOld   = SelectObject_(hdcSave, bmSave) 
  SetMapMode_(hdcTemp, GetMapMode_(hdc)) 
  BitBlt_(hdcSave, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCCOPY) 
  cColor = SetBkColor_(hdcTemp, TransColor) 
  BitBlt_(hdcObject, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCCOPY) 
  SetBkColor_(hdcTemp, cColor) 
  BitBlt_(hdcBack, 0, 0, ptSize\x, ptSize\y, hdcObject, 0, 0, #NOTSRCCOPY) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdc, xStart, yStart, #SRCCOPY) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdcObject, 0, 0, #SRCAND) 
  BitBlt_(hdcTemp, 0, 0, ptSize\x, ptSize\y, hdcBack, 0, 0, #SRCAND) 
  BitBlt_(hdcMem, 0, 0, ptSize\x, ptSize\y, hdcTemp, 0, 0, #SRCPAINT) 
  BitBlt_(hdc, xStart, yStart, ptSize\x, ptSize\y, hdcMem, 0, 0, #SRCCOPY) 
  BitBlt_(hdcTemp, 0, 0, ptSize\x, ptSize\y, hdcSave, 0, 0, #SRCCOPY) 
  DeleteObject_(SelectObject_(hdcBack, bmBackOld)) 
  DeleteObject_(SelectObject_(hdcObject, bmObjectOld)) 
  DeleteObject_(SelectObject_(hdcMem, bmMemOld)) 
  DeleteObject_(SelectObject_(hdcSave, bmSaveOld)) 
  DeleteDC_(hdcMem) 
  DeleteDC_(hdcBack) 
  DeleteDC_(hdcObject) 
  DeleteDC_(hdcSave) 
  DeleteDC_(hdcTemp) 
EndProcedure 
BERESHEIT
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

Yup, beautiful code. Thanks all and especially netmaestro, it certainly pays to ask if you're stuck. With netmaestro's test code for drawtransparentimage it only draws about 500 images here, but using transblit with the test code it was still drawing after 30,000 and I closed the window. Thanks again netmaestro.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

...I thought the movie-lib can play animated gifs as a movie...?
oh... and have a nice day.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

Good point, I would think it can. It seems you can use RenderMovieFrame to draw animated sprites, but I don't know how you would transfer movie frames to an image.
User avatar
einander
Enthusiast
Enthusiast
Posts: 744
Joined: Thu Jun 26, 2003 2:09 am
Location: Spain (Galicia)

Post by einander »

This is a traslation For PB 4.20 beta 2 from my code on the same page mentioned by AKG
www.purebasic.fr/english/viewtopic.php?T=12829
;Tested on Xp SP2 and Vista Home Premium; fast and no leaks found.

Code: Select all

Global _DRAWING

Procedure CreateTranspBMP(IMG,RGB)
  TranspImg=ImageList_Create_(ImageWidth(IMG),ImageHeight(IMG),#ILC_COLOR16|#ILC_MASK,1,0)
  ImageList_AddMasked_(TranspImg,ImageID(IMG),RGB)
  ProcedureReturn TranspImg
EndProcedure

; Or use this procedure instead of the direct call to ImageList_Draw_()  
; Procedure DrawTransparentImg(TranspImg,X,Y,mask=#ILD_TRANSPARENT)
  ; ImageList_Draw_(TranspImg,0,_DRAWING,X,Y,mask)
; EndProcedure

 ;test it <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
hwnd=OpenWindow(0, 100, 100,700,500 ,"Test transparent image",  #WS_OVERLAPPEDWINDOW | #WS_MAXIMIZE) 
Wi=WindowWidth(0):He=WindowHeight(0)
_DRAWING=StartDrawing(WindowOutput(0))
  LoadImage(0,"c:\test1.bmp") ;*************** your image here*************
  ResizeImage(0,80,80)
  TransparentRGB=#White   ; select RGB for transparency ********************
  
  TranspImg=CreateTranspBMP(0,TransparentRGB)
  mask=#ILD_TRANSPARENT ; see API ImageList_Draw Function to change Masks
  Repeat 
    count+1
    
    Ev=WindowEvent() 
    If GetAsyncKeyState_(#VK_ESCAPE):Break:EndIf
    ;
    ImageList_Draw_(TranspImg,0,_DRAWING,Random(Wi),Random(He),mask)
    ;       or call instead of ImageList_Draw() the commented procedure 
    ;       DrawTransparentImg(TranspImg,Random(Wi),Random(He),mask)
    If count%100=0
      DrawText(0,0,Str(count)+"      ")
      Delay(20)
    EndIf 
  Until Ev=#WM_CLOSE 
  
Cheers
Post Reply