need help with bitmaps and Undo

Dave O

Well-Known Member
Licensed User
Longtime User
I've been playing with B4A by creating a little kaleidoscope app - you draw on the screen, and it mirrors your drawing in various ways.

I tried adding an Undo function, so you can undo your last stroke. Trouble is, I can't get it working right, so maybe someone here with more B4A kung fu can spot the problem.

Basically, it works like this:
- All drawing is done on a canvas initialized from a panel. (That part works.)
- On touch-down, I copy the canvas bitmap to a second bitmap.
- On touch-move, I do the drawing.
- On touch-up, I enable the Undo button.
- The Undo button draws the second bitmap back onto the canvas.

All this works (apparently), except for the last action - clicking Undo does not visibly affect the canvas bitmap.

Excerpted code attached here for your viewing pleasure. Any help GREATLY appreciated. :)

B4X:
Sub Process_Globals
   Dim penColor As Int : penColor = Colors.Red
   Dim penWidth As Int : penWidth = 5
   Dim mirrorNumber As Int : mirrorNumber = 7   'default to all mirrors
   Dim oldX, oldY As Float
   Dim screenHalfX, screenHalfY As Float
   Dim panelRect As Rect
   Dim preserveBitmap, undoBitmap As Bitmap
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim drawingPanel As Panel
   Dim panelCanvas As Canvas
   Dim clearButton As Button
   Dim colorButton As Button
   Dim horizontalToggle As ToggleButton
   Dim slashToggle As ToggleButton
   Dim verticalToggle As ToggleButton
   Dim widthSeek As SeekBar
   Dim widthLabel As Label
   Dim backSlashToggle As ToggleButton
   Dim mirrorSpinner As Spinner
   Dim undoButton As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
   activity.LoadLayout("main")
   If activity.Height > activity.Width   Then   'portrait
      drawingPanel.Width = activity.Width
   Else                                       'landscape 800x480
      drawingPanel.Width = activity.height
      drawingPanel.Left = activity.Width - drawingPanel.Width            'flush right
      mirrorSpinner.Width = drawingPanel.Left - 7 - mirrorSpinner.Left   'stretch to fit
      widthSeek.Width = drawingPanel.Left - 10 - widthSeek.Left         'stretch to fit
      undoButton.Width = drawingPanel.Left - 7 - undoButton.Left         'stretch to fit
      clearButton.Width = drawingPanel.Left - 7 - clearButton.Left      'stretch to fit
   End If
   drawingPanel.Height = drawingPanel.Width   'for now, keep it square
   If FirstTime Then                     
      preserveBitmap.InitializeMutable(drawingPanel.Width, drawingPanel.Height)   'preserve it on subsequent creations
      undoBitmap.InitializeMutable(drawingPanel.Width, drawingPanel.Height)
      drawingPanel.Color = Colors.Black
   End If
   panelCanvas.Initialize(drawingPanel)
   colorButton.Color = penColor
   widthSeek.Value = penWidth - .99
   screenHalfX = drawingPanel.Width / 2
   screenHalfY = drawingPanel.Width / 2
   mirrorSpinner.AddAll(Array As String("No mirrors", "Vertical mirror", "Horizontal mirror", "Vert. & horiz. mirrors", "Right diagonal mirror", "Left diagonal mirror", "Both diagonal mirrors", "All mirrors!"))
   mirrorSpinner.SelectedIndex = mirrorNumber   'set to last item (all mirrors)
   panelRect.Initialize(0, 0, panelCanvas.Bitmap.Width, panelCanvas.Bitmap.Height)
   panelCanvas.DrawBitmap(preserveBitmap, Null, panelRect)   'restore the existing bitmap, if any
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   preserveBitmap = panelCanvas.Bitmap      'preserve existing drawing
End Sub

Sub drawingPanel_Touch (Action As Int, X As Float, Y As Float)
   If Action = activity.ACTION_DOWN Then   'start a new line
      undoBitmap = panelCanvas.Bitmap      'save existing drawing for possible Undo
      oldX = x
      oldY = y
   Else If Action = activity.ACTION_MOVE Then   'continue the current line
      panelCanvas.DrawLine(oldX, oldY, x, y, penColor, penWidth)
      Select mirrorSpinner.SelectedIndex
         Case 1 : drawVerticalMirror(oldX, oldY, x, y)
         Case 2 : drawHorizontalMirror(oldX, oldY, x, y)
         Case 3 : drawVertHorizMirrors(oldX, oldY, x, y)
         Case 4 : drawSlashMirror(oldX, oldY, x, y)
         Case 5 : drawBackSlashMirror(oldX, oldY, x, y)
         Case 6 : drawDiagonalMirrors(oldX, oldY, x, y)
         Case 7 :   'all mirrors
            drawVertHorizMirrors(oldX, oldY, x, y)
            drawDiagonalMirrors(oldX, oldY, x, y)
            drawDiagonalMirrors(mirrorVertical(oldX), oldY, mirrorVertical(x), y)
         End Select
      drawingPanel.Invalidate
      oldX = x
      oldY = y
   Else       'action up - end the line
      undoButton.Enabled = True
   End If
End Sub

Sub mirrorVertical(argX As Float)
   Return (screenHalfX - argX) + screenHalfX
End Sub

...

Sub undoButton_Click
   panelCanvas.DrawBitmap(undoBitmap, Null, panelRect)
   drawingPanel.Invalidate
   undoButton.Enabled = False
End Sub
 

Dave O

Well-Known Member
Licensed User
Longtime User
re: upload file

Yes, here you go. Thanks!
 

Attachments

  • Kaleida1.zip
    7.5 KB · Views: 284
Upvote 0

Dave O

Well-Known Member
Licensed User
Longtime User
Hi Klaus, thanks for that - your suggestion indeed fixes the problem. Undo now works.

However, I'm still not sure why the original code didn't work. I use similar code to preserve the bitmap when the orientation changes (activity pause/create), so I'm puzzled why it didn't work for Undo.

Any illuminations?

Thanks again!
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
In your code the bitmap is the same, that means that even the further drawings are also on it.
Initializing the bitmap generates a new instance of the bitmap independant from the other one.
That's how I understand it.

Best regards.
 
Upvote 0
Top