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