I am trying to render an animation using Windows API. My problem is that half of the screen shows the previous frame, and the bottom half shows the current frame. I thought that this would be fixed when using a double buffer, but I have inserted
BitBlt(hdc, 0, 0, iWidth, iHeight, hdcMem, 0, 0, SRCCOPY);
which I understand is a double buffer, but the horizontal division still exists. How can I fix this?
BitBlt is not synchronized to the vertical blank, so if you BitBlt to the screen at the same time the hardware is rendering to the display, it may tear. Double-buffering with BitBlt reduces the likelihood of tearing but does not eliminate it. To eliminate it, you need to do your BitBlt during the vertical blank period.
GDI BitBlt wasn't designed for rapid, continuous animation. Whether you get tearing or not may depend on your hardware and drivers.
There are other graphics APIs, like Direct2D and Direct3D (and the deprecated DirectDraw) which will let you synchronize to the vertical sync.
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.
I have a MFC application with toolbars (using CMFCToolbar). I create the toolbar bitmap on the fly using bitmaps from files and resources. The DIBs have different color formats.
So I create an empty bitmap toolbar image compatible to screen DC.
Then I open all the bitmaps and blit the content to the toolbar bitmap (GDI does colorspace conversion and stretching for me).
Then I save the bitmap to a 24-bit DIB file.
Then I create the toolbar object and load the image.
That has worked for ages and is working now except for one case:
Recently we had to enable GDI scaling for Windows 10 1703 and later.
On a system with high resolution display and 200% scaling (like Surface) the following effect occurs:
All toolbar icons are distorted.
I also found the reason:
When saving the composed image I only get the top-left quarter of the image.
Width and height of the bitmap did not change (say 1024x15) compared to normal resolution display without GDI scaling. But that bitmap only contains the pixels of the top-left quarter (see example below).
So I assume the device context tells Windows about 200% scaling. When blitting from source to target the image gets scaled up automatically but the dimension of the bitmap does not change.
How can I save the unscaled bitmap?
-or-
How can I correctly save the scaled bitmap? Where to get the missing pixels? Where to get proper dimensions? (HBITMAP refers only to unscaled dimensions).
Example:
no GDI scaling, correct:
200% scaling, same dimensions, but only top-left quarter of the correct image:
Summary and solution:
Let's say we create a memory bitmap compatible to screen format (DDB):
CBitmap toolBitmap;
toolBitmap.CreateCompatibleBitmap (pDC, 1000, 20);
Later we blit something into the memory bitmap (does not matter here). Now we want to save the bitmap (write as DIB to file).
Although we know the dimensions (here: 1000x20) we should not use them. Because on Window 10 and process has GDI scaling activated and high resolution display with scaling is used - the dimensions might have changed internally. Thus the bitmap is not 1000x20 anymore.
This one fails:
BITMAP bmHdr;
toolBitmap.GetObject(sizeof(BITMAP), &bmHdr);
The Bitmap header contains the original dimensions (1000x20). Using them for saving to file results in incomplete image. Only upper left part will be stored.
This one works - we can retrieve scaled dimensions:
BITMAPINFO bi = {};
bi.dwSize = sizeof(bi);
int result = GetDIBits(pDC->GetSafeHdc(), (HBITMAP)toolBitmap.GetSafeHandle(), 0, 0, NULL, &bi, DIB_RGB_COLORS);
Now we can proceed with new dimensions.
I ended up using GDI+ functions, which also save the complete (scaled) bitmap:
Gdiplus::Bitmap bm((HBITMAP)toolBitmap.GetSafeHandle(), NULL);
Gdiplus::Status status = bm.Save(pwszFileName, &clsidEncoder, NULL);
I presume there are tons of old MFC and GDI code which will not work correctly with activated GDI scaling on Windows 10.
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.
Using a double-buffered screen with the Simple DirectMedia Layer, is it more efficient to call SDL_UpdateRect once after blitting multiple images or to call it once after blitting each individual image before calling SDL_Flip? In other words, will SDL_UpdateRect cause the screen to be updated immediately, or does it simply tell the Simple DirectMedia Layer which areas must be updated when the screen is flipped? How should it typically be used with a double-buffered screen?
For reference, here is the description of SDL_UpdateRect.
Makes sure the given area is updated on the given screen. The
rectangle must be confined within the screen boundaries (no clipping
is done).
If 'x', 'y', 'w' and 'h' are all 0, SDL_UpdateRect will update the
entire screen.
Yes, SDL_UpdateRect update screen (or rect) immediately.
No need to use the SDL_UpdateRect and SDL_Flip jointly, they do the same things but different ways.
The difference is that SDL_UpdateRect can update only part of the screen (and work by copying pixels from your surface to screen), and SDL_Flip refresh the entire screen (and works by flip buffers).
So, if you use double-buffered screen no need to call SDL_UpdateRect, just call SDL_Flip after all blits.
http://www.libsdl.org/docs/html/sdlsetvideomode.html
Calling SDL_Flip will flip the buffers and update the screen. All
drawing will take place on the surface that is not displayed at the
moment. If double buffering could not be enabled then SDL_Flip will
just perform a SDL_UpdateRect on the entire screen.
And don't forget that double buffering works only with SDL_HWSURFACE and SDL_FULLSCREEN video mode.
Does anybody have any pointers on how to successfully draw a bitmap that has
an alpha channel using Graphics::DrawImage() when the Graphics context is
created based on a printer HDC? The printer drivers don't generally support alpha blending - so is there an alternative to rendering everything to an offscreen bitmap and just sending that to the printer. This is often not feasible, especially for high res printing to large format printers.
What kind of printer is that? Regular printers don't print white. Create in-memory image and 'flatten' it (remove alpha channel) and then print the result.
Have you tried drawing a white rectangle to initialize the image before you call the DrawImage method?
The whole point is that I need the line-drawn graphics behind the image to be visible. I did try filling the rectangle first the with RGBA color of (255, 255, 255, 0) but this does not help. Pixels with an alpha value of zero do get printed as fully transparent but partially transparent pixels are drawn fully opaque.
Thanks for asking this question because I was just thinking of perhaps trying to use GDIplus to see whether it could get me around the problems I'm still facing getting patterned diamond shapes to print correctly. Although nowadays alpha-blending does appear to work on most printers, there are still some that draw black corners on the diamonds.
Aside from alpha-blending, I've also tried using diamond-shaped clip regions to surround the shape, but normally the printers that don't support alpha-blending don't seem to support polygonal clip-regions either. I've tried copying from the printer-dc into a bitmap to prime it before drawing the diamond on top, hoping that this will allow me to put back (in the corners) what was there before. This doesn't work either because it appears that the problem boils down to the fact that the printer driver doesn't actually know what is being printed on what part of the page.
In my case, my next plan is to try using a large bitmap brush for drawing the diamond fill directly to the printer hdc. I suspect there's a moderate chance that this too will fail for certain printers. It sounds like it may not be an option for what you were doing.