Results 1 to 17 of 17

Thread: UpdateLayeredWindow() Drove Me Crazy

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Question UpdateLayeredWindow() Drove Me Crazy

    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

    Name:  sshot.png
Views: 1257
Size:  14.6 KB

    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.
    Attached Files Attached Files

  2. #2
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: UpdateLayeredWindow() Drove Me Crazy

    Quote Originally Posted by dilettante View Post
    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"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  3. #3
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: UpdateLayeredWindow() Drove Me Crazy

    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.)

    cheers,
    </wqw>

  4. #4

  5. #5

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    I'm not sure any of this is very useful, but it comes up from time to time so I tried playing with it. At best it feels like a stunt.

    Maybe others have more imagination.

  7. #7
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: UpdateLayeredWindow() Drove Me Crazy

    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
    Name:  alpha_test.png
Views: 1164
Size:  18.5 KB

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    Yes, that's true. Thanks.

    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().

    What am I missing?

  9. #9

  10. #10
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: UpdateLayeredWindow() Drove Me Crazy

    I'm not sure what you are trying to achieve dilettante, if you want the drawings and texts antialiased and blended with the background or what?
    Attached Files Attached Files

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    I was talking about drawing with alpha blending. Not a fixed alpha for the entire drawing, but "draw by draw" differences in alpha.

    I can do it with a memory DC but I wasn't sure how to use the Form's DC. Here's an example using a 32bpp memory DC:

    Name:  sshot.png
Views: 1051
Size:  9.8 KB

    Code:
        With New GdipARGB
            .ScaleMode = vbPixels
            .CreateGraphics hDCMem
            .BackARGBColor = GdipStatic.COLOR_TRANSPARENT 'Clears to this color.
            .DrawWidth = 1
            .ForeARGBColor = &HFF505050
            .FillARGBColor = GdipStatic.NOFILL_COLOR
            .Rectangle .MakeRF(0, 0, ScaleWidth - 1, ScaleHeight - 1)
            .DrawWidth = 10
            .ForeARGBColor = &HFFA0A0A0
            .FillARGBColor = GdipStatic.NOFILL_COLOR
            .Rectangle .MakeRF(6, 6, ScaleWidth - 12, ScaleHeight - 12)
            .DrawWidth = 1
            .ForeARGBColor = &HFF505050
            .FillARGBColor = GdipStatic.NOFILL_COLOR
            .Rectangle .MakeRF(11, 11, ScaleWidth - 22, ScaleHeight - 22)
            
            .DrawWidth = 4
            dX = 240 / ScaleWidth
            For I = 12 To ScaleWidth - 13
                Y = Cos(I / 10) * 50 + ScaleHeight / 2
                If I > 13 Then
                    .ForeARGBColor = .ToARGB(ColorHLSToRGB(dX * I, 120, 240), _
                                             (I Mod 192) + 64)
                    .Line I - 1, oldY, I, Y
                End If
                oldY = Y
            Next
            .ReleaseGraphics
        End With

  12. #12
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: UpdateLayeredWindow() Drove Me Crazy

    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.

  13. #13
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,997

    Re: UpdateLayeredWindow() Drove Me Crazy

    I changed the way the opacity is set.
    Attached Files Attached Files

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    Quote Originally Posted by Eduardo- View Post
    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.

    Name:  sshot.png
Views: 1008
Size:  11.2 KB


    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
    The RED shows where I am varying the alpha level.
    Attached Files Attached Files

  15. #15

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    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.

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: UpdateLayeredWindow() Drove Me Crazy

    Well for some forms of animation SLWA seems good enough:

    Name:  sshot.png
Views: 1164
Size:  14.2 KB

    Most of the attachment is images.
    Attached Files Attached Files

  17. #17
    PowerPoster
    Join Date
    Jan 2020
    Posts
    3,746

    Re: UpdateLayeredWindow() Drove Me Crazy

    For aliasing problems, don’t use gdi32.dll, you can use gdiplus.dll

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width