Skip to main content

outline/vectorizer/
vtracer.rs

1use image::GrayImage;
2use visioncortex::PathSimplifyMode;
3use vtracer::{ColorImage, ColorMode, Config, Hierarchical, SvgFile, convert};
4
5use crate::mask::gray_to_color_image_rgba;
6use crate::{OutlineError, OutlineResult};
7
8use super::MaskVectorizer;
9
10/// Configuration for vectorizing masks into SVG paths using VTracer.
11///
12/// These options map directly to [`vtracer::Config`].
13#[derive(Debug, Clone)]
14pub struct TraceOptions {
15    pub tracer_color_mode: ColorMode,
16    pub tracer_hierarchical: Hierarchical,
17    pub tracer_mode: PathSimplifyMode,
18    pub tracer_filter_speckle: usize,
19    pub tracer_color_precision: i32,
20    pub tracer_layer_difference: i32,
21    pub tracer_corner_threshold: i32,
22    pub tracer_length_threshold: f64,
23    pub tracer_max_iterations: usize,
24    pub tracer_splice_threshold: i32,
25    pub tracer_path_precision: Option<u32>,
26    pub invert_svg: bool,
27}
28
29impl Default for TraceOptions {
30    fn default() -> Self {
31        Self {
32            tracer_color_mode: ColorMode::Binary,
33            tracer_hierarchical: Hierarchical::Stacked,
34            tracer_mode: PathSimplifyMode::Spline,
35            tracer_filter_speckle: 4,
36            tracer_color_precision: 6,
37            tracer_layer_difference: 16,
38            tracer_corner_threshold: 60,
39            tracer_length_threshold: 4.0,
40            tracer_max_iterations: 10,
41            tracer_splice_threshold: 45,
42            tracer_path_precision: Some(2),
43            invert_svg: false,
44        }
45    }
46}
47
48/// Converts grayscale masks to SVG using the VTracer library.
49///
50/// Implements [`MaskVectorizer`] to provide path tracing functionality. Pair with
51/// [`TraceOptions`] to control output style and precision.
52#[derive(Debug, Clone, Copy, Default)]
53pub struct VtracerSvgVectorizer;
54
55impl MaskVectorizer for VtracerSvgVectorizer {
56    type Options = TraceOptions;
57    type Output = String;
58
59    fn vectorize(&self, mask: &GrayImage, options: &Self::Options) -> OutlineResult<Self::Output> {
60        trace_to_svg_string(mask, options)
61    }
62}
63
64/// The helper function that uses VTracer to trace a grayscale mask to an SVG string.
65pub fn trace_to_svg_string(
66    mask_image: &GrayImage,
67    options: &TraceOptions,
68) -> OutlineResult<String> {
69    let color_img = gray_to_color_image_rgba(mask_image, None, options.invert_svg);
70    let svg_file = trace(color_img, options)?;
71    Ok(svg_file.to_string())
72}
73
74/// Trace a ColorImage into an SVG using VTracer with the given options.
75pub fn trace(img: ColorImage, options: &TraceOptions) -> OutlineResult<SvgFile> {
76    let cfg = Config {
77        color_mode: options.tracer_color_mode.clone(),
78        hierarchical: options.tracer_hierarchical.clone(),
79        mode: options.tracer_mode,
80        filter_speckle: options.tracer_filter_speckle,
81        color_precision: options.tracer_color_precision,
82        layer_difference: options.tracer_layer_difference,
83        corner_threshold: options.tracer_corner_threshold,
84        length_threshold: options.tracer_length_threshold,
85        max_iterations: options.tracer_max_iterations,
86        splice_threshold: options.tracer_splice_threshold,
87        path_precision: options.tracer_path_precision,
88    };
89
90    let svg_file = convert(img, cfg).map_err(OutlineError::Trace)?;
91    Ok(svg_file)
92}