|
-
October 19th, 2010, 10:13 PM
#1
WM_PAINT logic
hi all,
I'm still a bit new to things here, but I am trying to understand the logic behind the processing of the WM_PAINT message in this case. I have put my comments in below, trying to make sense of it.
Code:
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
si.cbSize = sizeof (si) ;
si.fMask = SIF_POS ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
iVert = si.nPos ; //Okay, current position of vertical scroll bar
GetScrollInfo (hwnd, SB_HORZ, &si) ;
iHorz = si.nPos ; //Okay, current position of horizontal scroll bar
//See below for questions about iPaintBeg and iPaintEnd
iPnBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
iPnEnd = min (NUMLINES - 1,
iVertPos + ps.rcPaint.bottom / cyChar) ;
for (i = iPnBeg ; i <= iPnEnd ; i++)
{
x = cxChar * (1 - iHorz) ;
y = cyChar * (i - iVert) ;
// Some TextOut calls here using x and y, for printing multiple values on the same line
}
EndPaint (hwnd, &ps) ;
return 0 ;
First of all, when will iPnBeg be less than 0 in this case. Here cyChar is initialized to tm.tmHeight + tm.tmExternalLeading. It will never be zero.
The value of ps.rcPaint.top represents the start of the invalid rectangle (y coord). The lowest value it can have is 0. (Which would mean the invalid rectangle starts at the very top of the client area).
ps.rcPaint.top could never be negative, and cyChar could never be negative. Therefore the expression ps.rcPaint.top / cyChar could never be negative.
Then we add iVert. The range for most standard scroll bars I have come across begin at 0. So, the lowest value that iVert could possibly have is 0.
So, the first question is why do we need max here, if the lowest possible value the expression can evaluate to is 0?
Now, forgetting the max part of things, I am trying to understand the logic behind it. rcPaint.top / cyChar seems like another way of saying "How many rows of characters fit from the top of the client area to the top of the invalid rectangle?".
For this example, let's say that cyChar = 5, there are 75 lines to print (NUMLINES), and the page size is 50. The scroll bar ranges from 0 (showing lines 0 through 49) to 25 (showing lines 25 through 74).
Let's say the scroll bar is currently at position 20, meaning it is showing lines 20 though 69.
Let's also say the top of the invalid rectangle is (y coord) 10 and the bottom of the invalid rectangle (y coord) is 20.
So we have:
iPnBeg = (20 + 10 / 5) = 22
Now looking at iPnEnd, it is the smaller value between 74 and (20 + 20 / 5). That is, 74 and 24. So it will be 24.
So, in this case the loop will be:
for(i = 22; i <= 24; i++)
This is the only place where iPnBeg and iPnEnd are used. Why does it go through the loop 3 times, when there is only room to print 2 rows?
Why can't we do:
int y = (bottom - top) / cyChar; //evaluates to 2 in this example
for(int i = 0; i < y; i++)
//print the 2 rows
Sorry if I messed up anything fundamental in my explanation there. I've been staring at this for a while and I think my brain stopped working.
Last edited by ttrz; October 19th, 2010 at 10:31 PM.
-
October 20th, 2010, 09:57 AM
#2
Re: WM_PAINT logic
 Originally Posted by ttrz
