Files
vectorcraft/crates/trace/src/lib.rs
T
VoiceLessQ 1411a821c1 Fix #3098: Image Trace traces off the UI thread, with progress and Cancel
Tracing a photo or a large image ran inside imageTrace.make on the UI
thread, so the window froze for as long as the trace took, with nothing
to say it was working.

A trace the user asks for (Object > Image Trace > Make and Make and
Expand, the task bar's Image Trace, the Image Trace panel's Trace and
presets) now runs on a thread of its own, as heavy plug-in filters do.
The status bar says so, and after 0.4 s a progress window shows how
many of the image's colors are traced, with Cancel and Continue in
Background (imageTrace.cancelRuns, imageTrace.hideRuns).

The result goes back to the command it was prepared for
(cmd::TraceRun, Traced::hand_in), which takes it only for the same
object, image and settings, so the edit is one undo step as before. A
newer trace of the same object replaces one still running, and a trace
whose image or settings changed meanwhile changes nothing. The control
channel, MCP, scripts and the web trace at once, as before.

The tracer reports its stages and color layers to a callback and stops
when it says so (trace_watched).
2026-10-29 22:39:34 +07:00

89 lines
4.4 KiB
Rust

//! VectorCraft Image Trace: raster → vector.
//!
//! A clean-room tracer built from the published ideas behind bitmap tracers (the potrace paper
//! and classic colour quantisation), not from any existing implementation:
//!
//! 1. **Quantise** the image into a small palette: a luminance threshold (Black and White), 1-D
//! k-means over the luminance histogram (Grayscale), or, in Color, over a 15-bit colour
//! histogram as the [`Palette`] asks: median cut to a colour count followed by weighted k-means
//! (Limited), median cut until every colour box is tight (Full Tone), one of those two by
//! whether the image is flat art (Automatic), or the most used colours of a swatch library
//! (Document Library). Transparent pixels are left out. The palette is made of the picture's own
//! colours: the blends that anti-aliasing and blur leave along edges take none of it, colours that
//! noise made of one colour are merged, and a blend pixel goes to one of the two colours it lies
//! between (see `quantize`).
//! A continuous-tone image (a photo) is first smoothed within its tones, edges kept (see
//! `smooth`): otherwise its noise decides, pixel by pixel, which of two neighbouring tones a
//! pixel gets, and every such pixel is a speck.
//! 2. **Remove noise**: whole 4-connected same-colour regions are merged, smallest first, into
//! their most common neighbouring colour until no mergeable region is smaller than `noise`.
//! Raising Noise continues the same merge sequence without splitting earlier regions.
//! 3. **Follow boundaries**: for each colour layer, the pixel-crack edges between inside and
//! outside are linked into closed loops (inside on the right, right turns at saddles, so
//! diagonal pixels stay separate, matching 4-connectivity). Outer loops run clockwise and
//! holes counter-clockwise (y down), so the result fills correctly with either fill rule.
//! 4. **Sub-pixel edges**: each crack of a loop is moved to where the image says the edge really
//! is (an anti-aliased or blurred edge pixel is part one tone, part the other), so the outline
//! is right to a fraction of a pixel before any fitting; hard-edged images are read as
//! staircases instead (see `subpixel`).
//! Outlines are cut at their junctions (where three regions meet, and at the image's border)
//! and each piece between two is fitted on its own, the same way from either side, so that two
//! abutting regions have one and the same curve along the edge they share.
//! 5. **Curves**: the outline is fitted as Flat Logo mode fits its own (`logo::fit_outline`):
//! corners the pixels rounded are put back, straight runs become lines, a round outline a
//! true circle, and the rest as few cubic Béziers as the Paths tolerance allows; Snap Curves To
//! Lines also straightens slightly curved runs and makes arcs of parts of circles.
//!
//! With **Create: Strokes**, the parts of a colour layer no wider than the stroke width are traced
//! as stroked centre lines instead (see `centerline`): placed in the middle of their ink to a
//! fraction of a pixel, as wide as their ink, and fitted as outlines are. Without Fills, wider
//! parts become stroked outlines.
//!
//! Colour layers are traced either *abutting* (each colour's own area; shapes share edges) or
//! *overlapping* (stacked: each layer also covers the layers above it within the areas that hold
//! its own color, so no hairline gaps).
#![forbid(unsafe_code)]
mod centerline;
mod contour;
mod fit;
mod gradient;
mod group;
mod logo;
mod mosaic;
mod quantize;
mod shapes;
mod smooth;
mod subpixel;
use std::sync::atomic::{AtomicUsize, Ordering};
use serde::{Deserialize, Serialize};
use vectorcraft_geom::PathData;
pub use contour::{Component, Loop, trace_mask};
pub use gradient::TracedGradient;
pub use mosaic::mosaic;
pub use quantize::{FULL_TONE_MAX, Quantized, TRANSPARENT, denoise, quantize};
pub use shapes::TracedShape;
use subpixel::Crack;
mod layers;
mod params;
mod pipeline;
mod raster;
use layers::parallel_map;
#[cfg(test)]
use layers::parallel_map_at_most;
#[cfg(test)]
pub(crate) use layers::shift_probe;
pub use params::{Method, Mode, PRESET_NAMES, Palette, TraceParams, enhanced_preset, preset, presets};
pub use pipeline::{TraceResult, TracedPath, trace, trace_watched, trace_within};
#[cfg(test)]
use raster::fit_size;
pub use raster::{MAX_PIXELS, MAX_SOURCE_PIXELS, Raster, TraceError};
#[cfg(test)]
mod tests;