draw
@profile(t1);
import gfx from "gfx";
import fill from "fill";
import draw from "draw";
/* What draw works in: your memory, nothing allocated inside */
struct DrawMemory {
cmds: [64]draw.Cmd;
edges: [2048]fill.Edge;
order: [2048]i32;
active: [2048]i32;
cov: [256]u32;
coverage: [65536]u8; // one 256 x 256 mask
filler: fill.Filler;
}
var mem: DrawMemory;
fn main() -> i32 {
fill.init(&mem.filler, mem.edges[0..2048], mem.order[0..2048], mem.active[0..2048], mem.cov[0..256]);
var p: draw.Path = draw.path(mem.cmds[0..64]);
if (!draw.parse_svg(&p, "M12 2 L22 20 H2 Z")) { return 1; } // a triangle, 24 units wide
var s: gfx.Surface = gfx.new_surface(256, 256, gfx.Format.Rgbx) or { return 2; };
s.clear(#FFFFFF);
let t: draw.Target = draw.target(s.pixels() as *u32, s.width, s.height, s.stride, true);
var m: fill.Mask = fill.Mask { pixels: mem.coverage[0..65536]; width: 256; height: 256; };
// Filled with a gradient, scaled 10x and moved
let place: draw.Transform = draw.Transform { scale: 10.0; tx: 8.0; ty: 8.0; };
draw.fill_path(&mem.filler, &m, &p, place, fill.Rule.NonZero);
var g: draw.Paint = draw.linear(0.0, 0.0, 0.0, 240.0);
draw.stop(&g, 0.0, 0xFFB13B, 1.0);
draw.stop(&g, 1.0, 0xE0452B, 1.0);
draw.paint(&t, &m, 0, 0, &g, null);
// Its outline stroked 4 px wide, round caps and joins
draw.stroke_path(&mem.filler, &m, &p, place, 4.0);
let ink: draw.Paint = draw.solid(0x202020, 1.0);
draw.paint(&t, &m, 0, 0, &ink, null);
s.free();
return 0;
}draw fills and strokes vector paths onto 32-bit pixels. Like
fill, which it draws with, it allocates nothing: you
give it the path’s storage, the filler’s arrays and the masks, so the same
code runs in a program, a kernel and on Uusi. It is a pure module.
| Function | Does |
|---|---|
path(storage) | A path over your []Cmd |
move_to, line_to, quad_to, cubic_to, close | Add to it; false when the storage is full |
parse_svg(&p, d) | SVG path data (M L H V Q C Z, absolute and relative); false on arcs (A, S, T) or when it doesn’t fit |
fill_path(&f, &mask, &p, t, rule) | Cover a mask with the path placed by t (Transform { scale, tx, ty }) |
stroke_path(&f, &mask, &p, t, width) | Cover it with the path’s outline, round caps and joins |
solid(rgb, alpha), linear(x0, y0, x1, y1), radial(cx, cy, r) | Paints; add a gradient’s colours with stop(&g, t, rgb, alpha) (up to 8) |
target(pixels, width, height, stride, bgr) | Pixels to paint on, opaque (a gfx.Surface of format Rgbx); set_clip(&t, x, y, w, h) keeps drawing inside a rectangle |
target_alpha(pixels, width, height, stride, bgr) | The same over pixels with alpha (format Argb) |
paint(&target, &mask, x, y, &paint, clip) | Blend the paint through the mask placed at (x, y), times a clip mask or null |
blur(&mask, &scratch, r) | Soften a mask (a shadow) |
Colours are 0xRRGGBB, alphas 0 to 1. A gradient’s coordinates are
device pixels (placed(&g, t) moves one from path units). bgr: true
means the pixels are 0xRRGGBB words, as a gfx.Surface is.
A target from target_alpha (its field alpha is true) has the pixel’s
alpha in the word’s top byte, whichever bgr says, and colours unmultiplied.
Painting blends as gfx does on an Argb surface, with the paint’s
alpha times the coverage as the source’s alpha: over an opaque pixel it stays
opaque, and over a transparent one the pixel gets the paint’s colour with
that alpha as its own. An uncovered pixel is left as it is. A target from
target is for opaque pixels: it writes 0 to the top byte, so on an Argb
surface it makes what it touches transparent.
For SVG files, with arcs, dashes, any transform and CSS, use
svg.
Last modified: