0

I have a layered window which is normally drawn this way:

    private void SelectBitmap(Bitmap bitmap)
    {
        IntPtr screenDc = GetDC(IntPtr.Zero);
        IntPtr memDc = CreateCompatibleDC(screenDc);
        IntPtr hBitmap = IntPtr.Zero;
        IntPtr hOldBitmap = IntPtr.Zero;

        try
        {
            hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
            hOldBitmap = SelectObject(memDc, hBitmap);

            POINT sourceLocation = new POINT(0, 0);
            BLENDFUNCTION blend = new BLENDFUNCTION();

            blend.BlendOp = AC_SRC_OVER;
            blend.BlendFlags = 0;
            blend.SourceConstantAlpha = 255;
            blend.AlphaFormat = AC_SRC_ALPHA;

            SIZE newSize = new SIZE(bitmap.Width, bitmap.Height);
            POINT newLocation = new POINT(Location.X, Location.Y);

            UpdateLayeredWindow(Handle, screenDc,
                ref newLocation, ref newSize,
                memDc,
                ref sourceLocation, 0,
                ref blend,
                ULW_ALPHA);
        }
        finally
        {
            ReleaseDC(IntPtr.Zero, screenDc);

            if (hBitmap != IntPtr.Zero)
            {
                SelectObject(memDc, hOldBitmap);
                DeleteObject(hBitmap);
            }

            DeleteDC(memDc);
        }
    }

However, this obviously redraw the whole window every time it's called. It's quite a performance drain on large window. (even on my top of the line PC, which make me wonder how people could handle that in Win2K)

If I read the Microsoft paper on the layered window, it says: UpdateLayeredWindow always updates the entire window. To update part of a window, use the traditional WM_PAINT and set the blend value using SetLayeredWindowAttributes.

I just can't understand the above. How is WM_PAINT supposed to access the layered window bitmap and redraw only part of it on the window? From what I understood, layered windows simply disable the WM_PAINT message and expect the user to draw the window by himself. There's obviously no way to bind the WM_PAINT to the custom drawing done.

Am I missing something very obvious?

