📜 ⬆️ ⬇️

15,000 FPS. Hardcore tricks part 2 - well, a non-standard window

Striving for “pixel perfect” + hiding a window from RDP!


image This is a continuation of a series of posts with the prefix "15000 FPS", beginning here: part 1 and part 1.5 . It is possible to get 15K FPS to this window, but a reasonable approach here is not to hammer FPS in the Render () cycle, but to redraw it only if necessary, and most of the rest of the work for us will be done by Windows itself. Looking at the screenshot, the first thought of the coder - “ha, yes, we all know how to make non-standard windows!”.
But the designer will suspect that something is wrong: the shadow from the window is not some kind of windowed one, and in general there are gradients and alpha blending, there were no honest 8 bits on the alpha channel. How?
And you only need Win32 API + System.Drawing.Bitmap, it will work even on Win2K with .Net 2.0 and this window scales perfectly and quickly and moves without glitches.
"Custom alpha blending windows on the desktop and does not slow down? You are joking?".

No kidding at all. So, for the beginning we will prepare a thread for the future skin of this window.
image Since the article is also published in the Design blog, I ask the designers not to kick me for the lack of Pixel Perfect — I made this cutting myself ... Starpere coders like me have time to get acquainted with music and write photoshop in Photoshop , and with a strong need, I myself easel and piano, as far as I can.
But you can immediately notice that the rounding of corners is already an order of magnitude better than native Windows ones - and this is because the Windows use the vector REGION, which is never anti-aliased, and I am a cheater.
But of course, you can achieve a better result by attracting a real discrete designer. Which of the designers will be boring further text, you can immediately follow the complete project with the source code and the sample on CodePlex at the end of the article. And to torture your coders that you have hundreds of ideas and chips for windows and controls directly from Photoshop in 32-bit PNG (just don’t tell from whom you learned about it, thanks for such adventures the coders will not tell me for sure).

As you can see, we have 4 corner elements, between which we will stretch the middle elements, and fill the main window background through:
GFX.Clear(WindowFillColor); 
Yes, our whole window is stupid System.Drawing.Bitmap, but those who have read the last parts of the article know how indifferent I am to this. The background of a window is easily assembled from pieces of skin with simple operations on Graphics.
(For the screenshot in the title, I instead used the GradientBrush fill, but this is only for the sake of the special effect in the screenshot)

I will touch on the key points in the article, and I will not describe too many details - I don’t pretend to the tutorial, I have more details on CodePlex, the source code is there, as in the previous parts - as simple as a boot I tried.
')
Immediately little cheat. In the last parts I was for the full alpha channel, and here, on the contrary. Since the original skin bitmaps do not change with each redrawing, you can safely use Premultiplied Alpha, and at the start of the application do this:
 internal static Bitmap Shadow_L = Resources.Shadow_L.Clone( new Rectangle(0, 0, Resources.Shadow_L.Width, Resources.Shadow_L.Height), PixelFormat.Format32bppPArgb); //  ..     
those. We take a piece of skin from the application's resources, and pump it once, until [the correct term is “pre-optimized for alpha?”] and then use it from the static class, and this greatly increases the FPS. Profit

How do we draw this weird window?

As in previous times, we need the Win32 API.
Need a little "magic constants":
 public const Int32 ULW_COLORKEY = 0x00000001; public const Int32 ULW_ALPHA = 0x00000002; public const Int32 ULW_OPAQUE = 0x00000004; public const byte AC_SRC_OVER = 0x00; public const byte AC_SRC_ALPHA = 0x01; public const uint WM_SYSCOMMAND = 0x0112; public const uint DOMOVE = 0xF012; public const uint DOSIZE1 = 0xF001; //... public const uint DOSIZE8 = 0xF008; public const uint SRCCOPY = 0x00CC0020; 
Some will ask: “Is Win32 really holding up on some kind of numbers?” Roughly speaking, yes. But in the majority it was solved through implicit conversions of enum. At the time of the 486th processor, the concept of “Everything is an object” would have been inadmissible wastefulness!

And some imports:
 [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll", ExactSpelling = true)] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern TBool DeleteDC(IntPtr hdc); //  .. 


The key method # 1 is when creating a window form:
 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; if (!DesignMode) cp.ExStyle |= 0x00080000; return cp; } } 
