Transparency using old school BitBlt masking techniques: Broken?

Joined
Feb 26, 2012
Messages
11
Location
Visalia, California, United States
Programming Experience
10+
I remember way back in my VB6 days, I wrote a module that would create a device context in memory, load and select a compatible bitmap, and create a mask for the image, given a color to be marked as "transparent" (white) in the mask.
Then all you had to do was BitBlt the mask to the target DC using the SRCAND raster operation, followed by the image using SRCPAINT to knock out any parts of the image where the mask's pixels are marked as white.

After a nostalgic moment of digging through my ancient code, I migrated and tried this in VB.NET 2010 Express using p/invoke and the standard Win32 API functions. Everything works as expected, except the SRCAND operation seems to function as SRCCOPY for some reason. I was wondering if somewhere between Win98 to Win7, this became broken or changed? Has anyone else got transparency to work in VB 2010 using the old BitBlt methods?
 
I'm guessing people need some code to see what I'm talking about...

[VB.NET] Win32API.vb - Pastebin.com
[VB.NET] Canvas.vb - Pastebin.com
[VB.NET] Sprite.vb - Pastebin.com

Mainly, look at the last Render() function in Sprite.vb to see what I mean.
And here's an example of how to use these objects:

VB.NET:
Imports JMEngine

Public Class Form1

    Private c As Canvas
    Private sprite As Sprite

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        c = New Canvas(PictureBox1)
        Dim g As Graphics = PictureBox1.CreateGraphics()
        Dim s As New Sprite("C:\path\to\myimage.bmp", g)
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        If Not dc Is Nothing Then
            c.Clear()
            sprite.Render(c, True)
            c.Render(e)
        End If
        PictureBox1.Invalidate()
    End Sub

    Private Sub PictureBox1_Resize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Resize
        If Not c Is Nothing Then c.Resize()
    End Sub

End Class

The CreateMask() method in Sprite.vb creates a black and white image based off the original image, changing the given RGB color found in the image to white and the rest of the pixels to black.
Then when sprite.Render(c, True) is called, the mask is blitted to the device context using SRCAND, followed by the unmasked image using SRCPAINT.
From what I remember, this was supposed to create transparency... Only rendering the parts of the image where the mask's pixels are black.
But the whole image is drawn instead...??

I should probably note that I'm not using pure GDI because this is a project I'm doing for school where we have to make some sort of game using pure VB/Win32 code, so speed and efficiency is important.
And if it weren't for that specific reason, then I'd just use C# and DirectX/OpenGL/XNA.
 
Okay so I figured it out, though it's definitely not how I remembered it being done in the past and it even seems a bit slower...
Turns out, you need to condition the original image to contain white pixels for transparent regions, and condition the mask image to contain black pixels for transparent areas & white pixels for opaque areas.
Then you need to blit the destination to your backbuffer (to achieve that "what's behind the image" effect), and then do the raster ops in reverse order.

For example, say you have myimage.bmp which contains magenta to indicate transparency.
You load the image into it's own device context, then create another image/device context combo containing the B&W mask (where black=transparent, white=opaque).
Then, you modify the contents of the original image, changing magenta to white.
Then during each draw call, you:

- Blit the destination to your backbuffer (or just clear, or blit some sort of background to fill the transparent regions of your original image)
- Blit the B&W mask to your backbuffer using SRCPAINT
- Blit your modified image to your backbuffer using SRCAND
- Blit your backbuffer to your destination

Hope this helps someone in the future.
 
Back
Top