none
How should I interpret the invalid region generated in case 5 in the code below, where the clipping rectangle doesn't intersect the scrolling rectangle, but is close enough to it?

    Question

  • To better understand what I'm asking above, you should run the code below, which simulates the following cases:

    1. The entire window client area is scrolled (no scrolling and no clipping rectangle)

    2. Scrolling rectangle (s_ScrollRect2) and no clipping rectangle

    3. Scrolling rectangle (s_ScrollRect3) and clipping rectangle (s_ClipRect3) entirely contained in the scrolling rectangle.

    4. Scrolling rectangle (s_ScrollRect4) and clipping rectangle (s_ClipRect4) which intersects s_ScrollRect4, but is not contained in it.

    5. Scrolling rectangle (s_ScrollRect5) and clipping rectangle (s_ClipRect5) which doesn't intercept s_ScrollRect5, but is close enough to it.

    6. Scrolling rectangle (s_ScrollRect6) and clipping rectangle (s_ClipRect6) which doesn't intercept s_ScrollRect6, and is not close enough to it.

    The following colors are used to illustrate the rectangles and invalid regions:

    White - window client area

    Yellow - scrolling rect

    Green - clipping rect

    Red - invalid regions

    To start the drawings, click with the left mouse button in the window client area and wait for a beep. To see the next drawing, click again with the left mouse button and wait for the beep, and so on.

    If at any point (after the beep) you want to start from the beginning, click with the mouse right button to clear the client area and start the process again with a new left mouse click.

    My problem is to find a reasonable interpretation for the invalid region in case 5. For all other cases, my interpretation for those regions, when they exist, is that they are formed by the "uncovered" pixels, either in the window client area, or in the clipping rectangle, after the scrolling operation.

    #include <Windows.h>
    
    #define SCROLLWINDOWEX
    
    LRESULT CALLBACK		AppProc(HWND, UINT, UINT, LONG);
    
    namespace
    {
    	int s_NumberOfLeftMouseClikcs;
    	
    	RECT s_ScrollRect2 = { 0, 0, 100, 100 };
    	RECT s_ScrollRect3 = {  0, 0, 100, 100 };
    	RECT s_ClipRect3 = { 50, 50, 70, 70 };
    	RECT s_ScrollRect4 = {  0, 0, 100, 100 };
    	RECT s_ClipRect4 = { 50, 50, 150, 150 };
    	RECT s_ScrollRect5 = {  0, 0, 100, 100 };
    	RECT s_ClipRect5 = { 100, 100, 200, 200 };
    	RECT s_ScrollRect6 = {  0, 0, 100, 100 };
    	RECT s_ClipRect6 = { 120, 120, 220, 220 };
    }
    
    int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
    {
    	WNDCLASSEX	wndclassx;
    
    	wndclassx.cbSize		= sizeof(WNDCLASSEX);
    	wndclassx.style         = CS_HREDRAW | CS_VREDRAW;
    	wndclassx.lpfnWndProc   = AppProc;
    	wndclassx.cbClsExtra    = 0;
    	wndclassx.cbWndExtra    = 0;
    	wndclassx.hInstance     = hInstance;
    	wndclassx.hIcon         = 0;
    	wndclassx.hCursor       = LoadCursor(nullptr, IDC_ARROW);
    	wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    	wndclassx.lpszMenuName  = nullptr;
    	wndclassx.lpszClassName = L"App";
    	wndclassx.hIconSm		= nullptr;
    	
    	if( !RegisterClassEx(&wndclassx) ) return -1;
    
    	HWND hWnd;
    	if( !(hWnd = CreateWindow(L"App", L"ScrollWindow", WS_OVERLAPPEDWINDOW, 100, 100, 400, 400, nullptr, nullptr, hInstance,
    							  nullptr)) ) return -1;
    
    	ShowWindow(hWnd, nCmdShow);
    	UpdateWindow(hWnd);
    
    	MSG msg;
    	while( GetMessage(&msg, nullptr, 0, 0) )
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return (int)msg.wParam;
    }
    
    LRESULT CALLBACK AppProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
    {
    	static HBRUSH s_Yellow;
    	static HBRUSH s_Green;
    
    	switch ( message )
    	{
    		case WM_CREATE:
    
    		//	Create yellow brush
    
    		if( !(s_Yellow = CreateSolidBrush(RGB(255, 255, 0))) ) return -1;
    
    		//	Create green brush
    
    		if( !(s_Green = CreateSolidBrush(RGB(0, 255, 0))) ) return -1;
    		break;
    		
    		case WM_LBUTTONDOWN:
    		{
    			s_NumberOfLeftMouseClikcs++;
    			
    			switch( s_NumberOfLeftMouseClikcs )
    			{
    				case 1:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    					
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					ReleaseDC(hwnd, hDC);
    
    					//	Scroll the windows's client area
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, nullptr, nullptr, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, nullptr, nullptr);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 2:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint rectangle s_ScrollRect2 to be scrolled with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect2, s_Yellow);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect2
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect2, nullptr, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect2, nullptr);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 3:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect3 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect3, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec3 with green brush
    					
    					FillRect(hDC, &s_ClipRect3, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect3 clipped by s_ClipRect3, which is entirely contained in s_ScrollRect3.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect3, &s_ClipRect3, nullptr, nullptr,  SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect3, &s_ClipRect3);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 4:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect4 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect4, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec4 with green brush
    					
    					FillRect(hDC, &s_ClipRect4, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect4 clipped by s_ClipRect4, which intersects s_ScrollRect4, but is not
    					//	contained in this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect4, &s_ClipRect4, nullptr, nullptr,  SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect4, &s_ClipRect4);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 5:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect5 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect5, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec5 with green brush
    					
    					FillRect(hDC, &s_ClipRect5, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect5 clipped by s_ClipRect5, which doesn't intersect s_ScrollRect5, but is
    					//	close enough to this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect5, &s_ClipRect5, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect5, &s_ClipRect5);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				default:
    
    				//	s_NumberOfLeftMouseClikcs > 5
    
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect5 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect6, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec5 with green brush
    					
    					FillRect(hDC, &s_ClipRect6, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect6 clipped by s_ClipRect6, which doesn't intersect s_ScrollRect6, and is
    					//	not close enough to this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect6, &s_ClipRect6, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect6, &s_ClipRect6);
    
    					#endif
    
    					Sleep(1000);
    
    					//	Beep, as there'll be no painting in this case.
    
    					MessageBeep(-1);
    				}
    			}
    		}
    		break;
    
    		case WM_RBUTTONDOWN:
    		{
    			//	Reinitialize the simulation
    			
    			s_NumberOfLeftMouseClikcs = 0;
    
    			//	Clear client area
    				
    			HDC hDC;
    			if( !(hDC = GetDC(hwnd)) )
    			{
    				DestroyWindow(hwnd);
    				break;
    			}
    					
    			RECT rect;
    			GetClientRect(hwnd, &rect);
    					
    			PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    			ReleaseDC(hwnd, hDC);
    		}
    		break;
    
    		case WM_PAINT:
    		{
    			if( !s_NumberOfLeftMouseClikcs ) return DefWindowProc(hwnd, message, wParam, lParam);
    
    			//	s_NumberOfLeftMouseClikcs > 0
    		
    			PAINTSTRUCT ps;
    			BeginPaint(hwnd, &ps);
    
    			HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
    
    			//	Fill invalid region with red brush
    		
    			FillRect(ps.hdc, &ps.rcPaint, hBrush);
    		
    			DeleteObject(hBrush);
    		
    			EndPaint(hwnd, &ps);
    
    			//	Beep
    
    			MessageBeep(-1);
    		}
    		break;
    
    		case WM_DESTROY:
    
    		DeleteObject(s_Green);
    		DeleteObject(s_Yellow);
    
    		PostQuitMessage(0);
    		break;
    
    		default:
    
    		return DefWindowProc(hwnd, message, wParam, lParam);
    	}
    	return 0;
    }



    Wednesday, October 30, 2013 6:40 PM

