Aero glass causes alot of people problems trying to draw on it. Anything with an alpha value of 255 seems to be treated as transparent with DWM using an additive blur to draw it. I want a part of client area to use Aero glass with the rest of it treated as opaque, so I don't have to deal with the headache of common controls not rendering properly.
MSDN lists a function DwmEnableBlurBehindWindow which lets you mark part of the client area as blurred by DWM. It takes a DWM_BLURBEHIND pointer which which has an HRGN handle to the region of the window. When I use this function, the entire window becomes transparent with an additive blend, but only the region of the window I passed to DwmEnableBlurBehindWindow gets blurred. Is there a way I can keep the rest of the window from becoming transparent?
What I have looks a bit like:
blur.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
blur.hRgnBlur = CreateRectRgn(0, 0, 90, 90);
blur.fEnable = true;
DwmEnableBlurBehindWindow(hwnd, &blur);
RECT rect;
GetClientArea(&rect);
FillRect(hdc, &rect, CreateSolidBrush(0));
From the MSDN Library article:
The alpha values in the window are honored and the rendering atop the blur will use these alpha values. It is the applications responsiblity for ensuring that the alpha values of all pixels in the window are correct. Some GDI operations do not perserve alpha values so care must be taken when presenting child windows as the alpha values they contribute are unpredicitable.
Make that most GDI operations, like FillRect(). The brush you created is drawn with 24-bit colors, the alpha will be 0. Which makes the window transparent. You'll need to switch to, say, GDI+. Text is particularly troublesome. As well as legacy Windows controls, like EDIT and LISTBOX which draw with GDI.
Related
So I followed a few tutorials on how to draw on window using windows.h library, the part of the tutorial that I don't really understand is the bitmap part. They used createbitmap() and StrechBit() functions to draw on window. Do window references bitmap to draw pixels on the screen accordingly and bitmap is basically a chunk of memory large enough to store pixel's position and color value. If so, does bitmap automatically generate every time you created a window, because it seems that you don't really need to declare bitmap or use createbitmap() functions to type word on the window you created, you only need to create bitmap when you want to draw a custom pixel.
A window will receive the WM_PAINT message when it needs to be painted. This can happen because InvalidateRect was called, the window was resized etc.
Where the pixels are stored ("in" the HWND) is an implementation detail you don't have to worry about. On some versions/configurations the GDI functions are hardware accelerated and the result might be stored directly in the GPU, in others everything might be implemented in software and run on the CPU. When using a layered window I'm guessing everything older than Vista will use an internal bitmap to store the pixels.
GDI/GDI+ is the classic way to draw windows. If you need per-pixel alpha transparency you would draw to a bitmap and call UpdateLayeredWindow, otherwise you would just draw using any GDI function you want in WM_PAINT. This might include drawing one or several bitmaps, text, and lines/curves directly to the HWNDs HDC. As this can cause flicker in certain cases (if any area is drawn to more than once in one paint cycle), so people might draw to their own bitmap first and then BitBlt this bitmap to the window, this is called double-buffering.
The new way to draw is Direct2d/DirectComposition.
When resizing down in DirectX I noticed a flicker along the bottom/right edges.
If you fix the size of the swapchain (in red), and set the win32 background to green you will get the following on a resize down:
It appears that the window size lags behind the mouse position ("Drag Rectangle") but the area DirectX fills (in red and black) matches the Drag Rectangle, with the remainder to be painted with the window background color (green).
The DirectX Utility Toolkit (DXUT) didn't have this problem. Experimenting with their settings I found that setting DXGI_SWAP_CHAIN_DESC::SwapEffect = DXGI_SWAP_EFFECT_DISCARD would just paint the screen green (the window background color) during a sizemove. I assume that DXUT has their own special handling for when win32 enters a modal sizemove loop in order to display DirectX content during a sizemove.
DXGI_SWAP_EFFECT_DISCARD is part of the old blit mode presentation model. Notably it cant have DXGI_SWAP_CHAIN_DESC1::Scaling = DXGI_SCALING_NONE the scaling must be stretched to the window size. So my best guess is this behavior is due to the implementation of the flip presentation model (it flips a rectangle too small (black) and then covers by painting the rest green).
Does anyone know how to stop the win32 background from showing?
Edit:
Thanks to IInspectable! I can confirm the WS_EX_NOREDIRECTIONBITMAP extended style works: it stops the artifact from the win32 background showing
I reproduced the exact same behavior when using CreateSwapChainForComposition() with DirectComposition as I got using CreateSwapChainForHwnd().
This means that there are two path for pixels to get to the screen. The green pixels are going through the Redirection Surface. Hence, explicitly requesting WS_EX_NOREDIRECTIONBITMAP prevents any drawing through a Redirection Surface stopping the win32 background from showing.
The other path is the flip presentation behavior which is how the red and black pixels are get displayed. Hence, not using flip presentation when resizing also stops the win32 background from being shown.
There must be a bug with DWM: when there is a Redirection Surface, the clipping to prevent the swapchain's contents extending past the window is smaller than the redirection surface allowing it to be seen along the bottom/right edges.
There are two observations of interest:
when using WS_EX_LAYOUTRTL or manually positioning a IDCompositionVisual to the right edge of the screen with GetClientRect(), the swapchains contents are correctly position, but still clipped.
when using WS_EX_NOREDIRECTIONBITMAP the window`s nonclient area lines up with the contents of the swapchain rather than the swapchains contents being clipped
These observations would seam to imply that the cause of the issue is that DWM is using it's prefered size for the window size sometimes and the size of the redirection surface othertimes.
When reading source code which draws lines in Windows using GDI it is relatively common to see FillRect() being used despite the only purpose being to draw a line. But the end product of drawing a line with a width value and a filled rectangle are quite similar aren't they?
FillRect() is 1 function call, Using MoveToEx & LineTo requires 2
Which is more efficient when needing to draw a line, using FillRect() or MoveToEx() and LineTo()?
In the most common cases, FillRect will do the same thing as MoveToEx and LineTo for perfectly horizontal and vertical rectangles. Nowadays, there are so many layers of indirection between GDI and the screen that the performance difference is almost certainly not relevant.
Drawing operations in GDI typically depend on the current "state" of the device context (DC). Lines are drawn with whichever pen is currently selected into the DC. The pen determines the color, style (solid, dashed, etc.), thickness, end caps, etc.
FillRect, however, doesn't depend on much of the DC state. All drawing primitives depend on the mapping mode and clipping region, but, unlike lines, FillRect doesn't even depend on the selected brush, since you get to provide one right in the call.
Changing state (which objects are selected into the DC) can be a lot of work. If you know you want a horizontal line, 2 pixels thick, in blue, it's a tad easier to use FillRect than to first create a pen, select it into the DC, draw your line, select the pen back out, and then decide how to manage the lifetime of that pen (when do you delete it?). If the rest of the drawing is a bunch of dashed yellow lines with round endcaps, not having to keep switching state can make the code simpler.
What color must i paint in the client area in order to make glass appear?
i've extended the frame of my form into the client area using:
DwmExtendFrameIntoClientArea(self.Handle, margins);
i cannot find any official documentation from Microsoft on what color and/or alpha the DWM will look for to replace with glass. The documentation on DwmExtendFrameInClientArea doesn't even mention that a custom color is required. There's only hearsay and myth that a special color is even required.
The closest i can find is a topic on MSDN:
Custom Window Frame Using DWM
For the extended frames to be visible, the regions underlying each of the extended frame's sides must have pixel data that has an alpha value of 0.
Update: And a blog post:
Windows Vista for Developers – Part 3 – The Desktop Window Manager
It so happens that the bit pattern for RGB black (0x00000000) is the same as the bit pattern for 100% transparent ARGB so you can actually draw with “black” GDI brush and assuming you’ve instructed the DWM to blur the painted area, the result will be the desired glass effect.
If i take what they say literally (pixel data with an alpha value of zero), i construct a color with zero alpha, and paint that in the extended area:
Color fillColor = Color.FromArgb(0, 0, 0, 0); //(a, r, g, b)
e.Graphics.FillRectangle(new SolidBrush(fillColor), e.ClipRectangle);
but the glass effect does not appear:
If i ignore the quoted MSDN topic, and instead use fully opaque black (rather than a completely transparent black):
Color fillColor = Color.FromArgb(255, 0, 0, 0); //(a, r, g, b)
e.Graphics.FillRectangle(new SolidBrush(fillColor), e.ClipRectangle);
the glass effect does appear:
i am then lead to believe that opaque black is the pixel value that the DWM will look for to replace with glass.
But then how do i paint black items on the glass area?
i've tested painting a black rectangle on the glass area, with a circle next to it. Oddly enough, the rectangle doesn't appear, while the circle does appear; both are the same color:
Brush b = new SolidBrush(Color.FromArgb(255, 0, 0, 0));
e.Graphics.FillRectangle(b, 11, 11, 32, 32);
e.Graphcis.FillEllipse(b, 43, 11, 32, 32);
So what in the world is going on? What is the proper color to paint in the extended frame area to make glass appear?
Update 2
Using Adisak's suggestion to isolate exactly where the stupidness of Aero lives, here i draw a black rectangle inside the black circle:
Does FillEllipse not support drawing black circles?
Update 3
Pondidum wondered if calling Graphics.Clear with a transparent black color would make the glass visible:
e.Graphics.Clear(Color.FromArgb(0,0,0,0));
It does work, but you still can't draw opaque black items on the glass:
Update 4
Looking at Microsoft's Vista Bridge Library (managed wrappers around Vista functionality that won't be added to .NET), they only ever manage to get extended glass frame working on WPF forms, not WinForms.
See also
Aero: How to draw ClearType text on glass?
Aero: How to draw solid colors on glass?
Color fillColor = Color.FromArgb(0, 0, 0, 0); //(a, r, g, b)
e.Graphics.FillRectangle(new SolidBrush(fillColor), e.ClipRectangle);
This is actually rather amusing. It means that you are drawing something completely transparent - so this changes absolutely nothing! :-)
Guess: if black (0,0,0) should mean "glass", how about drawing (1,1,1) to get (almost) black?
One of the blog posts that you linked to above has a discussion about this. The native solution was to use SetLayeredWindowAttributes to switch the color key away from black.
Try setting the TransparencyKey of the form to Color.FromArgb(1,1,1) (or some other suitable value of your choosing) then setting the backcolor of the form (or the part you want to be glass) to that same value.
That's how I got it to work without it making all of my black text transparent/glass.
I couldn't ever figure out how to paint the "glowy" text on the glass though. It always had a black rectangle behind it.
i have created a Bitmap using GDI+.I am drawing text on to that bitmap using GDI Drawtext.Using Drawtext i am unable to apply tranparency.
Any help or code will be appreciated.
If you want to draw text without a background fill, SetBkMode(hdc,TRANSPARENT) will tell GDI to leave the background when drawing text.
To actually render the foreground color of the text with alpha... is going to be more complicated. GDI does not actually support alpha channels all that widely in its APIs. Outside of AlphaBlend actually all it does is preserve the channel. Its actually not valid to set the upper bits of a COLOREF to alpha values as the high byte is actually used for flags to indicate whether the COLOREF is (rather than an RGB value) a palette entry.
So, unfortunately, your only real way forward is to:
Create a 32bit DIBSection. (CreateDIBSection). This gives you an HBITMAP that is guaranteed to be able to hold alpha information. If you create a bitmap via one of the other bitmap creation functions its going to be at the device colordepth that might not be 32bpp.
DrawText onto the DIBSection.
When you created the DIBSection you got a pointer to the actual memory. At this point you need to go through the memory and set the alpha values. I don't think that DrawText is going to do anything to the alpha channel by itself at all. Im thinking a simple check of the RGB components of each DWORD of the bitmap - if theyre the forground color, rewrite the DWORD with a 50% (or whatever) alpha in the alpha byte, if theyre the background color, rewrite with a 100% alpha in the alpha byte. *
AlphaBlend the bitmap onto the final destination. AlphaBlend requires the alpha channel in the source to be pre-multiplied.
*
It might be sufficient to simply memset the DIBSection with a 50% alpha before doing the DrawText, and ensure that the BKColor is black. I don't know what DrawText might do to the alpha channel though. Some experimentation is called for.
SIMPLE and EASY solution:)
Had this problem, i tried to change alpha values and premultiply, but there was another problem - antialiased and cleartype fonts where not shown correctly (ugly edges). So what i did...
Compose your scene (bitmaps, graphics, etc.)
BitBlt required rectangle from this scene (same as your text rectangle, from the place where you want your text to be) to memory DC with compatible bitmap selected at 0,0 destination coordinates
Draw Your text to that rectangle in memory DC.
Now AlphaBlend that rectangle without AC_SRC_ALPHA in the BLENDFUNCTION and with desired SourceConstantAlpha from this memory DC back to your scene DC.
I think You got it :)
Hmmmm - trying to do same here - wondering - I see that when you create a dib section youi specify the "masks" that is a R,G,B (and alpha) mask.
IF and thats a big if it really does not alter the alpha chhannel, then you might specify the mask differently for two bitmap headers. ONe specifies thr RGB in the proper places, the other makes them all have their bits assigned to the alpha channel. (set the text color to white in this case) then render in two passes, one to load the color values, the other to load the alpha values.
???? anyway just musing :)
While this question is about making text semi-transparent, I had the opposite problem.
DrawText was making the text in my layered window (UpdateLayeredWindow) semi-transparent ... and I didn't want it to be.
Take a look at this other question ... since in the other question I post some code that you could easily modify ... and is almost exactly what Chris Becke suggests in his answer.
A limited answer for a specific situation:
If you have a graphic with alpha channel and you want to draw opaque text over a locally opaque background, first prepare your 32 bit bitmap with 32 bit brushes created with CreateDIBPatternBrushPt. Then scan through the bitmap bits inverting the alpha channel, draw your text as you normally would (including SetBkMode to TRANSPARENT), then invert the alpha in the bitmap again. You can skip the first inversion if you invert the alpha of your brushes.