LightStriker
  • 19,738
  • 3
  • 23
  • 27
  • @HighCore: I guess I have to ask... Which current UI Technology? Somehow, I have the feeling your answer is going to start by W and end with a F. However, I haven't found any advantages over there, since AllowsTransparency is just a shortcut to declaring a LayeredWindow. However, it might have some gain from hardware rendering, but probably not from the screen refresh, which still take a bitmap. – LightStriker Mar 19 '13 at 03:49
  • 3
    Yes, pay no attention to HighCore. He trolls the WinForms tag, posting mostly misinformed jeers at the technology, chastises people for using it, and rudely implores them to switch to WPF. It's not particularly helpful, you wouldn't have taken the time to write the question if throwing out everything you had was an option. – Cody Gray - on strike Mar 19 '13 at 04:30
  • I'm pretty sure what the documentation you quote is telling you, is that it's possible to update what is painted in the layered window and its blend value using only the `WM_PAINT` message and the `SetLayeredWindowAttributes` function. If you need to do more than that (like changing the size or shape of the window), you need to call `UpdateLayeredWindow`, which will update the entire window. Honestly, I'm not sure, I've never actually used `UpdateLayeredWindow`. I can do everything I've ever needed with `WM_PAINT` and `SetLayeredWindowAttributes`. – Cody Gray - on strike Mar 19 '13 at 04:36
  • @Lightstriker WPF has retained graphics, and only redraws the relevant parts of the window when needed, whereas winforms sucks and is slow as hell. You're right, don't pay attention to me, because I can't help you HACK thru a dead technology (which is completely useless compared to the current stuff) in order to somehow get it working. – Federico Berasategui Mar 19 '13 at 12:28
  • @CodyGray: That's what I first assumed, but there's simply no way to passed the updated bitmap region in `SetLayeredWindowAttributes`. Or I'm missing something very obvious? – LightStriker Mar 19 '13 at 12:55
  • @HighCore: Under the hood, WPF calls the same method WinForms does when it comes to updating a window. The fact that it's "fancy" and "new" doesn't change that. The advantages WPF has is that it draws it's components in hardware. When you want semi-transparent WPF, you will have to use `AllowsTransparency`, which make the WPF calls the `UpdatedLayeredWindow` method just like I'm doing. Which means the refresh of a full screen semi-transparent window will be as slow in WPF, simply the bitmap will be updated faster before being fed to the update method. – LightStriker Mar 19 '13 at 12:59
  • @LightStriker then how do you explain I have WPF applications with `AllowsTransparency` windows running in my clients' low-end PCs and performance is acceptable? – Federico Berasategui Mar 19 '13 at 14:18
  • @HighCore: Full screen? And how "acceptable" is acceptable? Can you clock a smooth 30 FPS while refreshing dozen or hundred of controls? – LightStriker Mar 19 '13 at 14:23
  • @LightStriker yes, full screen, and I don't know what you mean by "refreshing controls". My applications are CRUDs and datagrids, of course if you need things such as complex animations and stuff you need a high-end PC. That doesn't change the fact that winforms is useless. – Federico Berasategui Mar 19 '13 at 14:26
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/26472/discussion-between-highcore-and-lightstriker) – Federico Berasategui Mar 19 '13 at 15:47
  • WPF uses DirectX for rendering, rather than GDI+ like WinForms. Whether that's faster is debatable. It's unlikely to be faster on low-end hardware, where DirectX has to be rendered by software. Unfortunately, if I remember correctly, GDI+ is still not hardware accelerated in Windows 7. You have to use GDI for that, which means writing a lot of P/Invoke declarations in WinForms. Anyway yes, WPF allows lots of fancy, flashy effects. That doesn't make it "better". If it was, we'd all be writing programs in Adobe Flash. This is such a useless debate, I'm getting tired of seeing his WinForms rants. – Cody Gray - on strike Mar 19 '13 at 16:46
  • You don't need to pass in anything to `SetLayeredWindowAttributes`. All of the painting is done in response to the `WM_PAINT` message just like a normal window. The only difference is that you're getting some additional effects applied to it by calling `SetLayeredWindowAttributes`, like making it partially (or completely) transparent. All that you can do with `SetLayeredWindowAttributes` is to set a constant alpha value for the window (which governs its degree of transparency), and an optional color that will be treated as transparent. That works with the standard `WM_PAINT` infrastructure. – Cody Gray - on strike Mar 19 '13 at 16:51
  • If you need any of the other features provided by the Layered Windows API, you will need to call the `UpdateLayeredWindow` function instead. This one does update the entire window, not just a portion of it, but it is more powerful. For example, you can use the alpha channel of an image in the back buffer for transparency, rather than setting a single color value as the transparent color or applying a fixed global transparency value. Honestly, after all of the irrelevant digression into alternative UI technologies, I forgot what the point of your question was. – Cody Gray - on strike Mar 19 '13 at 16:54
  • @CodyGray: I found my problem... not that LayeredWindow were slow, but that I had a custom control calling hundred of unneeded refresh. :) – LightStriker Mar 19 '13 at 17:04
  • @CodyGray regardless of the fancy stuff, WPF is much more developer friendly than hackforms. I can put whatever UI (with edit capabilities) inside a ListBox and I don't have to resort to hackner draw. So yeah, WPF allows a lot of "fancy" stuff, such as a non-gray, non-boring UI, which doesn't resemble windows 3.1 – Federico Berasategui Mar 19 '13 at 20:32
  • 3
    Too bad its users/advocates aren't more user-friendly. – Cody Gray - on strike Mar 19 '13 at 23:29

1 Answers1

1

After long profiling, I found out it wasn't really the layered window update that was bottleneck. Refreshing the whole screen, the SelectBitmap method above, on a 1920*1200 was taking about 6-8ms. Sure, not very amazing, but plenty enough to refresh at 30 FPS+.

In my case, the performance drains was coming from some thread asking for refresh almost a hundred time per redraw, making everything sluggish. The solution was to break down the refresh/redraw and separate them. One would update (union) a region and the other, when not drawing, would take that region, draw it and then empty it.

LightStriker
  • 19,738
  • 3
  • 23
  • 27