1use alloc::boxed::Box;
2use core::fmt;
3
4#[cfg(not(feature = "svg"))]
5pub use azul_core::svg::*;
6#[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 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 }
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 {
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 {
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 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 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 if (x_int.is_nan() || x_int.is_infinite()) || (y_int.is_nan() || y_int.is_infinite()) {
297 None
298 } else {
299 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
561pub fn svg_path_bevel(p: &SvgPath, distance: f32) -> SvgPath {
563 let mut items = p.items.as_slice().to_vec();
564
565 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 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 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
764pub 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#[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 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#[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(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(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(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 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 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 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 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
1664pub 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
1689pub fn apply_fxaa(texture: &mut Texture) -> Option<()> {
1691 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 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 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 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 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 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 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 Some(SkPathBuilder::from_rect(SkRect::from_xywh(
1948 r.x, r.y, r.width, r.height,
1949 )?))
1950 }
1951 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 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 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#[cfg(feature = "svg")]
2064#[derive(Debug)]
2065#[repr(C)]
2066pub struct SvgXmlNode {
2067 node: Box<usvg::Node>, 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#[cfg(feature = "svg")]
2125#[repr(C)]
2126pub struct Svg {
2127 tree: Box<usvg::Tree>, 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#[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#[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 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 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}