summaryrefslogtreecommitdiff
path: root/rust/crates/graphics/src/stroke.rs
blob: 4f2b6f31d3f69eb9497ec3960e2912b727a5b2f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use tiny_skia::{BlendMode, Color, FillRule, Paint, Path, Pixmap, PixmapPaint, Stroke, Transform};

use crate::definitions::StrokeAlign;

pub fn apply_aligned_stroke(
    pixmap: &mut Pixmap,
    path: &Path,
    stroke_width: f64,
    stroke_align: StrokeAlign,
    stroke_color: Color,
) {
    if stroke_width <= 0.0 {
        return;
    }

    match stroke_align {
        StrokeAlign::Inside => {
            let Some(mut stroke_pixmap) = Pixmap::new(pixmap.width(), pixmap.height()) else {
                return;
            };
            stroke_path(&mut stroke_pixmap, path, stroke_width * 2.0, stroke_color);
            erase_outside(&mut stroke_pixmap, path, BlendMode::DestinationIn);
            composite(pixmap, &stroke_pixmap);
        }
        StrokeAlign::Outside => {
            let Some(mut stroke_pixmap) = Pixmap::new(pixmap.width(), pixmap.height()) else {
                return;
            };
            stroke_path(&mut stroke_pixmap, path, stroke_width * 2.0, stroke_color);
            erase_outside(&mut stroke_pixmap, path, BlendMode::DestinationOut);
            composite(pixmap, &stroke_pixmap);
        }
        StrokeAlign::Center => {
            stroke_path(pixmap, path, stroke_width, stroke_color);
        }
    }
}

fn stroke_path(pixmap: &mut Pixmap, path: &Path, width: f64, color: Color) {
    let paint = Paint {
        shader: tiny_skia::Shader::SolidColor(color),
        anti_alias: true,
        ..Paint::default()
    };
    let stroke = Stroke {
        width: width as f32,
        ..Stroke::default()
    };
    pixmap.stroke_path(path, &paint, &stroke, Transform::default(), None);
}

fn erase_outside(pixmap: &mut Pixmap, path: &Path, blend_mode: BlendMode) {
    let paint = Paint {
        shader: tiny_skia::Shader::SolidColor(Color::BLACK),
        anti_alias: true,
        blend_mode,
        ..Paint::default()
    };
    pixmap.fill_path(path, &paint, FillRule::Winding, Transform::default(), None);
}

fn composite(target: &mut Pixmap, source: &Pixmap) {
    target.draw_pixmap(
        0,
        0,
        source.as_ref(),
        &PixmapPaint::default(),
        Transform::default(),
        None,
    );
}