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