azul_core/
svg.rs

1//! SVG rendering module
2
3use alloc::{
4    string::{String, ToString},
5    vec::Vec,
6};
7use core::fmt;
8
9use azul_css::{
10    AzString, ColorF, ColorU, OptionAzString, OptionColorU, OptionLayoutSize, StringVec,
11    StyleTransformOrigin, StyleTransformVec, U32Vec,
12};
13pub use azul_css::{SvgCubicCurve, SvgPoint, SvgQuadraticCurve, SvgRect, SvgVector};
14
15use crate::{
16    gl::{
17        GlContextPtr, IndexBufferFormat, Texture, VertexAttribute, VertexAttributeType,
18        VertexBuffer, VertexLayout, VertexLayoutDescription,
19    },
20    ui_solver::{ComputedTransform3D, RotationMode},
21    window::PhysicalSizeU32,
22    xml::XmlError,
23};
24
25const DEFAULT_MITER_LIMIT: f32 = 4.0;
26const DEFAULT_LINE_WIDTH: f32 = 1.0;
27const DEFAULT_TOLERANCE: f32 = 0.1;
28
29#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
30#[repr(C)]
31pub struct SvgSize {
32    pub width: f32,
33    pub height: f32,
34}
35
36#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
37#[repr(C)]
38pub struct SvgLine {
39    pub start: SvgPoint,
40    pub end: SvgPoint,
41}
42
43impl SvgLine {
44    pub fn inwards_normal(&self) -> Option<SvgPoint> {
45        let dx = self.end.x - self.start.x;
46        let dy = self.end.y - self.start.y;
47        let edge_length = (dx * dx + dy * dy).sqrt();
48        let x = -dy / edge_length;
49        let y = dx / edge_length;
50
51        if x.is_finite() && y.is_finite() {
52            Some(SvgPoint { x, y })
53        } else {
54            None
55        }
56    }
57
58    pub fn outwards_normal(&self) -> Option<SvgPoint> {
59        let inwards = self.inwards_normal()?;
60        Some(SvgPoint {
61            x: -inwards.x,
62            y: -inwards.y,
63        })
64    }
65
66    pub fn reverse(&mut self) {
67        let temp = self.start;
68        self.start = self.end;
69        self.end = temp;
70    }
71    pub fn get_start(&self) -> SvgPoint {
72        self.start
73    }
74    pub fn get_end(&self) -> SvgPoint {
75        self.end
76    }
77
78    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
79        offset / self.get_length()
80    }
81
82    pub fn get_tangent_vector_at_t(&self, _: f64) -> SvgVector {
83        let dx = self.end.x - self.start.x;
84        let dy = self.end.y - self.start.y;
85        SvgVector {
86            x: dx as f64,
87            y: dy as f64,
88        }
89        .normalize()
90    }
91
92    pub fn get_x_at_t(&self, t: f64) -> f64 {
93        self.start.x as f64 + (self.end.x as f64 - self.start.x as f64) * t
94    }
95
96    pub fn get_y_at_t(&self, t: f64) -> f64 {
97        self.start.y as f64 + (self.end.y as f64 - self.start.y as f64) * t
98    }
99
100    pub fn get_length(&self) -> f64 {
101        let dx = self.end.x - self.start.x;
102        let dy = self.end.y - self.start.y;
103        libm::hypotf(dx, dy) as f64
104    }
105
106    pub fn get_bounds(&self) -> SvgRect {
107        let min_x = self.start.x.min(self.end.x);
108        let max_x = self.start.x.max(self.end.x);
109
110        let min_y = self.start.y.min(self.end.y);
111        let max_y = self.start.y.max(self.end.y);
112
113        let width = (max_x - min_x).abs();
114        let height = (max_y - min_y).abs();
115
116        SvgRect {
117            width,
118            height,
119            x: min_x,
120            y: min_y,
121            ..SvgRect::default()
122        }
123    }
124}
125
126#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
127#[repr(C, u8)]
128pub enum SvgPathElement {
129    Line(SvgLine),
130    QuadraticCurve(SvgQuadraticCurve),
131    CubicCurve(SvgCubicCurve),
132}
133
134impl SvgPathElement {
135    pub fn set_last(&mut self, point: SvgPoint) {
136        match self {
137            SvgPathElement::Line(l) => l.end = point,
138            SvgPathElement::QuadraticCurve(qc) => qc.end = point,
139            SvgPathElement::CubicCurve(cc) => cc.end = point,
140        }
141    }
142
143    pub fn set_first(&mut self, point: SvgPoint) {
144        match self {
145            SvgPathElement::Line(l) => l.start = point,
146            SvgPathElement::QuadraticCurve(qc) => qc.start = point,
147            SvgPathElement::CubicCurve(cc) => cc.start = point,
148        }
149    }
150
151    pub fn reverse(&mut self) {
152        match self {
153            SvgPathElement::Line(l) => l.reverse(),
154            SvgPathElement::QuadraticCurve(qc) => qc.reverse(),
155            SvgPathElement::CubicCurve(cc) => cc.reverse(),
156        }
157    }
158    pub fn get_start(&self) -> SvgPoint {
159        match self {
160            SvgPathElement::Line(l) => l.get_start(),
161            SvgPathElement::QuadraticCurve(qc) => qc.get_start(),
162            SvgPathElement::CubicCurve(cc) => cc.get_start(),
163        }
164    }
165    pub fn get_end(&self) -> SvgPoint {
166        match self {
167            SvgPathElement::Line(l) => l.get_end(),
168            SvgPathElement::QuadraticCurve(qc) => qc.get_end(),
169            SvgPathElement::CubicCurve(cc) => cc.get_end(),
170        }
171    }
172    pub fn get_bounds(&self) -> SvgRect {
173        match self {
174            SvgPathElement::Line(l) => l.get_bounds(),
175            SvgPathElement::QuadraticCurve(qc) => qc.get_bounds(),
176            SvgPathElement::CubicCurve(cc) => cc.get_bounds(),
177        }
178    }
179    pub fn get_length(&self) -> f64 {
180        match self {
181            SvgPathElement::Line(l) => l.get_length(),
182            SvgPathElement::QuadraticCurve(qc) => qc.get_length(),
183            SvgPathElement::CubicCurve(cc) => cc.get_length(),
184        }
185    }
186    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
187        match self {
188            SvgPathElement::Line(l) => l.get_t_at_offset(offset),
189            SvgPathElement::QuadraticCurve(qc) => qc.get_t_at_offset(offset),
190            SvgPathElement::CubicCurve(cc) => cc.get_t_at_offset(offset),
191        }
192    }
193    pub fn get_tangent_vector_at_t(&self, t: f64) -> SvgVector {
194        match self {
195            SvgPathElement::Line(l) => l.get_tangent_vector_at_t(t),
196            SvgPathElement::QuadraticCurve(qc) => qc.get_tangent_vector_at_t(t),
197            SvgPathElement::CubicCurve(cc) => cc.get_tangent_vector_at_t(t),
198        }
199    }
200    pub fn get_x_at_t(&self, t: f64) -> f64 {
201        match self {
202            SvgPathElement::Line(l) => l.get_x_at_t(t),
203            SvgPathElement::QuadraticCurve(qc) => qc.get_x_at_t(t),
204            SvgPathElement::CubicCurve(cc) => cc.get_x_at_t(t),
205        }
206    }
207    pub fn get_y_at_t(&self, t: f64) -> f64 {
208        match self {
209            SvgPathElement::Line(l) => l.get_y_at_t(t),
210            SvgPathElement::QuadraticCurve(qc) => qc.get_y_at_t(t),
211            SvgPathElement::CubicCurve(cc) => cc.get_y_at_t(t),
212        }
213    }
214}
215
216impl_vec!(
217    SvgPathElement,
218    SvgPathElementVec,
219    SvgPathElementVecDestructor
220);
221impl_vec_debug!(SvgPathElement, SvgPathElementVec);
222impl_vec_clone!(
223    SvgPathElement,
224    SvgPathElementVec,
225    SvgPathElementVecDestructor
226);
227impl_vec_partialeq!(SvgPathElement, SvgPathElementVec);
228impl_vec_partialord!(SvgPathElement, SvgPathElementVec);
229
230#[derive(Debug, Clone, PartialEq, PartialOrd)]
231#[repr(C)]
232pub struct SvgPath {
233    pub items: SvgPathElementVec,
234}
235
236impl SvgPath {
237    pub fn get_start(&self) -> Option<SvgPoint> {
238        self.items.as_ref().first().map(|s| s.get_start())
239    }
240
241    pub fn get_end(&self) -> Option<SvgPoint> {
242        self.items.as_ref().last().map(|s| s.get_end())
243    }
244
245    pub fn close(&mut self) {
246        let first = match self.items.as_ref().first() {
247            Some(s) => s,
248            None => return,
249        };
250        let last = match self.items.as_ref().last() {
251            Some(s) => s,
252            None => return,
253        };
254        if first.get_start() != last.get_end() {
255            let mut elements = self.items.as_slice().to_vec();
256            elements.push(SvgPathElement::Line(SvgLine {
257                start: last.get_end(),
258                end: first.get_start(),
259            }));
260            self.items = elements.into();
261        }
262    }
263
264    pub fn is_closed(&self) -> bool {
265        let first = self.items.as_ref().first();
266        let last = self.items.as_ref().last();
267        match (first, last) {
268            (Some(f), Some(l)) => (f.get_start() == l.get_end()),
269            _ => false,
270        }
271    }
272
273    // reverses the order of elements in the path
274    pub fn reverse(&mut self) {
275        // swap self.items with a default vec
276        let mut vec = SvgPathElementVec::from_const_slice(&[]);
277        core::mem::swap(&mut vec, &mut self.items);
278        let mut vec = vec.into_library_owned_vec();
279
280        // reverse the order of items in the vec
281        vec.reverse();
282
283        // reverse the order inside the item itself
284        // i.e. swap line.start and line.end
285        for item in vec.iter_mut() {
286            item.reverse();
287        }
288
289        // swap back
290        let mut vec = SvgPathElementVec::from_vec(vec);
291        core::mem::swap(&mut vec, &mut self.items);
292    }
293
294    pub fn join_with(&mut self, mut path: Self) -> Option<()> {
295        let self_last_point = self.items.as_ref().last()?.get_end();
296        let other_start_point = path.items.as_ref().first()?.get_start();
297        let interpolated_join_point = SvgPoint {
298            x: (self_last_point.x + other_start_point.x) / 2.0,
299            y: (self_last_point.y + other_start_point.y) / 2.0,
300        };
301
302        // swap self.items with a default vec
303        let mut vec = SvgPathElementVec::from_const_slice(&[]);
304        core::mem::swap(&mut vec, &mut self.items);
305        let mut vec = vec.into_library_owned_vec();
306
307        let mut other = SvgPathElementVec::from_const_slice(&[]);
308        core::mem::swap(&mut other, &mut path.items);
309        let mut other = other.into_library_owned_vec();
310
311        let vec_len = vec.len() - 1;
312        vec.get_mut(vec_len)?.set_last(interpolated_join_point);
313        other.get_mut(0)?.set_first(interpolated_join_point);
314        vec.append(&mut other);
315
316        // swap back
317        let mut vec = SvgPathElementVec::from_vec(vec);
318        core::mem::swap(&mut vec, &mut self.items);
319
320        Some(())
321    }
322    pub fn get_bounds(&self) -> SvgRect {
323        let mut first_bounds = match self.items.as_ref().get(0) {
324            Some(s) => s.get_bounds(),
325            None => return SvgRect::default(),
326        };
327
328        for mp in self.items.as_ref().iter().skip(1) {
329            let mp_bounds = mp.get_bounds();
330            first_bounds.union_with(&mp_bounds);
331        }
332
333        first_bounds
334    }
335}
336
337#[derive(Debug, Clone, PartialEq, PartialOrd)]
338#[repr(C)]
339pub struct SvgMultiPolygon {
340    /// NOTE: If a ring represends a hole, simply reverse the order of points
341    pub rings: SvgPathVec,
342}
343
344impl SvgMultiPolygon {
345    pub fn get_bounds(&self) -> SvgRect {
346        let mut first_bounds = match self
347            .rings
348            .get(0)
349            .and_then(|b| b.items.get(0).map(|i| i.get_bounds()))
350        {
351            Some(s) => s,
352            None => return SvgRect::default(), // TODO: error?
353        };
354
355        for ring in self.rings.iter() {
356            for item in ring.items.iter() {
357                first_bounds.union_with(&item.get_bounds());
358            }
359        }
360
361        first_bounds
362    }
363}
364
365impl_vec!(SvgPath, SvgPathVec, SvgPathVecDestructor);
366impl_vec_debug!(SvgPath, SvgPathVec);
367impl_vec_clone!(SvgPath, SvgPathVec, SvgPathVecDestructor);
368impl_vec_partialeq!(SvgPath, SvgPathVec);
369impl_vec_partialord!(SvgPath, SvgPathVec);
370
371impl_vec!(
372    SvgMultiPolygon,
373    SvgMultiPolygonVec,
374    SvgMultiPolygonVecDestructor
375);
376impl_vec_debug!(SvgMultiPolygon, SvgMultiPolygonVec);
377impl_vec_clone!(
378    SvgMultiPolygon,
379    SvgMultiPolygonVec,
380    SvgMultiPolygonVecDestructor
381);
382impl_vec_partialeq!(SvgMultiPolygon, SvgMultiPolygonVec);
383impl_vec_partialord!(SvgMultiPolygon, SvgMultiPolygonVec);
384
385/// One `SvgNode` corresponds to one SVG `<path></path>` element
386#[derive(Debug, Clone, PartialOrd, PartialEq)]
387#[repr(C, u8)]
388pub enum SvgNode {
389    /// Multiple multipolygons, merged to one CPU buf for efficient drawing
390    MultiPolygonCollection(SvgMultiPolygonVec),
391    MultiPolygon(SvgMultiPolygon),
392    MultiShape(SvgSimpleNodeVec),
393    Path(SvgPath),
394    Circle(SvgCircle),
395    Rect(SvgRect),
396}
397
398/// One `SvgSimpleNode` is either a path, a rect or a circle
399#[derive(Debug, Clone, PartialOrd, PartialEq)]
400#[repr(C, u8)]
401pub enum SvgSimpleNode {
402    Path(SvgPath),
403    Circle(SvgCircle),
404    Rect(SvgRect),
405    CircleHole(SvgCircle),
406    RectHole(SvgRect),
407}
408
409impl_vec!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor);
410impl_vec_debug!(SvgSimpleNode, SvgSimpleNodeVec);
411impl_vec_clone!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor);
412impl_vec_partialeq!(SvgSimpleNode, SvgSimpleNodeVec);
413impl_vec_partialord!(SvgSimpleNode, SvgSimpleNodeVec);
414
415impl SvgSimpleNode {
416    pub fn get_bounds(&self) -> SvgRect {
417        match self {
418            SvgSimpleNode::Path(a) => a.get_bounds(),
419            SvgSimpleNode::Circle(a) => a.get_bounds(),
420            SvgSimpleNode::Rect(a) => a.clone(),
421            SvgSimpleNode::CircleHole(a) => a.get_bounds(),
422            SvgSimpleNode::RectHole(a) => a.clone(),
423        }
424    }
425    pub fn is_closed(&self) -> bool {
426        match self {
427            SvgSimpleNode::Path(a) => a.is_closed(),
428            SvgSimpleNode::Circle(a) => true,
429            SvgSimpleNode::Rect(a) => true,
430            SvgSimpleNode::CircleHole(a) => true,
431            SvgSimpleNode::RectHole(a) => true,
432        }
433    }
434}
435
436impl SvgNode {
437    pub fn get_bounds(&self) -> SvgRect {
438        match self {
439            SvgNode::MultiPolygonCollection(a) => {
440                let mut first_mp_bounds = match a.get(0) {
441                    Some(s) => s.get_bounds(),
442                    None => return SvgRect::default(),
443                };
444                for mp in a.iter().skip(1) {
445                    let mp_bounds = mp.get_bounds();
446                    first_mp_bounds.union_with(&mp_bounds);
447                }
448
449                first_mp_bounds
450            }
451            SvgNode::MultiPolygon(a) => a.get_bounds(),
452            SvgNode::MultiShape(a) => {
453                let mut first_mp_bounds = match a.get(0) {
454                    Some(s) => s.get_bounds(),
455                    None => return SvgRect::default(),
456                };
457                for mp in a.iter().skip(1) {
458                    let mp_bounds = mp.get_bounds();
459                    first_mp_bounds.union_with(&mp_bounds);
460                }
461
462                first_mp_bounds
463            }
464            SvgNode::Path(a) => a.get_bounds(),
465            SvgNode::Circle(a) => a.get_bounds(),
466            SvgNode::Rect(a) => a.clone(),
467        }
468    }
469    pub fn is_closed(&self) -> bool {
470        match self {
471            SvgNode::MultiPolygonCollection(a) => {
472                for mp in a.iter() {
473                    for p in mp.rings.as_ref().iter() {
474                        if !p.is_closed() {
475                            return false;
476                        }
477                    }
478                }
479
480                true
481            }
482            SvgNode::MultiPolygon(a) => {
483                for p in a.rings.as_ref().iter() {
484                    if !p.is_closed() {
485                        return false;
486                    }
487                }
488
489                true
490            }
491            SvgNode::MultiShape(a) => {
492                for p in a.as_ref().iter() {
493                    if !p.is_closed() {
494                        return false;
495                    }
496                }
497
498                true
499            }
500            SvgNode::Path(a) => a.is_closed(),
501            SvgNode::Circle(a) => true,
502            SvgNode::Rect(a) => true,
503        }
504    }
505}
506
507#[derive(Debug, Clone, PartialOrd, PartialEq)]
508#[repr(C)]
509pub struct SvgStyledNode {
510    pub geometry: SvgNode,
511    pub style: SvgStyle,
512}
513
514#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
515#[repr(C)]
516pub struct SvgVertex {
517    pub x: f32,
518    pub y: f32,
519}
520
521impl VertexLayoutDescription for SvgVertex {
522    fn get_description() -> VertexLayout {
523        VertexLayout {
524            fields: vec![VertexAttribute {
525                name: String::from("vAttrXY").into(),
526                layout_location: None.into(),
527                attribute_type: VertexAttributeType::Float,
528                item_count: 2,
529            }]
530            .into(),
531        }
532    }
533}
534
535#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
536#[repr(C)]
537pub struct SvgColoredVertex {
538    pub x: f32,
539    pub y: f32,
540    pub z: f32,
541    pub r: f32,
542    pub g: f32,
543    pub b: f32,
544    pub a: f32,
545}
546
547impl VertexLayoutDescription for SvgColoredVertex {
548    fn get_description() -> VertexLayout {
549        VertexLayout {
550            fields: vec![
551                VertexAttribute {
552                    name: String::from("vAttrXY").into(),
553                    layout_location: None.into(),
554                    attribute_type: VertexAttributeType::Float,
555                    item_count: 3,
556                },
557                VertexAttribute {
558                    name: String::from("vColor").into(),
559                    layout_location: None.into(),
560                    attribute_type: VertexAttributeType::Float,
561                    item_count: 4,
562                },
563            ]
564            .into(),
565        }
566    }
567}
568
569#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
570#[repr(C)]
571pub struct SvgCircle {
572    pub center_x: f32,
573    pub center_y: f32,
574    pub radius: f32,
575}
576
577impl SvgCircle {
578    pub fn contains_point(&self, x: f32, y: f32) -> bool {
579        let x_diff = libm::fabsf(x - self.center_x);
580        let y_diff = libm::fabsf(y - self.center_y);
581        (x_diff * x_diff) + (y_diff * y_diff) < (self.radius * self.radius)
582    }
583    pub fn get_bounds(&self) -> SvgRect {
584        SvgRect {
585            width: self.radius * 2.0,
586            height: self.radius * 2.0,
587            x: self.center_x - self.radius,
588            y: self.center_y - self.radius,
589            radius_top_left: 0.0,
590            radius_top_right: 0.0,
591            radius_bottom_left: 0.0,
592            radius_bottom_right: 0.0,
593        }
594    }
595}
596
597#[derive(Debug, Clone, PartialEq, PartialOrd)]
598#[repr(C)]
599pub struct TessellatedSvgNode {
600    pub vertices: SvgVertexVec,
601    pub indices: U32Vec,
602}
603
604impl Default for TessellatedSvgNode {
605    fn default() -> Self {
606        Self {
607            vertices: Vec::new().into(),
608            indices: Vec::new().into(),
609        }
610    }
611}
612
613impl_vec!(
614    TessellatedSvgNode,
615    TessellatedSvgNodeVec,
616    TessellatedSvgNodeVecDestructor
617);
618impl_vec_debug!(TessellatedSvgNode, TessellatedSvgNodeVec);
619impl_vec_partialord!(TessellatedSvgNode, TessellatedSvgNodeVec);
620impl_vec_clone!(
621    TessellatedSvgNode,
622    TessellatedSvgNodeVec,
623    TessellatedSvgNodeVecDestructor
624);
625impl_vec_partialeq!(TessellatedSvgNode, TessellatedSvgNodeVec);
626
627impl TessellatedSvgNode {
628    pub fn empty() -> Self {
629        Self::default()
630    }
631}
632
633impl TessellatedSvgNodeVec {
634    pub fn get_ref(&self) -> TessellatedSvgNodeVecRef {
635        let slice = self.as_ref();
636        TessellatedSvgNodeVecRef {
637            ptr: slice.as_ptr(),
638            len: slice.len(),
639        }
640    }
641}
642
643impl fmt::Debug for TessellatedSvgNodeVecRef {
644    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
645        self.as_slice().fmt(f)
646    }
647}
648
649// C ABI wrapper over &[TessellatedSvgNode]
650#[repr(C)]
651pub struct TessellatedSvgNodeVecRef {
652    pub ptr: *const TessellatedSvgNode,
653    pub len: usize,
654}
655
656impl Clone for TessellatedSvgNodeVecRef {
657    fn clone(&self) -> Self {
658        Self {
659            ptr: self.ptr,
660            len: self.len,
661        }
662    }
663}
664
665impl TessellatedSvgNodeVecRef {
666    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedSvgNode] {
667        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
668    }
669}
670
671#[derive(Debug, Clone, PartialEq, PartialOrd)]
672#[repr(C)]
673pub struct TessellatedColoredSvgNode {
674    pub vertices: SvgColoredVertexVec,
675    pub indices: U32Vec,
676}
677
678impl Default for TessellatedColoredSvgNode {
679    fn default() -> Self {
680        Self {
681            vertices: Vec::new().into(),
682            indices: Vec::new().into(),
683        }
684    }
685}
686
687impl_vec!(
688    TessellatedColoredSvgNode,
689    TessellatedColoredSvgNodeVec,
690    TessellatedColoredSvgNodeVecDestructor
691);
692impl_vec_debug!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
693impl_vec_partialord!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
694impl_vec_clone!(
695    TessellatedColoredSvgNode,
696    TessellatedColoredSvgNodeVec,
697    TessellatedColoredSvgNodeVecDestructor
698);
699impl_vec_partialeq!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
700
701impl TessellatedColoredSvgNode {
702    pub fn empty() -> Self {
703        Self::default()
704    }
705}
706
707impl TessellatedColoredSvgNodeVec {
708    pub fn get_ref(&self) -> TessellatedColoredSvgNodeVecRef {
709        let slice = self.as_ref();
710        TessellatedColoredSvgNodeVecRef {
711            ptr: slice.as_ptr(),
712            len: slice.len(),
713        }
714    }
715}
716
717impl fmt::Debug for TessellatedColoredSvgNodeVecRef {
718    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
719        self.as_slice().fmt(f)
720    }
721}
722
723// C ABI wrapper over &[TessellatedColoredSvgNode]
724#[repr(C)]
725pub struct TessellatedColoredSvgNodeVecRef {
726    pub ptr: *const TessellatedColoredSvgNode,
727    pub len: usize,
728}
729
730impl Clone for TessellatedColoredSvgNodeVecRef {
731    fn clone(&self) -> Self {
732        Self {
733            ptr: self.ptr,
734            len: self.len,
735        }
736    }
737}
738
739impl TessellatedColoredSvgNodeVecRef {
740    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedColoredSvgNode] {
741        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
742    }
743}
744
745impl_vec!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor);
746impl_vec_debug!(SvgVertex, SvgVertexVec);
747impl_vec_partialord!(SvgVertex, SvgVertexVec);
748impl_vec_clone!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor);
749impl_vec_partialeq!(SvgVertex, SvgVertexVec);
750
751impl_vec!(
752    SvgColoredVertex,
753    SvgColoredVertexVec,
754    SvgColoredVertexVecDestructor
755);
756impl_vec_debug!(SvgColoredVertex, SvgColoredVertexVec);
757impl_vec_partialord!(SvgColoredVertex, SvgColoredVertexVec);
758impl_vec_clone!(
759    SvgColoredVertex,
760    SvgColoredVertexVec,
761    SvgColoredVertexVecDestructor
762);
763impl_vec_partialeq!(SvgColoredVertex, SvgColoredVertexVec);
764
765#[derive(Debug, PartialEq, PartialOrd)]
766#[repr(C)]
767pub struct TessellatedGPUSvgNode {
768    pub vertex_index_buffer: VertexBuffer,
769}
770
771impl TessellatedGPUSvgNode {
772    /// Uploads the tesselated SVG node to GPU memory
773    pub fn new(node: &TessellatedSvgNode, gl: GlContextPtr) -> Self {
774        let svg_shader_id = gl.ptr.svg_shader;
775        Self {
776            vertex_index_buffer: VertexBuffer::new(
777                gl,
778                svg_shader_id,
779                node.vertices.as_ref(),
780                node.indices.as_ref(),
781                IndexBufferFormat::Triangles,
782            ),
783        }
784    }
785
786    /// Draw the vertex buffer to the texture with the given color and transform
787    pub fn draw(
788        &self,
789        texture: &mut Texture,
790        target_size: PhysicalSizeU32,
791        color: ColorU,
792        transforms: StyleTransformVec,
793    ) -> bool {
794        use azul_css::PixelValue;
795
796        use crate::gl::{GlShader, Uniform, UniformType};
797
798        let transform_origin = StyleTransformOrigin {
799            x: PixelValue::px(target_size.width as f32 / 2.0),
800            y: PixelValue::px(target_size.height as f32 / 2.0),
801        };
802
803        let computed_transform = ComputedTransform3D::from_style_transform_vec(
804            transforms.as_ref(),
805            &transform_origin,
806            target_size.width as f32,
807            target_size.height as f32,
808            RotationMode::ForWebRender,
809        );
810
811        // NOTE: OpenGL draws are column-major, while ComputedTransform3D
812        // is row-major! Need to transpose the matrix!
813        let column_major = computed_transform.get_column_major();
814
815        let color: ColorF = color.into();
816
817        // uniforms for the SVG shader
818        let uniforms = [
819            Uniform {
820                name: "vBboxSize".into(),
821                uniform_type: UniformType::FloatVec2([
822                    target_size.width as f32,
823                    target_size.height as f32,
824                ]),
825            },
826            Uniform {
827                name: "fDrawColor".into(),
828                uniform_type: UniformType::FloatVec4([color.r, color.g, color.b, color.a]),
829            },
830            Uniform {
831                name: "vTransformMatrix".into(),
832                uniform_type: UniformType::Matrix4 {
833                    transpose: false,
834                    matrix: unsafe { core::mem::transmute(column_major.m) },
835                },
836            },
837        ];
838
839        GlShader::draw(
840            texture.gl_context.ptr.svg_shader,
841            texture,
842            &[(&self.vertex_index_buffer, &uniforms[..])],
843        );
844
845        true
846    }
847}
848
849#[derive(Debug, PartialEq, PartialOrd)]
850#[repr(C)]
851pub struct TessellatedColoredGPUSvgNode {
852    pub vertex_index_buffer: VertexBuffer,
853}
854
855impl TessellatedColoredGPUSvgNode {
856    /// Uploads the tesselated SVG node to GPU memory
857    pub fn new(node: &TessellatedColoredSvgNode, gl: GlContextPtr) -> Self {
858        let svg_shader_id = gl.ptr.svg_multicolor_shader;
859        Self {
860            vertex_index_buffer: VertexBuffer::new(
861                gl,
862                svg_shader_id,
863                node.vertices.as_ref(),
864                node.indices.as_ref(),
865                IndexBufferFormat::Triangles,
866            ),
867        }
868    }
869
870    /// Draw the vertex buffer to the texture with the given color and transform
871    pub fn draw(
872        &self,
873        texture: &mut Texture,
874        target_size: PhysicalSizeU32,
875        transforms: StyleTransformVec,
876    ) -> bool {
877        use azul_css::PixelValue;
878
879        use crate::gl::{GlShader, Uniform, UniformType};
880
881        let transform_origin = StyleTransformOrigin {
882            x: PixelValue::px(target_size.width as f32 / 2.0),
883            y: PixelValue::px(target_size.height as f32 / 2.0),
884        };
885
886        let computed_transform = ComputedTransform3D::from_style_transform_vec(
887            transforms.as_ref(),
888            &transform_origin,
889            target_size.width as f32,
890            target_size.height as f32,
891            RotationMode::ForWebRender,
892        );
893
894        // NOTE: OpenGL draws are column-major, while ComputedTransform3D
895        // is row-major! Need to transpose the matrix!
896        let column_major = computed_transform.get_column_major();
897
898        // uniforms for the SVG shader
899        let uniforms = [
900            Uniform {
901                name: "vBboxSize".into(),
902                uniform_type: UniformType::FloatVec2([
903                    target_size.width as f32,
904                    target_size.height as f32,
905                ]),
906            },
907            Uniform {
908                name: "vTransformMatrix".into(),
909                uniform_type: UniformType::Matrix4 {
910                    transpose: false,
911                    matrix: unsafe { core::mem::transmute(column_major.m) },
912                },
913            },
914        ];
915
916        GlShader::draw(
917            texture.gl_context.ptr.svg_multicolor_shader,
918            texture,
919            &[(&self.vertex_index_buffer, &uniforms[..])],
920        );
921
922        true
923    }
924}
925
926#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
927#[repr(C, u8)]
928pub enum SvgStyle {
929    Fill(SvgFillStyle),
930    Stroke(SvgStrokeStyle),
931}
932
933impl SvgStyle {
934    pub fn get_antialias(&self) -> bool {
935        match self {
936            SvgStyle::Fill(f) => f.anti_alias,
937            SvgStyle::Stroke(s) => s.anti_alias,
938        }
939    }
940    pub fn get_high_quality_aa(&self) -> bool {
941        match self {
942            SvgStyle::Fill(f) => f.high_quality_aa,
943            SvgStyle::Stroke(s) => s.high_quality_aa,
944        }
945    }
946    pub fn get_transform(&self) -> SvgTransform {
947        match self {
948            SvgStyle::Fill(f) => f.transform,
949            SvgStyle::Stroke(s) => s.transform,
950        }
951    }
952}
953#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
954#[repr(C)]
955pub enum SvgFillRule {
956    Winding,
957    EvenOdd,
958}
959
960impl Default for SvgFillRule {
961    fn default() -> Self {
962        SvgFillRule::Winding
963    }
964}
965
966#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
967#[repr(C)]
968pub struct SvgTransform {
969    pub sx: f32,
970    pub kx: f32,
971    pub ky: f32,
972    pub sy: f32,
973    pub tx: f32,
974    pub ty: f32,
975}
976
977#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
978#[repr(C)]
979pub struct SvgFillStyle {
980    /// See the SVG specification.
981    ///
982    /// Default value: `LineJoin::Miter`.
983    pub line_join: SvgLineJoin,
984    /// See the SVG specification.
985    ///
986    /// Must be greater than or equal to 1.0.
987    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
988    pub miter_limit: f32,
989    /// Maximum allowed distance to the path when building an approximation.
990    ///
991    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
992    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
993    pub tolerance: f32,
994    /// Whether to use the "winding" or "even / odd" fill rule when tesselating the path
995    pub fill_rule: SvgFillRule,
996    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
997    /// expensive)
998    pub transform: SvgTransform,
999    /// Whether the fill is intended to be anti-aliased (default: true)
1000    pub anti_alias: bool,
1001    /// Whether the anti-aliasing has to be of high quality (default: false)
1002    pub high_quality_aa: bool,
1003}
1004
1005impl Default for SvgFillStyle {
1006    fn default() -> Self {
1007        Self {
1008            line_join: SvgLineJoin::Miter,
1009            miter_limit: DEFAULT_MITER_LIMIT,
1010            tolerance: DEFAULT_TOLERANCE,
1011            fill_rule: SvgFillRule::default(),
1012            transform: SvgTransform::default(),
1013            anti_alias: true,
1014            high_quality_aa: false,
1015        }
1016    }
1017}
1018
1019#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1020#[repr(C)]
1021pub struct SvgStrokeStyle {
1022    /// What cap to use at the start of each sub-path.
1023    ///
1024    /// Default value: `LineCap::Butt`.
1025    pub start_cap: SvgLineCap,
1026    /// What cap to use at the end of each sub-path.
1027    ///
1028    /// Default value: `LineCap::Butt`.
1029    pub end_cap: SvgLineCap,
1030    /// See the SVG specification.
1031    ///
1032    /// Default value: `LineJoin::Miter`.
1033    pub line_join: SvgLineJoin,
1034    /// Dash pattern
1035    pub dash_pattern: OptionSvgDashPattern,
1036    /// Line width
1037    ///
1038    /// Default value: `StrokeOptions::DEFAULT_LINE_WIDTH`.
1039    pub line_width: f32,
1040    /// See the SVG specification.
1041    ///
1042    /// Must be greater than or equal to 1.0.
1043    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
1044    pub miter_limit: f32,
1045    /// Maximum allowed distance to the path when building an approximation.
1046    ///
1047    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
1048    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
1049    pub tolerance: f32,
1050    /// Apply line width
1051    ///
1052    /// When set to false, the generated vertices will all be positioned in the centre
1053    /// of the line. The width can be applied later on (eg in a vertex shader) by adding
1054    /// the vertex normal multiplied by the line with to each vertex position.
1055    ///
1056    /// Default value: `true`. NOTE: currently unused!
1057    pub apply_line_width: bool,
1058    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
1059    /// expensive)
1060    pub transform: SvgTransform,
1061    /// Whether the fill is intended to be anti-aliased (default: true)
1062    pub anti_alias: bool,
1063    /// Whether the anti-aliasing has to be of high quality (default: false)
1064    pub high_quality_aa: bool,
1065}
1066
1067impl Default for SvgStrokeStyle {
1068    fn default() -> Self {
1069        Self {
1070            start_cap: SvgLineCap::default(),
1071            end_cap: SvgLineCap::default(),
1072            line_join: SvgLineJoin::default(),
1073            dash_pattern: OptionSvgDashPattern::None,
1074            line_width: DEFAULT_LINE_WIDTH,
1075            miter_limit: DEFAULT_MITER_LIMIT,
1076            tolerance: DEFAULT_TOLERANCE,
1077            apply_line_width: true,
1078            anti_alias: true,
1079            high_quality_aa: false,
1080            transform: SvgTransform::default(),
1081        }
1082    }
1083}
1084
1085#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1086#[repr(C)]
1087pub struct SvgDashPattern {
1088    pub offset: f32,
1089    pub length_1: f32,
1090    pub gap_1: f32,
1091    pub length_2: f32,
1092    pub gap_2: f32,
1093    pub length_3: f32,
1094    pub gap_3: f32,
1095}
1096
1097impl_option!(
1098    SvgDashPattern,
1099    OptionSvgDashPattern,
1100    [Debug, Copy, Clone, PartialEq, PartialOrd]
1101);
1102
1103#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
1104#[repr(C)]
1105pub enum SvgLineCap {
1106    Butt,
1107    Square,
1108    Round,
1109}
1110
1111impl Default for SvgLineCap {
1112    fn default() -> Self {
1113        SvgLineCap::Butt
1114    }
1115}
1116
1117#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
1118#[repr(C)]
1119pub enum SvgLineJoin {
1120    Miter,
1121    MiterClip,
1122    Round,
1123    Bevel,
1124}
1125
1126impl Default for SvgLineJoin {
1127    fn default() -> Self {
1128        SvgLineJoin::Miter
1129    }
1130}
1131
1132#[allow(non_camel_case_types)]
1133pub enum c_void {}
1134
1135pub type GlyphId = u16;
1136
1137#[derive(Debug, Clone)]
1138#[repr(C)]
1139pub struct SvgXmlNode {
1140    pub node: *const c_void, // usvg::Node
1141    pub run_destructor: bool,
1142}
1143
1144#[derive(Debug, Clone)]
1145#[repr(C)]
1146pub struct Svg {
1147    tree: *const c_void, // *mut usvg::Tree,
1148    pub run_destructor: bool,
1149}
1150
1151#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1152#[repr(C)]
1153pub enum ShapeRendering {
1154    OptimizeSpeed,
1155    CrispEdges,
1156    GeometricPrecision,
1157}
1158
1159#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1160#[repr(C)]
1161pub enum ImageRendering {
1162    OptimizeQuality,
1163    OptimizeSpeed,
1164}
1165
1166#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1167#[repr(C)]
1168pub enum TextRendering {
1169    OptimizeSpeed,
1170    OptimizeLegibility,
1171    GeometricPrecision,
1172}
1173
1174#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1175#[repr(C)]
1176pub enum FontDatabase {
1177    Empty,
1178    System,
1179}
1180
1181#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
1182#[repr(C)]
1183pub struct SvgRenderOptions {
1184    pub target_size: OptionLayoutSize,
1185    pub background_color: OptionColorU,
1186    pub fit: SvgFitTo,
1187    pub transform: SvgRenderTransform,
1188}
1189
1190#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
1191#[repr(C)]
1192pub struct SvgRenderTransform {
1193    pub sx: f32,
1194    pub kx: f32,
1195    pub ky: f32,
1196    pub sy: f32,
1197    pub tx: f32,
1198    pub ty: f32,
1199}
1200
1201#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1202#[repr(C, u8)]
1203pub enum SvgFitTo {
1204    Original,
1205    Width(u32),
1206    Height(u32),
1207    Zoom(f32),
1208}
1209
1210impl Default for SvgFitTo {
1211    fn default() -> Self {
1212        SvgFitTo::Original
1213    }
1214}
1215
1216#[derive(Debug, Clone, PartialEq, PartialOrd)]
1217#[repr(C)]
1218pub struct SvgParseOptions {
1219    /// SVG image path. Used to resolve relative image paths.
1220    pub relative_image_path: OptionAzString,
1221    /// Target DPI. Impact units conversion. Default: 96.0
1222    pub dpi: f32,
1223    /// Default font family. Will be used when no font-family attribute is set in the SVG. Default:
1224    /// Times New Roman
1225    pub default_font_family: AzString,
1226    /// A default font size. Will be used when no font-size attribute is set in the SVG. Default:
1227    /// 12
1228    pub font_size: f32,
1229    /// A list of languages. Will be used to resolve a systemLanguage conditional attribute.
1230    /// Format: en, en-US. Default: [en]
1231    pub languages: StringVec,
1232    /// Specifies the default shape rendering method. Will be used when an SVG element's
1233    /// shape-rendering property is set to auto. Default: GeometricPrecision
1234    pub shape_rendering: ShapeRendering,
1235    /// Specifies the default text rendering method. Will be used when an SVG element's
1236    /// text-rendering property is set to auto. Default: OptimizeLegibility
1237    pub text_rendering: TextRendering,
1238    /// Specifies the default image rendering method. Will be used when an SVG element's
1239    /// image-rendering property is set to auto. Default: OptimizeQuality
1240    pub image_rendering: ImageRendering,
1241    /// Keep named groups. If set to true, all non-empty groups with id attribute will not be
1242    /// removed. Default: false
1243    pub keep_named_groups: bool,
1244    /// When empty, text elements will be skipped. Default: `System`
1245    pub fontdb: FontDatabase,
1246}
1247
1248impl Default for SvgParseOptions {
1249    fn default() -> Self {
1250        let lang_vec: Vec<AzString> = vec![String::from("en").into()];
1251        SvgParseOptions {
1252            relative_image_path: OptionAzString::None,
1253            dpi: 96.0,
1254            default_font_family: "Times New Roman".to_string().into(),
1255            font_size: 12.0,
1256            languages: lang_vec.into(),
1257            shape_rendering: ShapeRendering::GeometricPrecision,
1258            text_rendering: TextRendering::OptimizeLegibility,
1259            image_rendering: ImageRendering::OptimizeQuality,
1260            keep_named_groups: false,
1261            fontdb: FontDatabase::System,
1262        }
1263    }
1264}
1265
1266#[derive(Debug, Clone, PartialEq, PartialOrd)]
1267#[repr(C)]
1268pub struct SvgXmlOptions {
1269    pub use_single_quote: bool,
1270    pub indent: Indent,
1271    pub attributes_indent: Indent,
1272}
1273
1274impl Default for SvgXmlOptions {
1275    fn default() -> Self {
1276        SvgXmlOptions {
1277            use_single_quote: false,
1278            indent: Indent::Spaces(2),
1279            attributes_indent: Indent::Spaces(2),
1280        }
1281    }
1282}
1283
1284#[derive(Debug, PartialEq, PartialOrd, Clone)]
1285#[repr(C, u8)]
1286pub enum SvgParseError {
1287    NoParserAvailable,
1288    ElementsLimitReached,
1289    NotAnUtf8Str,
1290    MalformedGZip,
1291    InvalidSize,
1292    ParsingFailed(XmlError),
1293}
1294
1295impl fmt::Display for SvgParseError {
1296    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1297        use self::SvgParseError::*;
1298        match self {
1299            NoParserAvailable => write!(
1300                f,
1301                "Library was compiled without SVG support (no parser available)"
1302            ),
1303            InvalidFileSuffix => write!(f, "Error parsing SVG: Invalid file suffix"),
1304            FileOpenFailed => write!(f, "Error parsing SVG: Failed to open file"),
1305            NotAnUtf8Str => write!(f, "Error parsing SVG: Not an UTF-8 String"),
1306            MalformedGZip => write!(
1307                f,
1308                "Error parsing SVG: SVG is compressed with a malformed GZIP compression"
1309            ),
1310            InvalidSize => write!(f, "Error parsing SVG: Invalid size"),
1311            ParsingFailed(e) => write!(f, "Error parsing SVG: Parsing SVG as XML failed: {}", e),
1312        }
1313    }
1314}
1315
1316impl_result!(
1317    SvgXmlNode,
1318    SvgParseError,
1319    ResultSvgXmlNodeSvgParseError,
1320    copy = false,
1321    [Debug, Clone]
1322);
1323impl_result!(
1324    Svg,
1325    SvgParseError,
1326    ResultSvgSvgParseError,
1327    copy = false,
1328    [Debug, Clone]
1329);
1330
1331#[derive(Debug, Clone, PartialEq, PartialOrd)]
1332#[repr(C, u8)]
1333pub enum Indent {
1334    None,
1335    Spaces(u8),
1336    Tabs,
1337}