Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

Undoing painting

DonotaloDonotalo Member Posts: 715
[purple]i'm trying add the undo option in a very simple paint program. my paint program can draw lines while the left mouse button is pressed and the mouse is moving on the client area. i'm keeping track of the two end points in a list of RECT of every line drawn by the LineTo() function. here is the code fragment of the window function:[code]
list line_points;
RECT dummy_rect;
int pendown, X, Y, StartX, StartY;
...

case WM_LBUTTONDOWN:
line_points.clear();//clear undo information
pendown = 1;
X = StartX = LOWORD(lParam);
Y = StartY = HIWORD(lParam);
break;
case WM_MOUSEMOVE:
if (pendown) {
hdc = GetDC(hwnd);
SelectObject(memdc, hCurrentpen);
//memdc is a HDC used as virtual window
SelectObject(hdc, hCurrentpen);
MoveToEx(memdc, X, Y, NULL);
MoveToEx(hdc, X, Y, NULL);

//Save undo information
dummy_rect.left = X;
dummy_rect.top = Y;

X = LOWORD(lParam);
Y = HIWORD(lParam);
LineTo(memdc, X, Y);
LineTo(hdc, X, Y);
ReleaseDC(hwnd, hdc);

//Save undo information
dummy_rect.right = X;
dummy_rect.bottom = Y;
line_points.push_back(dummy_rect);
}
break;
case WM_LBUTTONUP:
pendown = 0;
break;
//The undo case:
hOldpen = (HPEN) SelectObject(memdc, hNullpen);
hOldbrush = (HBRUSH) SelectObject(memdc, hBGbrush);
//hBGbrush is actually a white brush

iter = line_points.begin();
while(iter != line_points.end()) {
Rectangle(memdc, iter->left, iter->top, iter->right, iter->bottom);
iter++;
}

InvalidateRect(hwnd, 0, 1);
SelectObject(memdc, hOldpen);
SelectObject(memdc, hOldbrush);
break;
[/code]
certainly i'm doing it in a wrong way. the above code erases some portion of the line drawn by a pen, and painted it with the background color (which is white always).

how can i undo properly? i dont want to "erase" but i want to implement "undo". suppose a rectangle is drawn and filled with blue color. then some line is drawn by mouse move over the rectangle. undoing it must not erase the rectangle. please show me a way.
[/purple]
[hr][purple]~Donotalo()[/purple]

