Commit Graph
464 Commits
Author SHA1 Message Date
dotCooCoo 30f903bf60 Navigator: Panel Options sets the view box color and draws dashed lines as solid
The Navigator panel menu's Panel Options… is enabled and opens a dialog with
the view box color (a preset or a custom color; Light Red, today's color, is
the default) and Draw Dashed Lines as Solid Lines, off by default, which draws
dashed strokes without their dashes in the panel's thumbnail only. The
options are kept with the UI state, and ui.navigatorPanelOptions sets them.

RenderOptions::solid_dashes draws dashed strokes as solid lines, including
type strokes and the silhouettes of stroke effects.
2026-11-04 18:29:28 +07:00
dotCooCoo 4c51f853c1 Render: node renders follow the anti-aliasing option
render_node_with and render_node_thumbnail_with drew into a new context
without setting its aliasing threshold, so AntiAlias::None still gave
smooth edges. Both now set the threshold from the options, as a page
render does.
2026-11-04 18:28:35 +07:00
dotCooCoo d6190e79e0 Render: a filter layer's offscreen culls art and crops long strokes to its own area
On a multithreaded context, a drop shadow or glow drawn through a filter
layer is drawn into an offscreen that covers the art plus the blur's
spread, but its frame kept the view's visible rect. Art outside the view
but within the spread was culled, so it cast no shadow, and a long stroke
was outlined only near the view, so its far parts no longer fed the blur.
The offscreen's frame now takes its visible rect from its own area, and
the frame of content kept from an earlier split takes the window it is
drawn over.
2026-11-04 16:55:30 +07:00
dotCooCoo 676cc0ee8e Render: a long stroke is outlined only near the view after an edit
The renderer outlined the whole stroke of an edited path on every frame,
so dragging one anchor of a path with many anchors re-outlined every
segment. A stroke line of more than 1,024 elements without dashes or a
width profile is now outlined only for the segments within the stroke's
reach of the view, inflated by the view's own size on each side. The
cached outline keeps that region, and a frame outside it outlines the
stroke again. Closed subpaths that a wide stroke crosses over stay whole,
and a cut closed subpath is walked from a removed segment so no cut lands
where it starts and ends. The pixels are the same.

At 1600 x 1200, the frame after one anchor moves takes 28 to 30 ms
instead of 103 ms on an open path of 200,000 anchors, and 10 ms instead
of 30 ms on a closed path of 50,000 anchors (the new anchor_bench
example: cargo run --release -p vectorcraft-engine --example anchor_bench).
2026-11-04 16:55:30 +07:00
dotCooCoo 9c191793e2 Images: a PNG read a row at a time counts all of the reader's buffers
The room estimate for a large PNG read a row at a time counted 32 bytes
a column. The png reader also keeps at least 128 KiB of rows already
read, the 32 KiB deflate window and a growth step in its stream buffer,
and up to four rows for wide images, so a decode could be approved with
less room than reading takes. The estimate now comes from the PNG
header's row length and these buffers, and a PNG is read a row at a
time only while that estimate fits the decode allowance.
2026-11-04 15:41:03 +07:00
dotCooCoo f15a16f2c1 Render: placed-document bitmaps draw undecoded-image marks when the view does
On screen, a placed document is drawn from a bitmap made in the
background, and that bitmap was rendered with the default options. An
image inside the placed file whose pixels cannot be decoded therefore
drew nothing in the bitmap, even though the canvas marks such images
with a crossed box. The bitmap now follows the frame's
mark_undecoded_images setting, the bitmap cache keys on it, and a
bitmap standing in for one still being made has the same setting.
2026-11-04 14:56:36 +07:00
dotCooCoo c6e47fece3 Render: keep the automatic thread helper out of Wasm libraries 2026-11-04 08:12:09 +07:00
datatimp c02b62dacb Merge remote-tracking branch 'upstream/main' into fix/3260-halftone-radius-pixels 2026-11-03 14:27:49 -05:00
Dylan M. Taylor 05bdd182b8 fix(render): charge fx symbol instances, admit CMYK decodes, checked Targa size 2026-11-03 23:29:41 +07:00
Ahmed Gomaa 1d011ab8ad Render: a compound path's members are assembled once and kept with the node
A compound path was assembled from its members in every frame it was
drawn, and Node::stroke_path grew its buffer by doubling, copying the
path into ever larger buffers for a compound path of many anchors. The
renderer's geometry cache was swept only once it held more than 1,024
entries, so while an object was dragged (a new node, and layer, in
every frame) the old copies stayed alive with all their members.

