1use crate::render::Context;
6
7pub fn render(
8 path: &usvg::Path,
9 blend_mode: tiny_skia::BlendMode,
10 ctx: &Context,
11 transform: tiny_skia::Transform,
12 pixmap: &mut tiny_skia::PixmapMut,
13) {
14 if !path.is_visible() {
15 return;
16 }
17
18 if path.paint_order() == usvg::PaintOrder::FillAndStroke {
19 fill_path(path, blend_mode, ctx, transform, pixmap);
20 stroke_path(path, blend_mode, ctx, transform, pixmap);
21 } else {
22 stroke_path(path, blend_mode, ctx, transform, pixmap);
23 fill_path(path, blend_mode, ctx, transform, pixmap);
24 }
25}
26
27pub fn fill_path(
28 path: &usvg::Path,
29 blend_mode: tiny_skia::BlendMode,
30 ctx: &Context,
31 transform: tiny_skia::Transform,
32 pixmap: &mut tiny_skia::PixmapMut,
33) -> Option<()> {
34 let fill = path.fill()?;
35
36 if path.data().bounds().width() == 0.0 || path.data().bounds().height() == 0.0 {
38 return None;
39 }
40
41 let rule = match fill.rule() {
42 usvg::FillRule::NonZero => tiny_skia::FillRule::Winding,
43 usvg::FillRule::EvenOdd => tiny_skia::FillRule::EvenOdd,
44 };
45
46 let pattern_pixmap;
47 let mut paint = tiny_skia::Paint::default();
48 match fill.paint() {
49 usvg::Paint::Color(c) => {
50 paint.set_color_rgba8(c.red, c.green, c.blue, fill.opacity().to_u8());
51 }
52 usvg::Paint::LinearGradient(ref lg) => {
53 paint.shader = convert_linear_gradient(lg, fill.opacity())?;
54 }
55 usvg::Paint::RadialGradient(ref rg) => {
56 paint.shader = convert_radial_gradient(rg, fill.opacity())?;
57 }
58 usvg::Paint::Pattern(ref pattern) => {
59 let (patt_pix, patt_ts) = render_pattern_pixmap(pattern, ctx, transform)?;
60
61 pattern_pixmap = patt_pix;
62 paint.shader = tiny_skia::Pattern::new(
63 pattern_pixmap.as_ref(),
64 tiny_skia::SpreadMode::Repeat,
65 tiny_skia::FilterQuality::Bicubic,
66 fill.opacity().get(),
67 patt_ts,
68 )
69 }
70 }
71 paint.anti_alias = path.rendering_mode().use_shape_antialiasing();
72 paint.blend_mode = blend_mode;
73
74 pixmap.fill_path(path.data(), &paint, rule, transform, None);
75 Some(())
76}
77
78fn stroke_path(
79 path: &usvg::Path,
80 blend_mode: tiny_skia::BlendMode,
81 ctx: &Context,
82 transform: tiny_skia::Transform,
83 pixmap: &mut tiny_skia::PixmapMut,
84) -> Option<()> {
85 let stroke = path.stroke()?;
86 let pattern_pixmap;
87 let mut paint = tiny_skia::Paint::default();
88 match stroke.paint() {
89 usvg::Paint::Color(c) => {
90 paint.set_color_rgba8(c.red, c.green, c.blue, stroke.opacity().to_u8());
91 }
92 usvg::Paint::LinearGradient(ref lg) => {
93 paint.shader = convert_linear_gradient(lg, stroke.opacity())?;
94 }
95 usvg::Paint::RadialGradient(ref rg) => {
96 paint.shader = convert_radial_gradient(rg, stroke.opacity())?;
97 }
98 usvg::Paint::Pattern(ref pattern) => {
99 let (patt_pix, patt_ts) = render_pattern_pixmap(pattern, ctx, transform)?;
100
101 pattern_pixmap = patt_pix;
102 paint.shader = tiny_skia::Pattern::new(
103 pattern_pixmap.as_ref(),
104 tiny_skia::SpreadMode::Repeat,
105 tiny_skia::FilterQuality::Bicubic,
106 stroke.opacity().get(),
107 patt_ts,
108 )
109 }
110 }
111 paint.anti_alias = path.rendering_mode().use_shape_antialiasing();
112 paint.blend_mode = blend_mode;
113
114 pixmap.stroke_path(path.data(), &paint, &stroke.to_tiny_skia(), transform, None);
115
116 Some(())
117}
118
119fn convert_linear_gradient(
120 gradient: &usvg::LinearGradient,
121 opacity: usvg::Opacity,
122) -> Option<tiny_skia::Shader> {
123 let (mode, points) = convert_base_gradient(gradient, opacity)?;
124
125 let shader = tiny_skia::LinearGradient::new(
126 (gradient.x1(), gradient.y1()).into(),
127 (gradient.x2(), gradient.y2()).into(),
128 points,
129 mode,
130 gradient.transform(),
131 )?;
132
133 Some(shader)
134}
135
136fn convert_radial_gradient(
137 gradient: &usvg::RadialGradient,
138 opacity: usvg::Opacity,
139) -> Option<tiny_skia::Shader> {
140 let (mode, points) = convert_base_gradient(gradient, opacity)?;
141
142 let shader = tiny_skia::RadialGradient::new(
143 (gradient.fx(), gradient.fy()).into(),
144 (gradient.cx(), gradient.cy()).into(),
145 gradient.r().get(),
146 points,
147 mode,
148 gradient.transform(),
149 )?;
150
151 Some(shader)
152}
153
154fn convert_base_gradient(
155 gradient: &usvg::BaseGradient,
156 opacity: usvg::Opacity,
157) -> Option<(tiny_skia::SpreadMode, Vec<tiny_skia::GradientStop>)> {
158 let mode = match gradient.spread_method() {
159 usvg::SpreadMethod::Pad => tiny_skia::SpreadMode::Pad,
160 usvg::SpreadMethod::Reflect => tiny_skia::SpreadMode::Reflect,
161 usvg::SpreadMethod::Repeat => tiny_skia::SpreadMode::Repeat,
162 };
163
164 let mut points = Vec::with_capacity(gradient.stops().len());
165 for stop in gradient.stops() {
166 let alpha = stop.opacity() * opacity;
167 let color = tiny_skia::Color::from_rgba8(
168 stop.color().red,
169 stop.color().green,
170 stop.color().blue,
171 alpha.to_u8(),
172 );
173 points.push(tiny_skia::GradientStop::new(stop.offset().get(), color))
174 }
175
176 Some((mode, points))
177}
178
179fn render_pattern_pixmap(
180 pattern: &usvg::Pattern,
181 ctx: &Context,
182 transform: tiny_skia::Transform,
183) -> Option<(tiny_skia::Pixmap, tiny_skia::Transform)> {
184 let (sx, sy) = {
185 let ts2 = transform.pre_concat(pattern.transform());
186 ts2.get_scale()
187 };
188
189 let rect = pattern.rect();
190 let img_size = tiny_skia::IntSize::from_wh(
191 (rect.width() * sx).round() as u32,
192 (rect.height() * sy).round() as u32,
193 )?;
194 let mut pixmap = tiny_skia::Pixmap::new(img_size.width(), img_size.height())?;
195
196 let transform = tiny_skia::Transform::from_scale(sx, sy);
197 crate::render::render_nodes(pattern.root(), ctx, transform, &mut pixmap.as_mut());
198
199 let mut ts = tiny_skia::Transform::default();
200 ts = ts.pre_concat(pattern.transform());
201 ts = ts.pre_translate(rect.x(), rect.y());
202 ts = ts.pre_scale(1.0 / sx, 1.0 / sy);
203
204 Some((pixmap, ts))
205}