MFC OnDraw vs. OnPaint - mfc

I am wondering about the difference between OnDraw() and OnPaint() in MFC.
After searching the Internet for a while, I found a useful article. In summary,
WM_PAINT will trigger OnPaint(), which calls OnDraw() and passes a CDC*:
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
Another article mentions that when printing a document, OnPrint() also calls OnDraw() by passing a printer DC. Therefore, by overriding OnDraw(), you get screen painting and printing both in one function, which is convenient.
I tried to put my statements for drawing in either OnDraw() and OnPaint(). Either can work well. OnDraw() is a little easier because it has already gotten a pointer pDC.

Device contexts are an ancient abstraction. They have been described as early as 1982 in the first edition of Computer Graphics: Principles and Practice (probably even earlier) and seem to confuse people to this day.
The primary purpose of a device context is to abstract peculiarities of render devices (such as displays, printers, in-memory bitmaps, etc.) and provide a coherent interface. Code that's rendering into a device context generally does not need to know, which device is ultimately consuming the render commands.
The documentation entry titled Drawing in a View goes on to explain how the system is intended to work: In short, all painting should be performed in an OnDraw override that receives a device context. The system-provided OnPaint implementation then constructs a CPaintDC and calls OnDraw.
Up to this point this seems to be just an overly complex way to render the contents of a window. Things start to make sense when you implement, say, printing support. Now all you have to do is set up a printing device context and call OnDraw. Nothing in your OnDraw implementation needs to change.

Related

Is there a safe way to paint a little extra onto a subclassed control?

Let's say you want to add a little extra graphical info to a Windows control. For example, you want to add drag/drop functionality to a listview (using the procedure discussed here), but with horizontal lines signaling the drop/insertion points as the user drags an item. (The control belongs to your own application.)
Is there a safe way to subclass the control and draw onto it directly? In my limited experimentation in trying to do this, I encountered some problems. First, it wasn't clear whether I should call BeginPaint and EndPaint during the WM_PAINT message, since the control itself would be calling those functions once the message was passed along to the default procedure. I also inevitably encountered flickering, since some areas were being painted twice.
I thought a safer way would be just to create a transparent overlay window and draw on that, since that would avoid conflicts with the default paint procedure, but I thought I'd ask before going down that road. Thanks for any advice.

Is there an XLib alternative for glXSwapBuffers()?

I want my application to instantly draw all the data to a display. In windows there is SwapBuffers() function to do such kind of things, where you can do all the drawings to a second virtual window and then swap that virtual window with an existing one. OpenGL provides a glXSwapBuffers() function to do roughly the same. However I don't want to use it. Therefore, I am curios, what are the ways to implement this functionality in pure XLib
In X11 there are the Pixmap resources which are considered as Drawable (like Window).
Then you can draw to a Pixmap using as many steps as necessary, and finally use XCopyArea() to send the resulting drawing to a Window.
Note that a Pixmap stands on the server side, like a Window, so the final copy operation is local to the server.
There is the X Double Buffer Extension: https://www.x.org/releases/X11R7.6/doc/libXext/dbelib.html
The Double Buffer Extension (DBE) provides a standard way to utilize
double-buffering within the framework of the X Window System.
Protocol: https://www.x.org/releases/X11R7.7/doc/xextproto/dbe.html
Never seen it used in practice. Let me know if you pull it off.

MFC GUI custom control: how to draw cursor updates in response to mouse moves?

