-
Notifications
You must be signed in to change notification settings - Fork 2
Architecture
Where to start, which of the package's drawing paths fits what you are building, and the rendering model underneath them. For what was built and why, see Changelog; for what was deliberately not, see Backlog.
flowchart LR
Start(["canvas"]) --> Pick{"Pixels, markup or pages?"}
Pick -->|"raster"| Canvas["Canvas(width, height, fill)"]
Pick -->|"vector"| Svg["SvgCanvas(width, height)"]
Pick -->|"document"| Pdf["PdfCanvas(width, height)"]
Canvas --> Draw
Svg --> Draw
Pdf --> Draw
subgraph Draw["Draw — the same DrawTarget methods on any backend"]
direction TB
Prim["Shape primitives<br/>fill_rect, fill_circle_aa, fill_arc_aa,<br/>draw_line_aa, fill_circles_aa, fill_mesh …"]
PathAPI["Path<br/>move_to → line_to / curve_to / arc_to<br/>→ fill_path_aa / stroke_path_aa"]
Text["draw_text / stroke_text / draw_text_on_path<br/>(native font discovery → native TTF/CFF parser<br/>→ glyph rasterization)"]
Grad["Gradients and patterns<br/>(paint source for either)"]
State["save / restore, transforms, clips,<br/>blend modes, color space, begin_batch"]
Text --> PathAPI
Grad -.->|"paint"| Prim
Grad -.->|"paint"| PathAPI
end
Img["read_png / read_bmp / read_jpeg<br/>→ a Canvas to compose or place"] --> Canvas
Canvas --> Png["write_png / write_bmp"]
Svg --> Str["to_string() / write_svg"]
Pdf --> File["write_pdf<br/>→ pages, fonts and images embedded"]
Canvas (canvas/buffer.mojo) rasterizes into an in-memory RGBA
buffer with straight alpha. SvgCanvas (canvas/vector/svg.mojo)
accumulates SVG markup, and PdfCanvas (canvas/vector/pdf.mojo)
accumulates PDF page content with embedded font subsets and images;
neither does any anti-aliasing of its own, since a viewer rasterizes
at whatever resolution it displays. All three implement DrawTarget
(canvas/vector/draw_target.mojo). A fourth conformer,
BoundsTarget (canvas/bounds.mojo), draws nothing and keeps the
union of what it was asked to draw, so a scene can be measured before
a real target is sized to it: render once into it, read
ink_pixels(), render again translated by minus the box's corner.
The trait carries the drawing primitives a chart needs: rectangles,
lines, circles, ellipses, arcs and ring sectors; bulk markers
(fill_circles_aa, fill_ellipses_aa, fill_arcs_aa); meshes
(fill_mesh, fill_mesh_shaded); path fill and stroke with the full
stroke style; draw_image for a raster block; rectangle clips;
annotated groups; begin_batch/end_batch; and the state model, which
is transforms with save/restore, the blend mode and the color
space. Text is deliberately not on the trait, because its
representation differs by backend: Canvas rasterizes glyphs,
SvgCanvas emits text elements, PdfCanvas embeds font subsets. A
caller draws text through the concrete backend.
Pick Canvas for pixels, a PNG to embed or a notebook preview;
SvgCanvas when the viewer should own resolution and zoom;
PdfCanvas for print, multiple pages, or a document whose text must
stay extractable.
Shape primitives (canvas/shapes/): one function per shape, in a
hard-edged and an anti-aliased form. lines.mojo holds lines,
polylines and polygon outlines with dashes, caps and joins;
rects.mojo, circles.mojo, ellipses.mojo and arcs.mojo the
filled and outlined shapes; polygon_fill.mojo the polygon fills;
mesh.mojo the seam-free mesh. See examples/circles.mojo,
examples/arc.mojo, examples/mesh.mojo.
Path (canvas/path.mojo): arbitrary geometry from move_to,
line_to, quad_curve_to, cubic_curve_to, arc_to and close,
then fill_path_aa under either fill rule, stroke_path_aa, or the
gradient and pattern fills. Paths can also be transformed, measured,
and hit-tested with in_fill and in_stroke. See examples/path.mojo
and examples/fill_rule.mojo.
Text (canvas/text/): draw_text, stroke_text,
draw_text_on_path, measure_text, measure_text_block, and the
prepared-layout API (prepare_text, measure_layout, draw_layout).
font_discovery.mojo resolves a family, slant and weight to a font
file by reading the installed fonts' own tables; ttf.mojo and
cff.mojo parse TrueType and OpenType outlines, metrics and CBDT
color bitmaps; bidi.mojo and joining.mojo handle bidirectional
runs and Arabic joining; font_cache.mojo is the caller-owned cache
for resolved paths, parsed faces and rasterized glyph masks. A glyph
outline is a Path, so text is a consumer of the path rasterizer
rather than a separate drawing mechanism. See examples/text.mojo.
Paint sources (canvas/gradient.mojo, canvas/pattern.mojo):
linear, radial and conic gradients and raster or hatch patterns, which
the rectangle and path fills accept in place of a flat color. See
examples/gradient.mojo and examples/patterns.mojo.
Compositing and effects (canvas/compose.mojo, blend.mojo,
mask.mojo, blur.mojo, resize.mojo): place one canvas on another
with an offset, a transform or a mask; the Porter-Duff operators and
the separable and non-separable blend modes; 8-bit coverage masks;
Gaussian blur and drop shadows; integer downsampling and general
resizing. See examples/layers.mojo and examples/shadows.mojo.
Every pixel is written once per primitive. Each rasterizer resolves
coverage for a pixel and calls set_pixel once, so a translucent fill
never blends over its own interior seams. Pixel (x, y) is the square
centered at (x, y), and every rasterizer shares that convention, so a
hard-edged and an anti-aliased fill of the same shape agree on the
boundary.
Two anti-aliasing rasterizers. aa_area.mojo computes exact
signed-area coverage per pixel and is what nonzero fills, strokes and
paths use; aa_crossing.mojo samples a sub-pixel grid and serves the
even-odd rule and the shapes whose coverage has no closed form. Disks
and ellipses under a size limit use closed-form coverage instead of
either. A mesh rasterizes its faces hard-edged at sixteen sub-samples
per pixel with a fixed rule for which face owns a sub-sample on a
shared edge, which is what makes shared edges seam-free.
Row bands across cores. A large pass splits the canvas into
horizontal bands, one task each, through std.runtime.asyncrt. Bands
write disjoint rows, which is the whole safety argument. Whether a
pass bands at all is decided per pass from what it does per pixel: a
pass that only stores switches on bytes against the cache slice
canvas/machine.mojo probes from the operating system, and a pass
that reads and computes bands regardless of size. Every threshold in
canvas/workers.mojo and the primitives was measured on the machine
that set it, and the reasoning is in the Changelog.
Batches. begin_batch records every subsequent primitive as a
compact op with ranges into shared side lists rather than drawing it;
end_batch builds the geometry in parallel and replays the ops band
by band (canvas/batch.mojo). A scene of thousands of small shapes
renders across cores as one pass instead of thousands of serial calls.
Output is byte-identical to drawing the same calls at once.
Supersampled regions. begin_supersampled(factor) records, and
end_supersampled replays one output band at a time into a scratch
holding only that band's enlarged rows, downsampling each into the
canvas. An 800x600 canvas at factor 3 never holds the 16.5 MB
intermediate the two-step recipe needs, and the result is
byte-identical to that recipe. A primitive with no recorded form falls
back to materializing the buffer, with the output unchanged.
Straight alpha, sRGB by default. Color holds four bytes with
straight alpha so get_pixel returns the color a caller recognizes.
set_color_space(ColorSpace.LINEAR) moves source-over blends,
gradient interpolation and mesh shading into linear light on the
raster backend.
Canvas writes through write_png and write_bmp (canvas/io/),
and read_png, read_bmp and read_jpeg (baseline and progressive)
read a file into a fresh Canvas for composing or placing. SvgCanvas
returns its markup from to_string() or writes it with write_svg.
PdfCanvas writes one or more pages with write_pdf; new_page()
finishes the current page and starts another.
| Directory | Contents |
|---|---|
canvas/ |
buffer.mojo (Canvas), path.mojo, batch.mojo, gradient.mojo, pattern.mojo, compose.mojo, blend.mojo, mask.mojo, blur.mojo, resize.mojo, color.mojo, geometry.mojo, machine.mojo, workers.mojo, the two AA rasterizers |
canvas/shapes/ |
one module per shape family, plus mesh.mojo and dash.mojo
|
canvas/text/ |
discovery, parsing, layout, bidi, the font cache and rendering |
canvas/vector/ |
draw_target.mojo, svg.mojo, pdf.mojo, pdf_font.mojo
|
canvas/io/ |
PNG, BMP, JPEG and the DEFLATE codec |
tests/ |
one file per module, plus golden images and byte-identity suites |
examples/ |
one runnable program per feature, rendered into the docs site |
benchmarks/ |
the survey, the micro-benchmarks, the recorded reference and digests |
CONTRIBUTING.md in the repository covers the conventions a change is
expected to hold to, and AGENTS.md the operational rules for working
on the code.