Canvas limitations - Copy, Reset?

Silentsea

Member
Licensed User
Longtime User
Hi there,

yesterday I spend a couple of hours in writing some drawing features. The turning point was the step by step undo function. I tried different approaches. I thought to create different panel layers (and one cavas for the active layer) and hide them or set color = transparent if the use hit the undo function. A layer counter managed the active layer panel etc. Everything worked as aspected, in preview mode, the strokes could be hidden.

But now the question was how to combine all cavas objects to one. Or how to save the preview panel with different canvases on different panels ;). I created some cavas objects (a canvas array), initialized by one hidden panel. The First canvas has only the first stroke, the second cavas the first and the second stroke and so on. But now the question was, how to reset a canvas, or copy a canvas to annother or combine more canvas objects. I tried only initializing the first canvas and set the other to the canvas with counter -1. But this didn't work as aspected:

B4X:
If i = ActiveLayer AND i = 1 Then
  pnlTemp.Color = Colors.Transparent
  cvsTemp(i).initialize(pnlTemp)
  Debug("initialize cvsTemp("&i&" via pnlTemp)")
Else If i = ActiveLayer Then
  cvsTemp(i) = cvsTemp(i-1)
  Debug("Load cvsTemp("&i&") with: cvsTemp("& (i-1) &")")
End If

Is there annother solution to copy a canvas without saving it to bitmap?


Annother approch I actually try is so save the layer via c.bitmap to an bitmap array and then initialize the main canvas:

B4X:
Dim BitmapToLoad As Bitmap
BitmapToLoad.InitializeMutable(480,360)
BitmapToLoad = Bitmaps(ActiveLayer-1)
c.Initialize2(BitmapToLoad)

My fear is, that this solution will become a little memory overkill on small devices. Because i have to hold the layer pics (from cam) in memory.

Greetings.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
I think you are confusing Canvas with Bitmaps. Canvas object is responsible for the drawing actions. The drawing is always done on a bitmap object. When you initialize the canvas object with a view then a bitmap is created for you and set as the view's background.

Which type of drawings are you allowing the user to do? How many drawings are expected?
 
Upvote 0

Silentsea

Member
Licensed User
Longtime User
Topic is to draw some marks (arrows, circles, freehand) on a picture from cam to give attention to some areas of the picture. Normally there will be 20 drawings at maximum i think. So if arrow 19 is not correctly drawn the user should have the chance to undo this drawing step. So he has not to reset the complete picture.

When you initialize the canvas object with a view then a bitmap is created for you and set as the view's background

So this means there is no other way as saving the state after every new arrow drawing, as mentioned in my alternative approach?
 
Upvote 0

Biscuit

Member
Licensed User
Longtime User
This probably sounds like more work but I think you'd have to record the user's actions as a set of commands and then compose the image from the commands when you update the screen. I don't think it would be that tricky really, you'd need to store the type of drawing, the mouse start and end (and the trace if they have freehand drawing) and the colour. Then just loop through your list of commands so they are drawn in the correct order.

If this is not appealing you risk running out of memory and you also have to AND the images together so you draw the custom bits and ignore the background. Not sure if the canvas object has built in support for that or not...?
 
Upvote 0

Silentsea

Member
Licensed User
Longtime User
Yes this is annother approach I thought off and I will follow if saving bitmaps in mem will cause memory problems. It might be a performant solution.

Actually i save the changed bitmap to memory and reset the panel by undo function like this:

B4X:
Sub UndoLastDrawing
   If ActiveLayer >= 1 Then
      Debug("undo layer: "& ActiveLayer)
      
      pnlImage.SetBackgroundImage(Bitmaps(ActiveLayer-1))
      pnlimage.Invalidate
      
      c.Initialize(pnlImage)
      Debug("initialize canvas with new pnlImage")
      
      ActiveLayer = ActiveLayer - 1
   End If
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
This probably sounds like more work but I think you'd have to record the user's actions as a set of commands and then compose the image from the commands when you update the screen. I don't think it would be that tricky really, you'd need to store the type of drawing, the mouse start and end (and the trace if they have freehand drawing) and the colour. Then just loop through your list of commands so they are drawn in the correct order.
I also recommend you to try this approach. Especially if you do not allow freehand drawings.
 
Upvote 0

Silentsea

Member
Licensed User
Longtime User
Hi there,

in my previous approach working with my bitmap layers worked fine. But now I noticed a little problem, I didn't realized before. I only saved the bitmaps in of the preview canvas in 480x360 Pixel. So in the end there were only small images, which I could save out of with the canvas.Bitmap.WriteToStream method.

Now I worked out the approach with a db driven command pattern for my undo function, as mentioned before. Thank you!

But now I get scared about the the filesize after saving with the canvas.Bitmap.WriteToStream. If I saved an image 1600 x 1200 which was original about 500 kb, it grows up to 1.9 Mb without compression. What is my mistake? Is this normal? Please give me a hint ;).

Thanks in advance.
 
Last edited:
Upvote 0

Biscuit

Member
Licensed User
Longtime User
But now I get scared about the the filesize after saving with the canvas.Bitmap.WriteToStream. If I saved an image 1600 x 1200 which was original about 500 kb, it grows up to 1.9 Mb without compression. What is my mistake? Is this normal? Please give me a hint ;).

Thanks in advance.

I think that's just the size of uncompressed bitmaps. There are various encoding / compression techniques but the simple maths of using 1 byte (8 bits, 256 colours) to store 1 pixel would give you 1600 x 1200 bytes which is 1,920,000 bytes which is pretty much what you said above. Scale this to 3 bytes for full RGB 16.7 million colours (or 4 bytes if you want alpha values) and you can see why image compression exists :cool:

I think you want / need to store the image as a JPEG or PNG. JPGs are best for 'real life' pictures and PNGs are best for e.g screenshots. Note that JPGs use what's termed 'lossy' compression in that they remove information from the image which it thinks you won't notice, however, repeatedly saving & loading the same picture will quickly show up the compression problems.

Hope this helps
 
Upvote 0
Top