saveLayer vs clip
Two different mechanisms answer questions that look similar:
clipRect / clipRRect / clipPath— masks the destination. Subsequent draws are intersected with the clip region. Cheap. The clip stacks withsave / restore.saveLayer— creates an offscreen layer. Subsequent draws go into a fresh buffer. Whenrestore()runs, that buffer composites back into the parent layer through the layer'spaint(which can carry blend modes, color filters, image filters). Expensive — Skia allocates an offscreen GPU texture.
Picking the wrong one is one of the most common CanvasKit performance traps.
Use a clip when …
You want to bound where future draws land. The classic "rounded card with content inside" pattern:
const card = CK.RRectXY(CK.LTRBRect(60, 30, 360, 220), 18, 18);
const fill = new CK.Paint();
fill.setColor(CK.Color(220, 60, 60, 1));
const stripe = new CK.Paint();
stripe.setColor(CK.Color(40, 90, 180, 1));
loop(() => {
canvas.clear(CK.WHITE);
canvas.save();
canvas.clipRRect(card, CK.ClipOp.Intersect, true);
canvas.drawRect(CK.LTRBRect(0, 0, 420, 256), fill);
for (let i = 0; i < 10; i++) {
canvas.drawRect(CK.LTRBRect(40 + i * 40, 0, 60 + i * 40, 256), stripe);
}
canvas.restore();
surface.flush();
});
The stripes draw straight across, but the clip masks them to the card's rounded shape. Draws outside the rounded rect simply don't land. No offscreen buffer.
Use saveLayer when …
You want to apply an effect or blend to a group of draws, treated as one composite. Or you want a backdrop filter (frosted glass).
Group blend mode
Error: Line 1: Unexpected identifier
The two circles are drawn into an offscreen layer. When restore() runs, the composite (red ∪ blue) gets multiplied against the chess background — not each circle individually. Without saveLayer, the second circle would multiply against the first circle instead of against the background.
Backdrop blur (frosted glass)
Error: Line 1: Unexpected identifier
The saveLayer(null, null, blur, 0) followed by an immediate restore does no new drawing — but the backdrop filter samples the underlying chess pattern through the blur as the layer initializes. The clip first bounds the region.
The performance picture
clipRect etc. — pure scissor test. Free.
saveLayer — Skia must allocate an offscreen texture the size of the bounds (or the canvas if bounds = null), redirect rasterization there, then composite the texture back. On large canvases this is a significant cost. Use it when the effect actually requires it; reach for clip first if the goal is just "bound the area".
When in doubt
- "I want shapes I'm about to draw to be masked into a region" → clip.
- "I want a single blend / opacity / filter to apply to a group of draws as one composite" → saveLayer.
- "I want what's behind to be blurred / transformed / colored through a shape" → saveLayer with backdrop.
See also
Canvas.clipRect,clipRRect,clipPath.Canvas.saveLayer.ImageFilter— backdrops live here.ClipOp— Intersect vs Difference.