I never played with UpdateLayeredWindow() before. So I took a look and didn't find many concise examples in VB6. Examples here and there, but not clear enough to get through my thick skull.
I kept thinking it was something like SetLayeredWindowAttributes() but in reality it's a different beast to work with.
What I was after was the ability to draw on a transparent Form varying the transparency as I drew stuff.
I didn't have a lot of time to fiddle with this so after a couple of days tinkering with it on and off I got a little frustrated. What I had missed is that you really need to render into a memory DC, not your Form's hDC.
To exit, close Form1 (the Main form). Here's the code of Form2 (the Alpha form) with the boring bits removed:
Code:
Option Explicit
Private GrabX As Single
Private GrabY As Single
Private Sub Form_Load()
Dim WidthBM As Long
Dim HeightBM As Long
Dim hDCScreen As Long
Dim hDCMem As Long
Dim hBitmap As Long
Dim hBitmapOrig As Long
Dim ptDst As User32.POINT '0, 0
Dim sizeNew As User32.SIZE
Dim ptSrc As User32.POINT '0, 0
Dim BLENDFUNCTION As User32.BLENDFUNCTION
'Layered window.
SetWindowLong hWnd, _
GWL_EXSTYLE, _
GetWindowLong(hWnd, GWL_EXSTYLE) Or WS_EX_LAYERED
'Create a memory DC and 32bpp BITMAP selected into the new DC.
ScaleMode = vbTwips 'To match what GdipARGB uses.
WidthBM = ScaleX(ScaleWidth, ScaleMode, vbPixels)
HeightBM = ScaleY(ScaleHeight, ScaleMode, vbPixels)
hDCScreen = GetDC(WIN32_NULL)
hDCMem = CreateCompatibleDC(hDCScreen)
hBitmap = CreateBitmap(WidthBM, HeightBM, 1, 32, ByVal WIN32_NULL)
hBitmapOrig = SelectObject(hDCMem, hBitmap)
'Clear the DC to transparent and do a little random drawing.
'
'Use GDI, or GDI+, etc.
*** BORING DRAWING OPERATIONS OMITTED HERE ***
'Update the layered window, causing it to display.
With sizeNew
.cx = WidthBM
.cy = HeightBM
End With
With BLENDFUNCTION
.BlendOp = AC_SRC_OVER
.SourceConstantAlpha = &HFF
.AlphaFormat = AC_SRC_ALPHA
End With
User32.UpdateLayeredWindow hWnd, _
hDCScreen, _
ptDst, _
sizeNew, _
hDCMem, _
ptSrc, _
0, _
BLENDFUNCTION, _
ULW_ALPHA
'Clean up the BITMAP and DCs.
ReleaseDC WIN32_NULL, hDCScreen 'Do this as soon as you can.
SelectObject hDCMem, hBitmapOrig
DeleteObject hBitmap
DeleteDC hDCMem
MousePointer = vbCrosshair 'For visual feedback on mouseover.
End Sub
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
GrabX = X
GrabY = Y
MousePointer = vbSizePointer 'For visual feedback while dragging.
End If
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
Move Left + (X - GrabX), Top + (Y - GrabY)
End If
End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
MousePointer = vbCrosshair 'Restore it.
End Sub
Forms dragged around to show the translucency as well as "transparent holes"
My questions are:
Am I wrong about not being able to render into the Form itself and use its hDC for this?
I used GDI+ for drawing. I requested NONANTIALIASED_QUALITY but the text still seems to get some sort of anti-aliasing or outlining or something. What's going on with this?
This stuff is outside my areas of expertise. I just limp along working from the documentation since I have little graphics experience.
I hope what i have here is useful to somebody else. Maybe more so once those with deeper background offer answers to my questions and propose improvements.
Am I wrong about not being able to render into the Form itself and use its hDC for this?
I haven't played with that API for at least 10 years. But from what I recall, you are not wrong. The 32bpp pARGB bitmap is rendered by Windows. You don't draw to the form, your draw to the memory DC.
Insomnia is just a byproduct of, "It can't be done"
Did you see this sample code from Windows-classic-samples? Yes, working simultaneously with both hdcDst and hdcSrc seems a bit convoluted.
Another caveat if I remember correctly is that normal WM_PAINT processing is stopped and instead of this you have to "push" content to the layered window when changes in UI occur (i.e. the OS caches the window appearance and does not call WM_PAINT but blends its cached image when redraw is needed by background windows being moved etc.)
All i wanted to show you can draw using only Form.hdc.
You can draw using Line/Circle/etc and other GDI functions only you should consider the raster operation. When you use vbCopyPen it means the destination color is replaced with the new color. So regarding the GDI color behavior (the alpha component is always zero) you just set the alpha to 0. You can combine the raster operations to draw onto Form's hdc. For example:
Code:
Option Explicit
Private Declare Function UpdateLayeredWindow Lib "user32.dll" ( _
ByVal hwnd As Long, _
ByVal hdcDst As Long, _
ByRef pptDst As Any, _
ByRef psize As Any, _
ByVal hdcSrc As Long, _
ByRef pptSrc As Any, _
ByVal crKey As Long, _
ByRef pblend As Long, _
ByVal dwFlags As Long) As Long
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32.dll" _
Alias "GetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function ColorHLSToRGB Lib "shlwapi.dll" ( _
ByVal wHue As Integer, _
ByVal wLuminance As Integer, _
ByVal wSaturation As Integer) As Long
Private Const WS_EX_LAYERED As Long = &H80000
Private Const GWL_EXSTYLE As Long = -20
Private Const ULW_ALPHA As Long = &H2
Private Const AB_32Bpp255 As Long = 33488896
Private Sub Form_Load()
Dim sz As Currency, pt As Currency
Dim idx As Long, idy As Long, y As Long, oy As Long, dx As Single
SetWindowLong Me.hwnd, GWL_EXSTYLE, GetWindowLong(Me.hwnd, GWL_EXSTYLE) Or WS_EX_LAYERED
' // Make frame
' // 0. Background
DrawMode = vbMergePenNot: FillStyle = vbFSSolid
Line (1, 1)-(ScaleWidth - 2, ScaleHeight - 2), &HA0A0A0, BF
' // 1. Thin border
FillStyle = vbFSTransparent
Line (0, 0)-(ScaleWidth - 1, ScaleHeight - 1), &H505050, B
Line (10, 10)-(ScaleWidth - 11, ScaleHeight - 11), vbWhite, B
' // 3. Hole
DrawMode = vbMergePenNot: FillStyle = vbFSSolid: Line (10, 10)-(ScaleWidth - 11, ScaleHeight - 11), &H505050, B
' // Draw color sine
DrawWidth = 4
dx = 240 / ScaleWidth
For idx = 12 To ScaleWidth - 13
y = Cos(idx / 10) * 50 + ScaleHeight / 2
If idx > 13 Then
DrawMode = vbBlackness: Line (idx - 1, oy)-(idx, y)
DrawMode = vbMergeNotPen: Line (idx - 1, oy)-(idx, y), ColorHLSToRGB(dx * idx, 120, 240)
End If
oy = y
Next
' // Update
sz = (Me.ScaleWidth + CCur(Me.ScaleHeight) * 4294967296#) / 10000
UpdateLayeredWindow Me.hwnd, Me.hDC, pt, sz, Me.hDC, pt, 0, AB_32Bpp255, ULW_ALPHA
End Sub
But the result looks like color-key transparency. Can you get different translucency for different drawing operations? I don't see anything here that improves on SetLayeredWindowAttributes().
Well, in the modified project I posted, the drawings and texts are anti-aliased and blended with varying transparency according to the anti-alias, so it is sort of doing what you say.
Edit: you can also set the opacity percent of the color of any brush to other than 100 and that will set a different transparency for any brush.
Last edited by Eduardo-; Sep 25th, 2020 at 12:47 PM.
Well, in the modified project I posted, the drawings and texts are anti-aliased and blended with varying transparency according to the anti-alias, so it is sort of doing what you say.
I see your changes above, but I'm not sure how they answer my questions.
One question was about using the Form's DC and you haven't addressed that.
The Trick addressed that for rendering alpha images like a PNG, but drawing operations look entirely opaque against the windows under the layered window. Perhaps I am just misunderstanding and need to look at it more to draw with more than just 0 or 255 as alpha levels.
The other question was about drawing text, and the outline or fringing I seem to be getting.
Code:
.DrawWidth = 8
dX = 240 / ScaleWidth
For I = 12 To ScaleWidth - 13
Y = Cos(I / 10) * 50 + ScaleHeight / 2
If I > 13 Then
.ForeColor = .ToARGB(ColorHLSToRGB(dX * I, 120, 240), (I Mod 192) + 64)
.Line I - 1, oldY, I, Y
End If
oldY = Y
Next
I should add that I understand the value of UpdateLayeredWindow() for animation or displaying ongoing changes to your "canvas." I suppose it could also be useful for creating "whole screen overlays" on a second monitor or something.
And (Win8+) it probably works between parent and child windows too, like SetLayeredWindowAttributes() does.
I don't really have a lot of use for any of it, but it can be fun to play with for a little while.