Comments

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    : [purple]i'm trying add the undo option in a very simple paint program. my paint program can draw lines while the left mouse button is pressed and the mouse is moving on the client area. i'm keeping track of the two end points in a list of RECT of every line drawn by the LineTo() function. here is the code fragment of the window function:[code]
    : list line_points;
    : RECT dummy_rect;
    : int pendown, X, Y, StartX, StartY;
    : ...
    :
    : case WM_LBUTTONDOWN:
    : line_points.clear();//clear undo information
    : pendown = 1;
    : X = StartX = LOWORD(lParam);
    : Y = StartY = HIWORD(lParam);
    : break;
    : case WM_MOUSEMOVE:
    : if (pendown) {
    : hdc = GetDC(hwnd);
    : SelectObject(memdc, hCurrentpen);
    : //memdc is a HDC used as virtual window
    : SelectObject(hdc, hCurrentpen);
    : MoveToEx(memdc, X, Y, NULL);
    : MoveToEx(hdc, X, Y, NULL);
    :
    : //Save undo information
    : dummy_rect.left = X;
    : dummy_rect.top = Y;
    :
    : X = LOWORD(lParam);
    : Y = HIWORD(lParam);
    : LineTo(memdc, X, Y);
    : LineTo(hdc, X, Y);
    : ReleaseDC(hwnd, hdc);
    :
    : //Save undo information
    : dummy_rect.right = X;
    : dummy_rect.bottom = Y;
    : line_points.push_back(dummy_rect);
    : }
    : break;
    : case WM_LBUTTONUP:
    : pendown = 0;
    : break;
    : //The undo case:
    : hOldpen = (HPEN) SelectObject(memdc, hNullpen);
    : hOldbrush = (HBRUSH) SelectObject(memdc, hBGbrush);
    : //hBGbrush is actually a white brush
    :
    : iter = line_points.begin();
    : while(iter != line_points.end()) {
    : Rectangle(memdc, iter->left, iter->top, iter->right, iter->bottom);
    : iter++;
    : }
    :
    : InvalidateRect(hwnd, 0, 1);
    : SelectObject(memdc, hOldpen);
    : SelectObject(memdc, hOldbrush);
    : break;
    : [/code]
    : certainly i'm doing it in a wrong way. the above code erases some portion of the line drawn by a pen, and painted it with the background color (which is white always).
    :
    : how can i undo properly? i dont want to "erase" but i want to implement "undo". suppose a rectangle is drawn and filled with blue color. then some line is drawn by mouse move over the rectangle. undoing it must not erase the rectangle. please show me a way.
    : [/purple]
    : [hr][purple]~Donotalo()[/purple]
    :
    [blue]
    The main idea of Windows program is:

    1. changing the data structure
    2. refreshing the part of screen where this change was done

    It is that simple. Just remove the line from your data collection and refresh the rectangle, which was occupied by the line:[/blue]
    [code]
    RECT area;

    // Fill 'area' with line data
    InvalidateRect (hWnd, &area, TRUE);
    UpdateWindow (hWnd); // always call this immediately after invalidating
    [/code]
    [blue]Also, by the design of your program - I see that you have a lot of elements to draw (am I right?) - in that case - draw in memory first and then BitBlt() it to 'real' Device Context. Much faster this way!
    [/blue]
  • DonotaloDonotalo Member Posts: 715
    : : [purple]i'm trying add the undo option in a very simple paint program. my paint program can draw lines while the left mouse button is pressed and the mouse is moving on the client area. i'm keeping track of the two end points in a list of RECT of every line drawn by the LineTo() function. here is the code fragment of the window function:[code]
    : : list line_points;
    : : RECT dummy_rect;
    : : int pendown, X, Y, StartX, StartY;
    : : ...
    : :
    : : case WM_LBUTTONDOWN:
    : : line_points.clear();//clear undo information
    : : pendown = 1;
    : : X = StartX = LOWORD(lParam);
    : : Y = StartY = HIWORD(lParam);
    : : break;
    : : case WM_MOUSEMOVE:
    : : if (pendown) {
    : : hdc = GetDC(hwnd);
    : : SelectObject(memdc, hCurrentpen);
    : : //memdc is a HDC used as virtual window
    : : SelectObject(hdc, hCurrentpen);
    : : MoveToEx(memdc, X, Y, NULL);
    : : MoveToEx(hdc, X, Y, NULL);
    : :
    : : //Save undo information
    : : dummy_rect.left = X;
    : : dummy_rect.top = Y;
    : :
    : : X = LOWORD(lParam);
    : : Y = HIWORD(lParam);
    : : LineTo(memdc, X, Y);
    : : LineTo(hdc, X, Y);
    : : ReleaseDC(hwnd, hdc);
    : :
    : : //Save undo information
    : : dummy_rect.right = X;
    : : dummy_rect.bottom = Y;
    : : line_points.push_back(dummy_rect);
    : : }
    : : break;
    : : case WM_LBUTTONUP:
    : : pendown = 0;
    : : break;
    : : //The undo case:
    : : hOldpen = (HPEN) SelectObject(memdc, hNullpen);
    : : hOldbrush = (HBRUSH) SelectObject(memdc, hBGbrush);
    : : //hBGbrush is actually a white brush
    : :
    : : iter = line_points.begin();
    : : while(iter != line_points.end()) {
    : : Rectangle(memdc, iter->left, iter->top, iter->right, iter->bottom);
    : : iter++;
    : : }
    : :
    : : InvalidateRect(hwnd, 0, 1);
    : : SelectObject(memdc, hOldpen);
    : : SelectObject(memdc, hOldbrush);
    : : break;
    : : [/code]
    : : certainly i'm doing it in a wrong way. the above code erases some portion of the line drawn by a pen, and painted it with the background color (which is white always).
    : :
    : : how can i undo properly? i dont want to "erase" but i want to implement "undo". suppose a rectangle is drawn and filled with blue color. then some line is drawn by mouse move over the rectangle. undoing it must not erase the rectangle. please show me a way.
    : : [/purple]
    : : [hr][purple]~Donotalo()[/purple]
    : :
    : [blue]
    : The main idea of Windows program is:
    :
    : 1. changing the data structure
    : 2. refreshing the part of screen where this change was done
    :
    : It is that simple. Just remove the line from your data collection and refresh the rectangle, which was occupied by the line:[/blue]
    : [code]
    : RECT area;
    :
    : // Fill 'area' with line data
    : InvalidateRect (hWnd, &area, TRUE);
    : UpdateWindow (hWnd); // always call this immediately after invalidating
    : [/code]
    : [blue]Also, by the design of your program - I see that you have a lot of elements to draw (am I right?) - in that case - draw in memory first and then BitBlt() it to 'real' Device Context. Much faster this way!
    : [/blue]
    :
    [purple]
    u r right. i'm doing lots of unnecessary drawing in my code. also, i didn't keep track of drawn objects. what my program does, it immediately draws the object in the virtual window and then call InvalidateRect(). a very bad design as i understand now. so i did the undo option in another way. before each object drawn, i save the current virtual window in a stack. and whenever an undo command has to be performed, i popped the last virtual window of the stack and paint it in the client window. how about that? is it taking too much CPU time or memory?

    the book i'm following to learn win32 api does not mention this issue (yet) about calling UpdateWindow() immediately after calling InvalidateRect(). can u please clarify this thing?
    [/purple]
    [hr][purple]~Donotalo()[/purple]

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    [blue]
    UpdateWindow() does only one thing - it calls your WM_PAINT handling code immediately. Without that call your invalidated area will be waiting until the message loop is free of other messages and only then it will proceed with WM_PAINT. This call is just for drawing the changes right away and not wait until the mesage loop is free. You do not have to do it in your situation, because you do not depend on other messages too much. However, if you decide to do scrolling in your application - you cannot do interactive scrolling without this.

    Interactive scrolling is when you drag the mouse holding the scroller thumb - the message loop is busy at this point, so the changes will not be drawn until you release the mouse - not good...

    More on design:

    You need to keep a collection of objects, like an array of pointers, where each object is an allocated data structure, where you keep the values needed to operate (and draw) these objects.

    When you add objects - just add them to a collection and call Invalidate()/UpdateWindow() pair. When you remove them - same thing. You should not draw objects twice.
    [/blue]
Sign In or Register to comment.