1use 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 pub fn reverse(&mut self) {
275 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 vec.reverse();
282
283 for item in vec.iter_mut() {
286 item.reverse();
287 }
288
289 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 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 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 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(), };
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#[derive(Debug, Clone, PartialOrd, PartialEq)]
387#[repr(C, u8)]
388pub enum SvgNode {
389 MultiPolygonCollection(SvgMultiPolygonVec),
391 MultiPolygon(SvgMultiPolygon),
392 MultiShape(SvgSimpleNodeVec),
393 Path(SvgPath),
394 Circle(SvgCircle),
395 Rect(SvgRect),
396}
397
398#[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#[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#[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 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 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 let column_major = computed_transform.get_column_major();
814
815 let color: ColorF = color.into();
816
817 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 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 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 let column_major = computed_transform.get_column_major();
897
898 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 pub line_join: SvgLineJoin,
984 pub miter_limit: f32,
989 pub tolerance: f32,
994 pub fill_rule: SvgFillRule,
996 pub transform: SvgTransform,
999 pub anti_alias: bool,
1001 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 pub start_cap: SvgLineCap,
1026 pub end_cap: SvgLineCap,
1030 pub line_join: SvgLineJoin,
1034 pub dash_pattern: OptionSvgDashPattern,
1036 pub line_width: f32,
1040 pub miter_limit: f32,
1045 pub tolerance: f32,
1050 pub apply_line_width: bool,
1058 pub transform: SvgTransform,
1061 pub anti_alias: bool,
1063 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, pub run_destructor: bool,
1142}
1143
1144#[derive(Debug, Clone)]
1145#[repr(C)]
1146pub struct Svg {
1147 tree: *const c_void, 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 pub relative_image_path: OptionAzString,
1221 pub dpi: f32,
1223 pub default_font_family: AzString,
1226 pub font_size: f32,
1229 pub languages: StringVec,
1232 pub shape_rendering: ShapeRendering,
1235 pub text_rendering: TextRendering,
1238 pub image_rendering: ImageRendering,
1241 pub keep_named_groups: bool,
1244 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}