Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is Canvas.save and Canvas.restore?

Tags:

flutter

dart

skia

I am wondering why and when you would use Canvas.save, Canvas.restore, and maybe even Canvas.saveLayer.
I have heard that they are really useful, but I do not know when to use them.

like image 483
creativecreatorormaybenot Avatar asked Jan 08 '20 16:01

creativecreatorormaybenot


People also ask

What is restore canvas?

It removes the change in settings, but not the drawing itself. Settings can include scaling the canvas etc., and it restores the scaling to the initial state when you called canvas.

What does .save do in canvas?

The CanvasRenderingContext2D. save() method of the Canvas 2D API saves the entire state of the canvas by pushing the current state onto a stack.


1 Answers

Canvas.save and Canvas.saveLayer work slightly differently, but they both have the same counterpart:

Canvas.restore

This allows you to restore the state before the most recent entry on the save stack, i.e. it "Pops the current save stack". This means that any transformations and clips done to the canvas in the current state will be removed and if saveLayer was used, the saved layer will be composited into the canvas (the draw order will remain the same).

Canvas.save

As I mentioned earlier, this allows you to save the state the canvas is in. You can do any transformation and clips you want and those will be removed using restore:

canvas.save();

canvas.transform(..); // Transforms the canvas, which will affect the draw call.
canvas.drawRect(...); // Affected by the transform.

canvas.restore();

canvas.drawRect(...); // Not affected by the transform.

You can use any number of save before restore and the stack will remember all entries, i.e. restore will always pop the most recent entry.

When would I use this?

Example: if you wanted to rotate a single piece when drawing a bigger picture, you can simply do the rotation inside a save-restore block and draw something without the rotation on top afterwards.

Note that all children of a RenderObject will use the same PaintingContext, i.e. the same Canvas. So if you transform the canvas in a single child, it will also be transformed for all other children that draw afterwards. This is potentially unwanted behavior and the reason why you always want to save and restore the canvas state.

Canvas.saveLayer

This is a bit more complicated and I urge you to read the comprehensive documentation of this method. Btw, saveLayer does not work in Flutter web as of January, 2019.

The basic difference between saveLayer and save is that saveLayer will composite the layer upon usage of restore. For a simple example, I constructed this snippet without bounds (which is why null is passed), which will save the whole canvas:

canvas.drawRect(rect, Paint()..color = const Color(0xffff0000)); // Draws a red rect.
canvas.saveLayer(null, Paint()..blendMode = BlendMode.multiply); // Saves the whole canvas.

canvas.drawRect(
  rect.shift(const Offset(20, 20)),
  Paint()..color = const Color(0xff0000ff), // Draws a blue rect.
);

canvas.restore(); // Composites the red rect into the blue rect.

Screen capture

Note that the blue rect will still be composited above the red rect.

This example could also be achieved without using saveLayer because the Paint.blendMode I used can also be passed to Canvas.drawRect. However, when using e.g. a TextPainter, you cannot pass blend modes. Additionally, saveLayer allows you to pass bounds, which gives far more possibilites (read the documentation for more information, also about clips). Clipping is actually probably the most useful operation in combination with saveLayer - I did not include it in order to have a simple example.

This is how the example would look without saveLayer:

Screen capture

like image 148
creativecreatorormaybenot Avatar answered Oct 04 '22 07:10

creativecreatorormaybenot