First of all, when will iPnBeg be less than 0 in this case. Here cyChar is initialized to tm.tmHeight + tm.tmExternalLeading. It will never be zero.
The value of ps.rcPaint.top represents the start of the invalid rectangle (y coord). The lowest value it can have is 0. (Which would mean the invalid rectangle starts at the very top of the client area).
ps.rcPaint.top could never be negative, and cyChar could never be negative. Therefore the expression ps.rcPaint.top / cyChar could never be negative.
Then we add iVert. The range for most standard scroll bars I have come across begin at 0. So, the lowest value that iVert could possibly have is 0.
So, the first question is why do we need max here, if the lowest possible value the expression can evaluate to is 0?
Is any of those variables are signed integers, then regardless of what you're stating, it is possible to have negative numbers. To prevent this from happening, the max() is used to enforce that the smallest value is 0. If all of those variables involved are unsigned, then of course you can't get a negative number. That is the only reason to state that max() shouldn't be used, and that is all variables are unsigned quantities-- everything else is just theory that may make sense to you, but what if you're wrong somewhere, or something really unexpected happens on that odd computer that runs your program?
Programmers program defensively, and using max() in this case is an example. There is now no chance whatsoever of that number being less than 0. Your claims are on paper and in theory only -- it is not enforced by the program. That's why you have constructs such as assert(), exceptions, and defensive programming etc. Things may not work out the way you have planned on paper all the time.
Many (naive) programmers try to do what you're doing now -- attempting to outthink what may or may not happen, and then program assuming that things work the way they think it should work intuitively. Then they get the call from the customer that their program crashed on their system, because, for example, that number came up negative (for some reason).
Now, forgetting the max part of things, I am trying to understand the logic behind it. rcPaint.top / cyChar seems like another way of saying "How many rows of characters fit from the top of the client area to the top of the invalid rectangle?".
For this example, let's say that cyChar = 5, there are 75 lines to print (NUMLINES), and the page size is 50. The scroll bar ranges from 0 (showing lines 0 through 49) to 25 (showing lines 25 through 74).
Let's say the scroll bar is currently at position 20, meaning it is showing lines 20 though 69.
Let's also say the top of the invalid rectangle is (y coord) 10 and the bottom of the invalid rectangle (y coord) is 20.
So we have:
iPnBeg = (20 + 10 / 5) = 22
Now looking at iPnEnd, it is the smaller value between 74 and (20 + 20 / 5). That is, 74 and 24. So it will be 24.
So, in this case the loop will be:
for(i = 22; i <= 24; i++)
This is the only place where iPnBeg and iPnEnd are used. Why does it go through the loop 3 times, when there is only room to print 2 rows?
Why can't we do:
int y = (bottom - top) / cyChar; //evaluates to 2 in this example
for(int i = 0; i < y; i++)
//print the 2 rows
Sorry if I messed up anything fundamental in my explanation there. I've been staring at this for a while and I think my brain stopped working.
Use your debugger -- that's what it's there for, so that you understand why things work the way they work. If you're using Visual Studio, you can change those loop limits in the debugger to see how your program behaves.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; October 20th, 2010 at 10:34 AM.
-
October 20th, 2010, 03:27 PM
#3
Re: WM_PAINT logic
Hello Paul. Thanks for your suggestion.
I set the window size to 500 x 500 (the 6th and 7th arguments to CreateWindow).
cyChar is 16.
The first time though, the top and bottom of the invalid rectangle are 0 and 447 respectively. This represents the entire client area as it should on the first WM_PAINT message received.
iPnBegin will be 0 and iPnEnd will be 27. This means the loop will go through 28 times, although only 27 full lines can fit in the client area. I guess this is done just in case a PART of a row fits on the screen (maybe just the top part of the characters). I guess this answers the first part of my question, as to why it goes through one time more than the number of full rows that can fit on the screen. That is, if I am correct.
When I click the single down arrow on the scroll bar, iVert is equal to 1, and the top and bottom of the invalid rectangle are 431 and 447 respectively. And so:
iPnBegin = (1 + 431/16) = (1 + 26) = 27
iPnEnd = (1 + 447/16) = (1 + 27) = 28
Why doesn't the entire client area become invalidated after scroll bar moves by clicking once on the down arrow? Doesn't the entire client area need to be repainted?
I'm also trying to think about why iVert is added to the loop control variables. If the scroll bar is at its max value and the bottom of the invalid rectangle is at it's max value for the client area, NUMLINES - 1 will be used instead, which will be 1 less than the calculated value. I guess I will need to think about this some more, because it's not immediately making sense.
-
October 20th, 2010, 03:38 PM
#4
Re: WM_PAINT logic
 Originally Posted by ttrz
Why doesn't the entire client area become invalidated after scroll bar moves by clicking once on the down arrow? Doesn't the entire client area need to be repainted?
Are you using your debugger? If you are, there is a caveat that I didn't mention.
If you're debugging a WM_PAINT message, then it is advantageous to have a dual monitor setup -- one monitor has the debugger, the other has the output of your app. The reason why is that setting breakpoints in the debugger, and then running your app on a single monitor may cause the WM_PAINT message to be generated many times than necessary when your debugger pops up on the screen.
Either you have a dual monitor setup, or you have to size the app window to make sure it doesn't interfere with your debugger screen (a pain in the neck, but that's what I've used to debug WM_PAINT messages).
Regards,
Paul McKenzie
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|