Now:
- a compound path is drawn from the path the renderer keeps for it in
  its geometry cache, as a plain path already was, and is assembled
  only when the node changes;
- Node::stroke_path sizes a compound path's buffer up front from its
  members' segment counts (the same elements, in the same order);
- unused geometry entries are dropped after a few frames however few
  entries there are.

Tests:
- crates/render/src/tests/memory.rs: the second frame draws the kept
  path and the same pixels as the first and as a new renderer; after a
  five-frame move the renderer no longer holds the compound path drawn
  before it. The test fails without the renderer change.
- crates/doc/src/node/tests.rs: a compound path's path is its members'
  paths in order (open, closed, empty and single-anchor members).

Not timed; vectorcraft-cli perf was not rerun.
2026-11-03 16:03:40 +07:00
dotCooCoo d324440afb Pathfinder: blends, envelopes and warped objects can be compound shape members
Squashed from #5226 onto main.
2026-11-02 15:22:32 +07:00
dotCooCoo 83f2bfe9d6 Symbols: a scaled instance of a symbol with 9-slice guides keeps its corners and edges unstretched
Squashed from #5079 onto main.
2026-11-02 15:22:07 +07:00
Don Omondi 85138b2652 Repeat isolation: draw the isolated repeat undimmed; test exit, undo and groups
A live object reached the renderer without passing the isolated-object check that groups go
through, so an isolated repeat was dimmed with the art around it. Tests cover leaving isolation
(double-click outside, exitIsolation), undo/redo of an edit inside, and a repeat of a group.
2026-11-02 14:36:15 +07:00
bflatastic 59992ce155 Tests: serial()'s doc comment goes back above it 2026-11-02 14:36:14 +07:00
Don Omondi b903e2e7e7 Placed-document tests: register the gated loader everywhere
set_loader keeps the first registration, so a test registering blue_page first left the gate
out. Every test in the file now registers gated_blue_page (blue_page while the gate is open).
2026-11-02 14:36:14 +07:00
Don Omondi f4979c7e54 Fix #5152: placed-document bitmap test no longer races its worker
a_ready_bitmap_draws_again_where_its_file_shows assumed the first frame shows placeholders,
but the background worker can make the tiny test bitmaps between scheduling and the frame's
second lookup, so the frame already showed them ("the bitmaps show" failed on FreeBSD CI).
The test's loader now waits until that frame is drawn.
2026-11-02 14:36:14 +07:00
dotCooCoo 52f6adfa3b Symbols: the instance a symbol is edited from is not drawn in the view while it is edited
Entering symbol editing mode from an instance puts a copy of the symbol's art on the editing layer in the instance's place, and the instance stays in the document. The view drew the instance dimmed under the copy, so art with transparency showed darker, and its edges showed a dimmed fringe.

The renderer now leaves that instance out of the screen view while the editing layer is isolated, with its masks and effects, and in Outline view inside a clip group. Exports and thumbnails draw it as before.
2026-11-02 12:04:19 +07:00
dotCooCoo da078bf11b Type: test that type past the paint group limit takes the anti-aliasing method
Nine runs with opaque solid fills and strokes make ten groups of paints, past
the eight the renderer draws one at a time. The test checks that the type is
drawn as one group whose outer edges Crisp and Strong reshape, and that None
paints whole pixels.
2026-11-01 09:54:31 -08:00
dotCooCoo 92e71a9fce Type: test that the four anti-aliasing methods draw the same text four different ways 2026-11-01 08:49:19 -08:00
dotCooCoo db40218790 Type: Crisp and Strong reshape the inner edge of a character stroke
A run's solid fill and its stroke no longer share one edge group, so the
stroke's edge inside the characters takes the method. docs/mcp.md says the
native document an SVG, PDF or EPS file carries keeps each frame's method.
2026-11-01 06:08:13 -08:00
dotCooCoo 16972e6589 Undo: on Linux the memory budgets follow the process's cgroup memory limit
The installed memory that sizes the image cache and the undo history is
now the lower of MemTotal and the memory limit of the process's cgroups
and their ancestors (memory.max under cgroup v2, memory.limit_in_bytes
under v1), looked up under the cgroup mounts in /proc/self/mountinfo. A
file that holds max or can't be read sets no limit.
2026-10-31 19:56:33 -07:00
dotCooCoo faf62c515d Character: Crisp and Strong reshape the edges of each of the type's paints on its own
Crisp and Strong scaled each edge pixel of the composited type by the
share the method gives the characters' coverage. Where translucent
paints overlap, that drew the edges too opaque: two 50% fills at half
coverage came out at 0.875 with Strong instead of 0.75.

