0

Im trying to implement undo and redo using gdi in wpf. I am not very familiar with gdi and my attempts have been unsuccessful.

On my mouse move event I draw like this:

    using (var g = Gdi.Graphics.FromImage(tempBitmap))
    {
        g.SmoothingMode = Gdi.Drawing2D.SmoothingMode.AntiAlias;
        g.CompositingQuality = Gdi.Drawing2D.CompositingQuality.HighQuality;
        if (currentTool == "eraserBrush")
            g.CompositingMode = Gdi.Drawing2D.CompositingMode.SourceCopy;
        else
            g.CompositingMode = Gdi.Drawing2D.CompositingMode.SourceOver;
        g.DrawLine(pen,p0,p1);
    }

    // Copy GDI bitmap to WPF bitmap.
    var hbmp = tempBitmap.GetHbitmap();
    var options = BitmapSizeOptions.FromEmptyOptions();
    this.writableBmp.Source = Imaging.CreateBitmapSourceFromHBitmap(hbmp,
        IntPtr.Zero, Int32Rect.Empty, options);

    // Redraw the WPF Image control.
    this.writableBmp.InvalidateMeasure();
    this.writableBmp.InvalidateVisual();

tempBitmap is a Gdi bitmap

On my mouse up event I push tempBitmap to a stack, and on my undo event I pop from the stack and do the following:

if (paintStack.Count <= 1)
                    return;

                paintStack.Pop();
                tempBitmap = paintStack.Peek();
 var hbmp = paintStack.Peek().GetHbitmap();
                var options = BitmapSizeOptions.FromEmptyOptions();
                this.writableBmp.Source = Imaging.CreateBitmapSourceFromHBitmap(hbmp,
                    IntPtr.Zero, Int32Rect.Empty, options);

                // Redraw the WPF Image control.
                this.writableBmp.InvalidateMeasure();
                this.writableBmp.InvalidateVisual();

But hitting undo does nothing. I believe I am pushing and poping the wrong item to the stack. I think I should be doing the gdi graphics, and not the tempbitmap, but I am not sure how.

Edit:----------------------------------------------------

I took a different approach but it seems its a dead end. I created a stack for the history of actions. Pushed an action to the stack after every stroke, and pop'd after an undo. Then on my undo event I just redraw everything minus the last action. This works fine if I am just drawing lines and simple stuff with gdi. However, I also have a tool that draws images, and a lot of them. This approach is way too slow. enter image description here The above demonstrates only three levels of undo, the more I draw the slower the undo is (obviously has more actions to read from the stack and redraw).

Theres got to be a way to make this work by just taking a snapshot of the current canvas. Any ideas guys?

Deniz Cetinalp
  • 901
  • 1
  • 18
  • 34
  • I don't understand: In your animation the undo action seems to be reasonably fast. FWIW, your last approach is also what I have done in the past (maintain a list of actions and implement undo as starting over and doing every action again, except the last one). – 500 - Internal Server Error Feb 28 '15 at 00:59
  • @500-InternalServerError I need it to be much faster. Like I mentioned, that is only with 3 actions on the stack. If however there are 20+ actions on the stack, with each action having 50+ drawImage calls, and a canvas size of 2048x2028 the speed becomes unacceptable. Also for some reason it am getting out of mem exception when I undo while there are too many things to redraw. I will have to think of something else. Maybe instead of redrawing multiple images with each stroke, render those to one image first, then I will just have to redraw one image composed of many images. – Deniz Cetinalp Feb 28 '15 at 04:08

0 Answers0