Answer to yesterday’s exercise


iMin is the lowest-index element which intersects the paint rectangle, so a simple truncating division produces the desired index.

The formula for iMax can be interpreted two ways. One is that it is the roundup of the first invisible line. Recall the rectangles are exclusive of the endpoint, so rcPaint.bottom is actually the first row outside the rectangle. Since we want the first element that is completely outside the rectangle, we must round up.

A second interpretation begins with the seemingly equivalent formula. First, the expression

    (pps->rcPaint.bottom - 1) / g_cyLine

computes the index of the last visible item. By adding unity, we get the index of the first invisible item.

In both cases, we do not allow the computed value to exceed g_cItems so we don’t try to draw items that don’t exist.

The answer to the next question is that the seemingly equivalent formula does not work correctly when rcPaint.bottom is zero or negative because the result of integer division is rounded towards zero, which would result in an erroneous computation that the value of iMax should be one instead of zero. If integer divisions were rounded towards negative infinity, then the formula would be correct.

And the answer to the final question is that the only harm is that we sometimes draw one item that we really didn’t need to. In our example, this is not that big a deal since drawing an item is relatively fast. But in cases where drawing an item is expensive, avoiding the drawing of even a single item may prove significant.

And we’ll see in Part 4 that playing with the origin can cause the paint rectangle to end up in odd positions.

Comments (5)
  1. "Recall the rectangles are exclusive of the endpoint, so rcPaint.bottom is actually the first row outside the rectangle."

    MSDN Libraries for both October 2001 and October 2003 have the following characteristics.

    For GetClientRect(), the description contradicts itself. The first paragraph falsely says that the lower-right corner is involved. A later paragraph correctly says that the width and height are involved.

    For GetWindowRect(), the description is false only. The first paragraph doesn’t say, but a later paragraph says that the lower-right corner is involved.

    The fact is exactly as you say, a point which is adjacent but outside of the lower-right corner is involved.

    For one customer, I had to position a bunch of scrollbars and their related frames. Every time I resized the main window, every scroll bar grew in width. I fixed it by deliberately coding some off-by-one errors. Then I remembered your series on scrollbars, and here is your information, you got it right and the MSDN library got it wrong.

    Sometimes you hinted that you have contacts at MSDN. Please see if you can persuade them to fix the descriptions of GetClientRect() and GetWindowRect().

  2. Raymond Chen says:

    Done. I also asked for a special remark in the documentation for RECT to indicate that "By convention, the bottom right coordinates of rectangles are normally exclusive. In other words, the pixel whose coordinates are (right, bottom) lies immediately outside the rectangle." Hopefully this blanket statement will cover all the other functions that operate on rectangles.

  3. Thank you for making that request, but may I also ask you to make the English a bit clearer? Even though I handle English much better than I handle Japanese, I would still get tripped up on "normally exclusive". Backing up and reparsing the sentence yields a more meaningful result, "are normally", but the word "exclusive" isn’t quite clear. The second sentence does clear up the confusion, but still there’s no need for the first sentence to create confusion.

    Maybe "By convention, the bottom right coordinates of rectangles are usually the height and width instead of the actual bottom and right." The second sentence still helps explain it fully.

  4. Raymond Chen says:

    Except that’s still not correct. If top/left is not (0,0) then bottom and right are not the height and width. I have a blog entry scheduled for February 17th to explain rectangles better – I hope you can hold out until then.

  5. You’re right, my wording isn’t correct for GetWindowRect().

    For GetClientRect() I think my wording is correct and understandable. If you agree with that, then we can fix the wording for GetWindowRect() next. Anyway, I think it is possible to be both correct and non-obfuscatory.

    Yes I’ll wait until February 17th.

Comments are closed.