Takumi

Drawing

Replay paint steps and draw shapes, paints, and effects.

A node is a rectangle placed on the canvas. Its drawables sit in its local space, and node.transform maps that space onto the canvas. Canvas accepts the six matrix values in the same order.

NodeLocal space
boxIts border box
textThe border box of the block its lines fill
imageIts content box

All lengths use device pixels. With devicePixelRatio: 2, a CSS length of 20px becomes 40 in the tree.

Replay the steps

tree.steps lists the steps a renderer takes, bottom to top. z-index, negative stacking, and outlines that paint after later siblings are already in order.

replay.ts
import type { ,  } from "takumi-paint";

declare function (: , : ): void;
declare function (): ;

function (: , : ) {
  const : [] = [];
  let  = ;

  for (const  of .) {
    switch (.) {
      case "draw":
        .(.....);
        for (const  of .) (, );
        break;
      case "begin-clip":
        .();
        .(.....);
        // Clip to step.clip.
        break;
      case "end-clip":
        .();
        break;
      case "begin-group":
        .();
         = ();
        break;
      case "end-group": {
        const  = .() ?? ;
        // Filter, clip, and mask the layer per step.node.effects, then blend it into parent.
        .();
        . = ..?. ?? 1;
        .(., 0, 0);
        . = 1;
         = ;
        break;
      }
    }
  }
}

A group needs its own layer. Setting globalAlpha on each draw instead darkens where the group's draws overlap.

Draw a drawable

TypeDraw
fillFill shape with paint, clipped to every shape in clips
strokeStroke shape with stroke and paint
shadowBlur a copy of shape, filled or stroked with stroke, moved by offset, visible only outside or inside its box
glyphsFill the run's outline with paint, blurred by blur, or stroke it when stroke is set
imageDraw image into rect, clipped to clip
maskedDraw content into a layer, keep it only where mask covers, then composite it
groupDraw drawables into a layer, then composite it at opacity

A shadow or glyphs blur is the Gaussian's standard deviation, half of a CSS blur radius.

Every drawable carries a role, such as background, border, box-shadow, outline, text, or text-decoration. An exporter that rebuilds editable objects can use it to put a box's background, border, and shadow back into one shape.

Read paints

Gradient stops interpolate in sRGB. Takumi adds stops where the CSS interpolation space, such as the default Oklab, would differ. A repeating gradient's stops cover one period with spread: "repeat", as SVG's spreadMethod takes it; canvas gradients only pad, so repeat the stops yourself.

Background and mask layers arrive as pattern paints: one tile drawn at every x and y, clipped to area.

Apply effects

A box with effects composites as a group. Render the group's steps into a layer, run filters over it, clip it to clip, multiply it by the alpha of mask, then blend it with opacity and blendMode.

backdropFilters apply to what is already drawn behind the node, inside backdropClip, before the node draws.

Last updated on

On this page