# CanvasRenderer

Import it from `@grafloria/renderer`.

```ts
class CanvasRenderer implements IRenderer
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` |  |  | Renderer mode |
| `capabilities` | `RendererCapabilities` |  | Optional: capability flags for runtime feature detection, so callers can ask instead of assuming (e.g. "can this renderer do foreignObject?"). |

**Methods**

- `constructor(engine: DiagramEngine, options: CanvasRendererOptions = {})`
- `render(viewport: Rectangle, zoom: number): VNode` — Produce the frame's VNode tree and paint it.

Returns the tree (rather than `void`) on purpose: it is the same tree SVG
mode would have reconciled, so a caller can assert parity, or hand it to the
SVG patcher, without re-rendering.
- `getPerformanceMetrics(): PerformanceMetrics` — Get performance metrics
- `hitTest(x: number, y: number): VNode | null` — The topmost element at a WORLD coordinate.

Colour-keyed pixel picking when a real hit canvas is available (O(1), and
exactly what was drawn); the geometric fallback otherwise. They are two
implementations of ONE geometry — both read the hit records the paint pass
produced — and they are pinned to each other, and to the engine's own
`getNodeAtPosition`, in `canvas-hit-parity.spec.ts` and in the browser e2e.
- `pick(x: number, y: number): CanvasPick | null` — Hit-test, with the entity identity the interaction layer wants.
- `pickPixel(x: number, y: number): HitRecord | null` — Colour-key pick: read one pixel off the hit canvas and look the colour up. `null` when there is no hit canvas (headless) OR when the pixel is empty.
- `pickGeometric(x: number, y: number): HitRecord | null` — Geometric pick: reverse paint order over the hit records, point-in-path for
filled regions and distance-to-path for stroked ones. O(n), used headlessly
and as the oracle the pixel picker is checked against.
- `measureText(text: string, style: TextStyle): TextMetrics` — Text measurement through the real 2D context when there is one (proper glyph
metrics), falling back to the same average-glyph estimate the SVG text-block
engine uses so headless measurement stays consistent with the wrapping the
producer already did.
- `async export(format: ExportFormat, options: ExportOptions = {}): Promise<string>` — Export the CURRENT canvas contents. `svg` is not a canvas capability.
- `dispose(): void` — Dispose renderer and clean up resources
- `getTheme(): Theme`
- `setTheme(theme: Theme): void` — Swap the theme. Repaints everything: every resolved colour just changed.
- `getProducer(): SVGRenderer` — The VNode producer — shared with the SVG backend on a mode switch.
- `setCanvas(canvas: CanvasLike | null, hitCanvas?: CanvasLike | null): void` — Attach (or replace) the canvas this renderer paints into.
- `setDevicePixelRatio(dpr: number): void` — Change the device pixel ratio (a window moved to a retina display).
- `getDevicePixelRatio(): number`
- `getFrameStats(): CanvasFrameStats` — What the last frame actually repainted — the dirty-region proof.
- `getHitRecords(): readonly HitRecord[]` — Every pickable region of the current frame, in paint order.
- `getUnpaintableNodes(): readonly VNode[]` — Nodes the canvas could not paint (`foreignObject` — live HTML/components). A host that uses them should mount an HTML overlay for exactly these.
- `invalidate(): void` — Force the next frame to repaint the whole canvas.