Each of the object's own fills and strokes, and each fill and stroke of
a run, is now drawn offscreen on its own and takes the method from its
own coverage, and the results are composited in paint order. Fills and
strokes of consecutive runs in solid colors are drawn together while the
colors are opaque. Type with more than eight such groups is drawn as one
group, as before. A pixel that every group covers wholly or not at all
keeps the pixel Sharp draws, so faint fills composite inside the
characters before their colors are rounded.
2026-10-31 19:51:22 -07:00
dotCooCoo 9bb38208c1 Character: type whose method would draw past the pixel budget is drawn as Sharp
None, Crisp and Strong type is drawn offscreen on the output's pixels.
The offscreen drawing was limited to 32,768 pixels a side, but not in
area, so type across a large raster export held several more images of
the export's size. Type whose offscreen drawing would hold more than the
renderer's pixel budget (8.4 million pixels) is now drawn as Sharp draws
it.
2026-10-31 19:17:08 -07:00
dotCooCoo c3b88e3e92 Merge main into feat/character-anti-aliasing-method
# Conflicts:
#	crates/engine/src/cmd/panelcmds.rs
#	crates/ui-egui/src/panels/character.rs
2026-10-31 18:10:03 -07:00
bflatastic f22caa6108 Fix #4938: Dynamic symbols keep per-instance appearance linked to the symbol 2026-11-01 04:53:38 +07:00
dotCooCoo 9a2d6a2c0e Merge main into fix/undo-history-follows-memory 2026-10-31 09:33:30 -07:00
dotCooCoo 4c32032d34 Part of #3602: a node thumbnail reports the size it is drawn at
render_node_thumbnail_with draws into a context of size.clamp(1,
MAX_CONTEXT_SIDE) pixels but reported width and height as the requested
size, so a size of 0 gave a 0x0 image holding 4 bytes. The view was
also built from the requested size, and the fit scale (size - 2) /
extent was 0 or negative at sizes 1 and 2, so the art drew nothing or
mirrored.

The thumbnail now reports the side it draws, builds the view from it,
and drops the 1-pixel margin when the side is too small to keep one.

