Skip to main content

azul_layout/xml/
svg.rs

1use alloc::boxed::Box;
2use core::fmt;
3
4#[cfg(not(feature = "svg"))]
5pub use azul_core::svg::*;
6// re-export everything except for Svg and SvgXmlNode
7#[cfg(feature = "svg")]
8pub use azul_core::svg::{
9    c_void,
10    FontDatabase,
11    ImageRendering,
12    Indent,
13    OptionSvgDashPattern,
14    ResultSvgSvgParseError,
15    ResultSvgXmlNodeSvgParseError,
16    ShapeRendering,
17    SvgCircle,
18    SvgColoredVertex,
19    SvgColoredVertexVec,
20    SvgColoredVertexVecDestructor,
21    SvgDashPattern,
22    SvgFillRule,
23    SvgFillStyle,
24    SvgFitTo,
25    SvgLine,
26    SvgLineCap,
27    SvgLineJoin,
28    SvgMultiPolygon,
29    SvgMultiPolygonVec,
30    SvgMultiPolygonVecDestructor,
31    SvgNode,
32    SvgParseError,
33    SvgParseOptions,
34    SvgPath,
35    SvgPathElement,
36    SvgPathElementVec,
37    SvgPathElementVecDestructor,
38    SvgPathVec,
39    SvgPathVecDestructor,
40    SvgRenderOptions,
41    SvgRenderTransform,
42
43    SvgSimpleNode,
44    SvgSimpleNodeVec,
45    SvgSimpleNodeVecDestructor,
46    SvgSize,
47    SvgStrokeStyle,
48    SvgStyle,
49    SvgStyledNode,
50    SvgTransform,
51    SvgVertex,
52    SvgVertexVec,
53    SvgVertexVecDestructor,
54    SvgXmlOptions,
55    TessellatedColoredSvgNode,
56    TessellatedColoredSvgNodeVec,
57    TessellatedColoredSvgNodeVecDestructor,
58    // SvgXmlNode, Svg
59    TessellatedGPUSvgNode,
60    TessellatedSvgNode,
61    TessellatedSvgNodeVec,
62    TessellatedSvgNodeVecDestructor,
63    TessellatedSvgNodeVecRef,
64    TextRendering,
65};
66use azul_core::{
67    geom::PhysicalSizeU32,
68    gl::{GlContextPtr, Texture},
69    resources::{RawImage, RawImageFormat},
70};
71#[cfg(feature = "svg")]
72pub use azul_css::props::basic::animation::{
73    SvgCubicCurve, SvgPoint, SvgQuadraticCurve, SvgRect, SvgVector,
74};
75use azul_css::{
76    impl_result, impl_result_inner,
77    props::basic::{ColorU, LayoutSize, OptionColorU, OptionLayoutSize},
78    AzString, OptionI16, OptionString, OptionU16, StringVec, U8Vec,
79};
80#[cfg(feature = "svg")]
81use lyon::{
82    geom::euclid::{Point2D, Rect, Size2D, UnknownUnit},
83    math::Point,
84    path::Path,
85    tessellation::{
86        BuffersBuilder, FillOptions, FillTessellator, FillVertex, StrokeOptions, StrokeTessellator,
87        StrokeVertex, VertexBuffers,
88    },
89};
90
91use crate::xml::XmlError;
92
93#[cfg(feature = "svg")]
94extern crate tiny_skia;
95
96use azul_core::gl::GL_RESTART_INDEX;
97
98#[cfg(feature = "svg")]
99fn translate_svg_line_join(e: SvgLineJoin) -> lyon::tessellation::LineJoin {
100    use azul_core::svg::SvgLineJoin::*;
101    match e {
102        Miter => lyon::tessellation::LineJoin::Miter,
103        MiterClip => lyon::tessellation::LineJoin::MiterClip,
104        Round => lyon::tessellation::LineJoin::Round,
105        Bevel => lyon::tessellation::LineJoin::Bevel,
106    }
107}
108
109#[cfg(feature = "svg")]
110fn translate_svg_line_cap(e: SvgLineCap) -> lyon::tessellation::LineCap {
111    use azul_core::svg::SvgLineCap::*;
112    match e {
113        Butt => lyon::tessellation::LineCap::Butt,
114        Square => lyon::tessellation::LineCap::Square,
115        Round => lyon::tessellation::LineCap::Round,
116    }
117}
118
119#[cfg(feature = "svg")]
120fn translate_svg_stroke_style(e: SvgStrokeStyle) -> lyon::tessellation::StrokeOptions {
121    lyon::tessellation::StrokeOptions::tolerance(e.tolerance)
122        .with_start_cap(translate_svg_line_cap(e.start_cap))
123        .with_end_cap(translate_svg_line_cap(e.end_cap))
124        .with_line_join(translate_svg_line_join(e.line_join))
125        .with_line_width(e.line_width)
126        .with_miter_limit(e.miter_limit)
127    // TODO: e.apply_line_width - not present in lyon 17!
128}
129
130#[cfg(feature = "svg")]
131fn svg_multipolygon_to_lyon_path(polygon: &SvgMultiPolygon) -> Path {
132    let mut builder = Path::builder();
133
134    for p in polygon.rings.as_ref().iter() {
135        if p.items.as_ref().is_empty() {
136            continue;
137        }
138
139        let start_item = p.items.as_ref()[0];
140        let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
141
142        builder.begin(first_point);
143
144        for q in p.items.as_ref().iter().rev()
145        /* NOTE: REVERSE ITERATOR */
146        {
147            match q {
148                SvgPathElement::Line(l) => {
149                    builder.line_to(Point2D::new(l.end.x, l.end.y));
150                }
151                SvgPathElement::QuadraticCurve(qc) => {
152                    builder.quadratic_bezier_to(
153                        Point2D::new(qc.ctrl.x, qc.ctrl.y),
154                        Point2D::new(qc.end.x, qc.end.y),
155                    );
156                }
157                SvgPathElement::CubicCurve(cc) => {
158                    builder.cubic_bezier_to(
159                        Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
160                        Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
161                        Point2D::new(cc.end.x, cc.end.y),
162                    );
163                }
164            }
165        }
166
167        builder.end(p.is_closed());
168    }
169
170    builder.build()
171}
172
173#[cfg(feature = "svg")]
174fn svg_multi_shape_to_lyon_path(polygon: &[SvgSimpleNode]) -> Path {
175    use lyon::{
176        geom::Box2D,
177        path::{traits::PathBuilder, Winding},
178    };
179
180    let mut builder = Path::builder();
181
182    for p in polygon.iter() {
183        match p {
184            SvgSimpleNode::Path(p) => {
185                let items = p.items.as_ref();
186                if p.items.as_ref().is_empty() {
187                    continue;
188                }
189
190                let start_item = p.items.as_ref()[0];
191                let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
192
193                builder.begin(first_point);
194
195                for q in p.items.as_ref().iter().rev()
196                /* NOTE: REVERSE ITERATOR */
197                {
198                    match q {
199                        SvgPathElement::Line(l) => {
200                            builder.line_to(Point2D::new(l.end.x, l.end.y));
201                        }
202                        SvgPathElement::QuadraticCurve(qc) => {
203                            builder.quadratic_bezier_to(
204                                Point2D::new(qc.ctrl.x, qc.ctrl.y),
205                                Point2D::new(qc.end.x, qc.end.y),
206                            );
207                        }
208                        SvgPathElement::CubicCurve(cc) => {
209                            builder.cubic_bezier_to(
210                                Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
211                                Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
212                                Point2D::new(cc.end.x, cc.end.y),
213                            );
214                        }
215                    }
216                }
217
218                builder.end(p.is_closed());
219            }
220            SvgSimpleNode::Circle(c) => {
221                builder.add_circle(
222                    Point::new(c.center_x, c.center_y),
223                    c.radius,
224                    Winding::Positive,
225                );
226            }
227            SvgSimpleNode::CircleHole(c) => {
228                builder.add_circle(
229                    Point::new(c.center_x, c.center_y),
230                    c.radius,
231                    Winding::Negative,
232                );
233            }
234            SvgSimpleNode::Rect(c) => {
235                builder.add_rectangle(
236                    &Box2D::from_origin_and_size(
237                        Point::new(c.x, c.y),
238                        Size2D::new(c.width, c.height),
239                    ),
240                    Winding::Positive,
241                );
242            }
243            SvgSimpleNode::RectHole(c) => {
244                builder.add_rectangle(
245                    &Box2D::from_origin_and_size(
246                        Point::new(c.x, c.y),
247                        Size2D::new(c.width, c.height),
248                    ),
249                    Winding::Negative,
250                );
251            }
252        }
253    }
254
255    builder.build()
256}
257
258pub fn raw_line_intersection(p: &SvgLine, q: &SvgLine) -> Option<SvgPoint> {
259    let p_min_x = p.start.x.min(p.end.x);
260    let p_min_y = p.start.y.min(p.end.y);
261    let p_max_x = p.start.x.max(p.end.x);
262    let p_max_y = p.start.y.max(p.end.y);
263
264    let q_min_x = q.start.x.min(q.end.x);
265    let q_min_y = q.start.y.min(q.end.y);
266    let q_max_x = q.start.x.max(q.end.x);
267    let q_max_y = q.start.y.max(q.end.y);
268
269    let int_min_x = p_min_x.max(q_min_x);
270    let int_max_x = p_max_x.min(q_max_x);
271    let int_min_y = p_min_y.max(q_min_y);
272    let int_max_y = p_max_y.min(q_max_y);
273
274    let two = 2.0;
275    let mid_x = (int_min_x + int_max_x) / two;
276    let mid_y = (int_min_y + int_max_y) / two;
277
278    // condition ordinate values by subtracting midpoint
279    let p1x = p.start.x - mid_x;
280    let p1y = p.start.y - mid_y;
281    let p2x = p.end.x - mid_x;
282    let p2y = p.end.y - mid_y;
283    let q1x = q.start.x - mid_x;
284    let q1y = q.start.y - mid_y;
285    let q2x = q.end.x - mid_x;
286    let q2y = q.end.y - mid_y;
287
288    // unrolled computation using homogeneous coordinates eqn
289    let px = p1y - p2y;
290    let py = p2x - p1x;
291    let pw = p1x * p2y - p2x * p1y;
292
293    let qx = q1y - q2y;
294    let qy = q2x - q1x;
295    let qw = q1x * q2y - q2x * q1y;
296
297    let xw = py * qw - qy * pw;
298    let yw = qx * pw - px * qw;
299    let w = px * qy - qx * py;
300
301    let x_int = xw / w;
302    let y_int = yw / w;
303
304    // check for parallel lines
305    if (x_int.is_nan() || x_int.is_infinite()) || (y_int.is_nan() || y_int.is_infinite()) {
306        None
307    } else {
308        // de-condition intersection point
309        Some(SvgPoint {
310            x: x_int + mid_x,
311            y: y_int + mid_y,
312        })
313    }
314}
315
316/// By-value wrapper for raw_line_intersection (for FFI)
317pub fn raw_line_intersection_byval(p: &SvgLine, q: SvgLine) -> Option<SvgPoint> {
318    raw_line_intersection(p, &q)
319}
320
321pub fn svg_path_offset(p: &SvgPath, distance: f32, join: SvgLineJoin, cap: SvgLineCap) -> SvgPath {
322    if distance == 0.0 {
323        return p.clone();
324    }
325
326    let mut items = p.items.as_slice().to_vec();
327    if let Some(mut first) = items.first() {
328        items.push(first.clone());
329    }
330
331    let mut items = items
332        .iter()
333        .map(|l| match l {
334            SvgPathElement::Line(q) => {
335                let normal = match q.outwards_normal() {
336                    Some(s) => SvgPoint {
337                        x: s.x * distance,
338                        y: s.y * distance,
339                    },
340                    None => return l.clone(),
341                };
342
343                SvgPathElement::Line(SvgLine {
344                    start: SvgPoint {
345                        x: q.start.x + normal.x,
346                        y: q.start.y + normal.y,
347                    },
348                    end: SvgPoint {
349                        x: q.end.x + normal.x,
350                        y: q.end.y + normal.y,
351                    },
352                })
353            }
354            SvgPathElement::QuadraticCurve(q) => {
355                let n1 = match (SvgLine {
356                    start: q.start.clone(),
357                    end: q.ctrl.clone(),
358                }
359                .outwards_normal())
360                {
361                    Some(s) => SvgPoint {
362                        x: s.x * distance,
363                        y: s.y * distance,
364                    },
365                    None => return l.clone(),
366                };
367
368                let n2 = match (SvgLine {
369                    start: q.ctrl.clone(),
370                    end: q.end.clone(),
371                }
372                .outwards_normal())
373                {
374                    Some(s) => SvgPoint {
375                        x: s.x * distance,
376                        y: s.y * distance,
377                    },
378                    None => return l.clone(),
379                };
380
381                let nl1 = SvgLine {
382                    start: SvgPoint {
383                        x: q.start.x + n1.x,
384                        y: q.start.y + n1.y,
385                    },
386                    end: SvgPoint {
387                        x: q.ctrl.x + n1.x,
388                        y: q.ctrl.y + n1.y,
389                    },
390                };
391
392                let nl2 = SvgLine {
393                    start: SvgPoint {
394                        x: q.ctrl.x + n2.x,
395                        y: q.ctrl.y + n2.y,
396                    },
397                    end: SvgPoint {
398                        x: q.end.x + n2.x,
399                        y: q.end.y + n2.y,
400                    },
401                };
402
403                let nctrl = match raw_line_intersection(&nl1, &nl2) {
404                    Some(s) => s,
405                    None => return l.clone(),
406                };
407
408                SvgPathElement::QuadraticCurve(SvgQuadraticCurve {
409                    start: nl1.start,
410                    ctrl: nctrl,
411                    end: nl2.end,
412                })
413            }
414            SvgPathElement::CubicCurve(q) => {
415                let n1 = match (SvgLine {
416                    start: q.start.clone(),
417                    end: q.ctrl_1.clone(),
418                }
419                .outwards_normal())
420                {
421                    Some(s) => SvgPoint {
422                        x: s.x * distance,
423                        y: s.y * distance,
424                    },
425                    None => return l.clone(),
426                };
427
428                let n2 = match (SvgLine {
429                    start: q.ctrl_1.clone(),
430                    end: q.ctrl_2.clone(),
431                }
432                .outwards_normal())
433                {
434                    Some(s) => SvgPoint {
435                        x: s.x * distance,
436                        y: s.y * distance,
437                    },
438                    None => return l.clone(),
439                };
440
441                let n3 = match (SvgLine {
442                    start: q.ctrl_2.clone(),
443                    end: q.end.clone(),
444                }
445                .outwards_normal())
446                {
447                    Some(s) => SvgPoint {
448                        x: s.x * distance,
449                        y: s.y * distance,
450                    },
451                    None => return l.clone(),
452                };
453
454                let nl1 = SvgLine {
455                    start: SvgPoint {
456                        x: q.start.x + n1.x,
457                        y: q.start.y + n1.y,
458                    },
459                    end: SvgPoint {
460                        x: q.ctrl_1.x + n1.x,
461                        y: q.ctrl_1.y + n1.y,
462                    },
463                };
464
465                let nl2 = SvgLine {
466                    start: SvgPoint {
467                        x: q.ctrl_1.x + n2.x,
468                        y: q.ctrl_1.y + n2.y,
469                    },
470                    end: SvgPoint {
471                        x: q.ctrl_2.x + n2.x,
472                        y: q.ctrl_2.y + n2.y,
473                    },
474                };
475
476                let nl3 = SvgLine {
477                    start: SvgPoint {
478                        x: q.ctrl_2.x + n3.x,
479                        y: q.ctrl_2.y + n3.y,
480                    },
481                    end: SvgPoint {
482                        x: q.end.x + n3.x,
483                        y: q.end.y + n3.y,
484                    },
485                };
486
487                let nctrl_1 = match raw_line_intersection(&nl1, &nl2) {
488                    Some(s) => s,
489                    None => return l.clone(),
490                };
491
492                let nctrl_2 = match raw_line_intersection(&nl2, &nl3) {
493                    Some(s) => s,
494                    None => return l.clone(),
495                };
496
497                SvgPathElement::CubicCurve(SvgCubicCurve {
498                    start: nl1.start,
499                    ctrl_1: nctrl_1,
500                    ctrl_2: nctrl_2,
501                    end: nl3.end,
502                })
503            }
504        })
505        .collect::<Vec<_>>();
506
507    for i in 0..items.len().saturating_sub(2) {
508        let a_end_line = match items[i] {
509            SvgPathElement::Line(q) => q.clone(),
510            SvgPathElement::QuadraticCurve(q) => SvgLine {
511                start: q.ctrl.clone(),
512                end: q.end.clone(),
513            },
514            SvgPathElement::CubicCurve(q) => SvgLine {
515                start: q.ctrl_2.clone(),
516                end: q.end.clone(),
517            },
518        };
519
520        let b_start_line = match items[i + 1] {
521            SvgPathElement::Line(q) => q.clone(),
522            SvgPathElement::QuadraticCurve(q) => SvgLine {
523                start: q.ctrl.clone(),
524                end: q.start.clone(),
525            },
526            SvgPathElement::CubicCurve(q) => SvgLine {
527                start: q.ctrl_1.clone(),
528                end: q.start.clone(),
529            },
530        };
531
532        if let Some(intersect_pt) = raw_line_intersection(&a_end_line, &b_start_line) {
533            items[i].set_last(intersect_pt.clone());
534            items[i + 1].set_first(intersect_pt);
535        }
536    }
537
538    items.pop();
539
540    SvgPath {
541        items: items.into(),
542    }
543}
544
545fn shorten_line_end_by(line: SvgLine, distance: f32) -> SvgLine {
546    let dx = line.end.x - line.start.x;
547    let dy = line.end.y - line.start.y;
548    let dt = (dx * dx + dy * dy).sqrt();
549    let dt_short = dt - distance;
550
551    SvgLine {
552        start: line.start,
553        end: SvgPoint {
554            x: line.start.x + (dt_short / dt) * (dx / dt),
555            y: line.start.y + (dt_short / dt) * (dx / dt),
556        },
557    }
558}
559
560fn shorten_line_start_by(line: SvgLine, distance: f32) -> SvgLine {
561    let dx = line.end.x - line.start.x;
562    let dy = line.end.y - line.start.y;
563    let dt = (dx * dx + dy * dy).sqrt();
564    let dt_short = dt - distance;
565
566    SvgLine {
567        start: SvgPoint {
568            x: line.start.x + (1.0 - (dt_short / dt)) * (dx / dt),
569            y: line.start.y + (1.0 - (dt_short / dt)) * (dx / dt),
570        },
571        end: line.end,
572    }
573}
574
575// Creates a "bevel"
576pub fn svg_path_bevel(p: &SvgPath, distance: f32) -> SvgPath {
577    let mut items = p.items.as_slice().to_vec();
578
579    // duplicate first & last items
580    let first = items.first().cloned();
581    let last = items.last().cloned();
582    if let Some(first) = first {
583        items.push(first);
584    }
585    items.reverse();
586    if let Some(last) = last {
587        items.push(last);
588    }
589    items.reverse();
590
591    let mut final_items = Vec::new();
592    for i in 0..items.len() {
593        let a = items[i].clone();
594        let b = items[i + 1].clone();
595        match (a, b) {
596            (SvgPathElement::Line(a), SvgPathElement::Line(b)) => {
597                let a_short = shorten_line_end_by(a, distance);
598                let b_short = shorten_line_start_by(b, distance);
599                final_items.push(SvgPathElement::Line(a_short));
600                final_items.push(SvgPathElement::CubicCurve(SvgCubicCurve {
601                    start: a_short.end,
602                    ctrl_1: a.end,
603                    ctrl_2: b.start,
604                    end: b_short.start,
605                }));
606                final_items.push(SvgPathElement::Line(b_short));
607            }
608            (other_a, other_b) => {
609                final_items.push(other_a);
610                final_items.push(other_b);
611            }
612        }
613    }
614
615    // remove first & last items again
616    final_items.pop();
617    final_items.reverse();
618    final_items.pop();
619    final_items.reverse();
620
621    SvgPath {
622        items: final_items.into(),
623    }
624}
625
626fn svg_multi_polygon_to_geo(poly: &SvgMultiPolygon) -> geo::MultiPolygon {
627    use geo::{Coord, Intersects, Winding};
628
629    let linestrings = poly
630        .rings
631        .iter()
632        .map(|p| {
633            let mut p = p.clone();
634
635            if !p.is_closed() {
636                p.close();
637            }
638
639            let mut coords = p
640                .items
641                .iter()
642                .flat_map(|p| {
643                    match p {
644                        SvgPathElement::Line(l) => vec![
645                            Coord {
646                                x: l.start.x as f64,
647                                y: l.start.y as f64,
648                            },
649                            Coord {
650                                x: l.end.x as f64,
651                                y: l.end.y as f64,
652                            },
653                        ],
654                        SvgPathElement::QuadraticCurve(l) => vec![
655                            Coord {
656                                x: l.start.x as f64,
657                                y: l.start.y as f64,
658                            },
659                            Coord {
660                                x: l.ctrl.x as f64,
661                                y: l.ctrl.y as f64,
662                            },
663                            Coord {
664                                x: l.end.x as f64,
665                                y: l.end.y as f64,
666                            },
667                        ],
668                        SvgPathElement::CubicCurve(l) => vec![
669                            Coord {
670                                x: l.start.x as f64,
671                                y: l.start.y as f64,
672                            },
673                            Coord {
674                                x: l.ctrl_1.x as f64,
675                                y: l.ctrl_1.y as f64,
676                            },
677                            Coord {
678                                x: l.ctrl_2.x as f64,
679                                y: l.ctrl_2.y as f64,
680                            },
681                            Coord {
682                                x: l.end.x as f64,
683                                y: l.end.y as f64,
684                            },
685                        ],
686                    }
687                    .into_iter()
688                })
689                .collect::<Vec<_>>();
690
691            coords.dedup();
692
693            geo::LineString::new(coords)
694        })
695        .collect::<Vec<_>>();
696
697    let exterior_polys = linestrings
698        .iter()
699        .filter(|ls| ls.is_cw())
700        .cloned()
701        .collect::<Vec<geo::LineString<_>>>();
702    let mut interior_polys = linestrings
703        .iter()
704        .filter(|ls| ls.is_ccw())
705        .cloned()
706        .map(|p| Some(p))
707        .collect::<Vec<_>>();
708
709    let ext_int_matched = exterior_polys
710        .iter()
711        .map(|p| {
712            let mut interiors = Vec::new();
713            let p_poly = geo::Polygon::new(p.clone(), Vec::new());
714            for i in interior_polys.iter_mut() {
715                let cloned = match i.as_ref() {
716                    Some(s) => s.clone(),
717                    None => continue,
718                };
719
720                if geo::Polygon::new(cloned.clone(), Vec::new()).intersects(&p_poly) {
721                    interiors.push(cloned);
722                    *i = None;
723                }
724            }
725            geo::Polygon::new(p.clone(), interiors)
726        })
727        .collect::<Vec<geo::Polygon<_>>>();
728
729    geo::MultiPolygon(ext_int_matched)
730}
731
732fn linestring_to_svg_path(ls: geo::LineString<f64>) -> SvgPath {
733    // TODO: bezier curves?
734    SvgPath {
735        items: ls
736            .0
737            .windows(2)
738            .map(|a| {
739                SvgPathElement::Line(SvgLine {
740                    start: SvgPoint {
741                        x: a[0].x as f32,
742                        y: a[0].y as f32,
743                    },
744                    end: SvgPoint {
745                        x: a[1].x as f32,
746                        y: a[1].y as f32,
747                    },
748                })
749            })
750            .collect::<Vec<_>>()
751            .into(),
752    }
753}
754
755fn geo_to_svg_multipolygon(poly: geo::MultiPolygon<f64>) -> SvgMultiPolygon {
756    use geo::Winding;
757    SvgMultiPolygon {
758        rings: poly
759            .0
760            .into_iter()
761            .flat_map(|s| {
762                let mut exterior = s.exterior().clone();
763                let mut interiors = s.interiors().to_vec();
764                exterior.make_cw_winding();
765                for i in interiors.iter_mut() {
766                    i.make_ccw_winding();
767                }
768                interiors.push(exterior);
769                interiors.reverse();
770                interiors.into_iter()
771            })
772            .map(|s| linestring_to_svg_path(s))
773            .collect::<Vec<_>>()
774            .into(),
775    }
776}
777
778// TODO: produces wrong results for curve curve intersection
779pub fn svg_multi_polygon_union(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
780    use geo::{BooleanOps, Coord};
781
782    let a = svg_multi_polygon_to_geo(a);
783    let b = svg_multi_polygon_to_geo(b);
784
785    let u = a.union(&b);
786
787    geo_to_svg_multipolygon(u)
788}
789
790/// By-value wrapper for svg_multi_polygon_union (for FFI)
791pub fn svg_multi_polygon_union_byval(a: &SvgMultiPolygon, b: SvgMultiPolygon) -> SvgMultiPolygon {
792    svg_multi_polygon_union(a, &b)
793}
794
795pub fn svg_multi_polygon_intersection(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
796    use geo::{BooleanOps, Coord};
797
798    let a = svg_multi_polygon_to_geo(a);
799    let b = svg_multi_polygon_to_geo(b);
800
801    let u = a.intersection(&b);
802
803    geo_to_svg_multipolygon(u)
804}
805
806/// By-value wrapper for svg_multi_polygon_intersection (for FFI)
807pub fn svg_multi_polygon_intersection_byval(
808    a: &SvgMultiPolygon,
809    b: SvgMultiPolygon,
810) -> SvgMultiPolygon {
811    svg_multi_polygon_intersection(a, &b)
812}
813
814pub fn svg_multi_polygon_difference(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
815    use geo::{BooleanOps, Coord};
816
817    let a = svg_multi_polygon_to_geo(a);
818    let b = svg_multi_polygon_to_geo(b);
819
820    let u = a.difference(&b);
821
822    geo_to_svg_multipolygon(u)
823}
824
825/// By-value wrapper for svg_multi_polygon_difference (for FFI)
826pub fn svg_multi_polygon_difference_byval(
827    a: &SvgMultiPolygon,
828    b: SvgMultiPolygon,
829) -> SvgMultiPolygon {
830    svg_multi_polygon_difference(a, &b)
831}
832
833pub fn svg_multi_polygon_xor(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
834    use geo::{BooleanOps, Coord};
835
836    let a = svg_multi_polygon_to_geo(a);
837    let b = svg_multi_polygon_to_geo(b);
838
839    let u = a.xor(&b);
840
841    geo_to_svg_multipolygon(u)
842}
843
844/// By-value wrapper for svg_multi_polygon_xor (for FFI)
845pub fn svg_multi_polygon_xor_byval(a: &SvgMultiPolygon, b: SvgMultiPolygon) -> SvgMultiPolygon {
846    svg_multi_polygon_xor(a, &b)
847}
848
849#[cfg(feature = "svg")]
850fn svg_path_to_lyon_path_events(path: &SvgPath) -> Path {
851    let mut builder = Path::builder();
852
853    if !path.items.as_ref().is_empty() {
854        let start_item = path.items.as_ref()[0];
855        let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
856
857        builder.begin(first_point);
858
859        for p in path.items.as_ref().iter() {
860            match p {
861                SvgPathElement::Line(l) => {
862                    builder.line_to(Point2D::new(l.end.x, l.end.y));
863                }
864                SvgPathElement::QuadraticCurve(qc) => {
865                    builder.quadratic_bezier_to(
866                        Point2D::new(qc.ctrl.x, qc.ctrl.y),
867                        Point2D::new(qc.end.x, qc.end.y),
868                    );
869                }
870                SvgPathElement::CubicCurve(cc) => {
871                    builder.cubic_bezier_to(
872                        Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
873                        Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
874                        Point2D::new(cc.end.x, cc.end.y),
875                    );
876                }
877            }
878        }
879
880        builder.end(path.is_closed());
881    }
882
883    builder.build()
884}
885
886#[cfg(feature = "svg")]
887#[inline]
888fn vertex_buffers_to_tessellated_cpu_node(v: VertexBuffers<SvgVertex, u32>) -> TessellatedSvgNode {
889    TessellatedSvgNode {
890        vertices: v.vertices.into(),
891        indices: v.indices.into(),
892    }
893}
894
895#[cfg(feature = "svg")]
896pub fn tessellate_multi_polygon_fill(
897    polygon: &SvgMultiPolygon,
898    fill_style: SvgFillStyle,
899) -> TessellatedSvgNode {
900    let polygon = svg_multipolygon_to_lyon_path(polygon);
901
902    let mut geometry = VertexBuffers::new();
903    let mut tessellator = FillTessellator::new();
904
905    let tess_result = tessellator.tessellate_path(
906        &polygon,
907        &FillOptions::tolerance(fill_style.tolerance),
908        &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
909            let xy_arr = vertex.position();
910            SvgVertex {
911                x: xy_arr.x,
912                y: xy_arr.y,
913            }
914        }),
915    );
916
917    if let Err(_) = tess_result {
918        TessellatedSvgNode::empty()
919    } else {
920        vertex_buffers_to_tessellated_cpu_node(geometry)
921    }
922}
923
924#[cfg(not(feature = "svg"))]
925pub fn tessellate_multi_polygon_fill(
926    polygon: &SvgMultiPolygon,
927    fill_style: SvgFillStyle,
928) -> TessellatedSvgNode {
929    TessellatedSvgNode::default()
930}
931
932#[cfg(feature = "svg")]
933pub fn tessellate_multi_shape_fill(
934    ms: &[SvgSimpleNode],
935    fill_style: SvgFillStyle,
936) -> TessellatedSvgNode {
937    let polygon = svg_multi_shape_to_lyon_path(ms);
938
939    let mut geometry = VertexBuffers::new();
940    let mut tessellator = FillTessellator::new();
941
942    let tess_result = tessellator.tessellate_path(
943        &polygon,
944        &FillOptions::tolerance(fill_style.tolerance),
945        &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
946            let xy_arr = vertex.position();
947            SvgVertex {
948                x: xy_arr.x,
949                y: xy_arr.y,
950            }
951        }),
952    );
953
954    if let Err(_) = tess_result {
955        TessellatedSvgNode::empty()
956    } else {
957        vertex_buffers_to_tessellated_cpu_node(geometry)
958    }
959}
960
961#[cfg(not(feature = "svg"))]
962pub fn tessellate_multi_shape_fill(
963    ms: &[SvgMultiPolygon],
964    fill_style: SvgFillStyle,
965) -> TessellatedSvgNode {
966    TessellatedSvgNode::default()
967}
968
969pub fn svg_node_contains_point(
970    node: &SvgNode,
971    point: SvgPoint,
972    fill_rule: SvgFillRule,
973    tolerance: f32,
974) -> bool {
975    match node {
976        SvgNode::MultiPolygonCollection(a) => a
977            .as_ref()
978            .iter()
979            .any(|e| polygon_contains_point(e, point, fill_rule, tolerance)),
980        SvgNode::MultiPolygon(a) => polygon_contains_point(a, point, fill_rule, tolerance),
981        SvgNode::Path(a) => {
982            if !a.is_closed() {
983                return false;
984            }
985            path_contains_point(a, point, fill_rule, tolerance)
986        }
987        SvgNode::Circle(a) => a.contains_point(point.x, point.y),
988        SvgNode::Rect(a) => a.contains_point(point),
989        SvgNode::MultiShape(a) => a.as_ref().iter().any(|e| match e {
990            SvgSimpleNode::Path(a) => {
991                if !a.is_closed() {
992                    return false;
993                }
994                path_contains_point(a, point, fill_rule, tolerance)
995            }
996            SvgSimpleNode::Circle(a) => a.contains_point(point.x, point.y),
997            SvgSimpleNode::Rect(a) => a.contains_point(point),
998            SvgSimpleNode::CircleHole(a) => !a.contains_point(point.x, point.y),
999            SvgSimpleNode::RectHole(a) => !a.contains_point(point),
1000        }),
1001    }
1002}
1003
1004#[cfg(feature = "svg")]
1005pub fn path_contains_point(
1006    path: &SvgPath,
1007    point: SvgPoint,
1008    fill_rule: SvgFillRule,
1009    tolerance: f32,
1010) -> bool {
1011    use lyon::{
1012        algorithms::hit_test::hit_test_path, math::Point as LyonPoint,
1013        path::FillRule as LyonFillRule,
1014    };
1015    let path = svg_path_to_lyon_path_events(path);
1016    let fill_rule = match fill_rule {
1017        SvgFillRule::Winding => LyonFillRule::NonZero,
1018        SvgFillRule::EvenOdd => LyonFillRule::EvenOdd,
1019    };
1020    let point = LyonPoint::new(point.x, point.y);
1021    hit_test_path(&point, path.iter(), fill_rule, tolerance)
1022}
1023
1024#[cfg(not(feature = "svg"))]
1025pub fn path_contains_point(
1026    path: &SvgPath,
1027    point: SvgPoint,
1028    fill_rule: SvgFillRule,
1029    tolerance: f32,
1030) -> bool {
1031    false
1032}
1033
1034#[cfg(feature = "svg")]
1035pub fn polygon_contains_point(
1036    polygon: &SvgMultiPolygon,
1037    point: SvgPoint,
1038    fill_rule: SvgFillRule,
1039    tolerance: f32,
1040) -> bool {
1041    use lyon::{
1042        algorithms::hit_test::hit_test_path, math::Point as LyonPoint,
1043        path::FillRule as LyonFillRule,
1044    };
1045    polygon.rings.iter().any(|path| {
1046        let path = svg_path_to_lyon_path_events(&path);
1047        let fill_rule = match fill_rule {
1048            SvgFillRule::Winding => LyonFillRule::NonZero,
1049            SvgFillRule::EvenOdd => LyonFillRule::EvenOdd,
1050        };
1051        let point = LyonPoint::new(point.x, point.y);
1052        hit_test_path(&point, path.iter(), fill_rule, tolerance)
1053    })
1054}
1055
1056#[cfg(not(feature = "svg"))]
1057pub fn polygon_contains_point(
1058    polygon: &SvgMultiPolygon,
1059    point: SvgPoint,
1060    fill_rule: SvgFillRule,
1061    tolerance: f32,
1062) -> bool {
1063    false
1064}
1065
1066#[cfg(feature = "svg")]
1067pub fn tessellate_multi_shape_stroke(
1068    ms: &[SvgSimpleNode],
1069    stroke_style: SvgStrokeStyle,
1070) -> TessellatedSvgNode {
1071    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1072    let polygon = svg_multi_shape_to_lyon_path(ms);
1073
1074    let mut stroke_geometry = VertexBuffers::new();
1075    let mut stroke_tess = StrokeTessellator::new();
1076
1077    let tess_result = stroke_tess.tessellate_path(
1078        &polygon,
1079        &stroke_options,
1080        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1081            let xy_arr = vertex.position();
1082            SvgVertex {
1083                x: xy_arr.x,
1084                y: xy_arr.y,
1085            }
1086        }),
1087    );
1088
1089    if let Err(_) = tess_result {
1090        TessellatedSvgNode::empty()
1091    } else {
1092        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1093    }
1094}
1095
1096#[cfg(not(feature = "svg"))]
1097pub fn tessellate_multi_shape_stroke(
1098    polygon: &[SvgSimpleNode],
1099    stroke_style: SvgStrokeStyle,
1100) -> TessellatedSvgNode {
1101    TessellatedSvgNode::default()
1102}
1103
1104#[cfg(feature = "svg")]
1105pub fn tessellate_multi_polygon_stroke(
1106    polygon: &SvgMultiPolygon,
1107    stroke_style: SvgStrokeStyle,
1108) -> TessellatedSvgNode {
1109    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1110    let polygon = svg_multipolygon_to_lyon_path(polygon);
1111
1112    let mut stroke_geometry = VertexBuffers::new();
1113    let mut stroke_tess = StrokeTessellator::new();
1114
1115    let tess_result = stroke_tess.tessellate_path(
1116        &polygon,
1117        &stroke_options,
1118        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1119            let xy_arr = vertex.position();
1120            SvgVertex {
1121                x: xy_arr.x,
1122                y: xy_arr.y,
1123            }
1124        }),
1125    );
1126
1127    if let Err(_) = tess_result {
1128        TessellatedSvgNode::empty()
1129    } else {
1130        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1131    }
1132}
1133
1134#[cfg(not(feature = "svg"))]
1135pub fn tessellate_multi_polygon_stroke(
1136    polygon: &SvgMultiPolygon,
1137    stroke_style: SvgStrokeStyle,
1138) -> TessellatedSvgNode {
1139    TessellatedSvgNode::default()
1140}
1141
1142#[cfg(feature = "svg")]
1143pub fn tessellate_path_fill(path: &SvgPath, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1144    let polygon = svg_path_to_lyon_path_events(path);
1145
1146    let mut geometry = VertexBuffers::new();
1147    let mut tessellator = FillTessellator::new();
1148
1149    let tess_result = tessellator.tessellate_path(
1150        &polygon,
1151        &FillOptions::tolerance(fill_style.tolerance),
1152        &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
1153            let xy_arr = vertex.position();
1154            SvgVertex {
1155                x: xy_arr.x,
1156                y: xy_arr.y,
1157            }
1158        }),
1159    );
1160
1161    if let Err(_) = tess_result {
1162        TessellatedSvgNode::empty()
1163    } else {
1164        vertex_buffers_to_tessellated_cpu_node(geometry)
1165    }
1166}
1167
1168#[cfg(not(feature = "svg"))]
1169pub fn tessellate_path_fill(path: &SvgPath, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1170    TessellatedSvgNode::default()
1171}
1172
1173#[cfg(feature = "svg")]
1174pub fn tessellate_path_stroke(path: &SvgPath, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1175    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1176    let polygon = svg_path_to_lyon_path_events(path);
1177
1178    let mut stroke_geometry = VertexBuffers::new();
1179    let mut stroke_tess = StrokeTessellator::new();
1180
1181    let tess_result = stroke_tess.tessellate_path(
1182        &polygon,
1183        &stroke_options,
1184        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1185            let xy_arr = vertex.position();
1186            SvgVertex {
1187                x: xy_arr.x,
1188                y: xy_arr.y,
1189            }
1190        }),
1191    );
1192
1193    if let Err(_) = tess_result {
1194        TessellatedSvgNode::empty()
1195    } else {
1196        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1197    }
1198}
1199
1200#[cfg(not(feature = "svg"))]
1201pub fn tessellate_path_stroke(path: &SvgPath, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1202    TessellatedSvgNode::default()
1203}
1204
1205#[cfg(feature = "svg")]
1206pub fn tessellate_circle_fill(c: &SvgCircle, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1207    let center = Point2D::new(c.center_x, c.center_y);
1208
1209    let mut geometry = VertexBuffers::new();
1210    let mut tesselator = FillTessellator::new();
1211    let tess_result = tesselator.tessellate_circle(
1212        center,
1213        c.radius,
1214        &FillOptions::tolerance(fill_style.tolerance),
1215        &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
1216            let xy_arr = vertex.position();
1217            SvgVertex {
1218                x: xy_arr.x,
1219                y: xy_arr.y,
1220            }
1221        }),
1222    );
1223
1224    if let Err(_) = tess_result {
1225        TessellatedSvgNode::empty()
1226    } else {
1227        vertex_buffers_to_tessellated_cpu_node(geometry)
1228    }
1229}
1230
1231#[cfg(not(feature = "svg"))]
1232pub fn tessellate_circle_fill(c: &SvgCircle, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1233    TessellatedSvgNode::default()
1234}
1235
1236#[cfg(feature = "svg")]
1237pub fn tessellate_circle_stroke(c: &SvgCircle, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1238    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1239    let center = Point2D::new(c.center_x, c.center_y);
1240
1241    let mut stroke_geometry = VertexBuffers::new();
1242    let mut tesselator = StrokeTessellator::new();
1243
1244    let tess_result = tesselator.tessellate_circle(
1245        center,
1246        c.radius,
1247        &stroke_options,
1248        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1249            let xy_arr = vertex.position();
1250            SvgVertex {
1251                x: xy_arr.x,
1252                y: xy_arr.y,
1253            }
1254        }),
1255    );
1256
1257    if let Err(_) = tess_result {
1258        TessellatedSvgNode::empty()
1259    } else {
1260        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1261    }
1262}
1263
1264#[cfg(not(feature = "svg"))]
1265pub fn tessellate_circle_stroke(c: &SvgCircle, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1266    TessellatedSvgNode::default()
1267}
1268
1269// TODO: radii not respected on latest version of lyon
1270#[cfg(feature = "svg")]
1271fn get_radii(r: &SvgRect) -> lyon::geom::Box2D<f32> {
1272    let rect = lyon::geom::Box2D::from_origin_and_size(
1273        Point2D::new(r.x, r.y),
1274        Size2D::new(r.width, r.height),
1275    );
1276    /*
1277    let radii = BorderRadii {
1278        top_left: r.radius_top_left,
1279        top_right: r.radius_top_right,
1280        bottom_left: r.radius_bottom_left,
1281        bottom_right: r.radius_bottom_right
1282    };*/
1283    rect
1284}
1285
1286#[cfg(feature = "svg")]
1287pub fn tessellate_rect_fill(r: &SvgRect, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1288    let rect = get_radii(&r);
1289    let mut geometry = VertexBuffers::new();
1290    let mut tesselator = FillTessellator::new();
1291
1292    let tess_result = tesselator.tessellate_rectangle(
1293        &rect,
1294        &FillOptions::tolerance(fill_style.tolerance),
1295        &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
1296            let xy_arr = vertex.position();
1297            SvgVertex {
1298                x: xy_arr.x,
1299                y: xy_arr.y,
1300            }
1301        }),
1302    );
1303
1304    if let Err(_) = tess_result {
1305        TessellatedSvgNode::empty()
1306    } else {
1307        vertex_buffers_to_tessellated_cpu_node(geometry)
1308    }
1309}
1310
1311#[cfg(not(feature = "svg"))]
1312pub fn tessellate_rect_fill(r: &SvgRect, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1313    TessellatedSvgNode::default()
1314}
1315
1316#[cfg(feature = "svg")]
1317pub fn tessellate_rect_stroke(r: &SvgRect, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1318    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1319    let rect = get_radii(&r);
1320
1321    let mut stroke_geometry = VertexBuffers::new();
1322    let mut tesselator = StrokeTessellator::new();
1323
1324    let tess_result = tesselator.tessellate_rectangle(
1325        &rect,
1326        &stroke_options,
1327        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1328            let xy_arr = vertex.position();
1329            SvgVertex {
1330                x: xy_arr.x,
1331                y: xy_arr.y,
1332            }
1333        }),
1334    );
1335
1336    if let Err(_) = tess_result {
1337        TessellatedSvgNode::empty()
1338    } else {
1339        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1340    }
1341}
1342
1343#[cfg(not(feature = "svg"))]
1344pub fn tessellate_rect_stroke(r: &SvgRect, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1345    TessellatedSvgNode::default()
1346}
1347
1348/// Tessellate the path using lyon
1349#[cfg(feature = "svg")]
1350pub fn tessellate_styled_node(node: &SvgStyledNode) -> TessellatedSvgNode {
1351    match node.style {
1352        SvgStyle::Fill(fs) => tessellate_node_fill(&node.geometry, fs),
1353        SvgStyle::Stroke(ss) => tessellate_node_stroke(&node.geometry, ss),
1354    }
1355}
1356
1357#[cfg(not(feature = "svg"))]
1358pub fn tessellate_styled_node(node: &SvgStyledNode) -> TessellatedSvgNode {
1359    TessellatedSvgNode::default()
1360}
1361
1362#[cfg(feature = "svg")]
1363pub fn tessellate_line_stroke(
1364    svgline: &SvgLine,
1365    stroke_style: SvgStrokeStyle,
1366) -> TessellatedSvgNode {
1367    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1368
1369    let mut builder = Path::builder();
1370    builder.begin(Point2D::new(svgline.start.x, svgline.start.y));
1371    builder.line_to(Point2D::new(svgline.end.x, svgline.end.y));
1372    builder.end(/* closed */ false);
1373    let path = builder.build();
1374
1375    let mut stroke_geometry = VertexBuffers::new();
1376    let mut stroke_tess = StrokeTessellator::new();
1377
1378    let tess_result = stroke_tess.tessellate_path(
1379        &path,
1380        &stroke_options,
1381        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1382            let xy_arr = vertex.position();
1383            SvgVertex {
1384                x: xy_arr.x,
1385                y: xy_arr.y,
1386            }
1387        }),
1388    );
1389
1390    if let Err(_) = tess_result {
1391        TessellatedSvgNode::empty()
1392    } else {
1393        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1394    }
1395}
1396
1397#[cfg(not(feature = "svg"))]
1398pub fn tessellate_line_stroke(
1399    svgline: &SvgLine,
1400    stroke_style: SvgStrokeStyle,
1401) -> TessellatedSvgNode {
1402    TessellatedSvgNode::default()
1403}
1404
1405#[cfg(feature = "svg")]
1406pub fn tessellate_cubiccurve_stroke(
1407    svgcubiccurve: &SvgCubicCurve,
1408    stroke_style: SvgStrokeStyle,
1409) -> TessellatedSvgNode {
1410    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1411
1412    let mut builder = Path::builder();
1413    builder.begin(Point2D::new(svgcubiccurve.start.x, svgcubiccurve.start.y));
1414    builder.cubic_bezier_to(
1415        Point2D::new(svgcubiccurve.ctrl_1.x, svgcubiccurve.ctrl_1.y),
1416        Point2D::new(svgcubiccurve.ctrl_2.x, svgcubiccurve.ctrl_2.y),
1417        Point2D::new(svgcubiccurve.end.x, svgcubiccurve.end.y),
1418    );
1419    builder.end(/* closed */ false);
1420    let path = builder.build();
1421
1422    let mut stroke_geometry = VertexBuffers::new();
1423    let mut stroke_tess = StrokeTessellator::new();
1424
1425    let tess_result = stroke_tess.tessellate_path(
1426        &path,
1427        &stroke_options,
1428        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1429            let xy_arr = vertex.position();
1430            SvgVertex {
1431                x: xy_arr.x,
1432                y: xy_arr.y,
1433            }
1434        }),
1435    );
1436
1437    if let Err(_) = tess_result {
1438        TessellatedSvgNode::empty()
1439    } else {
1440        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1441    }
1442}
1443
1444#[cfg(not(feature = "svg"))]
1445pub fn tessellate_cubiccurve_stroke(
1446    svgline: &SvgCubicCurve,
1447    stroke_style: SvgStrokeStyle,
1448) -> TessellatedSvgNode {
1449    TessellatedSvgNode::default()
1450}
1451
1452#[cfg(feature = "svg")]
1453pub fn tessellate_quadraticcurve_stroke(
1454    svgquadraticcurve: &SvgQuadraticCurve,
1455    stroke_style: SvgStrokeStyle,
1456) -> TessellatedSvgNode {
1457    let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1458
1459    let mut builder = Path::builder();
1460    builder.begin(Point2D::new(
1461        svgquadraticcurve.start.x,
1462        svgquadraticcurve.start.y,
1463    ));
1464    builder.quadratic_bezier_to(
1465        Point2D::new(svgquadraticcurve.ctrl.x, svgquadraticcurve.ctrl.y),
1466        Point2D::new(svgquadraticcurve.end.x, svgquadraticcurve.end.y),
1467    );
1468    builder.end(/* closed */ false);
1469    let path = builder.build();
1470
1471    let mut stroke_geometry = VertexBuffers::new();
1472    let mut stroke_tess = StrokeTessellator::new();
1473
1474    let tess_result = stroke_tess.tessellate_path(
1475        &path,
1476        &stroke_options,
1477        &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1478            let xy_arr = vertex.position();
1479            SvgVertex {
1480                x: xy_arr.x,
1481                y: xy_arr.y,
1482            }
1483        }),
1484    );
1485
1486    if let Err(_) = tess_result {
1487        TessellatedSvgNode::empty()
1488    } else {
1489        vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1490    }
1491}
1492
1493#[cfg(not(feature = "svg"))]
1494pub fn tessellate_quadraticcurve_stroke(
1495    svgquadraticcurve: &SvgQuadraticCurve,
1496    stroke_style: SvgStrokeStyle,
1497) -> TessellatedSvgNode {
1498    TessellatedSvgNode::default()
1499}
1500
1501#[cfg(feature = "svg")]
1502pub fn tessellate_svgpathelement_stroke(
1503    svgpathelement: &SvgPathElement,
1504    stroke_style: SvgStrokeStyle,
1505) -> TessellatedSvgNode {
1506    match svgpathelement {
1507        SvgPathElement::Line(l) => tessellate_line_stroke(l, stroke_style),
1508        SvgPathElement::QuadraticCurve(l) => tessellate_quadraticcurve_stroke(l, stroke_style),
1509        SvgPathElement::CubicCurve(l) => tessellate_cubiccurve_stroke(l, stroke_style),
1510    }
1511}
1512
1513#[cfg(not(feature = "svg"))]
1514pub fn tessellate_svgpathelement_stroke(
1515    svgpathelement: &SvgPathElement,
1516    stroke_style: SvgStrokeStyle,
1517) -> TessellatedSvgNode {
1518    TessellatedSvgNode::default()
1519}
1520
1521#[cfg(feature = "svg")]
1522pub fn join_tessellated_nodes(nodes: &[TessellatedSvgNode]) -> TessellatedSvgNode {
1523    let mut index_offset = 0;
1524
1525    // note: can not be parallelized!
1526    let all_index_offsets = nodes
1527        .as_ref()
1528        .iter()
1529        .map(|t| {
1530            let i = index_offset;
1531            index_offset += t.vertices.len();
1532            i
1533        })
1534        .collect::<Vec<_>>();
1535
1536    let all_vertices = nodes
1537        .as_ref()
1538        .iter()
1539        .flat_map(|t| t.vertices.clone().into_library_owned_vec())
1540        .collect::<Vec<_>>();
1541
1542    let all_indices = nodes
1543        .as_ref()
1544        .iter()
1545        .enumerate()
1546        .flat_map(|(buffer_index, t)| {
1547            // since the vertex buffers are now joined,
1548            // offset the indices by the vertex buffers lengths
1549            // encountered so far
1550            let vertex_buffer_offset: u32 = all_index_offsets
1551                .get(buffer_index)
1552                .copied()
1553                .unwrap_or(0)
1554                .min(core::u32::MAX as usize) as u32;
1555
1556            let mut indices = t.indices.clone().into_library_owned_vec();
1557            if vertex_buffer_offset != 0 {
1558                indices.iter_mut().for_each(|i| {
1559                    if *i != GL_RESTART_INDEX {
1560                        *i += vertex_buffer_offset;
1561                    }
1562                });
1563            }
1564
1565            indices.push(GL_RESTART_INDEX);
1566
1567            indices
1568        })
1569        .collect::<Vec<_>>();
1570
1571    TessellatedSvgNode {
1572        vertices: all_vertices.into(),
1573        indices: all_indices.into(),
1574    }
1575}
1576
1577#[cfg(feature = "svg")]
1578pub fn join_tessellated_colored_nodes(
1579    nodes: &[TessellatedColoredSvgNode],
1580) -> TessellatedColoredSvgNode {
1581    let mut index_offset = 0;
1582
1583    // note: can not be parallelized!
1584    let all_index_offsets = nodes
1585        .as_ref()
1586        .iter()
1587        .map(|t| {
1588            let i = index_offset;
1589            index_offset += t.vertices.len();
1590            i
1591        })
1592        .collect::<Vec<_>>();
1593
1594    let all_vertices = nodes
1595        .as_ref()
1596        .iter()
1597        .flat_map(|t| t.vertices.clone().into_library_owned_vec())
1598        .collect::<Vec<_>>();
1599
1600    let all_indices = nodes
1601        .as_ref()
1602        .iter()
1603        .enumerate()
1604        .flat_map(|(buffer_index, t)| {
1605            // since the vertex buffers are now joined,
1606            // offset the indices by the vertex buffers lengths
1607            // encountered so far
1608            let vertex_buffer_offset: u32 = all_index_offsets
1609                .get(buffer_index)
1610                .copied()
1611                .unwrap_or(0)
1612                .min(core::u32::MAX as usize) as u32;
1613
1614            let mut indices = t.indices.clone().into_library_owned_vec();
1615            if vertex_buffer_offset != 0 {
1616                indices.iter_mut().for_each(|i| {
1617                    if *i != GL_RESTART_INDEX {
1618                        *i += vertex_buffer_offset;
1619                    }
1620                });
1621            }
1622
1623            indices.push(GL_RESTART_INDEX);
1624
1625            indices
1626        })
1627        .collect::<Vec<_>>();
1628
1629    TessellatedColoredSvgNode {
1630        vertices: all_vertices.into(),
1631        indices: all_indices.into(),
1632    }
1633}
1634
1635#[cfg(not(feature = "svg"))]
1636pub fn join_tessellated_nodes(nodes: &[TessellatedSvgNode]) -> TessellatedSvgNode {
1637    TessellatedSvgNode::default()
1638}
1639
1640#[cfg(not(feature = "svg"))]
1641pub fn join_tessellated_colored_nodes(
1642    nodes: &[TessellatedColoredSvgNode],
1643) -> TessellatedColoredSvgNode {
1644    TessellatedColoredSvgNode::default()
1645}
1646
1647#[cfg(feature = "svg")]
1648pub fn tessellate_node_fill(node: &SvgNode, fs: SvgFillStyle) -> TessellatedSvgNode {
1649    match &node {
1650        SvgNode::MultiPolygonCollection(ref mpc) => {
1651            let tessellated_multipolygons = mpc
1652                .as_ref()
1653                .iter()
1654                .map(|mp| tessellate_multi_polygon_fill(mp, fs))
1655                .collect::<Vec<_>>();
1656            join_tessellated_nodes(&tessellated_multipolygons)
1657        }
1658        SvgNode::MultiPolygon(ref mp) => tessellate_multi_polygon_fill(mp, fs),
1659        SvgNode::Path(ref p) => tessellate_path_fill(p, fs),
1660        SvgNode::Circle(ref c) => tessellate_circle_fill(c, fs),
1661        SvgNode::Rect(ref r) => tessellate_rect_fill(r, fs),
1662        SvgNode::MultiShape(ref r) => tessellate_multi_shape_fill(r.as_ref(), fs),
1663    }
1664}
1665
1666#[cfg(not(feature = "svg"))]
1667pub fn tessellate_node_fill(node: &SvgNode, fs: SvgFillStyle) -> TessellatedSvgNode {
1668    TessellatedSvgNode::default()
1669}
1670
1671#[cfg(feature = "svg")]
1672pub fn tessellate_node_stroke(node: &SvgNode, ss: SvgStrokeStyle) -> TessellatedSvgNode {
1673    match &node {
1674        SvgNode::MultiPolygonCollection(ref mpc) => {
1675            let tessellated_multipolygons = mpc
1676                .as_ref()
1677                .iter()
1678                .map(|mp| tessellate_multi_polygon_stroke(mp, ss))
1679                .collect::<Vec<_>>();
1680            let mut all_vertices = Vec::new();
1681            let mut all_indices = Vec::new();
1682            for TessellatedSvgNode { vertices, indices } in tessellated_multipolygons {
1683                let mut vertices: Vec<SvgVertex> = vertices.into_library_owned_vec();
1684                let mut indices: Vec<u32> = indices.into_library_owned_vec();
1685                all_vertices.append(&mut vertices);
1686                all_indices.append(&mut indices);
1687                all_indices.push(GL_RESTART_INDEX);
1688            }
1689            TessellatedSvgNode {
1690                vertices: all_vertices.into(),
1691                indices: all_indices.into(),
1692            }
1693        }
1694        SvgNode::MultiPolygon(ref mp) => tessellate_multi_polygon_stroke(mp, ss),
1695        SvgNode::Path(ref p) => tessellate_path_stroke(p, ss),
1696        SvgNode::Circle(ref c) => tessellate_circle_stroke(c, ss),
1697        SvgNode::Rect(ref r) => tessellate_rect_stroke(r, ss),
1698        SvgNode::MultiShape(ms) => tessellate_multi_shape_stroke(ms.as_ref(), ss),
1699    }
1700}
1701
1702#[cfg(not(feature = "svg"))]
1703pub fn tessellate_node_stroke(node: &SvgNode, ss: SvgStrokeStyle) -> TessellatedSvgNode {
1704    TessellatedSvgNode::default()
1705}
1706
1707// NOTE: This is a separate step both in order to reuse GPU textures
1708// and also because texture allocation is heavy and can be offloaded to a different thread
1709pub fn allocate_clipmask_texture(
1710    gl_context: GlContextPtr,
1711    size: PhysicalSizeU32,
1712    _background: ColorU,
1713) -> Texture {
1714    use azul_core::gl::TextureFlags;
1715
1716    let textures = gl_context.gen_textures(1);
1717    let texture_id = textures.get(0).unwrap();
1718
1719    Texture::create(
1720        *texture_id,
1721        TextureFlags {
1722            is_opaque: true,
1723            is_video_texture: false,
1724        },
1725        size,
1726        ColorU::TRANSPARENT,
1727        gl_context,
1728        RawImageFormat::R8,
1729    )
1730}
1731
1732/// Applies an FXAA filter to the texture
1733pub fn apply_fxaa(texture: &mut Texture) -> Option<()> {
1734    // TODO
1735    Some(())
1736}
1737
1738pub fn render_tessellated_node_gpu(texture: &mut Texture, node: &TessellatedSvgNode) -> Option<()> {
1739    use std::mem;
1740
1741    use azul_core::gl::{GLuint, GlVoidPtrConst, VertexAttributeType};
1742    use gl_context_loader::gl;
1743
1744    const INDEX_TYPE: GLuint = gl::UNSIGNED_INT;
1745
1746    if texture.format != RawImageFormat::R8 {
1747        return None;
1748    }
1749
1750    let texture_size = texture.size;
1751    let gl_context = &texture.gl_context;
1752    let fxaa_shader = gl_context.get_fxaa_shader();
1753    let svg_shader = gl_context.get_svg_shader();
1754
1755    // start: save the OpenGL state
1756    let mut current_multisample = [0_u8];
1757    let mut current_index_buffer = [0_i32];
1758    let mut current_vertex_array = [0_i32];
1759    let mut current_vertex_buffer = [0_i32];
1760    let mut current_vertex_array_object = [0_i32];
1761    let mut current_program = [0_i32];
1762    let mut current_framebuffers = [0_i32];
1763    let mut current_texture_2d = [0_i32];
1764    let mut current_primitive_restart_enabled = [0_u8];
1765
1766    gl_context.get_boolean_v(gl::MULTISAMPLE, (&mut current_multisample[..]).into());
1767    gl_context.get_integer_v(gl::VERTEX_ARRAY, (&mut current_vertex_array[..]).into());
1768    gl_context.get_integer_v(
1769        gl::ARRAY_BUFFER_BINDING,
1770        (&mut current_vertex_buffer[..]).into(),
1771    );
1772    gl_context.get_integer_v(
1773        gl::ELEMENT_ARRAY_BUFFER_BINDING,
1774        (&mut current_index_buffer[..]).into(),
1775    );
1776    gl_context.get_integer_v(gl::CURRENT_PROGRAM, (&mut current_program[..]).into());
1777    gl_context.get_integer_v(
1778        gl::VERTEX_ARRAY_BINDING,
1779        (&mut current_vertex_array_object[..]).into(),
1780    );
1781    gl_context.get_integer_v(gl::FRAMEBUFFER, (&mut current_framebuffers[..]).into());
1782    gl_context.get_integer_v(gl::TEXTURE_2D, (&mut current_texture_2d[..]).into());
1783    gl_context.get_boolean_v(
1784        gl::PRIMITIVE_RESTART_FIXED_INDEX,
1785        (&mut current_primitive_restart_enabled[..]).into(),
1786    );
1787
1788    // stage 1: upload vertices / indices to GPU
1789
1790    let vertex_array_object = gl_context.gen_vertex_arrays(1);
1791    let vertex_array_object = vertex_array_object.get(0)?;
1792
1793    let vertex_buffer_id = gl_context.gen_buffers(1);
1794    let vertex_buffer_id = vertex_buffer_id.get(0)?;
1795
1796    let index_buffer_id = gl_context.gen_buffers(1);
1797    let index_buffer_id = index_buffer_id.get(0)?;
1798
1799    gl_context.bind_vertex_array(*vertex_array_object);
1800    gl_context.bind_buffer(gl::ARRAY_BUFFER, *vertex_buffer_id);
1801    gl_context.buffer_data_untyped(
1802        gl::ARRAY_BUFFER,
1803        (mem::size_of::<SvgVertex>() * node.vertices.len()) as isize,
1804        GlVoidPtrConst {
1805            ptr: &node.vertices as *const _ as *const std::ffi::c_void,
1806            run_destructor: true,
1807        },
1808        gl::STATIC_DRAW,
1809    );
1810
1811    gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, *index_buffer_id);
1812    gl_context.buffer_data_untyped(
1813        gl::ELEMENT_ARRAY_BUFFER,
1814        (mem::size_of::<u32>() * node.indices.len()) as isize,
1815        GlVoidPtrConst {
1816            ptr: &node.indices as *const _ as *const std::ffi::c_void,
1817            run_destructor: true,
1818        },
1819        gl::STATIC_DRAW,
1820    );
1821
1822    // stage 2: set up the data description
1823    let vertex_type = VertexAttributeType::Float;
1824    let vertex_count = 2;
1825    let stride = vertex_type.get_mem_size() * vertex_count;
1826    let offset = 0;
1827    let vertices_are_normalized = false;
1828
1829    let vertex_attrib_location = gl_context.get_attrib_location(svg_shader, "vAttrXY".into());
1830    gl_context.vertex_attrib_pointer(
1831        vertex_attrib_location as u32,
1832        vertex_count as i32,
1833        vertex_type.get_gl_id(),
1834        vertices_are_normalized,
1835        stride as i32,
1836        offset as u32,
1837    );
1838    gl_context.enable_vertex_attrib_array(vertex_attrib_location as u32);
1839
1840    // stage 3: draw
1841
1842    gl_context.bind_texture(gl::TEXTURE_2D, texture.texture_id);
1843    gl_context.tex_image_2d(
1844        gl::TEXTURE_2D,
1845        0,
1846        gl::R8 as i32,
1847        texture_size.width as i32,
1848        texture_size.height as i32,
1849        0,
1850        gl::RED,
1851        gl::UNSIGNED_BYTE,
1852        None.into(),
1853    );
1854    gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
1855    gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
1856    gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
1857    gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
1858
1859    let framebuffers = gl_context.gen_framebuffers(1);
1860    let framebuffer_id = framebuffers.get(0)?;
1861    gl_context.bind_framebuffer(gl::FRAMEBUFFER, *framebuffer_id);
1862
1863    gl_context.framebuffer_texture_2d(
1864        gl::FRAMEBUFFER,
1865        gl::COLOR_ATTACHMENT0,
1866        gl::TEXTURE_2D,
1867        texture.texture_id,
1868        0,
1869    );
1870    gl_context.draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
1871    gl_context.viewport(0, 0, texture_size.width as i32, texture_size.height as i32);
1872
1873    debug_assert!(
1874        gl_context.check_frame_buffer_status(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE
1875    );
1876
1877    gl_context.use_program(svg_shader);
1878    gl_context.disable(gl::MULTISAMPLE);
1879
1880    let bbox_uniform_location = gl_context.get_uniform_location(svg_shader, "vBboxSize".into());
1881
1882    gl_context.clear_color(0.0, 0.0, 0.0, 1.0);
1883    gl_context.clear(gl::COLOR_BUFFER_BIT);
1884    gl_context.bind_vertex_array(*vertex_buffer_id);
1885    gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, *index_buffer_id);
1886    gl_context.uniform_2f(
1887        bbox_uniform_location,
1888        texture_size.width as f32,
1889        texture_size.height as f32,
1890    );
1891    gl_context.draw_elements(gl::TRIANGLES, node.indices.len() as i32, INDEX_TYPE, 0);
1892
1893    // stage 4: cleanup - reset the OpenGL state
1894    if u32::from(current_multisample[0]) == gl::TRUE {
1895        gl_context.enable(gl::MULTISAMPLE);
1896    }
1897    if u32::from(current_primitive_restart_enabled[0]) == gl::FALSE {
1898        gl_context.disable(gl::PRIMITIVE_RESTART_FIXED_INDEX);
1899    }
1900    gl_context.bind_vertex_array(current_vertex_array_object[0] as u32);
1901    gl_context.bind_framebuffer(gl::FRAMEBUFFER, current_framebuffers[0] as u32);
1902    gl_context.bind_texture(gl::TEXTURE_2D, current_texture_2d[0] as u32);
1903    gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, current_index_buffer[0] as u32);
1904    gl_context.bind_buffer(gl::ARRAY_BUFFER, current_vertex_buffer[0] as u32);
1905    gl_context.use_program(current_program[0] as u32);
1906
1907    // delete resources
1908    gl_context.delete_framebuffers(framebuffers.as_ref().into());
1909    gl_context.delete_vertex_arrays(([current_vertex_array_object[0] as u32])[..].into());
1910    gl_context.delete_buffers(([*vertex_buffer_id, *index_buffer_id])[..].into());
1911
1912    Some(())
1913}
1914
1915#[cfg(feature = "svg")]
1916pub fn render_node_clipmask_cpu(
1917    image: &mut RawImage,
1918    node: &SvgNode,
1919    style: SvgStyle,
1920) -> Option<()> {
1921    use azul_core::resources::RawImageData;
1922    use tiny_skia::{
1923        FillRule as SkFillRule, LineCap as SkLineCap, LineJoin as SkLineJoin, Paint as SkPaint,
1924        Path as SkPath, PathBuilder as SkPathBuilder, Pixmap as SkPixmap, Rect as SkRect,
1925        Stroke as SkStroke, StrokeDash as SkStrokeDash, Transform as SkTransform,
1926    };
1927
1928    fn tiny_skia_translate_node(node: &SvgNode) -> Option<SkPath> {
1929        macro_rules! build_path {
1930            ($path_builder:expr, $p:expr) => {{
1931                if $p.items.as_ref().is_empty() {
1932                    return None;
1933                }
1934
1935                let start = $p.items.as_ref()[0].get_start();
1936                $path_builder.move_to(start.x, start.y);
1937
1938                for path_element in $p.items.as_ref() {
1939                    match path_element {
1940                        SvgPathElement::Line(l) => {
1941                            $path_builder.line_to(l.end.x, l.end.y);
1942                        }
1943                        SvgPathElement::QuadraticCurve(qc) => {
1944                            $path_builder.quad_to(qc.ctrl.x, qc.ctrl.y, qc.end.x, qc.end.y);
1945                        }
1946                        SvgPathElement::CubicCurve(cc) => {
1947                            $path_builder.cubic_to(
1948                                cc.ctrl_1.x,
1949                                cc.ctrl_1.y,
1950                                cc.ctrl_2.x,
1951                                cc.ctrl_2.y,
1952                                cc.end.x,
1953                                cc.end.y,
1954                            );
1955                        }
1956                    }
1957                }
1958
1959                if $p.is_closed() {
1960                    $path_builder.close();
1961                }
1962            }};
1963        }
1964
1965        match node {
1966            SvgNode::MultiPolygonCollection(mpc) => {
1967                let mut path_builder = SkPathBuilder::new();
1968                for mp in mpc.iter() {
1969                    for p in mp.rings.iter() {
1970                        build_path!(path_builder, p);
1971                    }
1972                }
1973                path_builder.finish()
1974            }
1975            SvgNode::MultiPolygon(mp) => {
1976                let mut path_builder = SkPathBuilder::new();
1977                for p in mp.rings.iter() {
1978                    build_path!(path_builder, p);
1979                }
1980                path_builder.finish()
1981            }
1982            SvgNode::Path(p) => {
1983                let mut path_builder = SkPathBuilder::new();
1984                build_path!(path_builder, p);
1985                path_builder.finish()
1986            }
1987            SvgNode::Circle(c) => SkPathBuilder::from_circle(c.center_x, c.center_y, c.radius),
1988            SvgNode::Rect(r) => {
1989                // TODO: rounded edges!
1990                Some(SkPathBuilder::from_rect(SkRect::from_xywh(
1991                    r.x, r.y, r.width, r.height,
1992                )?))
1993            }
1994            // TODO: test?
1995            SvgNode::MultiShape(ms) => {
1996                let mut path_builder = SkPathBuilder::new();
1997                for p in ms.as_ref() {
1998                    match p {
1999                        SvgSimpleNode::Path(p) => {
2000                            build_path!(path_builder, p);
2001                        }
2002                        SvgSimpleNode::Rect(r) => {
2003                            if let Some(r) = tiny_skia::Rect::from_xywh(r.x, r.y, r.width, r.height)
2004                            {
2005                                path_builder.push_rect(r);
2006                            }
2007                        }
2008                        SvgSimpleNode::Circle(c) => {
2009                            path_builder.push_circle(c.center_x, c.center_y, c.radius);
2010                        }
2011                        SvgSimpleNode::CircleHole(c) => {
2012                            path_builder.push_circle(c.center_x, c.center_y, c.radius);
2013                        }
2014                        SvgSimpleNode::RectHole(r) => {
2015                            if let Some(r) = tiny_skia::Rect::from_xywh(r.x, r.y, r.width, r.height)
2016                            {
2017                                path_builder.push_rect(r);
2018                            }
2019                        }
2020                    }
2021                }
2022                path_builder.finish()
2023            }
2024        }
2025    }
2026
2027    let mut paint = SkPaint::default();
2028    paint.set_color_rgba8(255, 255, 255, 255);
2029    paint.anti_alias = style.get_antialias();
2030    paint.force_hq_pipeline = style.get_high_quality_aa();
2031
2032    let transform = style.get_transform();
2033    let transform = SkTransform {
2034        sx: transform.sx,
2035        kx: transform.kx,
2036        ky: transform.ky,
2037        sy: transform.sy,
2038        tx: transform.tx,
2039        ty: transform.ty,
2040    };
2041
2042    let mut pixmap = SkPixmap::new(image.width as u32, image.height as u32)?;
2043    let path = tiny_skia_translate_node(node)?;
2044    let clip_mask = None;
2045
2046    match style {
2047        SvgStyle::Fill(fs) => {
2048            pixmap.fill_path(
2049                &path,
2050                &paint,
2051                match fs.fill_rule {
2052                    SvgFillRule::Winding => SkFillRule::Winding,
2053                    SvgFillRule::EvenOdd => SkFillRule::EvenOdd,
2054                },
2055                transform,
2056                clip_mask,
2057            );
2058        }
2059        SvgStyle::Stroke(ss) => {
2060            let stroke = SkStroke {
2061                width: ss.line_width,
2062                miter_limit: ss.miter_limit,
2063                line_cap: match ss.start_cap {
2064                    // TODO: end_cap?
2065                    SvgLineCap::Butt => SkLineCap::Butt,
2066                    SvgLineCap::Square => SkLineCap::Square,
2067                    SvgLineCap::Round => SkLineCap::Round,
2068                },
2069                line_join: match ss.line_join {
2070                    SvgLineJoin::Miter | SvgLineJoin::MiterClip => SkLineJoin::Miter,
2071                    SvgLineJoin::Round => SkLineJoin::Round,
2072                    SvgLineJoin::Bevel => SkLineJoin::Bevel,
2073                },
2074                dash: ss.dash_pattern.as_ref().and_then(|d| {
2075                    SkStrokeDash::new(
2076                        vec![
2077                            d.length_1, d.gap_1, d.length_2, d.gap_2, d.length_3, d.gap_3,
2078                        ],
2079                        d.offset,
2080                    )
2081                }),
2082            };
2083            pixmap.stroke_path(&path, &paint, &stroke, transform, clip_mask);
2084        }
2085    }
2086
2087    // RGBA to red channel
2088    let red_channel = pixmap
2089        .take()
2090        .chunks_exact(4)
2091        .map(|r| r[0])
2092        .collect::<Vec<_>>();
2093
2094    image.premultiplied_alpha = true;
2095    image.pixels = RawImageData::U8(red_channel.into());
2096    image.data_format = RawImageFormat::R8;
2097
2098    Some(())
2099}
2100
2101#[cfg(not(feature = "svg"))]
2102pub fn render_node_clipmask_cpu(
2103    image: &mut RawImage,
2104    node: &SvgNode,
2105    style: SvgStyle,
2106) -> Option<()> {
2107    None
2108}
2109
2110// ---------------------------- SVG RENDERING
2111
2112#[cfg(feature = "svg")]
2113#[derive(Debug)]
2114#[repr(C)]
2115pub struct ParsedSvgXmlNode {
2116    node: Box<usvg::Group>, // usvg::Node
2117    pub run_destructor: bool,
2118}
2119
2120#[cfg(feature = "svg")]
2121impl Clone for ParsedSvgXmlNode {
2122    fn clone(&self) -> Self {
2123        Self {
2124            node: self.node.clone(),
2125            run_destructor: true,
2126        }
2127    }
2128}
2129
2130#[cfg(feature = "svg")]
2131impl Drop for ParsedSvgXmlNode {
2132    fn drop(&mut self) {
2133        self.run_destructor = false;
2134    }
2135}
2136
2137#[cfg(not(feature = "svg"))]
2138pub use azul_core::svg::SvgXmlNode;
2139
2140#[cfg(feature = "svg")]
2141fn svgxmlnode_new(node: usvg::Group) -> ParsedSvgXmlNode {
2142    ParsedSvgXmlNode {
2143        node: Box::new(node),
2144        run_destructor: true,
2145    }
2146}
2147
2148#[cfg(feature = "svg")]
2149pub fn svgxmlnode_parse(
2150    svg_file_data: &[u8],
2151    options: SvgParseOptions,
2152) -> Result<ParsedSvgXmlNode, SvgParseError> {
2153    let svg = svg_parse(svg_file_data, options)?;
2154    Ok(svg_root(&svg))
2155}
2156
2157#[cfg(not(feature = "svg"))]
2158pub fn svgxmlnode_parse(
2159    svg_file_data: &[u8],
2160    options: SvgParseOptions,
2161) -> Result<ParsedSvgXmlNode, SvgParseError> {
2162    Err(SvgParseError::NoParserAvailable)
2163}
2164
2165/*
2166#[cfg(feature = "svg")]
2167pub fn svgxmlnode_from_xml(xml: Xml) -> Result<Self, SvgParseError> {
2168    // https://github.com/RazrFalcon/resvg/issues/308
2169    Ok(Svg::new(xml.into_tree()))
2170}
2171*/
2172
2173#[cfg(feature = "svg")]
2174#[repr(C)]
2175pub struct ParsedSvg {
2176    tree: Box<usvg::Tree>, // *mut usvg::Tree,
2177    pub run_destructor: bool,
2178}
2179
2180#[cfg(feature = "svg")]
2181impl Clone for ParsedSvg {
2182    fn clone(&self) -> Self {
2183        Self {
2184            tree: self.tree.clone(),
2185            run_destructor: true,
2186        }
2187    }
2188}
2189
2190#[cfg(feature = "svg")]
2191impl Drop for ParsedSvg {
2192    fn drop(&mut self) {
2193        self.run_destructor = false;
2194    }
2195}
2196
2197#[cfg(feature = "svg")]
2198impl_result!(
2199    ParsedSvg,
2200    SvgParseError,
2201    ResultParsedSvgSvgParseError,
2202    copy = false,
2203    [Debug, Clone]
2204);
2205
2206#[cfg(not(feature = "svg"))]
2207pub use azul_core::svg::Svg;
2208
2209#[cfg(feature = "svg")]
2210impl From<ParsedSvg> for azul_core::svg::Svg {
2211    fn from(mut parsed: ParsedSvg) -> Self {
2212        // Use ManuallyDrop to prevent the ParsedSvg destructor from running
2213        // while still allowing us to move out the tree
2214        let mut parsed = core::mem::ManuallyDrop::new(parsed);
2215        // Take ownership of the tree by replacing it with a dummy
2216        let tree = unsafe { core::ptr::read(&parsed.tree) };
2217        let tree_ptr = Box::into_raw(tree) as *const azul_core::svg::c_void;
2218        Self {
2219            tree: tree_ptr,
2220            run_destructor: true,
2221        }
2222    }
2223}
2224
2225#[cfg(feature = "svg")]
2226impl fmt::Debug for ParsedSvg {
2227    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2228        svg_to_string(&self, SvgXmlOptions::default()).fmt(f)
2229    }
2230}
2231
2232#[cfg(feature = "svg")]
2233impl ParsedSvg {
2234    /// Parses an SVG from a string
2235    pub fn from_string(
2236        svg_string: &str,
2237        parse_options: SvgParseOptions,
2238    ) -> Result<Self, SvgParseError> {
2239        svg_parse(svg_string.as_bytes(), parse_options)
2240    }
2241
2242    /// Parses an SVG from bytes
2243    pub fn from_bytes(
2244        svg_bytes: &[u8],
2245        parse_options: SvgParseOptions,
2246    ) -> Result<Self, SvgParseError> {
2247        svg_parse(svg_bytes, parse_options)
2248    }
2249
2250    /// Returns the root XML node of the SVG
2251    pub fn get_root(&self) -> ParsedSvgXmlNode {
2252        svg_root(self)
2253    }
2254
2255    /// Renders the SVG to a raw image
2256    pub fn render(&self, options: SvgRenderOptions) -> Option<RawImage> {
2257        svg_render(self, options)
2258    }
2259
2260    /// Converts the SVG back to a string
2261    pub fn to_string(&self, options: SvgXmlOptions) -> String {
2262        svg_to_string(self, options)
2263    }
2264}
2265
2266#[cfg(feature = "svg")]
2267fn svg_new(tree: usvg::Tree) -> ParsedSvg {
2268    ParsedSvg {
2269        tree: Box::new(tree),
2270        run_destructor: true,
2271    }
2272}
2273
2274/// NOTE: SVG file data may be Zlib compressed
2275#[cfg(feature = "svg")]
2276pub fn svg_parse(
2277    svg_file_data: &[u8],
2278    options: SvgParseOptions,
2279) -> Result<ParsedSvg, SvgParseError> {
2280    let rtree = usvg::Tree::from_data(svg_file_data, &translate_to_usvg_parseoptions(options))
2281        .map_err(translate_usvg_svgparserror)?;
2282
2283    Ok(svg_new(rtree))
2284}
2285
2286#[cfg(not(feature = "svg"))]
2287pub fn svg_parse(
2288    svg_file_data: &[u8],
2289    options: SvgParseOptions,
2290) -> Result<ParsedSvg, SvgParseError> {
2291    Err(SvgParseError::NoParserAvailable)
2292}
2293
2294#[cfg(feature = "svg")]
2295pub fn svg_root(s: &ParsedSvg) -> ParsedSvgXmlNode {
2296    svgxmlnode_new(s.tree.root().clone())
2297}
2298
2299#[cfg(not(feature = "svg"))]
2300pub fn svg_root(s: &ParsedSvg) -> ParsedSvgXmlNode {
2301    ParsedSvgXmlNode {
2302        node: core::ptr::null_mut(),
2303        run_destructor: false,
2304    }
2305}
2306
2307#[cfg(feature = "svg")]
2308pub fn svg_render(s: &ParsedSvg, options: SvgRenderOptions) -> Option<RawImage> {
2309    use azul_core::resources::RawImageData;
2310    use tiny_skia::Pixmap;
2311
2312    let root = s.tree.root();
2313    let (target_width, target_height) = svgrenderoptions_get_width_height_node(&options, &root)?;
2314
2315    if target_height == 0 || target_width == 0 {
2316        return None;
2317    }
2318
2319    let mut pixmap = Pixmap::new(target_width, target_height)?;
2320
2321    pixmap.fill(
2322        options
2323            .background_color
2324            .into_option()
2325            .map(translate_color)
2326            .unwrap_or(tiny_skia::Color::TRANSPARENT),
2327    );
2328
2329    let _ = resvg::render(
2330        &s.tree,
2331        translate_transform(options.transform),
2332        &mut pixmap.as_mut(),
2333    );
2334
2335    Some(RawImage {
2336        tag: Vec::new().into(),
2337        pixels: RawImageData::U8(pixmap.take().into()),
2338        width: target_width as usize,
2339        height: target_height as usize,
2340        premultiplied_alpha: true,
2341        data_format: RawImageFormat::RGBA8,
2342    })
2343}
2344
2345#[cfg(not(feature = "svg"))]
2346pub fn svg_render(s: &ParsedSvg, options: SvgRenderOptions) -> Option<RawImage> {
2347    None
2348}
2349
2350/*
2351#[cfg(feature = "svg")]
2352pub fn from_xml(xml: Xml) -> Result<Self, SvgParseError> {
2353    // https://github.com/RazrFalcon/resvg/issues/308
2354    Ok(Svg::new(xml.into_tree()))
2355}
2356*/
2357
2358#[cfg(feature = "svg")]
2359pub fn svg_to_string(s: &ParsedSvg, options: SvgXmlOptions) -> String {
2360    s.tree.to_string(&translate_to_usvg_xmloptions(options))
2361}
2362
2363#[cfg(not(feature = "svg"))]
2364pub fn svg_to_string(s: &ParsedSvg, options: SvgXmlOptions) -> String {
2365    String::new()
2366}
2367
2368#[cfg(feature = "svg")]
2369fn svgrenderoptions_get_width_height_node(
2370    s: &SvgRenderOptions,
2371    node: &usvg::Group,
2372) -> Option<(u32, u32)> {
2373    match s.target_size.as_ref() {
2374        None => {
2375            let bbox = node.bounding_box();
2376            let size = usvg::Size::from_wh(bbox.width(), bbox.height())?;
2377            Some((
2378                size.width().round().max(0.0) as u32,
2379                size.height().round().max(0.0) as u32,
2380            ))
2381        }
2382        Some(s) => Some((s.width as u32, s.height as u32)),
2383    }
2384}
2385
2386#[cfg(feature = "svg")]
2387fn translate_transform(e: SvgRenderTransform) -> tiny_skia::Transform {
2388    tiny_skia::Transform {
2389        sx: e.sx,
2390        kx: e.kx,
2391        ky: e.ky,
2392        sy: e.sy,
2393        tx: e.tx,
2394        ty: e.ty,
2395    }
2396}
2397
2398#[cfg(feature = "svg")]
2399fn translate_to_usvg_shaperendering(e: ShapeRendering) -> usvg::ShapeRendering {
2400    match e {
2401        ShapeRendering::OptimizeSpeed => usvg::ShapeRendering::OptimizeSpeed,
2402        ShapeRendering::CrispEdges => usvg::ShapeRendering::CrispEdges,
2403        ShapeRendering::GeometricPrecision => usvg::ShapeRendering::GeometricPrecision,
2404    }
2405}
2406
2407#[cfg(feature = "svg")]
2408fn translate_to_usvg_imagerendering(e: ImageRendering) -> usvg::ImageRendering {
2409    match e {
2410        ImageRendering::OptimizeQuality => usvg::ImageRendering::OptimizeQuality,
2411        ImageRendering::OptimizeSpeed => usvg::ImageRendering::OptimizeSpeed,
2412    }
2413}
2414
2415#[cfg(feature = "svg")]
2416fn translate_to_usvg_textrendering(e: TextRendering) -> usvg::TextRendering {
2417    match e {
2418        TextRendering::OptimizeSpeed => usvg::TextRendering::OptimizeSpeed,
2419        TextRendering::OptimizeLegibility => usvg::TextRendering::OptimizeLegibility,
2420        TextRendering::GeometricPrecision => usvg::TextRendering::GeometricPrecision,
2421    }
2422}
2423
2424#[cfg(feature = "svg")]
2425#[allow(dead_code)]
2426fn translate_color(i: ColorU) -> tiny_skia::Color {
2427    tiny_skia::Color::from_rgba8(i.r, i.g, i.b, i.a)
2428}
2429
2430#[cfg(feature = "svg")]
2431fn translate_to_usvg_parseoptions<'a>(e: SvgParseOptions) -> usvg::Options<'a> {
2432    use usvg::ImageHrefResolver;
2433
2434    let mut options = usvg::Options {
2435        // path: e.relative_image_path.into_option().map(|e| { let p: String = e.clone().into();
2436        // PathBuf::from(p) }),
2437        dpi: e.dpi,
2438        font_family: e.default_font_family.clone().into_library_owned_string(),
2439        font_size: e.font_size.into(),
2440        languages: e
2441            .languages
2442            .as_ref()
2443            .iter()
2444            .map(|e| e.clone().into_library_owned_string())
2445            .collect(),
2446        shape_rendering: translate_to_usvg_shaperendering(e.shape_rendering),
2447        text_rendering: translate_to_usvg_textrendering(e.text_rendering),
2448        image_rendering: translate_to_usvg_imagerendering(e.image_rendering),
2449        resources_dir: None,                                      // TODO
2450        default_size: usvg::Size::from_wh(100.0, 100.0).unwrap(), // TODO
2451        style_sheet: None,                                        // TODO
2452        image_href_resolver: ImageHrefResolver::default(),        // TODO
2453        ..Default::default()
2454    };
2455
2456    /*
2457    // only available with
2458    use usvg::SystemFontDB;
2459    use std::path::PathBuf;
2460
2461    match e.fontdb {
2462        FontDatabase::Empty => { },
2463        FontDatabase::System => { options.fontdb.load_system_fonts(); },
2464    }
2465    */
2466
2467    options
2468}
2469
2470#[cfg(feature = "svg")]
2471fn translate_to_usvg_xmloptions(f: SvgXmlOptions) -> usvg::WriteOptions {
2472    usvg::WriteOptions {
2473        id_prefix: None,
2474        preserve_text: false,
2475        coordinates_precision: 8,
2476        transforms_precision: 8,
2477        use_single_quote: f.use_single_quote,
2478        indent: translate_xmlwriter_indent(f.indent),
2479        attributes_indent: translate_xmlwriter_indent(f.attributes_indent),
2480    }
2481}
2482
2483#[cfg(feature = "svg")]
2484fn translate_usvg_svgparserror(e: usvg::Error) -> SvgParseError {
2485    match e {
2486        usvg::Error::ElementsLimitReached => SvgParseError::ElementsLimitReached,
2487        usvg::Error::NotAnUtf8Str => SvgParseError::NotAnUtf8Str,
2488        usvg::Error::MalformedGZip => SvgParseError::MalformedGZip,
2489        usvg::Error::InvalidSize => SvgParseError::InvalidSize,
2490        usvg::Error::ParsingFailed(e) => {
2491            // Note: usvg uses roxmltree 0.20, but we use 0.21, so we can't directly convert
2492            // Convert the error to a string representation instead
2493            use azul_core::xml::{XmlError, XmlTextPos};
2494            let error_string = format!("{:?}", e);
2495            SvgParseError::ParsingFailed(XmlError::UnknownToken(XmlTextPos { row: 0, col: 0 }))
2496        }
2497    }
2498}
2499
2500#[cfg(feature = "svg")]
2501fn translate_xmlwriter_indent(f: Indent) -> xmlwriter::Indent {
2502    match f {
2503        Indent::None => xmlwriter::Indent::None,
2504        Indent::Spaces(s) => xmlwriter::Indent::Spaces(s),
2505        Indent::Tabs => xmlwriter::Indent::Tabs,
2506    }
2507}
2508
2509/// Trait for tessellating SvgMultiPolygon shapes
2510pub trait SvgMultiPolygonTessellation {
2511    /// Tessellates the polygon with fill style, returns CPU-side vertex buffers
2512    fn tessellate_fill(&self, fill_style: SvgFillStyle) -> TessellatedSvgNode;
2513    /// Tessellates the polygon with stroke style, returns CPU-side vertex buffers
2514    fn tessellate_stroke(&self, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode;
2515}
2516
2517impl SvgMultiPolygonTessellation for SvgMultiPolygon {
2518    fn tessellate_fill(&self, fill_style: SvgFillStyle) -> TessellatedSvgNode {
2519        tessellate_multi_polygon_fill(self, fill_style)
2520    }
2521    fn tessellate_stroke(&self, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
2522        tessellate_multi_polygon_stroke(self, stroke_style)
2523    }
2524}