1use alloc::boxed::Box;
14use core::fmt;
15
16#[cfg(not(feature = "svg"))]
17pub use azul_core::svg::*;
18#[cfg(feature = "svg")]
20pub use azul_core::svg::{
21 c_void,
22 FontDatabase,
23 ImageRendering,
24 Indent,
25 OptionSvgDashPattern,
26 ResultSvgSvgParseError,
27 ResultSvgXmlNodeSvgParseError,
28 ShapeRendering,
29 SvgCircle,
30 SvgColoredVertex,
31 SvgColoredVertexVec,
32 SvgColoredVertexVecDestructor,
33 SvgDashPattern,
34 SvgFillRule,
35 SvgFillStyle,
36 SvgFitTo,
37 SvgLine,
38 SvgLineCap,
39 SvgLineJoin,
40 SvgMultiPolygon,
41 SvgMultiPolygonVec,
42 SvgMultiPolygonVecDestructor,
43 SvgNode,
44 SvgParseError,
45 SvgParseOptions,
46 SvgPath,
47 SvgPathElement,
48 SvgPathElementVec,
49 SvgPathElementVecDestructor,
50 SvgPathVec,
51 SvgPathVecDestructor,
52 SvgRenderOptions,
53 SvgRenderTransform,
54
55 SvgSimpleNode,
56 SvgSimpleNodeVec,
57 SvgSimpleNodeVecDestructor,
58 SvgSize,
59 SvgStrokeStyle,
60 SvgStyle,
61 SvgStyledNode,
62 SvgTransform,
63 SvgVertex,
64 SvgVertexVec,
65 SvgVertexVecDestructor,
66 SvgXmlOptions,
67 TessellatedColoredSvgNode,
68 TessellatedColoredSvgNodeVec,
69 TessellatedColoredSvgNodeVecDestructor,
70 TessellatedGPUSvgNode,
72 TessellatedSvgNode,
73 TessellatedSvgNodeVec,
74 TessellatedSvgNodeVecDestructor,
75 TessellatedSvgNodeVecRef,
76 TextRendering,
77};
78use azul_core::{
79 geom::PhysicalSizeU32,
80 gl::{GlContextPtr, Texture},
81 resources::{RawImage, RawImageFormat},
82};
83#[cfg(feature = "svg")]
84pub use azul_css::props::basic::animation::{
85 SvgCubicCurve, SvgPoint, SvgQuadraticCurve, SvgRect, SvgVector,
86};
87use azul_css::{
88 impl_result, impl_result_inner,
89 props::basic::{ColorU, LayoutSize, OptionColorU, OptionLayoutSize},
90 AzString, OptionI16, OptionString, OptionU16, StringVec, U8Vec,
91};
92#[cfg(feature = "svg")]
93use lyon::{
94 geom::euclid::{Point2D, Rect, Size2D, UnknownUnit},
95 math::Point,
96 path::Path,
97 tessellation::{
98 BuffersBuilder, FillOptions, FillTessellator, FillVertex, StrokeOptions, StrokeTessellator,
99 StrokeVertex, VertexBuffers,
100 },
101};
102
103use crate::xml::XmlError;
104
105#[cfg(feature = "svg")]
106extern crate agg_rust;
107
108use azul_core::gl::GL_RESTART_INDEX;
109
110const CIRCLE_BEZIER_KAPPA: f64 = 0.5522847498;
112
113const DEFAULT_SVG_RENDER_SIZE: (u32, u32) = (800, 600);
115
116#[cfg(feature = "svg")]
117fn translate_svg_line_join(e: SvgLineJoin) -> lyon::tessellation::LineJoin {
118 use azul_core::svg::SvgLineJoin::*;
119 match e {
120 Miter => lyon::tessellation::LineJoin::Miter,
121 MiterClip => lyon::tessellation::LineJoin::MiterClip,
122 Round => lyon::tessellation::LineJoin::Round,
123 Bevel => lyon::tessellation::LineJoin::Bevel,
124 }
125}
126
127#[cfg(feature = "svg")]
128fn translate_svg_line_cap(e: SvgLineCap) -> lyon::tessellation::LineCap {
129 use azul_core::svg::SvgLineCap::*;
130 match e {
131 Butt => lyon::tessellation::LineCap::Butt,
132 Square => lyon::tessellation::LineCap::Square,
133 Round => lyon::tessellation::LineCap::Round,
134 }
135}
136
137#[cfg(feature = "svg")]
138fn translate_svg_stroke_style(e: SvgStrokeStyle) -> lyon::tessellation::StrokeOptions {
139 lyon::tessellation::StrokeOptions::tolerance(e.tolerance)
140 .with_start_cap(translate_svg_line_cap(e.start_cap))
141 .with_end_cap(translate_svg_line_cap(e.end_cap))
142 .with_line_join(translate_svg_line_join(e.line_join))
143 .with_line_width(e.line_width)
144 .with_miter_limit(e.miter_limit)
145 }
147
148#[cfg(feature = "svg")]
149fn svg_multipolygon_to_lyon_path(polygon: &SvgMultiPolygon) -> Path {
150 let mut builder = Path::builder();
151
152 for p in polygon.rings.as_ref().iter() {
153 if p.items.as_ref().is_empty() {
154 continue;
155 }
156
157 let start_item = p.items.as_ref()[0];
158 let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
159
160 builder.begin(first_point);
161
162 for q in p.items.as_ref().iter().rev()
163 {
165 match q {
166 SvgPathElement::Line(l) => {
167 builder.line_to(Point2D::new(l.end.x, l.end.y));
168 }
169 SvgPathElement::QuadraticCurve(qc) => {
170 builder.quadratic_bezier_to(
171 Point2D::new(qc.ctrl.x, qc.ctrl.y),
172 Point2D::new(qc.end.x, qc.end.y),
173 );
174 }
175 SvgPathElement::CubicCurve(cc) => {
176 builder.cubic_bezier_to(
177 Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
178 Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
179 Point2D::new(cc.end.x, cc.end.y),
180 );
181 }
182 }
183 }
184
185 builder.end(p.is_closed());
186 }
187
188 builder.build()
189}
190
191#[cfg(feature = "svg")]
192fn svg_multi_shape_to_lyon_path(polygon: &[SvgSimpleNode]) -> Path {
193 use lyon::{
194 geom::Box2D,
195 path::{traits::PathBuilder, Winding},
196 };
197
198 let mut builder = Path::builder();
199
200 for p in polygon.iter() {
201 match p {
202 SvgSimpleNode::Path(p) => {
203 if p.items.as_ref().is_empty() {
204 continue;
205 }
206
207 let start_item = p.items.as_ref()[0];
208 let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
209
210 builder.begin(first_point);
211
212 for q in p.items.as_ref().iter().rev()
213 {
215 match q {
216 SvgPathElement::Line(l) => {
217 builder.line_to(Point2D::new(l.end.x, l.end.y));
218 }
219 SvgPathElement::QuadraticCurve(qc) => {
220 builder.quadratic_bezier_to(
221 Point2D::new(qc.ctrl.x, qc.ctrl.y),
222 Point2D::new(qc.end.x, qc.end.y),
223 );
224 }
225 SvgPathElement::CubicCurve(cc) => {
226 builder.cubic_bezier_to(
227 Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
228 Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
229 Point2D::new(cc.end.x, cc.end.y),
230 );
231 }
232 }
233 }
234
235 builder.end(p.is_closed());
236 }
237 SvgSimpleNode::Circle(c) => {
238 builder.add_circle(
239 Point::new(c.center_x, c.center_y),
240 c.radius,
241 Winding::Positive,
242 );
243 }
244 SvgSimpleNode::CircleHole(c) => {
245 builder.add_circle(
246 Point::new(c.center_x, c.center_y),
247 c.radius,
248 Winding::Negative,
249 );
250 }
251 SvgSimpleNode::Rect(c) => {
252 builder.add_rectangle(
253 &Box2D::from_origin_and_size(
254 Point::new(c.x, c.y),
255 Size2D::new(c.width, c.height),
256 ),
257 Winding::Positive,
258 );
259 }
260 SvgSimpleNode::RectHole(c) => {
261 builder.add_rectangle(
262 &Box2D::from_origin_and_size(
263 Point::new(c.x, c.y),
264 Size2D::new(c.width, c.height),
265 ),
266 Winding::Negative,
267 );
268 }
269 }
270 }
271
272 builder.build()
273}
274
275pub fn raw_line_intersection(p: &SvgLine, q: &SvgLine) -> Option<SvgPoint> {
276 let p_min_x = p.start.x.min(p.end.x);
277 let p_min_y = p.start.y.min(p.end.y);
278 let p_max_x = p.start.x.max(p.end.x);
279 let p_max_y = p.start.y.max(p.end.y);
280
281 let q_min_x = q.start.x.min(q.end.x);
282 let q_min_y = q.start.y.min(q.end.y);
283 let q_max_x = q.start.x.max(q.end.x);
284 let q_max_y = q.start.y.max(q.end.y);
285
286 let int_min_x = p_min_x.max(q_min_x);
287 let int_max_x = p_max_x.min(q_max_x);
288 let int_min_y = p_min_y.max(q_min_y);
289 let int_max_y = p_max_y.min(q_max_y);
290
291 let two = 2.0;
292 let mid_x = (int_min_x + int_max_x) / two;
293 let mid_y = (int_min_y + int_max_y) / two;
294
295 let p1x = p.start.x - mid_x;
297 let p1y = p.start.y - mid_y;
298 let p2x = p.end.x - mid_x;
299 let p2y = p.end.y - mid_y;
300 let q1x = q.start.x - mid_x;
301 let q1y = q.start.y - mid_y;
302 let q2x = q.end.x - mid_x;
303 let q2y = q.end.y - mid_y;
304
305 let px = p1y - p2y;
307 let py = p2x - p1x;
308 let pw = p1x * p2y - p2x * p1y;
309
310 let qx = q1y - q2y;
311 let qy = q2x - q1x;
312 let qw = q1x * q2y - q2x * q1y;
313
314 let xw = py * qw - qy * pw;
315 let yw = qx * pw - px * qw;
316 let w = px * qy - qx * py;
317
318 let x_int = xw / w;
319 let y_int = yw / w;
320
321 if (x_int.is_nan() || x_int.is_infinite()) || (y_int.is_nan() || y_int.is_infinite()) {
323 None
324 } else {
325 Some(SvgPoint {
327 x: x_int + mid_x,
328 y: y_int + mid_y,
329 })
330 }
331}
332
333pub fn raw_line_intersection_byval(p: &SvgLine, q: SvgLine) -> Option<SvgPoint> {
335 raw_line_intersection(p, &q)
336}
337
338pub fn svg_path_offset(p: &SvgPath, distance: f32, join: SvgLineJoin, cap: SvgLineCap) -> SvgPath {
339 if distance == 0.0 {
340 return p.clone();
341 }
342
343 let mut items = p.items.as_slice().to_vec();
344 if let Some(mut first) = items.first() {
345 items.push(first.clone());
346 }
347
348 let mut items = items
349 .iter()
350 .map(|l| match l {
351 SvgPathElement::Line(q) => {
352 let normal = match q.outwards_normal() {
353 Some(s) => SvgPoint {
354 x: s.x * distance,
355 y: s.y * distance,
356 },
357 None => return l.clone(),
358 };
359
360 SvgPathElement::Line(SvgLine {
361 start: SvgPoint {
362 x: q.start.x + normal.x,
363 y: q.start.y + normal.y,
364 },
365 end: SvgPoint {
366 x: q.end.x + normal.x,
367 y: q.end.y + normal.y,
368 },
369 })
370 }
371 SvgPathElement::QuadraticCurve(q) => {
372 let n1 = match (SvgLine {
373 start: q.start.clone(),
374 end: q.ctrl.clone(),
375 }
376 .outwards_normal())
377 {
378 Some(s) => SvgPoint {
379 x: s.x * distance,
380 y: s.y * distance,
381 },
382 None => return l.clone(),
383 };
384
385 let n2 = match (SvgLine {
386 start: q.ctrl.clone(),
387 end: q.end.clone(),
388 }
389 .outwards_normal())
390 {
391 Some(s) => SvgPoint {
392 x: s.x * distance,
393 y: s.y * distance,
394 },
395 None => return l.clone(),
396 };
397
398 let nl1 = SvgLine {
399 start: SvgPoint {
400 x: q.start.x + n1.x,
401 y: q.start.y + n1.y,
402 },
403 end: SvgPoint {
404 x: q.ctrl.x + n1.x,
405 y: q.ctrl.y + n1.y,
406 },
407 };
408
409 let nl2 = SvgLine {
410 start: SvgPoint {
411 x: q.ctrl.x + n2.x,
412 y: q.ctrl.y + n2.y,
413 },
414 end: SvgPoint {
415 x: q.end.x + n2.x,
416 y: q.end.y + n2.y,
417 },
418 };
419
420 let nctrl = match raw_line_intersection(&nl1, &nl2) {
421 Some(s) => s,
422 None => return l.clone(),
423 };
424
425 SvgPathElement::QuadraticCurve(SvgQuadraticCurve {
426 start: nl1.start,
427 ctrl: nctrl,
428 end: nl2.end,
429 })
430 }
431 SvgPathElement::CubicCurve(q) => {
432 let n1 = match (SvgLine {
433 start: q.start.clone(),
434 end: q.ctrl_1.clone(),
435 }
436 .outwards_normal())
437 {
438 Some(s) => SvgPoint {
439 x: s.x * distance,
440 y: s.y * distance,
441 },
442 None => return l.clone(),
443 };
444
445 let n2 = match (SvgLine {
446 start: q.ctrl_1.clone(),
447 end: q.ctrl_2.clone(),
448 }
449 .outwards_normal())
450 {
451 Some(s) => SvgPoint {
452 x: s.x * distance,
453 y: s.y * distance,
454 },
455 None => return l.clone(),
456 };
457
458 let n3 = match (SvgLine {
459 start: q.ctrl_2.clone(),
460 end: q.end.clone(),
461 }
462 .outwards_normal())
463 {
464 Some(s) => SvgPoint {
465 x: s.x * distance,
466 y: s.y * distance,
467 },
468 None => return l.clone(),
469 };
470
471 let nl1 = SvgLine {
472 start: SvgPoint {
473 x: q.start.x + n1.x,
474 y: q.start.y + n1.y,
475 },
476 end: SvgPoint {
477 x: q.ctrl_1.x + n1.x,
478 y: q.ctrl_1.y + n1.y,
479 },
480 };
481
482 let nl2 = SvgLine {
483 start: SvgPoint {
484 x: q.ctrl_1.x + n2.x,
485 y: q.ctrl_1.y + n2.y,
486 },
487 end: SvgPoint {
488 x: q.ctrl_2.x + n2.x,
489 y: q.ctrl_2.y + n2.y,
490 },
491 };
492
493 let nl3 = SvgLine {
494 start: SvgPoint {
495 x: q.ctrl_2.x + n3.x,
496 y: q.ctrl_2.y + n3.y,
497 },
498 end: SvgPoint {
499 x: q.end.x + n3.x,
500 y: q.end.y + n3.y,
501 },
502 };
503
504 let nctrl_1 = match raw_line_intersection(&nl1, &nl2) {
505 Some(s) => s,
506 None => return l.clone(),
507 };
508
509 let nctrl_2 = match raw_line_intersection(&nl2, &nl3) {
510 Some(s) => s,
511 None => return l.clone(),
512 };
513
514 SvgPathElement::CubicCurve(SvgCubicCurve {
515 start: nl1.start,
516 ctrl_1: nctrl_1,
517 ctrl_2: nctrl_2,
518 end: nl3.end,
519 })
520 }
521 })
522 .collect::<Vec<_>>();
523
524 for i in 0..items.len().saturating_sub(2) {
525 let a_end_line = match items[i] {
526 SvgPathElement::Line(q) => q.clone(),
527 SvgPathElement::QuadraticCurve(q) => SvgLine {
528 start: q.ctrl.clone(),
529 end: q.end.clone(),
530 },
531 SvgPathElement::CubicCurve(q) => SvgLine {
532 start: q.ctrl_2.clone(),
533 end: q.end.clone(),
534 },
535 };
536
537 let b_start_line = match items[i + 1] {
538 SvgPathElement::Line(q) => q.clone(),
539 SvgPathElement::QuadraticCurve(q) => SvgLine {
540 start: q.ctrl.clone(),
541 end: q.start.clone(),
542 },
543 SvgPathElement::CubicCurve(q) => SvgLine {
544 start: q.ctrl_1.clone(),
545 end: q.start.clone(),
546 },
547 };
548
549 if let Some(intersect_pt) = raw_line_intersection(&a_end_line, &b_start_line) {
550 items[i].set_last(intersect_pt.clone());
551 items[i + 1].set_first(intersect_pt);
552 }
553 }
554
555 items.pop();
556
557 SvgPath {
558 items: items.into(),
559 }
560}
561
562fn shorten_line_end_by(line: SvgLine, distance: f32) -> SvgLine {
563 let dx = line.end.x - line.start.x;
564 let dy = line.end.y - line.start.y;
565 let dt = (dx * dx + dy * dy).sqrt();
566 let dt_short = dt - distance;
567
568 SvgLine {
569 start: line.start,
570 end: SvgPoint {
571 x: line.start.x + (dt_short / dt) * dx,
572 y: line.start.y + (dt_short / dt) * dy,
573 },
574 }
575}
576
577fn shorten_line_start_by(line: SvgLine, distance: f32) -> SvgLine {
578 let dx = line.end.x - line.start.x;
579 let dy = line.end.y - line.start.y;
580 let dt = (dx * dx + dy * dy).sqrt();
581 let dt_short = dt - distance;
582
583 SvgLine {
584 start: SvgPoint {
585 x: line.start.x + (1.0 - dt_short / dt) * dx,
586 y: line.start.y + (1.0 - dt_short / dt) * dy,
587 },
588 end: line.end,
589 }
590}
591
592pub fn svg_path_bevel(p: &SvgPath, distance: f32) -> SvgPath {
594 let mut items = p.items.as_slice().to_vec();
595
596 let first = items.first().cloned();
598 let last = items.last().cloned();
599 if let Some(first) = first {
600 items.push(first);
601 }
602 items.reverse();
603 if let Some(last) = last {
604 items.push(last);
605 }
606 items.reverse();
607
608 let mut final_items = Vec::new();
609 for i in 0..items.len().saturating_sub(1) {
610 let a = items[i].clone();
611 let b = items[i + 1].clone();
612 match (a, b) {
613 (SvgPathElement::Line(a), SvgPathElement::Line(b)) => {
614 let a_short = shorten_line_end_by(a, distance);
615 let b_short = shorten_line_start_by(b, distance);
616 final_items.push(SvgPathElement::Line(a_short));
617 final_items.push(SvgPathElement::CubicCurve(SvgCubicCurve {
618 start: a_short.end,
619 ctrl_1: a.end,
620 ctrl_2: b.start,
621 end: b_short.start,
622 }));
623 final_items.push(SvgPathElement::Line(b_short));
624 }
625 (other_a, other_b) => {
626 final_items.push(other_a);
627 final_items.push(other_b);
628 }
629 }
630 }
631
632 final_items.pop();
634 final_items.reverse();
635 final_items.pop();
636 final_items.reverse();
637
638 SvgPath {
639 items: final_items.into(),
640 }
641}
642
643#[cfg(feature = "svg")]
644fn svg_path_to_lyon_path_events(path: &SvgPath) -> Path {
645 let mut builder = Path::builder();
646
647 if !path.items.as_ref().is_empty() {
648 let start_item = path.items.as_ref()[0];
649 let first_point = Point2D::new(start_item.get_start().x, start_item.get_start().y);
650
651 builder.begin(first_point);
652
653 for p in path.items.as_ref().iter() {
654 match p {
655 SvgPathElement::Line(l) => {
656 builder.line_to(Point2D::new(l.end.x, l.end.y));
657 }
658 SvgPathElement::QuadraticCurve(qc) => {
659 builder.quadratic_bezier_to(
660 Point2D::new(qc.ctrl.x, qc.ctrl.y),
661 Point2D::new(qc.end.x, qc.end.y),
662 );
663 }
664 SvgPathElement::CubicCurve(cc) => {
665 builder.cubic_bezier_to(
666 Point2D::new(cc.ctrl_1.x, cc.ctrl_1.y),
667 Point2D::new(cc.ctrl_2.x, cc.ctrl_2.y),
668 Point2D::new(cc.end.x, cc.end.y),
669 );
670 }
671 }
672 }
673
674 builder.end(path.is_closed());
675 }
676
677 builder.build()
678}
679
680#[cfg(feature = "svg")]
681#[inline]
682fn vertex_buffers_to_tessellated_cpu_node(v: VertexBuffers<SvgVertex, u32>) -> TessellatedSvgNode {
683 TessellatedSvgNode {
684 vertices: v.vertices.into(),
685 indices: v.indices.into(),
686 }
687}
688
689#[cfg(feature = "svg")]
690pub fn tessellate_multi_polygon_fill(
691 polygon: &SvgMultiPolygon,
692 fill_style: SvgFillStyle,
693) -> TessellatedSvgNode {
694 let polygon = svg_multipolygon_to_lyon_path(polygon);
695
696 let mut geometry = VertexBuffers::new();
697 let mut tessellator = FillTessellator::new();
698
699 let tess_result = tessellator.tessellate_path(
700 &polygon,
701 &FillOptions::tolerance(fill_style.tolerance),
702 &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
703 let xy_arr = vertex.position();
704 SvgVertex {
705 x: xy_arr.x,
706 y: xy_arr.y,
707 }
708 }),
709 );
710
711 if let Err(_) = tess_result {
712 TessellatedSvgNode::empty()
713 } else {
714 vertex_buffers_to_tessellated_cpu_node(geometry)
715 }
716}
717
718#[cfg(not(feature = "svg"))]
719pub fn tessellate_multi_polygon_fill(
720 polygon: &SvgMultiPolygon,
721 fill_style: SvgFillStyle,
722) -> TessellatedSvgNode {
723 TessellatedSvgNode::default()
724}
725
726#[cfg(feature = "svg")]
727pub fn tessellate_multi_shape_fill(
728 ms: &[SvgSimpleNode],
729 fill_style: SvgFillStyle,
730) -> TessellatedSvgNode {
731 let polygon = svg_multi_shape_to_lyon_path(ms);
732
733 let mut geometry = VertexBuffers::new();
734 let mut tessellator = FillTessellator::new();
735
736 let tess_result = tessellator.tessellate_path(
737 &polygon,
738 &FillOptions::tolerance(fill_style.tolerance),
739 &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
740 let xy_arr = vertex.position();
741 SvgVertex {
742 x: xy_arr.x,
743 y: xy_arr.y,
744 }
745 }),
746 );
747
748 if let Err(_) = tess_result {
749 TessellatedSvgNode::empty()
750 } else {
751 vertex_buffers_to_tessellated_cpu_node(geometry)
752 }
753}
754
755#[cfg(not(feature = "svg"))]
756pub fn tessellate_multi_shape_fill(
757 ms: &[SvgSimpleNode],
758 fill_style: SvgFillStyle,
759) -> TessellatedSvgNode {
760 TessellatedSvgNode::default()
761}
762
763pub fn svg_node_contains_point(
764 node: &SvgNode,
765 point: SvgPoint,
766 fill_rule: SvgFillRule,
767 tolerance: f32,
768) -> bool {
769 match node {
770 SvgNode::MultiPolygonCollection(a) => a
771 .as_ref()
772 .iter()
773 .any(|e| polygon_contains_point(e, point, fill_rule, tolerance)),
774 SvgNode::MultiPolygon(a) => polygon_contains_point(a, point, fill_rule, tolerance),
775 SvgNode::Path(a) => {
776 if !a.is_closed() {
777 return false;
778 }
779 path_contains_point(a, point, fill_rule, tolerance)
780 }
781 SvgNode::Circle(a) => a.contains_point(point.x, point.y),
782 SvgNode::Rect(a) => a.contains_point(point),
783 SvgNode::MultiShape(a) => a.as_ref().iter().any(|e| match e {
784 SvgSimpleNode::Path(a) => {
785 if !a.is_closed() {
786 return false;
787 }
788 path_contains_point(a, point, fill_rule, tolerance)
789 }
790 SvgSimpleNode::Circle(a) => a.contains_point(point.x, point.y),
791 SvgSimpleNode::Rect(a) => a.contains_point(point),
792 SvgSimpleNode::CircleHole(a) => !a.contains_point(point.x, point.y),
793 SvgSimpleNode::RectHole(a) => !a.contains_point(point),
794 }),
795 }
796}
797
798#[cfg(feature = "svg")]
799pub fn path_contains_point(
800 path: &SvgPath,
801 point: SvgPoint,
802 fill_rule: SvgFillRule,
803 tolerance: f32,
804) -> bool {
805 use lyon::{
806 algorithms::hit_test::hit_test_path, math::Point as LyonPoint,
807 path::FillRule as LyonFillRule,
808 };
809 let path = svg_path_to_lyon_path_events(path);
810 let fill_rule = match fill_rule {
811 SvgFillRule::Winding => LyonFillRule::NonZero,
812 SvgFillRule::EvenOdd => LyonFillRule::EvenOdd,
813 };
814 let point = LyonPoint::new(point.x, point.y);
815 hit_test_path(&point, path.iter(), fill_rule, tolerance)
816}
817
818#[cfg(not(feature = "svg"))]
819pub fn path_contains_point(
820 path: &SvgPath,
821 point: SvgPoint,
822 fill_rule: SvgFillRule,
823 tolerance: f32,
824) -> bool {
825 false
826}
827
828#[cfg(feature = "svg")]
829pub fn polygon_contains_point(
830 polygon: &SvgMultiPolygon,
831 point: SvgPoint,
832 fill_rule: SvgFillRule,
833 tolerance: f32,
834) -> bool {
835 use lyon::{
836 algorithms::hit_test::hit_test_path, math::Point as LyonPoint,
837 path::FillRule as LyonFillRule,
838 };
839 polygon.rings.iter().any(|path| {
840 let path = svg_path_to_lyon_path_events(&path);
841 let fill_rule = match fill_rule {
842 SvgFillRule::Winding => LyonFillRule::NonZero,
843 SvgFillRule::EvenOdd => LyonFillRule::EvenOdd,
844 };
845 let point = LyonPoint::new(point.x, point.y);
846 hit_test_path(&point, path.iter(), fill_rule, tolerance)
847 })
848}
849
850#[cfg(not(feature = "svg"))]
851pub fn polygon_contains_point(
852 polygon: &SvgMultiPolygon,
853 point: SvgPoint,
854 fill_rule: SvgFillRule,
855 tolerance: f32,
856) -> bool {
857 false
858}
859
860#[cfg(feature = "svg")]
861pub fn tessellate_multi_shape_stroke(
862 ms: &[SvgSimpleNode],
863 stroke_style: SvgStrokeStyle,
864) -> TessellatedSvgNode {
865 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
866 let polygon = svg_multi_shape_to_lyon_path(ms);
867
868 let mut stroke_geometry = VertexBuffers::new();
869 let mut stroke_tess = StrokeTessellator::new();
870
871 let tess_result = stroke_tess.tessellate_path(
872 &polygon,
873 &stroke_options,
874 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
875 let xy_arr = vertex.position();
876 SvgVertex {
877 x: xy_arr.x,
878 y: xy_arr.y,
879 }
880 }),
881 );
882
883 if let Err(_) = tess_result {
884 TessellatedSvgNode::empty()
885 } else {
886 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
887 }
888}
889
890#[cfg(not(feature = "svg"))]
891pub fn tessellate_multi_shape_stroke(
892 polygon: &[SvgSimpleNode],
893 stroke_style: SvgStrokeStyle,
894) -> TessellatedSvgNode {
895 TessellatedSvgNode::default()
896}
897
898#[cfg(feature = "svg")]
899pub fn tessellate_multi_polygon_stroke(
900 polygon: &SvgMultiPolygon,
901 stroke_style: SvgStrokeStyle,
902) -> TessellatedSvgNode {
903 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
904 let polygon = svg_multipolygon_to_lyon_path(polygon);
905
906 let mut stroke_geometry = VertexBuffers::new();
907 let mut stroke_tess = StrokeTessellator::new();
908
909 let tess_result = stroke_tess.tessellate_path(
910 &polygon,
911 &stroke_options,
912 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
913 let xy_arr = vertex.position();
914 SvgVertex {
915 x: xy_arr.x,
916 y: xy_arr.y,
917 }
918 }),
919 );
920
921 if let Err(_) = tess_result {
922 TessellatedSvgNode::empty()
923 } else {
924 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
925 }
926}
927
928#[cfg(not(feature = "svg"))]
929pub fn tessellate_multi_polygon_stroke(
930 polygon: &SvgMultiPolygon,
931 stroke_style: SvgStrokeStyle,
932) -> TessellatedSvgNode {
933 TessellatedSvgNode::default()
934}
935
936#[cfg(feature = "svg")]
937pub fn tessellate_path_fill(path: &SvgPath, fill_style: SvgFillStyle) -> TessellatedSvgNode {
938 let polygon = svg_path_to_lyon_path_events(path);
939
940 let mut geometry = VertexBuffers::new();
941 let mut tessellator = FillTessellator::new();
942
943 let tess_result = tessellator.tessellate_path(
944 &polygon,
945 &FillOptions::tolerance(fill_style.tolerance),
946 &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
947 let xy_arr = vertex.position();
948 SvgVertex {
949 x: xy_arr.x,
950 y: xy_arr.y,
951 }
952 }),
953 );
954
955 if let Err(_) = tess_result {
956 TessellatedSvgNode::empty()
957 } else {
958 vertex_buffers_to_tessellated_cpu_node(geometry)
959 }
960}
961
962#[cfg(not(feature = "svg"))]
963pub fn tessellate_path_fill(path: &SvgPath, fill_style: SvgFillStyle) -> TessellatedSvgNode {
964 TessellatedSvgNode::default()
965}
966
967#[cfg(feature = "svg")]
968pub fn tessellate_path_stroke(path: &SvgPath, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
969 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
970 let polygon = svg_path_to_lyon_path_events(path);
971
972 let mut stroke_geometry = VertexBuffers::new();
973 let mut stroke_tess = StrokeTessellator::new();
974
975 let tess_result = stroke_tess.tessellate_path(
976 &polygon,
977 &stroke_options,
978 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
979 let xy_arr = vertex.position();
980 SvgVertex {
981 x: xy_arr.x,
982 y: xy_arr.y,
983 }
984 }),
985 );
986
987 if let Err(_) = tess_result {
988 TessellatedSvgNode::empty()
989 } else {
990 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
991 }
992}
993
994#[cfg(not(feature = "svg"))]
995pub fn tessellate_path_stroke(path: &SvgPath, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
996 TessellatedSvgNode::default()
997}
998
999#[cfg(feature = "svg")]
1000pub fn tessellate_circle_fill(c: &SvgCircle, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1001 let center = Point2D::new(c.center_x, c.center_y);
1002
1003 let mut geometry = VertexBuffers::new();
1004 let mut tesselator = FillTessellator::new();
1005 let tess_result = tesselator.tessellate_circle(
1006 center,
1007 c.radius,
1008 &FillOptions::tolerance(fill_style.tolerance),
1009 &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
1010 let xy_arr = vertex.position();
1011 SvgVertex {
1012 x: xy_arr.x,
1013 y: xy_arr.y,
1014 }
1015 }),
1016 );
1017
1018 if let Err(_) = tess_result {
1019 TessellatedSvgNode::empty()
1020 } else {
1021 vertex_buffers_to_tessellated_cpu_node(geometry)
1022 }
1023}
1024
1025#[cfg(not(feature = "svg"))]
1026pub fn tessellate_circle_fill(c: &SvgCircle, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1027 TessellatedSvgNode::default()
1028}
1029
1030#[cfg(feature = "svg")]
1031pub fn tessellate_circle_stroke(c: &SvgCircle, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1032 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1033 let center = Point2D::new(c.center_x, c.center_y);
1034
1035 let mut stroke_geometry = VertexBuffers::new();
1036 let mut tesselator = StrokeTessellator::new();
1037
1038 let tess_result = tesselator.tessellate_circle(
1039 center,
1040 c.radius,
1041 &stroke_options,
1042 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1043 let xy_arr = vertex.position();
1044 SvgVertex {
1045 x: xy_arr.x,
1046 y: xy_arr.y,
1047 }
1048 }),
1049 );
1050
1051 if let Err(_) = tess_result {
1052 TessellatedSvgNode::empty()
1053 } else {
1054 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1055 }
1056}
1057
1058#[cfg(not(feature = "svg"))]
1059pub fn tessellate_circle_stroke(c: &SvgCircle, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1060 TessellatedSvgNode::default()
1061}
1062
1063#[cfg(feature = "svg")]
1065fn get_radii(r: &SvgRect) -> lyon::geom::Box2D<f32> {
1066 let rect = lyon::geom::Box2D::from_origin_and_size(
1067 Point2D::new(r.x, r.y),
1068 Size2D::new(r.width, r.height),
1069 );
1070 rect
1078}
1079
1080#[cfg(feature = "svg")]
1081pub fn tessellate_rect_fill(r: &SvgRect, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1082 let rect = get_radii(&r);
1083 let mut geometry = VertexBuffers::new();
1084 let mut tesselator = FillTessellator::new();
1085
1086 let tess_result = tesselator.tessellate_rectangle(
1087 &rect,
1088 &FillOptions::tolerance(fill_style.tolerance),
1089 &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
1090 let xy_arr = vertex.position();
1091 SvgVertex {
1092 x: xy_arr.x,
1093 y: xy_arr.y,
1094 }
1095 }),
1096 );
1097
1098 if let Err(_) = tess_result {
1099 TessellatedSvgNode::empty()
1100 } else {
1101 vertex_buffers_to_tessellated_cpu_node(geometry)
1102 }
1103}
1104
1105#[cfg(not(feature = "svg"))]
1106pub fn tessellate_rect_fill(r: &SvgRect, fill_style: SvgFillStyle) -> TessellatedSvgNode {
1107 TessellatedSvgNode::default()
1108}
1109
1110#[cfg(feature = "svg")]
1111pub fn tessellate_rect_stroke(r: &SvgRect, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1112 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1113 let rect = get_radii(&r);
1114
1115 let mut stroke_geometry = VertexBuffers::new();
1116 let mut tesselator = StrokeTessellator::new();
1117
1118 let tess_result = tesselator.tessellate_rectangle(
1119 &rect,
1120 &stroke_options,
1121 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1122 let xy_arr = vertex.position();
1123 SvgVertex {
1124 x: xy_arr.x,
1125 y: xy_arr.y,
1126 }
1127 }),
1128 );
1129
1130 if let Err(_) = tess_result {
1131 TessellatedSvgNode::empty()
1132 } else {
1133 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1134 }
1135}
1136
1137#[cfg(not(feature = "svg"))]
1138pub fn tessellate_rect_stroke(r: &SvgRect, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
1139 TessellatedSvgNode::default()
1140}
1141
1142#[cfg(feature = "svg")]
1144pub fn tessellate_styled_node(node: &SvgStyledNode) -> TessellatedSvgNode {
1145 match node.style {
1146 SvgStyle::Fill(fs) => tessellate_node_fill(&node.geometry, fs),
1147 SvgStyle::Stroke(ss) => tessellate_node_stroke(&node.geometry, ss),
1148 }
1149}
1150
1151#[cfg(not(feature = "svg"))]
1152pub fn tessellate_styled_node(node: &SvgStyledNode) -> TessellatedSvgNode {
1153 TessellatedSvgNode::default()
1154}
1155
1156#[cfg(feature = "svg")]
1157pub fn tessellate_line_stroke(
1158 svgline: &SvgLine,
1159 stroke_style: SvgStrokeStyle,
1160) -> TessellatedSvgNode {
1161 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1162
1163 let mut builder = Path::builder();
1164 builder.begin(Point2D::new(svgline.start.x, svgline.start.y));
1165 builder.line_to(Point2D::new(svgline.end.x, svgline.end.y));
1166 builder.end(false);
1167 let path = builder.build();
1168
1169 let mut stroke_geometry = VertexBuffers::new();
1170 let mut stroke_tess = StrokeTessellator::new();
1171
1172 let tess_result = stroke_tess.tessellate_path(
1173 &path,
1174 &stroke_options,
1175 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
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(stroke_geometry)
1188 }
1189}
1190
1191#[cfg(not(feature = "svg"))]
1192pub fn tessellate_line_stroke(
1193 svgline: &SvgLine,
1194 stroke_style: SvgStrokeStyle,
1195) -> TessellatedSvgNode {
1196 TessellatedSvgNode::default()
1197}
1198
1199#[cfg(feature = "svg")]
1200pub fn tessellate_cubiccurve_stroke(
1201 svgcubiccurve: &SvgCubicCurve,
1202 stroke_style: SvgStrokeStyle,
1203) -> TessellatedSvgNode {
1204 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1205
1206 let mut builder = Path::builder();
1207 builder.begin(Point2D::new(svgcubiccurve.start.x, svgcubiccurve.start.y));
1208 builder.cubic_bezier_to(
1209 Point2D::new(svgcubiccurve.ctrl_1.x, svgcubiccurve.ctrl_1.y),
1210 Point2D::new(svgcubiccurve.ctrl_2.x, svgcubiccurve.ctrl_2.y),
1211 Point2D::new(svgcubiccurve.end.x, svgcubiccurve.end.y),
1212 );
1213 builder.end(false);
1214 let path = builder.build();
1215
1216 let mut stroke_geometry = VertexBuffers::new();
1217 let mut stroke_tess = StrokeTessellator::new();
1218
1219 let tess_result = stroke_tess.tessellate_path(
1220 &path,
1221 &stroke_options,
1222 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1223 let xy_arr = vertex.position();
1224 SvgVertex {
1225 x: xy_arr.x,
1226 y: xy_arr.y,
1227 }
1228 }),
1229 );
1230
1231 if let Err(_) = tess_result {
1232 TessellatedSvgNode::empty()
1233 } else {
1234 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1235 }
1236}
1237
1238#[cfg(not(feature = "svg"))]
1239pub fn tessellate_cubiccurve_stroke(
1240 svgline: &SvgCubicCurve,
1241 stroke_style: SvgStrokeStyle,
1242) -> TessellatedSvgNode {
1243 TessellatedSvgNode::default()
1244}
1245
1246#[cfg(feature = "svg")]
1247pub fn tessellate_quadraticcurve_stroke(
1248 svgquadraticcurve: &SvgQuadraticCurve,
1249 stroke_style: SvgStrokeStyle,
1250) -> TessellatedSvgNode {
1251 let stroke_options: StrokeOptions = translate_svg_stroke_style(stroke_style);
1252
1253 let mut builder = Path::builder();
1254 builder.begin(Point2D::new(
1255 svgquadraticcurve.start.x,
1256 svgquadraticcurve.start.y,
1257 ));
1258 builder.quadratic_bezier_to(
1259 Point2D::new(svgquadraticcurve.ctrl.x, svgquadraticcurve.ctrl.y),
1260 Point2D::new(svgquadraticcurve.end.x, svgquadraticcurve.end.y),
1261 );
1262 builder.end(false);
1263 let path = builder.build();
1264
1265 let mut stroke_geometry = VertexBuffers::new();
1266 let mut stroke_tess = StrokeTessellator::new();
1267
1268 let tess_result = stroke_tess.tessellate_path(
1269 &path,
1270 &stroke_options,
1271 &mut BuffersBuilder::new(&mut stroke_geometry, |vertex: StrokeVertex| {
1272 let xy_arr = vertex.position();
1273 SvgVertex {
1274 x: xy_arr.x,
1275 y: xy_arr.y,
1276 }
1277 }),
1278 );
1279
1280 if let Err(_) = tess_result {
1281 TessellatedSvgNode::empty()
1282 } else {
1283 vertex_buffers_to_tessellated_cpu_node(stroke_geometry)
1284 }
1285}
1286
1287#[cfg(not(feature = "svg"))]
1288pub fn tessellate_quadraticcurve_stroke(
1289 svgquadraticcurve: &SvgQuadraticCurve,
1290 stroke_style: SvgStrokeStyle,
1291) -> TessellatedSvgNode {
1292 TessellatedSvgNode::default()
1293}
1294
1295#[cfg(feature = "svg")]
1296pub fn tessellate_svgpathelement_stroke(
1297 svgpathelement: &SvgPathElement,
1298 stroke_style: SvgStrokeStyle,
1299) -> TessellatedSvgNode {
1300 match svgpathelement {
1301 SvgPathElement::Line(l) => tessellate_line_stroke(l, stroke_style),
1302 SvgPathElement::QuadraticCurve(l) => tessellate_quadraticcurve_stroke(l, stroke_style),
1303 SvgPathElement::CubicCurve(l) => tessellate_cubiccurve_stroke(l, stroke_style),
1304 }
1305}
1306
1307#[cfg(not(feature = "svg"))]
1308pub fn tessellate_svgpathelement_stroke(
1309 svgpathelement: &SvgPathElement,
1310 stroke_style: SvgStrokeStyle,
1311) -> TessellatedSvgNode {
1312 TessellatedSvgNode::default()
1313}
1314
1315#[cfg(feature = "svg")]
1316pub fn join_tessellated_nodes(nodes: &[TessellatedSvgNode]) -> TessellatedSvgNode {
1317 let mut index_offset = 0;
1318
1319 let all_index_offsets = nodes
1321 .as_ref()
1322 .iter()
1323 .map(|t| {
1324 let i = index_offset;
1325 index_offset += t.vertices.len();
1326 i
1327 })
1328 .collect::<Vec<_>>();
1329
1330 let all_vertices = nodes
1331 .as_ref()
1332 .iter()
1333 .flat_map(|t| t.vertices.clone().into_library_owned_vec())
1334 .collect::<Vec<_>>();
1335
1336 let all_indices = nodes
1337 .as_ref()
1338 .iter()
1339 .enumerate()
1340 .flat_map(|(buffer_index, t)| {
1341 let vertex_buffer_offset: u32 = all_index_offsets
1345 .get(buffer_index)
1346 .copied()
1347 .unwrap_or(0)
1348 .min(core::u32::MAX as usize) as u32;
1349
1350 let mut indices = t.indices.clone().into_library_owned_vec();
1351 if vertex_buffer_offset != 0 {
1352 indices.iter_mut().for_each(|i| {
1353 if *i != GL_RESTART_INDEX {
1354 *i += vertex_buffer_offset;
1355 }
1356 });
1357 }
1358
1359 indices.push(GL_RESTART_INDEX);
1360
1361 indices
1362 })
1363 .collect::<Vec<_>>();
1364
1365 TessellatedSvgNode {
1366 vertices: all_vertices.into(),
1367 indices: all_indices.into(),
1368 }
1369}
1370
1371#[cfg(feature = "svg")]
1372pub fn join_tessellated_colored_nodes(
1373 nodes: &[TessellatedColoredSvgNode],
1374) -> TessellatedColoredSvgNode {
1375 let mut index_offset = 0;
1376
1377 let all_index_offsets = nodes
1379 .as_ref()
1380 .iter()
1381 .map(|t| {
1382 let i = index_offset;
1383 index_offset += t.vertices.len();
1384 i
1385 })
1386 .collect::<Vec<_>>();
1387
1388 let all_vertices = nodes
1389 .as_ref()
1390 .iter()
1391 .flat_map(|t| t.vertices.clone().into_library_owned_vec())
1392 .collect::<Vec<_>>();
1393
1394 let all_indices = nodes
1395 .as_ref()
1396 .iter()
1397 .enumerate()
1398 .flat_map(|(buffer_index, t)| {
1399 let vertex_buffer_offset: u32 = all_index_offsets
1403 .get(buffer_index)
1404 .copied()
1405 .unwrap_or(0)
1406 .min(core::u32::MAX as usize) as u32;
1407
1408 let mut indices = t.indices.clone().into_library_owned_vec();
1409 if vertex_buffer_offset != 0 {
1410 indices.iter_mut().for_each(|i| {
1411 if *i != GL_RESTART_INDEX {
1412 *i += vertex_buffer_offset;
1413 }
1414 });
1415 }
1416
1417 indices.push(GL_RESTART_INDEX);
1418
1419 indices
1420 })
1421 .collect::<Vec<_>>();
1422
1423 TessellatedColoredSvgNode {
1424 vertices: all_vertices.into(),
1425 indices: all_indices.into(),
1426 }
1427}
1428
1429#[cfg(not(feature = "svg"))]
1430pub fn join_tessellated_nodes(nodes: &[TessellatedSvgNode]) -> TessellatedSvgNode {
1431 TessellatedSvgNode::default()
1432}
1433
1434#[cfg(not(feature = "svg"))]
1435pub fn join_tessellated_colored_nodes(
1436 nodes: &[TessellatedColoredSvgNode],
1437) -> TessellatedColoredSvgNode {
1438 TessellatedColoredSvgNode::default()
1439}
1440
1441#[cfg(feature = "svg")]
1442pub fn tessellate_node_fill(node: &SvgNode, fs: SvgFillStyle) -> TessellatedSvgNode {
1443 match &node {
1444 SvgNode::MultiPolygonCollection(ref mpc) => {
1445 let tessellated_multipolygons = mpc
1446 .as_ref()
1447 .iter()
1448 .map(|mp| tessellate_multi_polygon_fill(mp, fs))
1449 .collect::<Vec<_>>();
1450 join_tessellated_nodes(&tessellated_multipolygons)
1451 }
1452 SvgNode::MultiPolygon(ref mp) => tessellate_multi_polygon_fill(mp, fs),
1453 SvgNode::Path(ref p) => tessellate_path_fill(p, fs),
1454 SvgNode::Circle(ref c) => tessellate_circle_fill(c, fs),
1455 SvgNode::Rect(ref r) => tessellate_rect_fill(r, fs),
1456 SvgNode::MultiShape(ref r) => tessellate_multi_shape_fill(r.as_ref(), fs),
1457 }
1458}
1459
1460#[cfg(not(feature = "svg"))]
1461pub fn tessellate_node_fill(node: &SvgNode, fs: SvgFillStyle) -> TessellatedSvgNode {
1462 TessellatedSvgNode::default()
1463}
1464
1465#[cfg(feature = "svg")]
1466pub fn tessellate_node_stroke(node: &SvgNode, ss: SvgStrokeStyle) -> TessellatedSvgNode {
1467 match &node {
1468 SvgNode::MultiPolygonCollection(ref mpc) => {
1469 let tessellated_multipolygons = mpc
1470 .as_ref()
1471 .iter()
1472 .map(|mp| tessellate_multi_polygon_stroke(mp, ss))
1473 .collect::<Vec<_>>();
1474 join_tessellated_nodes(&tessellated_multipolygons)
1475 }
1476 SvgNode::MultiPolygon(ref mp) => tessellate_multi_polygon_stroke(mp, ss),
1477 SvgNode::Path(ref p) => tessellate_path_stroke(p, ss),
1478 SvgNode::Circle(ref c) => tessellate_circle_stroke(c, ss),
1479 SvgNode::Rect(ref r) => tessellate_rect_stroke(r, ss),
1480 SvgNode::MultiShape(ms) => tessellate_multi_shape_stroke(ms.as_ref(), ss),
1481 }
1482}
1483
1484#[cfg(not(feature = "svg"))]
1485pub fn tessellate_node_stroke(node: &SvgNode, ss: SvgStrokeStyle) -> TessellatedSvgNode {
1486 TessellatedSvgNode::default()
1487}
1488
1489pub fn allocate_clipmask_texture(
1492 gl_context: GlContextPtr,
1493 size: PhysicalSizeU32,
1494 _background: ColorU,
1495) -> Texture {
1496 use azul_core::gl::TextureFlags;
1497
1498 let textures = gl_context.gen_textures(1);
1499 let texture_id = textures.get(0).unwrap();
1500
1501 Texture::create(
1502 *texture_id,
1503 TextureFlags {
1504 is_opaque: true,
1505 is_video_texture: false,
1506 },
1507 size,
1508 ColorU::TRANSPARENT,
1509 gl_context,
1510 RawImageFormat::R8,
1511 )
1512}
1513
1514pub fn apply_fxaa(texture: &mut Texture) -> Option<()> {
1520 apply_fxaa_with_config(texture, azul_core::gl_fxaa::FxaaConfig::enabled())
1521}
1522
1523pub fn apply_fxaa_with_config(
1525 texture: &mut Texture,
1526 config: azul_core::gl_fxaa::FxaaConfig,
1527) -> Option<()> {
1528 use std::mem;
1529
1530 use azul_core::gl::{GLuint, GlVoidPtrConst, VertexAttributeType};
1531 use gl_context_loader::gl;
1532
1533 if !config.enabled || texture.size.width == 0 || texture.size.height == 0 {
1534 return Some(());
1535 }
1536
1537 if texture.format != RawImageFormat::RGBA8 {
1539 return Some(());
1540 }
1541
1542 let texture_size = texture.size;
1543 let gl_context = &texture.gl_context;
1544 let fxaa_shader = gl_context.get_fxaa_shader();
1545 let w = texture_size.width as f32;
1546 let h = texture_size.height as f32;
1547
1548 let mut current_program = [0_i32];
1550 let mut current_framebuffers = [0_i32];
1551 let mut current_texture_2d = [0_i32];
1552 let mut current_vertex_array_object = [0_i32];
1553 let mut current_vertex_buffer = [0_i32];
1554 let mut current_index_buffer = [0_i32];
1555 let mut current_active_texture = [0_i32];
1556 let mut current_blend_enabled = [0_u8];
1557 let mut current_viewport = [0_i32; 4];
1558
1559 gl_context.get_integer_v(gl::CURRENT_PROGRAM, (&mut current_program[..]).into());
1560 gl_context.get_integer_v(gl::FRAMEBUFFER, (&mut current_framebuffers[..]).into());
1561 gl_context.get_integer_v(gl::TEXTURE_2D, (&mut current_texture_2d[..]).into());
1562 gl_context.get_integer_v(
1563 gl::VERTEX_ARRAY_BINDING,
1564 (&mut current_vertex_array_object[..]).into(),
1565 );
1566 gl_context.get_integer_v(
1567 gl::ARRAY_BUFFER_BINDING,
1568 (&mut current_vertex_buffer[..]).into(),
1569 );
1570 gl_context.get_integer_v(
1571 gl::ELEMENT_ARRAY_BUFFER_BINDING,
1572 (&mut current_index_buffer[..]).into(),
1573 );
1574 gl_context.get_integer_v(
1575 gl::ACTIVE_TEXTURE,
1576 (&mut current_active_texture[..]).into(),
1577 );
1578 gl_context.get_boolean_v(gl::BLEND, (&mut current_blend_enabled[..]).into());
1579 gl_context.get_integer_v(gl::VIEWPORT, (&mut current_viewport[..]).into());
1580
1581 let temp_textures = gl_context.gen_textures(1);
1583 let temp_tex_id = *temp_textures.get(0)?;
1584 gl_context.bind_texture(gl::TEXTURE_2D, temp_tex_id);
1585 gl_context.tex_image_2d(
1586 gl::TEXTURE_2D,
1587 0,
1588 gl::RGBA as i32,
1589 texture_size.width as i32,
1590 texture_size.height as i32,
1591 0,
1592 gl::RGBA,
1593 gl::UNSIGNED_BYTE,
1594 None.into(),
1595 );
1596 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
1597 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
1598 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
1599 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
1600
1601 let fbos = gl_context.gen_framebuffers(1);
1603 let fbo_id = *fbos.get(0)?;
1604 gl_context.bind_framebuffer(gl::FRAMEBUFFER, fbo_id);
1605 gl_context.framebuffer_texture_2d(
1606 gl::FRAMEBUFFER,
1607 gl::COLOR_ATTACHMENT0,
1608 gl::TEXTURE_2D,
1609 temp_tex_id,
1610 0,
1611 );
1612 gl_context.draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
1613
1614 debug_assert!(
1615 gl_context.check_frame_buffer_status(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE
1616 );
1617
1618 let quad_vertices: [f32; 8] = [
1621 -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, ];
1626 let quad_indices: [u32; 6] = [0, 1, 2, 0, 2, 3];
1627
1628 let vaos = gl_context.gen_vertex_arrays(1);
1629 let vao_id = *vaos.get(0)?;
1630 gl_context.bind_vertex_array(vao_id);
1631
1632 let vbos = gl_context.gen_buffers(1);
1633 let vbo_id = *vbos.get(0)?;
1634 gl_context.bind_buffer(gl::ARRAY_BUFFER, vbo_id);
1635 gl_context.buffer_data_untyped(
1636 gl::ARRAY_BUFFER,
1637 (mem::size_of::<f32>() * quad_vertices.len()) as isize,
1638 GlVoidPtrConst {
1639 ptr: quad_vertices.as_ptr() as *const std::ffi::c_void,
1640 run_destructor: true,
1641 },
1642 gl::STATIC_DRAW,
1643 );
1644
1645 let ibos = gl_context.gen_buffers(1);
1646 let ibo_id = *ibos.get(0)?;
1647 gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, ibo_id);
1648 gl_context.buffer_data_untyped(
1649 gl::ELEMENT_ARRAY_BUFFER,
1650 (mem::size_of::<u32>() * quad_indices.len()) as isize,
1651 GlVoidPtrConst {
1652 ptr: quad_indices.as_ptr() as *const std::ffi::c_void,
1653 run_destructor: true,
1654 },
1655 gl::STATIC_DRAW,
1656 );
1657
1658 let vertex_type = VertexAttributeType::Float;
1660 let stride = vertex_type.get_mem_size() * 2; gl_context.vertex_attrib_pointer(0, 2, vertex_type.get_gl_id(), false, stride as i32, 0);
1662 gl_context.enable_vertex_attrib_array(0);
1663
1664 gl_context.use_program(fxaa_shader);
1666 gl_context.viewport(0, 0, texture_size.width as i32, texture_size.height as i32);
1667 gl_context.disable(gl::BLEND); gl_context.active_texture(gl::TEXTURE0);
1671 gl_context.bind_texture(gl::TEXTURE_2D, texture.texture_id);
1672
1673 let u_texture = gl_context.get_uniform_location(fxaa_shader, "uTexture");
1675 gl_context.uniform_1i(u_texture, 0);
1676
1677 let u_texel_size = gl_context.get_uniform_location(fxaa_shader, "uTexelSize");
1678 gl_context.uniform_2f(u_texel_size, 1.0 / w, 1.0 / h);
1679
1680 let u_edge_threshold =
1681 gl_context.get_uniform_location(fxaa_shader, "uEdgeThreshold");
1682 gl_context.uniform_1f(u_edge_threshold, config.edge_threshold);
1683
1684 let u_edge_threshold_min =
1685 gl_context.get_uniform_location(fxaa_shader, "uEdgeThresholdMin");
1686 gl_context.uniform_1f(u_edge_threshold_min, config.edge_threshold_min);
1687
1688 gl_context.draw_elements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, 0);
1690
1691 let old_texture_id = texture.texture_id;
1695 texture.texture_id = temp_tex_id;
1696 gl_context.delete_textures((&[old_texture_id])[..].into());
1698
1699 gl_context.delete_framebuffers((&[fbo_id])[..].into());
1701 gl_context.disable_vertex_attrib_array(0);
1702 gl_context.delete_vertex_arrays((&[vao_id])[..].into());
1703 gl_context.delete_buffers((&[vbo_id, ibo_id])[..].into());
1704
1705 gl_context.bind_framebuffer(gl::FRAMEBUFFER, current_framebuffers[0] as u32);
1707 gl_context.bind_texture(gl::TEXTURE_2D, current_texture_2d[0] as u32);
1708 gl_context.bind_vertex_array(current_vertex_array_object[0] as u32);
1709 gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, current_index_buffer[0] as u32);
1710 gl_context.bind_buffer(gl::ARRAY_BUFFER, current_vertex_buffer[0] as u32);
1711 gl_context.use_program(current_program[0] as u32);
1712 gl_context.active_texture(current_active_texture[0] as u32);
1713 gl_context.viewport(
1714 current_viewport[0],
1715 current_viewport[1],
1716 current_viewport[2],
1717 current_viewport[3],
1718 );
1719 if u32::from(current_blend_enabled[0]) == gl::TRUE {
1720 gl_context.enable(gl::BLEND);
1721 }
1722
1723 Some(())
1724}
1725
1726#[cfg(feature = "svg")]
1727pub fn render_node_clipmask_cpu(
1728 image: &mut RawImage,
1729 node: &SvgNode,
1730 style: SvgStyle,
1731) -> Option<()> {
1732 use azul_core::resources::RawImageData;
1733 use agg_rust::{
1734 basics::{FillingRule, VertexSource, PATH_FLAGS_NONE},
1735 path_storage::PathStorage,
1736 color::Rgba8,
1737 conv_stroke::ConvStroke,
1738 conv_transform::ConvTransform,
1739 math_stroke::{LineCap, LineJoin},
1740 pixfmt_rgba::{PixfmtRgba32, PixelFormat},
1741 rasterizer_scanline_aa::RasterizerScanlineAa,
1742 renderer_base::RendererBase,
1743 renderer_scanline::render_scanlines_aa_solid,
1744 rendering_buffer::RowAccessor,
1745 scanline_u::ScanlineU8,
1746 trans_affine::TransAffine,
1747 };
1748
1749 fn agg_translate_node(node: &SvgNode) -> Option<PathStorage> {
1750 macro_rules! build_path {
1751 ($path:expr, $p:expr) => {{
1752 if $p.items.as_ref().is_empty() {
1753 return None;
1754 }
1755
1756 let start = $p.items.as_ref()[0].get_start();
1757 $path.move_to(start.x as f64, start.y as f64);
1758
1759 for path_element in $p.items.as_ref() {
1760 match path_element {
1761 SvgPathElement::Line(l) => {
1762 $path.line_to(l.end.x as f64, l.end.y as f64);
1763 }
1764 SvgPathElement::QuadraticCurve(qc) => {
1765 $path.curve3(
1766 qc.ctrl.x as f64, qc.ctrl.y as f64,
1767 qc.end.x as f64, qc.end.y as f64,
1768 );
1769 }
1770 SvgPathElement::CubicCurve(cc) => {
1771 $path.curve4(
1772 cc.ctrl_1.x as f64, cc.ctrl_1.y as f64,
1773 cc.ctrl_2.x as f64, cc.ctrl_2.y as f64,
1774 cc.end.x as f64, cc.end.y as f64,
1775 );
1776 }
1777 }
1778 }
1779
1780 if $p.is_closed() {
1781 $path.close_polygon(PATH_FLAGS_NONE);
1782 }
1783 }};
1784 }
1785
1786 let mut path = PathStorage::new();
1787 match node {
1788 SvgNode::MultiPolygonCollection(mpc) => {
1789 for mp in mpc.iter() {
1790 for p in mp.rings.iter() {
1791 build_path!(path, p);
1792 }
1793 }
1794 }
1795 SvgNode::MultiPolygon(mp) => {
1796 for p in mp.rings.iter() {
1797 build_path!(path, p);
1798 }
1799 }
1800 SvgNode::Path(p) => {
1801 build_path!(path, p);
1802 }
1803 SvgNode::Circle(c) => {
1804 let cx = c.center_x as f64;
1806 let cy = c.center_y as f64;
1807 let r = c.radius as f64;
1808 let k = CIRCLE_BEZIER_KAPPA;
1809 let kr = k * r;
1810 path.move_to(cx + r, cy);
1811 path.curve4(cx + r, cy + kr, cx + kr, cy + r, cx, cy + r);
1812 path.curve4(cx - kr, cy + r, cx - r, cy + kr, cx - r, cy);
1813 path.curve4(cx - r, cy - kr, cx - kr, cy - r, cx, cy - r);
1814 path.curve4(cx + kr, cy - r, cx + r, cy - kr, cx + r, cy);
1815 path.close_polygon(PATH_FLAGS_NONE);
1816 }
1817 SvgNode::Rect(r) => {
1818 let x = r.x as f64;
1819 let y = r.y as f64;
1820 let w = r.width as f64;
1821 let h = r.height as f64;
1822 path.move_to(x, y);
1823 path.line_to(x + w, y);
1824 path.line_to(x + w, y + h);
1825 path.line_to(x, y + h);
1826 path.close_polygon(PATH_FLAGS_NONE);
1827 }
1828 SvgNode::MultiShape(ms) => {
1829 for p in ms.as_ref() {
1830 match p {
1831 SvgSimpleNode::Path(p) => {
1832 build_path!(path, p);
1833 }
1834 SvgSimpleNode::Rect(r) => {
1835 let x = r.x as f64;
1836 let y = r.y as f64;
1837 let w = r.width as f64;
1838 let h = r.height as f64;
1839 path.move_to(x, y);
1840 path.line_to(x + w, y);
1841 path.line_to(x + w, y + h);
1842 path.line_to(x, y + h);
1843 path.close_polygon(PATH_FLAGS_NONE);
1844 }
1845 SvgSimpleNode::Circle(c) | SvgSimpleNode::CircleHole(c) => {
1846 let cx = c.center_x as f64;
1847 let cy = c.center_y as f64;
1848 let r = c.radius as f64;
1849 let k = CIRCLE_BEZIER_KAPPA;
1850 let kr = k * r;
1851 path.move_to(cx + r, cy);
1852 path.curve4(cx + r, cy + kr, cx + kr, cy + r, cx, cy + r);
1853 path.curve4(cx - kr, cy + r, cx - r, cy + kr, cx - r, cy);
1854 path.curve4(cx - r, cy - kr, cx - kr, cy - r, cx, cy - r);
1855 path.curve4(cx + kr, cy - r, cx + r, cy - kr, cx + r, cy);
1856 path.close_polygon(PATH_FLAGS_NONE);
1857 }
1858 SvgSimpleNode::RectHole(r) => {
1859 let x = r.x as f64;
1860 let y = r.y as f64;
1861 let w = r.width as f64;
1862 let h = r.height as f64;
1863 path.move_to(x, y);
1864 path.line_to(x + w, y);
1865 path.line_to(x + w, y + h);
1866 path.line_to(x, y + h);
1867 path.close_polygon(PATH_FLAGS_NONE);
1868 }
1869 }
1870 }
1871 }
1872 }
1873 if path.total_vertices() == 0 {
1874 return None;
1875 }
1876 Some(path)
1877 }
1878
1879 let w = image.width as u32;
1880 let h = image.height as u32;
1881 if w == 0 || h == 0 {
1882 return None;
1883 }
1884
1885 let transform_data = style.get_transform();
1886 let transform = TransAffine::new_custom(
1887 transform_data.sx as f64,
1888 transform_data.ky as f64,
1889 transform_data.kx as f64,
1890 transform_data.sy as f64,
1891 transform_data.tx as f64,
1892 transform_data.ty as f64,
1893 );
1894
1895 let mut agg_path = agg_translate_node(node)?;
1896 let white = Rgba8::new(255, 255, 255, 255);
1897
1898 let mut buf = vec![0u8; (w as usize) * (h as usize) * 4];
1900 let stride = (w * 4) as i32;
1901 let mut ra = unsafe { RowAccessor::new_with_buf(buf.as_mut_ptr(), w, h, stride) };
1902 let mut pf = PixfmtRgba32::new(&mut ra);
1903 let mut rb = RendererBase::new(pf);
1904 let mut ras = RasterizerScanlineAa::new();
1905 let mut sl = ScanlineU8::new();
1906
1907 match style {
1908 SvgStyle::Fill(fs) => {
1909 ras.filling_rule(match fs.fill_rule {
1910 SvgFillRule::Winding => FillingRule::NonZero,
1911 SvgFillRule::EvenOdd => FillingRule::EvenOdd,
1912 });
1913 if transform.is_identity(0.0001) {
1914 ras.add_path(&mut agg_path, 0);
1915 } else {
1916 let mut transformed = ConvTransform::new(&mut agg_path, transform);
1917 ras.add_path(&mut transformed, 0);
1918 }
1919 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, &white);
1920 }
1921 SvgStyle::Stroke(ss) => {
1922 let mut stroke = ConvStroke::new(agg_path);
1923 stroke.set_width(ss.line_width as f64);
1924 stroke.set_miter_limit(ss.miter_limit as f64);
1925 stroke.set_line_cap(match ss.start_cap {
1926 SvgLineCap::Butt => LineCap::Butt,
1927 SvgLineCap::Square => LineCap::Square,
1928 SvgLineCap::Round => LineCap::Round,
1929 });
1930 stroke.set_line_join(match ss.line_join {
1931 SvgLineJoin::Miter | SvgLineJoin::MiterClip => LineJoin::Miter,
1932 SvgLineJoin::Round => LineJoin::Round,
1933 SvgLineJoin::Bevel => LineJoin::Bevel,
1934 });
1935 if transform.is_identity(0.0001) {
1936 ras.add_path(&mut stroke, 0);
1937 } else {
1938 let mut transformed = ConvTransform::new(&mut stroke, transform);
1939 ras.add_path(&mut transformed, 0);
1940 }
1941 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, &white);
1942 }
1943 }
1944
1945 let red_channel = buf
1947 .chunks_exact(4)
1948 .map(|r| r[0])
1949 .collect::<Vec<_>>();
1950
1951 image.premultiplied_alpha = true;
1952 image.pixels = RawImageData::U8(red_channel.into());
1953 image.data_format = RawImageFormat::R8;
1954
1955 Some(())
1956}
1957
1958#[cfg(not(feature = "svg"))]
1959pub fn render_node_clipmask_cpu(
1960 image: &mut RawImage,
1961 node: &SvgNode,
1962 style: SvgStyle,
1963) -> Option<()> {
1964 None
1965}
1966
1967fn rasterize_multi_polygon(mp: &SvgMultiPolygon) -> agg_rust::rasterizer_scanline_aa::RasterizerScanlineAa {
1973 use agg_rust::{
1974 basics::{FillingRule, PATH_FLAGS_NONE},
1975 path_storage::PathStorage,
1976 rasterizer_scanline_aa::RasterizerScanlineAa,
1977 };
1978
1979 let mut ras = RasterizerScanlineAa::new();
1980 ras.filling_rule(FillingRule::NonZero);
1981
1982 let mut path = PathStorage::new();
1983 for ring in mp.rings.as_ref().iter() {
1984 let mut first = true;
1985 for item in ring.items.as_ref().iter() {
1986 match item {
1987 SvgPathElement::Line(l) => {
1988 if first {
1989 path.move_to(l.start.x as f64, l.start.y as f64);
1990 first = false;
1991 }
1992 path.line_to(l.end.x as f64, l.end.y as f64);
1993 }
1994 SvgPathElement::QuadraticCurve(q) => {
1995 if first {
1996 path.move_to(q.start.x as f64, q.start.y as f64);
1997 first = false;
1998 }
1999 path.curve3(q.ctrl.x as f64, q.ctrl.y as f64, q.end.x as f64, q.end.y as f64);
2000 }
2001 SvgPathElement::CubicCurve(c) => {
2002 if first {
2003 path.move_to(c.start.x as f64, c.start.y as f64);
2004 first = false;
2005 }
2006 path.curve4(
2007 c.ctrl_1.x as f64, c.ctrl_1.y as f64,
2008 c.ctrl_2.x as f64, c.ctrl_2.y as f64,
2009 c.end.x as f64, c.end.y as f64,
2010 );
2011 }
2012 }
2013 }
2014 path.close_polygon(PATH_FLAGS_NONE);
2015 }
2016 ras.add_path(&mut path, 0);
2017 ras
2018}
2019
2020fn storage_to_multi_polygon(
2027 storage: &mut agg_rust::scanline_storage_aa::ScanlineStorageAa,
2028) -> SvgMultiPolygon {
2029 use agg_rust::rasterizer_scanline_aa::Scanline;
2030 use azul_css::props::basic::SvgPoint;
2031
2032 let mut rows: Vec<(i32, Vec<(i32, i32)>)> = Vec::new(); let mut sl = agg_rust::scanline_u::ScanlineU8::new();
2036 if storage.rewind_scanlines() {
2037 sl.reset(storage.min_x(), storage.max_x());
2038 while storage.sweep_scanline(&mut sl) {
2039 let y = Scanline::y(&sl);
2040 let mut row_spans: Vec<(i32, i32)> = Vec::new();
2041 for span in sl.begin() {
2042 let len = span.len;
2044 if len <= 0 { continue; }
2045 let covers = sl.covers();
2047 let mut x_start = None;
2048 for j in 0..len as usize {
2049 let cov = covers.get(span.cover_offset + j).copied().unwrap_or(0);
2050 if cov > 128 {
2051 if x_start.is_none() { x_start = Some(span.x + j as i32); }
2052 } else if let Some(xs) = x_start.take() {
2053 row_spans.push((xs, span.x + j as i32));
2054 }
2055 }
2056 if let Some(xs) = x_start {
2057 row_spans.push((xs, span.x + len));
2058 }
2059 }
2060 if !row_spans.is_empty() {
2061 rows.push((y, row_spans));
2062 }
2063 }
2064 }
2065
2066 if rows.is_empty() {
2067 return SvgMultiPolygon { rings: SvgPathVec::from_const_slice(&[]) };
2068 }
2069
2070 let mut rings = Vec::new();
2074
2075 for (y, spans) in &rows {
2076 let yf = *y as f32;
2077 for &(x0, x1) in spans {
2078 let x0f = x0 as f32;
2079 let x1f = x1 as f32;
2080 let elements = vec![
2082 SvgPathElement::Line(SvgLine::new(
2083 SvgPoint { x: x0f, y: yf },
2084 SvgPoint { x: x1f, y: yf },
2085 )),
2086 SvgPathElement::Line(SvgLine::new(
2087 SvgPoint { x: x1f, y: yf },
2088 SvgPoint { x: x1f, y: yf + 1.0 },
2089 )),
2090 SvgPathElement::Line(SvgLine::new(
2091 SvgPoint { x: x1f, y: yf + 1.0 },
2092 SvgPoint { x: x0f, y: yf + 1.0 },
2093 )),
2094 SvgPathElement::Line(SvgLine::new(
2095 SvgPoint { x: x0f, y: yf + 1.0 },
2096 SvgPoint { x: x0f, y: yf },
2097 )),
2098 ];
2099 rings.push(SvgPath { items: SvgPathElementVec::from_vec(elements) });
2100 }
2101 }
2102
2103 SvgMultiPolygon { rings: SvgPathVec::from_vec(rings) }
2104}
2105
2106fn svg_bool_op(
2108 a: &SvgMultiPolygon,
2109 b: &SvgMultiPolygon,
2110 op: agg_rust::scanline_boolean_algebra::SBoolOp,
2111) -> SvgMultiPolygon {
2112 use agg_rust::{
2113 scanline_boolean_algebra::sbool_combine_shapes_aa,
2114 scanline_storage_aa::ScanlineStorageAa,
2115 scanline_u::ScanlineU8,
2116 };
2117
2118 let mut ras1 = rasterize_multi_polygon(a);
2119 let mut ras2 = rasterize_multi_polygon(b);
2120
2121 let mut sl1 = ScanlineU8::new();
2122 let mut sl2 = ScanlineU8::new();
2123 let mut sl_result = ScanlineU8::new();
2124 let mut storage1 = ScanlineStorageAa::new();
2125 let mut storage2 = ScanlineStorageAa::new();
2126 let mut storage_result = ScanlineStorageAa::new();
2127
2128 sbool_combine_shapes_aa(
2129 op,
2130 &mut ras1, &mut ras2,
2131 &mut sl1, &mut sl2, &mut sl_result,
2132 &mut storage1, &mut storage2, &mut storage_result,
2133 );
2134
2135 storage_to_multi_polygon(&mut storage_result)
2136}
2137
2138pub fn svg_multi_polygon_union(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
2139 svg_bool_op(a, b, agg_rust::scanline_boolean_algebra::SBoolOp::Or)
2140}
2141
2142pub fn svg_multi_polygon_union_byval(a: &SvgMultiPolygon, b: SvgMultiPolygon) -> SvgMultiPolygon {
2143 svg_multi_polygon_union(a, &b)
2144}
2145
2146pub fn svg_multi_polygon_intersection(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
2147 svg_bool_op(a, b, agg_rust::scanline_boolean_algebra::SBoolOp::And)
2148}
2149
2150pub fn svg_multi_polygon_intersection_byval(
2151 a: &SvgMultiPolygon, b: SvgMultiPolygon,
2152) -> SvgMultiPolygon {
2153 svg_multi_polygon_intersection(a, &b)
2154}
2155
2156pub fn svg_multi_polygon_difference(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
2157 svg_bool_op(a, b, agg_rust::scanline_boolean_algebra::SBoolOp::AMinusB)
2158}
2159
2160pub fn svg_multi_polygon_difference_byval(
2161 a: &SvgMultiPolygon, b: SvgMultiPolygon,
2162) -> SvgMultiPolygon {
2163 svg_multi_polygon_difference(a, &b)
2164}
2165
2166pub fn svg_multi_polygon_xor(a: &SvgMultiPolygon, b: &SvgMultiPolygon) -> SvgMultiPolygon {
2167 svg_bool_op(a, b, agg_rust::scanline_boolean_algebra::SBoolOp::Xor)
2168}
2169
2170pub fn svg_multi_polygon_xor_byval(a: &SvgMultiPolygon, b: SvgMultiPolygon) -> SvgMultiPolygon {
2171 svg_multi_polygon_xor(a, &b)
2172}
2173
2174#[derive(Debug, Clone)]
2183#[repr(C)]
2184pub struct ParsedSvgXmlNode {
2185 pub run_destructor: bool,
2186}
2187
2188impl Drop for ParsedSvgXmlNode {
2189 fn drop(&mut self) { self.run_destructor = false; }
2190}
2191
2192pub fn svgxmlnode_parse(
2193 svg_file_data: &[u8],
2194 _options: SvgParseOptions,
2195) -> Result<ParsedSvgXmlNode, SvgParseError> {
2196 let s = core::str::from_utf8(svg_file_data)
2198 .map_err(|_| SvgParseError::NotAnUtf8Str)?;
2199 let _nodes = crate::xml::parse_xml_string(s)
2200 .map_err(|_| SvgParseError::NoParserAvailable)?;
2201 Ok(ParsedSvgXmlNode { run_destructor: true })
2202}
2203
2204#[derive(Clone)]
2206#[repr(C)]
2207pub struct ParsedSvg {
2208 pub svg_data: azul_css::U8Vec,
2209 pub run_destructor: bool,
2210}
2211
2212impl Drop for ParsedSvg {
2213 fn drop(&mut self) { self.run_destructor = false; }
2214}
2215
2216impl_result!(
2217 ParsedSvg,
2218 SvgParseError,
2219 ResultParsedSvgSvgParseError,
2220 copy = false,
2221 [Debug, Clone]
2222);
2223
2224impl From<ParsedSvg> for azul_core::svg::Svg {
2225 fn from(_parsed: ParsedSvg) -> Self {
2226 Self {
2227 tree: core::ptr::null(),
2228 run_destructor: false,
2229 }
2230 }
2231}
2232
2233impl fmt::Debug for ParsedSvg {
2234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2235 write!(f, "ParsedSvg({} bytes)", self.svg_data.as_ref().len())
2236 }
2237}
2238
2239impl ParsedSvg {
2240 pub fn from_string(
2241 svg_string: &str,
2242 parse_options: SvgParseOptions,
2243 ) -> Result<Self, SvgParseError> {
2244 svg_parse(svg_string.as_bytes(), parse_options)
2245 }
2246
2247 pub fn from_bytes(
2248 svg_bytes: &[u8],
2249 parse_options: SvgParseOptions,
2250 ) -> Result<Self, SvgParseError> {
2251 svg_parse(svg_bytes, parse_options)
2252 }
2253
2254 pub fn get_root(&self) -> ParsedSvgXmlNode {
2255 svg_root(self)
2256 }
2257
2258 pub fn render(&self, options: SvgRenderOptions) -> Option<RawImage> {
2259 svg_render(self, options)
2260 }
2261
2262 pub fn to_string(&self, _options: SvgXmlOptions) -> String {
2263 String::from_utf8_lossy(self.svg_data.as_ref()).into_owned()
2264 }
2265}
2266
2267pub fn svg_parse(
2269 svg_file_data: &[u8],
2270 _options: SvgParseOptions,
2271) -> Result<ParsedSvg, SvgParseError> {
2272 let s = core::str::from_utf8(svg_file_data)
2274 .map_err(|_| SvgParseError::NotAnUtf8Str)?;
2275 let _nodes = crate::xml::parse_xml_string(s)
2276 .map_err(|_| SvgParseError::NoParserAvailable)?;
2277 Ok(ParsedSvg {
2278 svg_data: svg_file_data.to_vec().into(),
2279 run_destructor: true,
2280 })
2281}
2282
2283pub fn svg_root(s: &ParsedSvg) -> ParsedSvgXmlNode {
2284 ParsedSvgXmlNode { run_destructor: true }
2285}
2286
2287pub fn svg_render(s: &ParsedSvg, options: SvgRenderOptions) -> Option<RawImage> {
2289 use azul_core::resources::RawImageData;
2290
2291 let (target_width, target_height) = match options.target_size.as_ref() {
2292 Some(s) => (s.width as u32, s.height as u32),
2293 None => DEFAULT_SVG_RENDER_SIZE,
2294 };
2295
2296 if target_width == 0 || target_height == 0 {
2297 return None;
2298 }
2299
2300 let png_data = crate::cpurender::render_svg_to_png(s.svg_data.as_ref(), target_width, target_height).ok()?;
2301
2302 let decoder = png::Decoder::new(std::io::Cursor::new(&png_data));
2304 let mut reader = decoder.read_info().ok()?;
2305 let mut buf = vec![0u8; reader.output_buffer_size()?];
2306 let info = reader.next_frame(&mut buf).ok()?;
2307 buf.truncate(info.buffer_size());
2308
2309 Some(RawImage {
2310 tag: Vec::new().into(),
2311 pixels: RawImageData::U8(buf.into()),
2312 width: info.width as usize,
2313 height: info.height as usize,
2314 premultiplied_alpha: false,
2315 data_format: RawImageFormat::RGBA8,
2316 })
2317}
2318
2319pub fn svg_to_string(s: &ParsedSvg, _options: SvgXmlOptions) -> String {
2320 String::from_utf8_lossy(s.svg_data.as_ref()).into_owned()
2321}
2322
2323pub trait SvgMultiPolygonTessellation {
2329 fn tessellate_fill(&self, fill_style: SvgFillStyle) -> TessellatedSvgNode;
2330 fn tessellate_stroke(&self, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode;
2331}
2332
2333impl SvgMultiPolygonTessellation for SvgMultiPolygon {
2334 fn tessellate_fill(&self, fill_style: SvgFillStyle) -> TessellatedSvgNode {
2335 tessellate_multi_polygon_fill(self, fill_style)
2336 }
2337 fn tessellate_stroke(&self, stroke_style: SvgStrokeStyle) -> TessellatedSvgNode {
2338 tessellate_multi_polygon_stroke(self, stroke_style)
2339 }
2340}