When playing with this proggie (http://masm32.com/board/index.php?topic=94.msg51060#msg51060), I noticed somewhat unexpected behaviour. This is the paint handler (PtDC is the plot DC, i.e. canvas, OnP0 is the procedure that does user-defined graphics):
CASE WM_PAINT
invoke BeginPaint, hWnd, addr ps
push eax
mov edi, offset apStruct
mov PtDC, APs.apMemDC
.if GuiSized ; trigger complete repaint only after WM_SIZE, otherwise use memdc bitmap
invoke SelectObject, PtDC, wcx.hbrBackground
invoke PatBlt, PtDC, 0, 0, APs.apRectG.right, APs.apRectG.bottom, PATCOPY
call OnP0 ; Event Paint
and GuiSized, 0
.endif
pop eax
invoke BitBlt, eax, 0, 0, APs.apRectG.right, APs.apRectG.bottom, PtDC, 0, 0, SRCCOPY
invoke EndPaint, hWnd, addr ps
Works perfectly when a) resizing the window, b) moving it around. When parts of the the window move beyond the desktop, it gets repainted when moving back. So far, so good.
The unexpected behaviour is that when moving another window over this one, there are traces of unpainted areas left. Either the BitBlt doesn't copy everything (unlikely), or the destination DC doesn't catch the uncovering by the "other" window properly. As if WM_PAINT messages got lost somewhere.
This is particularly accentuated when dragging the "other" window near the top or bottom. The window manager (this is Win7-64) suggests then to enlarge the window vertically, the whole desktop gets a dark blue overlay. And in this very moment, it seems the paint handler doesn't handle it :(
Similar behaviour for professional stuff like Thunderbird or MS Word, when dragging the top window while the desktop turns blue, you can notice temporarily unpainted areas. They get painted shortly after, as if there was a WM_TIMER event taking care of that.
Is a timer the right way to deal with it? Has anybody else stumbled over this phenomenon?
P.S.: Found this in an old 2000 article by JM Newcomer (http://www.codeproject.com/Articles/587/Time-is-the-Simplest-Thing) - I wonder if newer Windows versions still do it exactly like this:
QuoteWhen there are no other messages in the message queue, and no WM_TIMER to send, Windows simulates a WM_PAINT message! It is never, ever in the queue.
seems to be too many code in the wm_paint message.
Try to prepare a dc before show it.
sample here:
http://masm32.com/board/index.php?topic=1917.msg19916#msg19916
first, you are trying to control the update region from within the WM_PAINT handler
second, you are ignoring the update region specified in the ps.rcPaint RECT structure
rather than doing it that way,
use InvalidateRect with an lpRect pointer to tell it what portion to update
and, in the paint handler, use only ps.rcPaint to determine which area to re-draw
Quote from: ToutEnMasm on October 24, 2015, 02:09:10 AMseems to be too many code in the wm_paint message.
One BitBlt is "too many" code??
QuoteTry to prepare a dc before show it.
That's what the code does.
Quote from: dedndave on October 24, 2015, 02:18:26 AM
first, you are trying to control the update region from within the WM_PAINT handler
second, you are ignoring the update region specified in the ps.rcPaint RECT structure
rather than doing it that way,
use InvalidateRect with an lpRect pointer to tell it what portion to update
and, in the paint handler, use only ps.rcPaint to determine which area to re-draw
Yep, that sounds good :t
In practice, it would mean that when dragging the small top window over my window, multiple paint events would be triggered, with only small rectangles to be updated via BitBlt. My current method always triggers a full BitBlt; I've implemented a WM_TIMER based solution that works fine, but what you suggest is closer to the manual, so I will give it a try. Thanks :icon14:
i typically go for a comprimise in speed vs simplicity
i create a memory DC and draw the entire contents of the client
then, only update the device DC according to ps.rcPaint
while, in some cases, the code could be faster by only updating the partial memory DC,
it doesn't save much because drawing in a memory DC is very fast - it's drawing into a device DC that is slow
and, in some cases, the code required to update a partial memory DC would be very complex
also - there are times when it cannot be done (StretchBlt, for example - hard to make it work correctly)
this makes for simple, fast code....
wmPaint PROC USES EBX ESI EDI hWnd:HWND
LOCAL ps :PAINTSTRUCT
LOCAL hbmpMem :HBITMAP
;------------------------------
INVOKE BeginPaint,hWnd,addr ps
xchg eax,ebx ;EBX = ps.hdc
INVOKE CreateCompatibleDC,ebx
xchg eax,esi ;ESI = hdcMem
INVOKE CreateCompatibleBitmap,ebx,rcClient.right,rcClient.bottom
INVOKE SelectObject,esi,eax
mov hbmpMem,eax
;at this point, prepare the entire client area in the memory DC
mov ecx,ps.rcPaint.right
mov edx,ps.rcPaint.bottom
mov eax,ps.rcPaint.left ;EAX = left
mov edi,ps.rcPaint.top ;EDI = top
sub ecx,eax ;ECX = width
sub edx,edi ;EDX = height
INVOKE BitBlt,ebx,eax,edi,ecx,edx,esi,eax,edi,SRCCOPY
INVOKE SelectObject,esi,hbmpMem ;EAX = hbmpCompat
INVOKE DeleteObject,eax
INVOKE DeleteDC,esi
INVOKE EndPaint,hWnd,addr ps
ret
wmPaint ENDP
(rcClient is updated in the WM_SIZE handler)
Quote from: dedndave on October 24, 2015, 03:48:16 AMthis makes for simple, fast code...
That is basically (pun intended) what I am doing. Except that I BitBlt the whole memdc to the device dc - the partial option fails half of the time (it works when dragging slowly). It's not a big deal anyway, the blitting takes typically less than a millisecond, while re-building the memdc must be done only on resize events, and it takes about 15 ms.
I attach the latest version, which works with a timer. It seems everybody does it that way, MS Word, Thunderbird, Acrobat Reader show the same odd behaviour when dragging a small window over them in this 'blue screen' mode close to the upper border. In this moment, i.e. while dragging a window over another, apparently no timer messages are being passed to the underlying window, and it stops updating.
i have never had any trouble with that code, and have used it in several places (probably 100 programs or more)
i suggest, that if it does not work correctly, something else is wrong :P
Can you post a working example? Just to make sure it isn't my video driver :(
i think you'll find a few "dynamic" examples in this thread...
http://masm32.com/board/index.php?topic=1969.15 (http://masm32.com/board/index.php?topic=1969.15)
i wouldn't suspect the driver, though
i was suggesting maybe something else is amiss with your code :P
Quote from: dedndave on October 24, 2015, 01:05:57 PM
i think you'll find a few "dynamic" examples in this thread...
Nice examples, but when you take away the dynamic generation of a fresh bitmap, i.e. you do only the BitBlt of the existing bitmap, they show the same behaviour when you drag a little window over them. In fact, dragging a window over poly3 just erases the curve, i.e. no updating...
The "standard" handling, correct me if I'm wrong, is that an app draws on the memdc once, and from then on (except if there is a need to produce new, different content) the WM_PAINT handler just BitBlts the memdc onto the devicedc. We are arguing basically if that BitBlt should copy the complete bitmap, or only the parts marked by the OS for repainting. My experience with the "partial" blitting is not the best, and complete blitting costs about 1 millisecond.
So the only reason to go for partial blitting would be a demo that it works. What I see, though, is that professional apps work with timers, which puzzles me because it goes against the theory that the OS tells you through the WM_PAINT message which rectangles need repainting.
To see the difference, take PolyBezier code of reply #54 (http://masm32.com/board/index.php?topic=1969.msg20895#msg20895) and modify as follows:
INVOKE BeginPaint,hWnd,addr ps
mov edi,uScnHeight
mov esi,uScnWidth
if 0 ; JJ - no artefacts
sub edi,rcMainClient.bottom
sub esi,rcMainClient.right
sar edi, 1
sar esi, 1
INVOKE BitBlt,ps.hdc,0,0,rcMainClient.right,rcMainClient.bottom,hdcBezier,esi,edi,SRCCOPY
; deb 1, "Test", eax, $Err$(), rcMainClient.right,rcMainClient.bottom
else ; original: artefacts when dragging smaller window over the canvas
mov ecx,ps.rcPaint.right
mov edx,ps.rcPaint.bottom
mov eax,ps.rcPaint.left ;EAX = window hdc left
mov ebx,ps.rcPaint.top ;EBX = window hdc top
sub edi,rcMainClient.bottom
sub esi,rcMainClient.right
sub ecx,eax ;ECX = update width
sub edx,ebx ;EDX = update height
sar edi,1 ;EDI = image origin top
sar esi,1 ;ESI = image origin left
; deb 1, "Test", eax, ebx, ecx, edx, esi, edi
INVOKE BitBlt,ps.hdc,eax,ebx,ecx,edx,hdcBezier,esi,edi,SRCCOPY
endif
INVOKE EndPaint,hWnd,addr ps
When searching around for more evidence, I found an example by MichaelW in the old forum (http://www.masmforum.com/board/index.php?topic=13204.msg102538#msg102538):
invoke BeginPaint, hwndDlg, ADDR ps ; http://www.masmforum.com/board/index.php?topic=13204.msg102538#msg102538
usebm2=0 ; 1=use 2nd bitmap, 0=don't
if usebm2
invoke CreateCompatibleBitmap, ps.hdc, clientW, clientH
push eax
invoke SelectObject, ps.hdc, eax
push eax
endif
invoke BitBlt, ps.hdc, 0, 0, clientW, clientH, hdcBM, 0, 0, SRCCOPY
if usebm2
pop eax
invoke SelectObject, ps.hdc, eax
pop eax
invoke DeleteObject, eax
endif
invoke EndPaint, hwndDlg, ADDR ps
I've added the usebm2=? for testing, and it turns out that there is no difference on Win7-64 ::)
Furthermore, I found this intriguing remark made by a prominent member of this forum:
If the bErase parameter is TRUE for any part of the update region, the background is erased in the entire region, not just in the specified part. (http://www.masmforum.com/board/index.php?topic=17636.msg148567#msg148567)
Does that imply if one pixel is marked for background delete, then the whole client area has to be repainted??
P.S.: Once upon a time, an evil extraterrestrial psychopath named Baltoro suggested:
You can call GetUpdateRect to determine if there is an update region (http://www.masmforum.com/board/index.php?topic=17309.msg145181#msg145181). However, MSDN states (https://msdn.microsoft.com/en-us/library/dd144943%28v=vs.85%29.aspx):
QuoteThe update rectangle retrieved by the BeginPaint function is identical to that retrieved by GetUpdateRect.
BeginPaint automatically validates the update region, so any call to GetUpdateRect made immediately after the call to BeginPaint retrieves an empty update region.
New version attached, needs MB 25 Oct 2015 (http://masm32.com/board/index.php?topic=94.0).
Painting is reliable, cpu usage very low, no leaks.
the PolyBezier programs work perfectly on my machine
if you are doing something that is CPU intensive, it may be delayed
WM_PAINT is a low priority message, only dispatched when nothing else is in the queue
so, if you are crunching on some other message, it will be delayed
i can see where that might cause remnants
i recall some OpenGL programs that stuck intensive things in the message loop
Quote from: dedndave on October 26, 2015, 02:40:03 AM
the PolyBezier programs work perfectly on my machine
There is nothing wrong with your programs, Dave :t
Runs just fine on Win7-64:
lea ecx, pmc
invoke GetProcessMemoryInfo, rv(GetCurrentProcess), ecx, PROCESS_MEMORY_COUNTERS
deb 4, "wss", pmc.WorkingSetSize
.if !flag
inc flag
For_ ct=0 To 999
invoke CreateCompatibleBitmap, APs.apDC, APs.apRectG.right, APs.apRectG.bottom ; hDC, not memDC, otherwise it's black & white
.Break .if !eax
Next
deb 4, "Out", ct, eax, $Err$(), APs.apDC, APs.apRectG.right, APs.apRectG.bottom
.endif
lea ecx, pmc
invoke GetProcessMemoryInfo, rv(GetCurrentProcess), ecx, PROCESS_MEMORY_COUNTERS
deb 4, "wss", pmc.WorkingSetSize
No errors whatsoever, 1000 bitmaps get created, each 888x609 pixels, about 1.6 MB each. The working set could barely handle a single one...
This stuff is badly documented. There is a vaguely related thread here (http://www.asmcommunity.net/forums/topic/?id=8610), involving inter alia Bogdan and Ultrano, in case somebody is interested.
Out
ct 1000
eax 328043
$Err$() Operazione completata.
APs.apDC -67041261
APs.apRectG.right 888
APs.apRectG.bottom 609
wss pmc.WorkingSetSize 2732032
P.S.: At 1975 iterations, CreateCompatibleBitmap returns zero but no error; afterwards, the system is very sluggish, but it eventually recovers.