All replies

  • A better interpretation of the invalid region is "pixels that GDI doesn't know how to paint". That includes the uncovered pixels, but also pixels that were not on the screen before.

    In your case 5, the scrolling area is out of the clip region, but the scroll operation will make a small portion of it visible (the tiny 10x10 pixel square). Since that's out of the clip region, GDI assumes it doesn't know how to paint those pixels and will therefore ask the application to do it. Everything else will just stay the same, as those pixels are not affected by the scroll.

    Compare with case 3: GDI knows how to repaint part of the scrolled area, namely the small 10x10 pixel rectangle in the bottom right corned (those were the pixels in the top left corner of the clip region before the scroll operation), but doesn't know how to paint the rest and therefore invalidates it.

    The fact that the two rectangles have two touching vertices may mislead you; try changing your default case so that you scroll by 30, 30 and you'll get the same effect.

    HTH
    --mc


    • Edited by Mario Cossi Thursday, October 31, 2013 4:10 PM
    Thursday, October 31, 2013 2:48 PM
  • Mario

    Thanks for your reply. But I have to disagree. You said (my emphasis) :

    "That includes the uncovered pixels, but also pixels that were not on the screen before."

    and

    "In your case 5, the scrolling area is out of the clip region, but the scroll operation will make a small portion of it visible (the tiny 10x10 pixel square). Since that's out of the clip region."

    The pixels in case 5 that were painted with red by the program, were there before being painted, as they were not affected by the scrolling operation, which was done in the yellow rectangle that doesn't intersect the clipping rectangle. One way to see this very clearly is to replace the calls to ScrollWindow(), or ScrollWindowEx(), by ScrollDC(), and you'll be able to verify that, in none of the cases where both rectangles are defined, you will see yellow pixels being painted over green pixels, even when the rectangles intersect.

    When I posted this question I was in doubt about the interpretation of the invalid rectangle in case 5. Now I'm almost positive that this is a bug in Windows, i.e., there shouldn't be an invalid region in this case, as the two rectangles don't intersect. My point of view is that the invalid region is always made of uncovered pixels, are they in the window client area (no scrolling and clipping rectangles defined), or in the scrolling rectangle (no clipping rectangle defined), or in the clipping rectangle (when both rectangles are defined), as long as the two rectangles intersect.


    Thursday, October 31, 2013 6:57 PM
  • No, it's not a bug.

    The scroll operation is not contained within the scroll rectangle: the whole rectangle gets shifted by the operation, so your original scroll rectangle [0,0,100,100] becomes [10,10,110,110] (see your case #2), which produces the intersection [100,100,110,110] with your clip region which is invalidated.

    GDI cannot use the yellow pixels, as those are out of the clipping region.

    --mc

    Friday, November 01, 2013 12:52 AM
  • I can understand what you're saying, and I appreciate your efforts in trying to convince me with your arguments. But I have difficulty accepting them, as they seem to carry an artificial flavor to avoid saying this is a bug. For instance,

    The scroll operation is not contained within the scroll rectangle

    I agree with that, but I would complement, the invalid region resulting from a scroll operation is contained in the intersection of the scrolling and the clipping rectangle. If the rectangles don't intersect this invalid region should be null. You mentioned case #2. Notice that the invalid region is still contained in the original position of the scrolling rectangle, before the scrolling operation was executed.

    Friday, November 01, 2013 2:36 PM
  • I wouldn't have a problem to say it's a bug, it's just that I don't see where this behavior contradicts the specification. I quote from ScrollWindowEx (prcClip parameter), emphasis mine:

    prcClip [in]

    Type: const RECT

    Pointer to a RECT structure that contains the coordinates of the clipping rectangle. Only device bits within the clipping rectangle are affected. Bits scrolled from the outside of the rectangle to the inside are painted; bits scrolled from the inside of the rectangle to the outside are not painted. This parameter may be NULL.

    The behavior you seem to expect would contradict the documentation.

    HTH
    --mc

    Saturday, November 02, 2013 2:57 PM
  • Bits scrolled from the outside of the rectangle to the inside are painted;

    This is completely wrong, as I think I have proven to you in a prior post. If you replace ScrollWindow() and ScrollWindowEx(), with ScrollDC(), it will be clear this doesn't happen. You'll never see a pixel from the outside of the clipping rectangle scrolled into it. This is exactly why I'm so adamant in claiming the invalid region in case #5, as a bug.

    Also take a look a the second comment on Community Additions here.

    Saturday, November 02, 2013 9:09 PM
  • You cannot compare different APIs and expect them to behave the same. More specifically, the documentation of ScrollDC states that:

    The only bits affected by the scroll operation are bits in the intersection of this rectangle and the rectangle specified by lprcClip.

    while ScrollWindowsEx doesn't.

    About painting... look back at my first post here, specifically to the part where I said that GDI will invalidate those pixels it doesn't know how to paint. ScrollWindowEx will consider pixels scrolled from the outside to the inside of the clipping rectangle to be in need of being painted, but since it doesn't know how to paint them it invalidates the area so that you get to handle them in WM_PAINT.

    As for the comment in the ScrollWindowEx documentation, please look at the first one on the same page.

    As for the rationale, let's consider a real-world application.

    We have a window, with client area { 0,0,100,100 } and we want to use it to scroll around an image that is larger than the client area, let's say it's { 0, 0, 1000, 1000 }. For simplicity, let's assume that the clip rectangle is the same as the client area.

    Now, we scroll the image to the right by 150 pixels, obtaining {150, 0, 1150, 1000 }; the client area gets invalidated and we clear it out. No surprises here.

    Then, we scroll the image back by -150 pixels, obtaining { 0, 0, 1000, 1000 } once again. As things work in Windows, ScrollWindowEx will invalidate the whole client area, and WM_PAINT will be in charge of repainting the visible portion of the image. If things worked as you expect them to, nothing would be invalidated and you would end up with an empty window as you don't get a chance to repaint anything. Unless you invalidate the rectangle yourself, that is.

    HTH
    --mc

    Sunday, November 03, 2013 2:31 AM
  • Mario

    You'll find below the original code with a small change in the WM_PAINT processing in AppProc(). What I did here was to suppress any painting after the scroll operation, by just validating the invalid region, just before the call to BeginPaint(). As you can see there are no bits scrolled from the outside of the clipping rectangle into it, which clearly shows that the statement  "Bits scrolled from the outside of the rectangle to the inside are painted" is completely mistaken. Also, if you change the calls to ScrollWindow() to ScrollDC(), as I suggested before, you'll see that the results will be exactly the same, i.e., the same drawings are obtained in both simulations. Therefore, I think the functions ScrollDC() and ScrollWindow() are almost equivalent, with the difference that ScrollWindow() invalidates a certain invalid region, sometimes inaccurately, in my view, and ScrollDC() just scrolls the pixels inside the intersection of the scrolling and clipping rectangle, without invalidating it.

    #include <Windows.h>
    
    #define SCROLLWINDOWEX
    
    LRESULT CALLBACK		AppProc(HWND, UINT, UINT, LONG);
    
    namespace
    {
    	int s_NumberOfLeftMouseClikcs;
    	
    	RECT s_ScrollRect2 = { 0, 0, 100, 100 };
    	RECT s_ScrollRect3 = {  0, 0, 100, 100 };
    	RECT s_ClipRect3 = { 50, 50, 70, 70 };
    	RECT s_ScrollRect4 = {  0, 0, 100, 100 };
    	RECT s_ClipRect4 = { 50, 50, 150, 150 };
    	RECT s_ScrollRect5 = {  0, 0, 100, 100 };
    	RECT s_ClipRect5 = { 100, 100, 200, 200 };
    	RECT s_ScrollRect6 = {  0, 0, 100, 100 };
    	RECT s_ClipRect6 = { 120, 120, 220, 220 };
    }
    
    int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
    {
    	WNDCLASSEX	wndclassx;
    
    	wndclassx.cbSize		= sizeof(WNDCLASSEX);
    	wndclassx.style         = CS_HREDRAW | CS_VREDRAW;
    	wndclassx.lpfnWndProc   = AppProc;
    	wndclassx.cbClsExtra    = 0;
    	wndclassx.cbWndExtra    = 0;
    	wndclassx.hInstance     = hInstance;
    	wndclassx.hIcon         = 0;
    	wndclassx.hCursor       = LoadCursor(nullptr, IDC_ARROW);
    	wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    	wndclassx.lpszMenuName  = nullptr;
    	wndclassx.lpszClassName = L"App";
    	wndclassx.hIconSm		= nullptr;
    	
    	if( !RegisterClassEx(&wndclassx) ) return -1;
    
    	HWND hWnd;
    	if( !(hWnd = CreateWindow(L"App", L"ScrollWindow", WS_OVERLAPPEDWINDOW, 100, 100, 400, 400, nullptr, nullptr, hInstance,
    							  nullptr)) ) return -1;
    
    	ShowWindow(hWnd, nCmdShow);
    	UpdateWindow(hWnd);
    
    	MSG msg;
    	while( GetMessage(&msg, nullptr, 0, 0) )
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return (int)msg.wParam;
    }
    
    LRESULT CALLBACK AppProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
    {
    	static HBRUSH s_Yellow;
    	static HBRUSH s_Green;
    
    	switch ( message )
    	{
    		case WM_CREATE:
    
    		//	Create yellow brush
    
    		if( !(s_Yellow = CreateSolidBrush(RGB(255, 255, 0))) ) return -1;
    
    		//	Create green brush
    
    		if( !(s_Green = CreateSolidBrush(RGB(0, 255, 0))) ) return -1;
    		break;
    		
    		case WM_LBUTTONDOWN:
    		{
    			s_NumberOfLeftMouseClikcs++;
    			
    			switch( s_NumberOfLeftMouseClikcs )
    			{
    				case 1:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    					
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					ReleaseDC(hwnd, hDC);
    
    					//	Scroll the windows's client area
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, nullptr, nullptr, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, nullptr, nullptr);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 2:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint rectangle s_ScrollRect2 to be scrolled with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect2, s_Yellow);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect2
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect2, nullptr, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect2, nullptr);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 3:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect3 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect3, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec3 with green brush
    					
    					FillRect(hDC, &s_ClipRect3, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect3 clipped by s_ClipRect3, which is entirely contained in s_ScrollRect3.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect3, &s_ClipRect3, nullptr, nullptr,  SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect3, &s_ClipRect3);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 4:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect4 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect4, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec4 with green brush
    					
    					FillRect(hDC, &s_ClipRect4, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect4 clipped by s_ClipRect4, which intersects s_ScrollRect4, but is not
    					//	contained in this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect4, &s_ClipRect4, nullptr, nullptr,  SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect4, &s_ClipRect4);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				case 5:
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect5 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect5, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec5 with green brush
    					
    					FillRect(hDC, &s_ClipRect5, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect5 clipped by s_ClipRect5, which doesn't intersect s_ScrollRect5, but is
    					//	close enough to this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect5, &s_ClipRect5, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect5, &s_ClipRect5);
    
    					#endif
    
    					Sleep(1000);
    				}
    				break;
    
    				default:
    
    				//	s_NumberOfLeftMouseClikcs > 5
    
    				{
    					//	Clear client area
    					
    					HDC hDC;
    					if( !(hDC = GetDC(hwnd)) )
    					{
    						DestroyWindow(hwnd);
    						break;
    					}
    					
    					RECT rect;
    					GetClientRect(hwnd, &rect);
    
    					PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    					Sleep(1000);
    
    					//	Paint scrolling rectangle s_ScrollRect5 with yellow brush
    					
    					FillRect(hDC, &s_ScrollRect6, s_Yellow);
    
    					Sleep(1000);
    
    					//	Paint clipping rectangle s_ClipRec5 with green brush
    					
    					FillRect(hDC, &s_ClipRect6, s_Green);
    
    					ReleaseDC(hwnd, hDC);
    
    					Sleep(1000);
    
    					//	Scroll rectangle s_ScrollRect6 clipped by s_ClipRect6, which doesn't intersect s_ScrollRect6, and is
    					//	not close enough to this rectangle.
    
    					#ifdef SCROLLWINDOWEX
    
    					ScrollWindowEx(hwnd, 10, 10, &s_ScrollRect6, &s_ClipRect6, nullptr, nullptr, SW_INVALIDATE);
    					
    					#else
    
    					ScrollWindow(hwnd, 10, 10, &s_ScrollRect6, &s_ClipRect6);
    
    					#endif
    
    					Sleep(1000);
    
    					//	Beep, as there'll be no painting in this case.
    
    					MessageBeep(-1);
    				}
    			}
    		}
    		break;
    
    		case WM_RBUTTONDOWN:
    		{
    			//	Reinitialize the simulation
    			
    			s_NumberOfLeftMouseClikcs = 0;
    
    			//	Clear client area
    				
    			HDC hDC;
    			if( !(hDC = GetDC(hwnd)) )
    			{
    				DestroyWindow(hwnd);
    				break;
    			}
    					
    			RECT rect;
    			GetClientRect(hwnd, &rect);
    					
    			PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);
    
    			ReleaseDC(hwnd, hDC);
    		}
    		break;
    
    /*		case WM_PAINT:
    		{
    			if( !s_NumberOfLeftMouseClikcs ) return DefWindowProc(hwnd, message, wParam, lParam);
    
    			//	s_NumberOfLeftMouseClikcs > 0
    		
    			PAINTSTRUCT ps;
    			BeginPaint(hwnd, &ps);
    
    			HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
    
    			//	Fill invalid region with red brush
    		
    			FillRect(ps.hdc, &ps.rcPaint, hBrush);
    		
    			DeleteObject(hBrush);
    		
    			EndPaint(hwnd, &ps);
    
    			//	Beep
    
    			MessageBeep(-1);
    		}
    		break; */
    
    		case WM_PAINT:
    		{
    			if( !s_NumberOfLeftMouseClikcs ) return DefWindowProc(hwnd, message, wParam, lParam);
    			
    			ValidateRect(hwnd, nullptr);
    			
    			PAINTSTRUCT ps;
    			BeginPaint(hwnd, &ps);
    			EndPaint(hwnd, &ps);
    			MessageBeep(-1);
    		}
    		break;
    
    		case WM_DESTROY:
    
    		DeleteObject(s_Green);
    		DeleteObject(s_Yellow);
    
    		PostQuitMessage(0);
    		break;
    
    		default:
    
    		return DefWindowProc(hwnd, message, wParam, lParam);
    	}
    	return 0;
    }
    

    As for the example you gave above, I have the following comments:

    Now, we scroll the image to the right by 150 pixels, obtaining {150, 0, 1150, 1000 }; the client area gets invalidated and we clear it out. No surprises here.

    I don't agree with the expression "we clear it out". The program can paint anything in the window at this point in time.

    Then yes, according to my reasoning the second scroll (-150 pixels) would not affect any drawing done in the window after the first scroll. What's the problem with that ?


    Sunday, November 03, 2013 9:03 PM