Harsh MFC coders will happily recognize the familiar “CreateParams”, but will be surprised at the style constant.
But now our window has become something of an impotent window.

Key method # 2 . Imagine that we have already drawn the view of our window in Bitmap in memory before. So that from him was a sense:
 private void AssignGFX() { IntPtr screenDc = Win32Helper.GetDC(IntPtr.Zero); IntPtr memDc = Win32Helper.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr objBitmap = IntPtr.Zero; try { hBitmap = BMP.GetHbitmap(Color.FromArgb(0)); objBitmap = Win32Helper.SelectObject(memDc, hBitmap); TSize size = new TSize(Width, Height); TPoint pointSource = new TPoint(0, 0); TPoint topPos = new TPoint(Left, Top); Win32Helper.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32Helper.ULW_ALPHA); } finally { Win32Helper.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { Win32Helper.DeleteObject(objBitmap); Win32Helper.DeleteObject(hBitmap); } Win32Helper.DeleteDC(memDc); } } 
"Take out" the content of our System.Drawing.Bitmap BMP on the window.

So that we can move and re-size the window, it's not so scary:
 private void Form1_MouseDown(object sender, MouseEventArgs e) { if (.. /*  */ ...) { Win32Helper.ReleaseCapture(); Win32Helper.PostMessage(Handle, Win32Helper.WM_SYSCOMMAND, Win32Helper.DOSIZE8, 0); } else if (eY < 28) /*   */ { Win32Helper.ReleaseCapture(); Win32Helper.PostMessage(Handle, Win32Helper.WM_SYSCOMMAND, Win32Helper.DOMOVE, 0); } } 


What about controls? Forget about normal controls, Windows will not display them on our under-window, not at all. Therefore, we create our own controls, from the interface,
 interface ISkinnableControl { void RedrawControl(Graphics GFX); } 
in which each control itself “draws” as he pleases on the GFX from the main window - I drew the buttons from the GraphicsPath, and you can also take some of the PNGs.

And how did I draw a normal Button on the screenshot? Yes, as nailed, and drew:
 for (int cnt = Controls.Count - 1; cnt >= 0; cnt--) if (Controls[cnt] is ISkinnableControl && Controls[cnt].Visible) ((ISkinnableControl)Controls[cnt]).RedrawControl(GFX); else Controls[cnt].DrawToBitmap(BMP, new Rectangle(/*W,H*/)); 
Of course, unfortunately the standard Control.DrawToBitmap () method only gives us a “screenshot”, control, but now the control is visible, although we need a little extra. backend code for dynamic updates of its view. However, the heirs of ISkinnableControl are also concerned.

Does it work fast? Damn fast. Sorts and the test application's binary as always on the CodePlex again under the MIT license, i.e. for whatever you like, for anything. I did not combine with the previous project a little different topics.

Why is this window not visible through RDP? Because when drawing it, Windows uses some kind of tricky screen overlay, so this is both fast and unavailable for RDP.
There are no glitches and black rectangles on the RDP, there is simply no window at all, all the normal windows below and above it are visible and work perfectly. I accidentally found it a few years later, after the release of the finished application, I was tired of running from the 3rd floor to the 1st and back, the users thought were scoffing, it took me 3 runs, until I remembered the overlay. Perhaps now, at the time of Win7, something has changed, and now this window is visible.

If, regarding the last article, Microsoft evangelists could only threaten with a finger: “ You shouldn’t do that, ” now they will say:
" Yes, you are generally stupid. So use the API, originally created to draw the shadow under the mouse cursor? "
Yes Yes. I found this API when mouse shadows and menus appeared in Windows. Hehe. He began to dig, as they are drawn, then it was a matter of technique and size. Well, now they’ll never take me to Microsoft for sure.

https://alphawindow.codeplex.com/
Have a nice Friday everyone!

PS Pictures on imageban.ru, in one of the Q & A on Habré it was advertised, I hope everything will be OK.

UPD : the problem with memory is solved, my jamb was. Sources and binaries updated to 0.6.

Source: https://habr.com/ru/post/165403/


All Articles