I have a custom Windows control subclassed from CButton (no idea why that was selected--this is 17-year-old code; no semblance of button functionality is present).
Its DrawItem( LPDRAWITEMSTRUCT pdis ) method is called by CButton::OnChildNotify in response to WM_DRAWITEM. It renders its scene with the DC CDC::FromHandle( pdis->hDC ).
The mouse event method OnMouseMove() calculates the new cursor position and calls RedrawWindow( NULL, NULL, RDW_INVALIDATE ). A cursor that follows the mouse duly appears at the new mouse position. It works fine, but it's slow. In fact, only the previous and new cursor cells need be redrawn (if that) yet the graphic updates start to lag as the entire scene is rendered many times.
I thought in my OnMouseMove() method, instead of repainting the entire scene, could just paint the cells in question. It already has the exact X and Y coordinates of the cells and pointers to their data. I thought CPaintDC(this) would provide a DC that allowed this, but it doesn't paint. (Doesn't crash either, which is a rare joy.)
My hazy recollection is that the "optimal" way to do this would be to invalidate just the areas of the two cells, and the DrawItem() method would eventually be told these areas were invalidated, and rather than totally repainting it could just work out from the coordinates which cells they were (not an easy operation btw) and repaint them, and that would streamline not only this cursor problem but also ensure only a few cells be painted were the partially-obscured control partially revealed. But time pressure doesn't allow and the use cases don't seem to call for this to be optimized.
So the question is: is there some nice way for OnMouseMove() to re-render a single control immediately, and if so with what DC? (For instance can I cache the DC that I've received in DrawItem() via FromHandle()?
Right now the only idea I have is to have an object member pointing to a single cell to be redrawn, to call RedrawWindow() with this RDW_UPDATENOW flag, and have DrawItem(), if that flag be set, do just that one item. That would result in DrawItem() getting a DC that presumably would work in the way it always has. Seems like a real hack though, is there a better way?
In a Windows application, it is customary to perform all rendering in response to a WM_PAINT (or WM_NCPAINT) message. Code that needs to trigger a repaint marks part or all of window's client area as dirty, by calling InvalidateRect (and friends). The system is optimized for this approach, coalescing multiple requests into a single update region, and subsequently issuing a WM_PAINT message, when there is no more important work to do (like handling input).
This works reliably, and is usually easier to implement than spreading the rendering across several places. It is, however, perfectly legal to deviate from this, and perform rendering anywhere in your code. While WM_PAINT messages can still arrive at any time, it is desirable to have the out-of-band rendering produce identical visual results as the WM_PAINT handler would, to prevent visual artifacts.
All rendering goes through an abstraction called a device context (DC). When handling a WM_PAINT message in an MFC application, a suitable DC can be obtained by constructing a CPaintDC instance. When rendering anywhere else you cannot use a CPaintDC, but need to use a CClientDC instead (or a CWindowDC, to render the non-client area as well). In general, rendering code need not know, which type of DC it is rendering to, and can usually be reused without change.

OpenGL flickering/damaged with window resize and DWM active

I have a wxWidgets application that has a number of child opengl windows. I'm using my own GL canvas class, not the wx one. The windows share their OpenGL context.
I don't think the fact it is wxwidgets is really relevant here.
The opengl windows are children of a windows that are siblings of one another, contained within a tab control. Kind of an MDI style interface, but it is not an MDI window.. Each one can be individually resized. All works lovely unless Aero is enabled and the DWM is active.
Resizing any window (not even the opengl ones) causes all of the opengl windows to flicker occasionally with a stale backing-store view that contains whatever rubbish has been on the screen at that point that is not opengl. This ONLY happens with Aero enabled.
I'm pretty certain that this is the DWM not actually having the opengl contents on its drawing surface backing store and the window not being repainted at the right moment.
I've tried so many things to get round this, I do have a solution but it is not very nice and involves reading the framebuffer with glReadPixels into a DIB and then blitting it to the paint DC in my onPaint routine. This workaround is only enabled if DWM is active but I'd rather not have to do this at all as it hurts performance slightly (but not too bad on a capable system - the scenes are relatively simple 3d graphs). Also mixing GDI and opengl is not recommended but this approach works, surprisingly. I can live with it for now but I'd rather not have to. I still have to do this in WM_PRINT if I want to take a screenshot of the child window anyway, I don't see a way around that.
Does anyone know of a better solution to this?
Before anyone asks I definitely do the following:
Window class has CS_OWNDC
WM_ERASEBACKGROUND does nothing and returns TRUE.
Double Buffering is enabled.
Windows have the WS_CLIPSIBLINGS and WS_CLIPCHILDREN window styles.
In my resize event handler I immediately repaint the window.
I've tried:
Setting PFD_SUPPORT_COMPOSITION in the pixel format descriptor.
Not using a wxPaintDC in the paint handler and calling
::ValidateRect(hwnd, NULL) instead.
Handling WM_NCPAINT and excluding the client area
Disabling NC paint via the DWM API
Excluding the client area in the paint event
Calling glFlush and/or glFinish before and after the buffer swap.
Invalidating the window at every paint event (as a test!) - still
flickers!
Not using a shared GL context.
Disabling double buffering.
Writing to GL_FRONT_AND_BACK
Disabling DWM is not an option.
And as far as I am aware this is even a problem if you are using Direct3D instead on OpenGL, though I have not tested this as it represents a lot of work.
This is a longshot, but I just solved exactly this same problem myself.
The longshot part comes in because we're doing owner draw of the outline of a captionless group box that surrounds our OpenGL window (i.e., to make a nice little border), and that may not describe your case.
What we found caused the problem was this:
We had been using a RoundRect() call (with a HOLLOW_BRUSH) to draw the outline of the group box. Changing it to a MoveToEx() and LineTo() calls to ensure JUST the lines are drawn and nothing gets done inside the group box kept the GDI from trying to unexpectedly repaint the whole content of the control. It's possible there's a difference in invalidation logic (or we had a bug somehow in loading the intended hollow brush). We're still investigating.
-Noel
My app has only a single OpenGL window (the main window) but I ran into some nasty DWM tearing issues on window resize and I wonder if one of the solutions may work for you.
First of all, I found that during window resize there are at least two different bad guys who want to "help" you by modifying your client area before you have a chance to update the window yourself, creating flicker.
The first bad guy dates back to a XP/Vista/7 BitBlt inside the SetWindowPos() that Windows does internally during window resize, and can be eliminated with a trick involving intercepting WM_NCCALCSIZE or another trick involving intercepting WM_WINDOWPOSCHANGING.
In Windows 8/10 we still have that problem but we have a new bad guy, the Aero DWM.exe window manager, who will do his own different kind of BitBlt when he thinks you are "behind" updating the screen.
I suspect that the rubbish pixels you are seeing might actually be an intentional and very very poor attempt by DWM to fill in something "acceptable" while it waits for you to draw. I discovered that DWM extends the edge pixels of old client area data when it blits the new client area, which is insane.
Unfortunately, I don't know of any 100% solution to prevent DWM from doing this, but I do have a timing hack that greatly reduces the frequency of it.
For source code to the WM_NCCALCSIZE/WM_WINDOWPOSCHANGING hack as well as the DWM timing hack, please see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Hmm, maybe you have ran into the same issue: if you are using "new" MFC
it will create and application with Tabs and Window Spliter.
The splitter has some logic (I am guessing somewhere around transparent window and drawing XOR
lines for the split) that causes this behavior. Remove the splitter to confirm it resolve
your issue. If you need split functionality -- put in a different splitter.
Also Tabs allow docking and again splitting the windows that has the same issue -- remove/replace.
Good luck,
Igor

DirectX Rendering

So I've created this program to render to a window using DirectX. It has an init() method which requires a HWND object so that it can initialize DirectX to the window, and then a render() method which is called inside of an infinite-loop, and then finally a cleanup() method to release DirectX-objects and devices. However, DirectX will render a couple of frames of a rotating cube (maybe enough for a half-rotation), and then the screen will go black. Then the cube will come back on, but it is still rotating during the black period. This continues in an on...off...on...off sort of pattern. Is DirectX maybe not rendering correctly to the window? What's wrong?
From my experience I think there is a good chance you need to explicitly implement the handling of the background erase event for your window (see this page), otherwise, the default implementation will kick in and get in your way (sometime erasing what DirectX just rendered as others suggested).
But well, as everybody mentioned already: this is only a little theory, and we would need some code to check this further :-).