Test: node_thumbnail_reports_the_size_it_draws.
2026-10-31 23:29:33 +07:00
Don Omondi 1679318034 Type: lam-alef highlight follow-up: optional features, all marks, exact ligature test (#394)
Highlight Alternate Glyphs no longer drops a lam-alef cluster's glyphs
outright: the ligature is clear when required shaping, or shaping with
liga left on (older fonts), gives it, so an optional feature such as
salt that changes it is highlighted again. is_lam_alef takes any
combining mark by Unicode general category (Arabic Extended-A/B/C
included) instead of a partial list. The test font maps the fatha to a
mark and skips marks in its ligature, so the mark-spanning test checks
the exact ligature glyph and cluster; it gains a salt lookup and test.

Also a one-line equivalent nonminimal_bool repair in pathops'
pathtools that strict Clippy needs on main.
2026-10-31 19:43:17 +07:00
Taha-Mahmoodi f65d28378f Fix #995: a width-profile stroke keeps its shape when the view zooms out
The canvas keeps a hairline visible by raising a stroke's weight to half a device pixel. A width profile multiplies that weight, so a 1 pt stroke with a 6x width point swelled to twice its size at 25% zoom and shrank back on zooming in. The minimum now goes to the profile's widest point instead; plain strokes are unchanged.
2026-10-31 19:43:16 +07:00
dotCooCoo dd83e063a4 Render: pattern tiles draw undecoded-image marks when the view does
Pattern tiles were drawn with the default render options, so an image
whose pixels cannot be decoded drew nothing inside a pattern fill even
when the render marks undecoded images, as the canvas does. Tiles now
follow the caller's mark_undecoded_images setting, and the tile cache
keys on it.

An opacity mask with such a pattern now treats the mark as part of the
mask, as it does for an undecodable image outside a pattern.
2026-10-31 19:43:15 +07:00
dotCooCoo 8aa8bb4d9d Render: a mask art root, symbol art root or thumbnail keeps its own opacity mask
The renderer drew the root node of an opacity mask's art, of placed symbol
art, of an art brush piece, of an Image Trace source image and of a node
thumbnail without its own opacity mask. Make Opacity Mask uses the top
object as the mask art, so a mask made from an object that already had an
opacity mask ignored that inner mask. These roots now draw through the
same masked group as any other node, still drawn whatever their visibility
and bounds, and an incomplete inner mask still leaves out the outer object.
2026-10-31 19:43:15 +07:00
dotCooCoo 4e785b40ce Part of #4516: Windows renders start workers only for the cores the process may use
The renderer sized its worker pool from available_parallelism, which on
Windows counts every processor in the machine, also when the process's
affinity mask allows fewer. A process allowed one core started 8 workers,
and the renderer's flush spun on that core while the workers got no time,
so renders crawled or stalled.

The desktop app and vectorcraft-cli now read the affinity mask at startup
(src/windows_cores.rs, shared) and pass the count to the renderer with
vectorcraft_render::set_usable_cores. With one usable core the renderer
renders on the calling thread.
2026-10-31 18:44:55 +07:00
dotCooCoo 8f08e24ae8 Merge main into fix/undo-history-follows-memory 2026-10-30 20:35:05 -07:00
dotCooCoo 21d00bf04c Merge main into feat/character-anti-aliasing-method 2026-10-30 19:23:59 -07:00
datatimp b8846ee00f Color Halftone: Max Radius in pixels of the raster effects resolution (#3260) 2026-10-30 21:16:40 -04:00
dotCooCoo 44bac0d7cf Render: leave out an object whose opacity mask lost an image
With undecoded-image marks off, an image in an opacity mask's art that
failed to draw left a hole in the mask. Without Clip, the area outside
the mask art counts as white, so the masked object showed in full.

The renderer now notes an image that fails to draw while mask art is
drawn and leaves the masked object out, as PDF export does since #4765.
This covers images in pattern tiles and in cached effect and shadow
rasters, which remember the failure and report it again when they are
reused. Nested masks pass the failure outward, and View Opacity Mask
shows no coverage for such a mask. An image still waiting to be decoded,
or drawn with the undecoded-image mark, keeps today's behavior.
2026-10-31 07:33:49 +07:00
Don Omondi de703fe722 M5: opaque knockout fills draw once in hard-edge and supersampled raster export
A knockout element with one opaque Normal solid fill (a path or
compound, no mask, effects or isolation) is drawn once under
AntiAlias::None, including the internal hard-edge frame of Art
Optimized supersampled exports, instead of being erased and added back
through per-element backdrop layers. Analytical antialiasing keeps the
existing compositor and rounding.

Exact-pixel tests compare against the general knockout path for paths
and compound holes across precision, supersampling, translucent parents
and copied backdrops, and check that the backdrop rasters are skipped;
translucent fills keep their knockout compositing.

# Conflicts:
#	docs/roadmap.md
#	docs/target-app-parity.md
2026-10-31 06:28:42 +07:00
Don Omondi c1971284ce Type: required lam-alef is not an alternate in fonts that put it in liga (#394)
Highlight Alternate Glyphs compares each glyph with required shaping
alone, which turns liga and calt off. Older Arabic fonts (such as the
legacy Arial of the Microsoft core fonts) put the mandatory lam-alef
ligature in liga instead of rlig, so every lam-alef was highlighted.
A lam + alef cluster (any alef form, marks allowed) is now never an
alternate candidate, whatever feature the font uses.

Adds a test font built in code (lam-alef under liga or rlig) and
host-independent regressions. Also two equivalent one-line repairs
strict Clippy needs on main: a nonminimal_bool in the Path Precision
Tools inspect check and is_none() in a gradient panel test.
2026-10-31 02:52:05 +07:00
dotCooCoo 406b307ec1 Merge main into feat/character-anti-aliasing-method 2026-10-30 12:05:42 -07:00
bflatastic ebc5b7038c Add-ons: Color Effects — Duotone effect and Print Ink Fixes
Effect › Color Effects › Duotone… (addon.color.duotone, also in Object ›
Plug-ins › Color Effects) prints the grey-equivalent tone of each colour
with one to four inks, each with its own tone curve. It is a colour
adjustment like the add-on's others, so it stacks with them and works as
a filter through addon.color.filter.

- One tone of a swatch the document has gives a tint of that swatch,
  linked, so a spot tone separates onto its plate; several tones give
  the CMYK mix of their tints (the inks overprint, so they multiply).
- Colour adjustments now map paints with their swatch links
  (ColorMap::apply_link); the existing adjustments behave as before.
- Skip Objects with Spot Colors leaves spot-linked colours as they are.
- The dialog has a row per tone and the picked tone's curve (the Curves
  graph, reused), its points and Reset to Linear / Inverse.

Print Ink Fixes (addon.color.printFixes, Object › Plug-ins › Color
Effects › Print Ink Fixes…) scales CMYK colours over a total ink limit
down to it, removes white overprint and sets black overprint (Overprint
Black's logic) in a CMYK document, as one undo step, and reports what
changed. RGB documents are refused.

The panel opens both under Inks.

Deferred: an opaque undercoat, tones for type only, extra gradient stops
for smoother tints, curve flip and all-100 % buttons.
2026-10-31 00:26:18 +07:00
dotCooCoo 7e61f10074 Merge main into feat/character-anti-aliasing-method 2026-10-30 09:45:49 -07:00
bflatastic f4c245286e Add-ons: Color Effects — live Vector Halftone effect
Effect › Color Effects › Vector Halftone… (addon.color.halftone, also in
Object › Plug-ins › Color Effects) draws its object as the halftone dots
Object › Vector Halftone makes, live: mono (one dot colour, or each dot in
the art's colour under it) or CMYK screens, the five dot shapes,
frequency, angle, invert and clip to the art's outline.

- The screens, dots and caps move to vectorcraft_effects::addons::halftone,
  shared by the command (its behaviour and tests unchanged) and the effect.
- The renderer samples the art (vectorcraft_render::addons::halftone) and
  keeps the dots by the object's content and what it reads from the
  document, so unchanged art isn't sampled again; past 100000 cells per
  screen the object draws as it is.
- Expand Appearance turns the object into its dots, and the SVG, PDF, EPS
  and AI exporters write them as paths.
- Its dialog reuses the command's fields, without Keep Original.

Deferred: stochastic and radial patterns, a dot-gain curve, per-screen
separation settings, type or symbols as dots.
2026-10-30 22:13:33 +07:00
dotCooCoo a9e64fe16f Merge main into feat/character-anti-aliasing-method
Frame literals take both the type output transform and main's Separations Preview plate table. The Catalan catalog, now a complete one, gets the Anti-aliasing Method rows.
2026-10-30 06:07:17 -07:00
Don Omondi 68ad249efd Fix #3510: editing data past 512 MiB opens with its layers where memory allows
A layered .ai or Illustrator EPS whose editing data (embedded rasters,
mostly) unpacks past 512 MiB opened as its PDF part or printed page, in
one layer, with "it decompresses to more than 512 MB" (#4628 on 0.17.0
hit the older 256 MiB limit the same way).

The limit is now an eighth of installed memory, from 512 MiB (what every
64-bit build read before) to 2 GiB: 1 GiB on an 8 GiB machine, 2 GiB on
16 GiB or more. Reading takes about as much memory again as the data
(1.1 GB of data with 24 embedded 4096 x 4096 images peaked 1.1 GB above
the start), so the ceiling keeps a small file that unpacks without end
from taking a large machine's memory. 32-bit and web builds keep 256 MiB.
vectorcraft_render::installed_memory exposes the reading the image cache
already sizes itself from.
2026-10-30 05:07:58 +07:00
bflatastic f1211e7c89 Separations Preview: hidden plates leave raster images too (#4153) 2026-10-30 04:25:27 +07:00
dotCooCoo 72c7610a39 Type: set the output transform in main's new shadow-filter test frame
Main added uncached_darkness_shadow_subtracts_before_separate_opacity, which builds a Frame
without the output field this branch adds, so the merged tree did not compile.
2026-10-29 12:34:14 -07:00
dotCooCoo c60be649a3 Merge main into feat/character-anti-aliasing-method 2026-10-29 12:26:08 -07:00
dotCooCoo 1538733954 Images: a fill color colorizes grayscale and 1-bit images
Squashed from #4431 onto main.
2026-10-30 00:15:57 +07:00
dotCooCoo c8a7420b93 Place: an image whose pixels can't be read is refused instead of placed blank
Squashed from #4257 onto main.
2026-10-29 18:54:38 +07:00
dotCooCoo a00647c604 Type: None, Crisp and Strong type composite precisely in exports
The offscreen pass that draws type with an anti-aliasing method other
than Sharp rendered with the default 8-bit compositing, also in exports
that composite with floating point. It now renders with the frame's
rasterizer settings, so translucent fills inside the characters draw
as Sharp type draws them.
2026-10-29 02:49:31 -07:00