A transparency group whose members blend with the art below it, and a
masked object with a blend mode, are drawn over a copy of that art. For
each such group in each frame, the renderer rendered everything drawn
so far into a new pixmap the size of its context (on the canvas, the
whole view), started that context again and drew that pixmap back into
it as an image, which the next copy and the frame rendered again. At
2880x1800, a document with 50 translucent groups, each holding a
Multiply rectangle, took 299 ms a frame on 8 threads.
Every render that composites in 8 bits, the canvas included, now keeps
that picture and renders what the context draws next over it. The
backdrop is still cut from the picture. The 50 groups take 65 ms a
frame, and the frames have the same pixels. Renders that composite in
floating point (raster exports, Save for Web, Object › Rasterize and
Crop Image) still draw the picture back.
Docs: docs/target-app-parity.md.
The renderer kept one tile per pattern name and ink plane, and used it
only at the raster scale it was made at: the device scale rounded up to
a power of two and capped. Two objects that paint one pattern at scales
for which the raster scale differs, such as a pattern scaled to 25%
with Transform Pattern Tiles next to the same pattern at 100%, replaced
each other's tile, and every frame rasterized both tiles again.
Tiles are now kept per pattern name and raster scale, within the tile
cache's budget of 128 MiB per thread and ink plane. When the budget
fills, the cache drops the least recently used tiles first. A tile is
still used only while the pattern's art, tile, tile type and overlap
are the ones it was made from. At 760%, with a pattern of 576 stars in
a 288 pt tile on a rectangle that fills the view, and the same pattern
at 25% on a square, release builds of vectorcraft-cli bench at
2880x1800 took a median of 26.0 ms a frame on main and 8.8 ms with this
change, with 8 threads.
Docs: docs/target-app-parity.md.
fx.rs draws blur, shadow and glow rasters as image paints that map 1:1
onto their rect, but only set the geometry transform: the paint
transform a pattern fill (or a radial or freeform gradient) left on the
context still applied, so in supersampled renders (raster exports) the
effect came out tiny and displaced. Both draws now reset the paint
transform first.
Fixes#1536
The renderer kept the rasters of an object's drop shadows, outer glows
and filtered content (Gaussian Blur, Feather and the Photoshop-style
filters), and drew them again while the object, the zoom and rotation
and the ink plane stayed the same. The content drawn into a raster also
reads things outside the object: patterns, symbols, brushes, images,
document, layer and color settings, the knockout a group inherits, the
fonts, and the options the frame is drawn with. When one of them
changed, a renderer that had drawn the object went on drawing the
raster it had kept, made from the old values.
Each kept raster now records what its content was drawn from: which of
those things the object reads, found by walking the object when the
raster is made, and a hash of their values then. The renderer draws
the raster only while that hash is the same in the current frame. Once
a frame, the renderer hashes the document's patterns, symbols and
images. It hashes their art and pixels by the addresses of their
shared values, and it keeps weak references to those values while a
raster made with them is kept, so no other value can take one of those
addresses. A filtered raster also records the color channels its
filters split, and under De-Interlace the raster resolution.
A temporary release test timed the inputs of 400 kept rasters at about
10 microseconds a frame.
Preferences › Appearance of Black › On Screen was stored and never read.
Display All Blacks Accurately (the default) shows pure CMYK black, black
ink alone at 100%, through the working CMYK profile, a dark grey, as the
canvas always did. Display All Blacks as Rich Black now shows it on the
canvas as RGB black: fills, strokes, type, gradient stops, effect colours
and pattern tiles, and in CMYK documents drawn by ink planes a pixel
whose inks come out as black ink alone. Rich blacks, black tints and
placed images show the same either way; exports and printing are
unchanged (Printing / Exporting is a separate option).
The renderer gets RenderOptions::rich_black, painted with a new
Ink::RichBlack (screen colours with pure black as RGB black) and applied
by ink::compose. The canvas sets it from blackOnScreen and keeps it in
its cache key, so changing the preference redraws.
A headless frame test reads the canvas raster: K100 shows its profile
grey by default, RGB black with rich, a rich black unchanged, and grey
again with accurate. A render test covers the opaque and ink-plane paths
and checks that rich blacks, tints and images don't change.
Docs: docs/mcp.md (blackOnScreen), docs/target-app-parity.md,
docs/gaps.md, docs/ui-parity.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text geometry, clips, clip paints, effect art, brushes, live effects, pattern tiles and freeform
grids were capped by entry count and cleared wholesale on overflow, so a document past the cap
(e.g. more than 4,096 type objects) recomputed every entry on every frame. A FrameCache keeps
per-entry byte sizes and last-used frames, drops the least recently used entries of earlier
frames, and sweeps stale ones. Text geometry keeps its glyph outlines once instead of twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
The renderer composited in 8-bit integers, so where a partly transparent
anti-aliased edge crossed opaque art (or a background colour) the result
came out at alpha 254 on some pixels, and PNG, TIFF and WebP files were
written with an alpha channel. Raster exports (and Save for Web) now
composite in floating point (vello_cpu's f32 pipeline, enabled as a
feature), offscreen groups, masks and patterns included; the canvas keeps
the faster 8-bit path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CMYK document that shows transparency is drawn once per ink plane
(complemented C, M, Y; complemented K as a grey) so every blend mode,
opacity, group, knockout and effect composites inks, PDF-style; the
planes' inks are shown through the working CMYK profile (cached 4-D XYZ
table). Cyan multiplied over magenta now shows C100 M100. Opacity masks
keep screen luminance; RGB colours and placed images are separated
first. RGB documents and opaque CMYK documents keep the screen-colour
path.
PDF export of CMYK documents writes its transparency groups with
/CS /DeviceCMYK (luminosity mask groups stay RGB) and puts top-level
transparency in a non-isolated page-wide group so it blends in CMYK too.
Flatten Transparency composites CMYK documents ink by ink and makes CMYK
regions. Non-separable modes no longer give NaN for greys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Object > Pattern > Tile Edge Color... picks the colour of the tile edge
(a layer-colour preset or a custom colour), stored in the preference
patternTileEdgeColor and passed to the renderer. Pattern Options gains
Show Swatch Bounds: a dashed outline of the part of the tiling the swatch
repeats (the period of brick and hex tilings).
Commands: prefs.set {patternTileEdgeColor}, pattern.options
{showSwatchBounds}, ui.tileEdgeColor. Model: PatternDef.show_swatch_bounds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On screen, a group (or object) with opacity, a blend mode, an opacity mask or
knockout whose content blends is drawn offscreen over a copy of its backdrop
and mixed back in by its opacity, mask and clip (a group blend mode then
applies to the backdrop-free result, as PDF defines it). Isolate Blending keeps
the content starting from transparency. Clip groups no longer isolate their
children (blending content is clipped per drawing), and non-isolated knockout
groups composite their elements against the art below the group. Groups
without blending inside still render as plain layers.
In PDF, such groups are written as non-isolated transparency groups (a
constant-alpha soft mask instead of the writer's isolated opacity group),
knockout groups included.
doc: Node::blends_through / children_blend.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New original DrawCraft mark (gradient + pen nib) replaces the two-letter badge
- render: thumbnails and pattern tiles use a single-threaded context (vello's default is
multithreaded and panics on filters); per-item raster effects detected
- Type tool state no longer leaks across documents
- ui.set {status} for clean screenshots
- README: new hero, feature shots, Made in DrawCraft gallery; examples/neon-drive, ribbons,
feature-sheet; ASSETS.md updated; heading renamed to Crafting Apps
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>