1use crate::core::text::LineHeight;
3use crate::core::{
4 self, Pixels, Point, Radians, Rectangle, Size, Svg, Transformation, Vector,
5};
6use crate::graphics::cache::{self, Cached};
7use crate::graphics::color;
8use crate::graphics::geometry::fill::{self, Fill};
9use crate::graphics::geometry::{
10 self, LineCap, LineDash, LineJoin, Path, Stroke, Style,
11};
12use crate::graphics::gradient::{self, Gradient};
13use crate::graphics::mesh::{self, Mesh};
14use crate::graphics::{Image, Text};
15use crate::text;
16use crate::triangle;
17
18use lyon::geom::euclid;
19use lyon::tessellation;
20
21use std::borrow::Cow;
22use std::sync::Arc;
23
24#[derive(Debug)]
25pub enum Geometry {
26 Live {
27 meshes: Vec<Mesh>,
28 images: Vec<Image>,
29 text: Vec<Text>,
30 },
31 Cached(Cache),
32}
33
34#[derive(Debug, Clone, Default)]
35pub struct Cache {
36 pub meshes: Option<triangle::Cache>,
37 pub images: Option<Arc<[Image]>>,
38 pub text: Option<text::Cache>,
39}
40
41impl Cached for Geometry {
42 type Cache = Cache;
43
44 fn load(cache: &Self::Cache) -> Self {
45 Geometry::Cached(cache.clone())
46 }
47
48 fn cache(
49 self,
50 group: cache::Group,
51 previous: Option<Self::Cache>,
52 ) -> Self::Cache {
53 match self {
54 Self::Live {
55 meshes,
56 images,
57 text,
58 } => {
59 let images = if images.is_empty() {
60 None
61 } else {
62 Some(Arc::from(images))
63 };
64
65 if let Some(mut previous) = previous {
66 if let Some(cache) = &mut previous.meshes {
67 cache.update(meshes);
68 } else {
69 previous.meshes = triangle::Cache::new(meshes);
70 }
71
72 if let Some(cache) = &mut previous.text {
73 cache.update(text);
74 } else {
75 previous.text = text::Cache::new(group, text);
76 }
77
78 previous.images = images;
79
80 previous
81 } else {
82 Cache {
83 meshes: triangle::Cache::new(meshes),
84 images,
85 text: text::Cache::new(group, text),
86 }
87 }
88 }
89 Self::Cached(cache) => cache,
90 }
91 }
92}
93
94#[allow(missing_debug_implementations)]
96pub struct Frame {
97 clip_bounds: Rectangle,
98 buffers: BufferStack,
99 meshes: Vec<Mesh>,
100 images: Vec<Image>,
101 text: Vec<Text>,
102 transforms: Transforms,
103 fill_tessellator: tessellation::FillTessellator,
104 stroke_tessellator: tessellation::StrokeTessellator,
105}
106
107impl Frame {
108 pub fn new(size: Size) -> Frame {
110 Self::with_clip(Rectangle::with_size(size))
111 }
112
113 pub fn with_clip(bounds: Rectangle) -> Frame {
115 Frame {
116 clip_bounds: bounds,
117 buffers: BufferStack::new(),
118 meshes: Vec::new(),
119 images: Vec::new(),
120 text: Vec::new(),
121 transforms: Transforms {
122 previous: Vec::new(),
123 current: Transform(lyon::math::Transform::translation(
124 bounds.x, bounds.y,
125 )),
126 },
127 fill_tessellator: tessellation::FillTessellator::new(),
128 stroke_tessellator: tessellation::StrokeTessellator::new(),
129 }
130 }
131}
132
133impl geometry::frame::Backend for Frame {
134 type Geometry = Geometry;
135
136 #[inline]
137 fn width(&self) -> f32 {
138 self.clip_bounds.width
139 }
140
141 #[inline]
142 fn height(&self) -> f32 {
143 self.clip_bounds.height
144 }
145
146 #[inline]
147 fn size(&self) -> Size {
148 self.clip_bounds.size()
149 }
150
151 #[inline]
152 fn center(&self) -> Point {
153 Point::new(self.clip_bounds.width / 2.0, self.clip_bounds.height / 2.0)
154 }
155
156 fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
157 let Fill { style, rule } = fill.into();
158
159 let mut buffer = self
160 .buffers
161 .get_fill(&self.transforms.current.transform_style(style));
162
163 let options = tessellation::FillOptions::default()
164 .with_fill_rule(into_fill_rule(rule));
165
166 if self.transforms.current.is_identity() {
167 self.fill_tessellator.tessellate_path(
168 path.raw(),
169 &options,
170 buffer.as_mut(),
171 )
172 } else {
173 let path = path.transform(&self.transforms.current.0);
174
175 self.fill_tessellator.tessellate_path(
176 path.raw(),
177 &options,
178 buffer.as_mut(),
179 )
180 }
181 .expect("Tessellate path.");
182 }
183
184 fn fill_rectangle(
185 &mut self,
186 top_left: Point,
187 size: Size,
188 fill: impl Into<Fill>,
189 ) {
190 let Fill { style, rule } = fill.into();
191
192 let mut buffer = self
193 .buffers
194 .get_fill(&self.transforms.current.transform_style(style));
195
196 let top_left = self
197 .transforms
198 .current
199 .0
200 .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
201
202 let size =
203 self.transforms.current.0.transform_vector(
204 lyon::math::Vector::new(size.width, size.height),
205 );
206
207 let options = tessellation::FillOptions::default()
208 .with_fill_rule(into_fill_rule(rule));
209
210 self.fill_tessellator
211 .tessellate_rectangle(
212 &lyon::math::Box2D::new(top_left, top_left + size),
213 &options,
214 buffer.as_mut(),
215 )
216 .expect("Fill rectangle");
217 }
218
219 fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
220 let stroke = stroke.into();
221
222 let mut buffer = self
223 .buffers
224 .get_stroke(&self.transforms.current.transform_style(stroke.style));
225
226 let mut options = tessellation::StrokeOptions::default();
227 options.line_width = stroke.width;
228 options.start_cap = into_line_cap(stroke.line_cap);
229 options.end_cap = into_line_cap(stroke.line_cap);
230 options.line_join = into_line_join(stroke.line_join);
231
232 let path = if stroke.line_dash.segments.is_empty() {
233 Cow::Borrowed(path)
234 } else {
235 Cow::Owned(dashed(path, stroke.line_dash))
236 };
237
238 if self.transforms.current.is_identity() {
239 self.stroke_tessellator.tessellate_path(
240 path.raw(),
241 &options,
242 buffer.as_mut(),
243 )
244 } else {
245 let path = path.transform(&self.transforms.current.0);
246
247 self.stroke_tessellator.tessellate_path(
248 path.raw(),
249 &options,
250 buffer.as_mut(),
251 )
252 }
253 .expect("Stroke path");
254 }
255
256 fn stroke_rectangle<'a>(
257 &mut self,
258 top_left: Point,
259 size: Size,
260 stroke: impl Into<Stroke<'a>>,
261 ) {
262 let stroke = stroke.into();
263
264 let mut buffer = self
265 .buffers
266 .get_stroke(&self.transforms.current.transform_style(stroke.style));
267
268 let top_left = self
269 .transforms
270 .current
271 .0
272 .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
273
274 let size =
275 self.transforms.current.0.transform_vector(
276 lyon::math::Vector::new(size.width, size.height),
277 );
278
279 let mut options = tessellation::StrokeOptions::default();
280 options.line_width = stroke.width;
281 options.start_cap = into_line_cap(stroke.line_cap);
282 options.end_cap = into_line_cap(stroke.line_cap);
283 options.line_join = into_line_join(stroke.line_join);
284
285 self.stroke_tessellator
286 .tessellate_rectangle(
287 &lyon::math::Box2D::new(top_left, top_left + size),
288 &options,
289 buffer.as_mut(),
290 )
291 .expect("Stroke rectangle");
292 }
293
294 fn stroke_text<'a>(
295 &mut self,
296 text: impl Into<geometry::Text>,
297 stroke: impl Into<Stroke<'a>>,
298 ) {
299 let text = text.into();
300 let stroke = stroke.into();
301
302 text.draw_with(|glyph, _color| self.stroke(&glyph, stroke));
303 }
304
305 fn fill_text(&mut self, text: impl Into<geometry::Text>) {
306 let text = text.into();
307
308 let (scale_x, scale_y) = self.transforms.current.scale();
309
310 if self.transforms.current.is_scale_translation()
311 && scale_x == scale_y
312 && scale_x > 0.0
313 && scale_y > 0.0
314 {
315 let (bounds, size, line_height) =
316 if self.transforms.current.is_identity() {
317 (
318 Rectangle::new(
319 text.position,
320 Size::new(text.max_width, f32::INFINITY),
321 ),
322 text.size,
323 text.line_height,
324 )
325 } else {
326 let position =
327 self.transforms.current.transform_point(text.position);
328
329 let size = Pixels(text.size.0 * scale_y);
330
331 let line_height = match text.line_height {
332 LineHeight::Absolute(size) => {
333 LineHeight::Absolute(Pixels(size.0 * scale_y))
334 }
335 LineHeight::Relative(factor) => {
336 LineHeight::Relative(factor)
337 }
338 };
339
340 (
341 Rectangle::new(
342 position,
343 Size::new(text.max_width, f32::INFINITY),
344 ),
345 size,
346 line_height,
347 )
348 };
349
350 self.text.push(Text::Cached {
351 content: text.content,
352 bounds,
353 color: text.color,
354 size,
355 line_height: line_height.to_absolute(size),
356 font: text.font,
357 align_x: text.align_x,
358 align_y: text.align_y,
359 shaping: text.shaping,
360 clip_bounds: self.clip_bounds,
361 });
362 } else {
363 text.draw_with(|path, color| self.fill(&path, color));
364 }
365 }
366
367 #[inline]
368 fn translate(&mut self, translation: Vector) {
369 self.transforms.current.0 =
370 self.transforms
371 .current
372 .0
373 .pre_translate(lyon::math::Vector::new(
374 translation.x,
375 translation.y,
376 ));
377 }
378
379 #[inline]
380 fn rotate(&mut self, angle: impl Into<Radians>) {
381 self.transforms.current.0 = self
382 .transforms
383 .current
384 .0
385 .pre_rotate(lyon::math::Angle::radians(angle.into().0));
386 }
387
388 #[inline]
389 fn scale(&mut self, scale: impl Into<f32>) {
390 let scale = scale.into();
391
392 self.scale_nonuniform(Vector { x: scale, y: scale });
393 }
394
395 #[inline]
396 fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
397 let scale = scale.into();
398
399 self.transforms.current.0 =
400 self.transforms.current.0.pre_scale(scale.x, scale.y);
401 }
402
403 fn push_transform(&mut self) {
404 self.transforms.previous.push(self.transforms.current);
405 }
406
407 fn pop_transform(&mut self) {
408 self.transforms.current = self.transforms.previous.pop().unwrap();
409 }
410
411 fn draft(&mut self, clip_bounds: Rectangle) -> Frame {
412 Frame::with_clip(clip_bounds)
413 }
414
415 fn paste(&mut self, frame: Frame) {
416 self.meshes.extend(frame.meshes);
417 self.meshes
418 .extend(frame.buffers.into_meshes(frame.clip_bounds));
419
420 self.images.extend(frame.images);
421 self.text.extend(frame.text);
422 }
423
424 fn into_geometry(mut self) -> Self::Geometry {
425 self.meshes
426 .extend(self.buffers.into_meshes(self.clip_bounds));
427
428 Geometry::Live {
429 meshes: self.meshes,
430 images: self.images,
431 text: self.text,
432 }
433 }
434
435 fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
436 let mut image = image.into();
437
438 let (bounds, external_rotation) =
439 self.transforms.current.transform_rectangle(bounds);
440
441 image.rotation += external_rotation;
442
443 self.images.push(Image::Raster(image, bounds));
444 }
445
446 fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
447 let mut svg = svg.into();
448
449 let (bounds, external_rotation) =
450 self.transforms.current.transform_rectangle(bounds);
451
452 svg.rotation += external_rotation;
453
454 self.images.push(Image::Vector(svg, bounds));
455 }
456}
457
458enum Buffer {
459 Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
460 Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
461}
462
463struct BufferStack {
464 stack: Vec<Buffer>,
465}
466
467impl BufferStack {
468 fn new() -> Self {
469 Self { stack: Vec::new() }
470 }
471
472 fn get_mut(&mut self, style: &Style) -> &mut Buffer {
473 match style {
474 Style::Solid(_) => match self.stack.last() {
475 Some(Buffer::Solid(_)) => {}
476 _ => {
477 self.stack.push(Buffer::Solid(
478 tessellation::VertexBuffers::new(),
479 ));
480 }
481 },
482 Style::Gradient(_) => match self.stack.last() {
483 Some(Buffer::Gradient(_)) => {}
484 _ => {
485 self.stack.push(Buffer::Gradient(
486 tessellation::VertexBuffers::new(),
487 ));
488 }
489 },
490 }
491
492 self.stack.last_mut().unwrap()
493 }
494
495 fn get_fill<'a>(
496 &'a mut self,
497 style: &Style,
498 ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> {
499 match (style, self.get_mut(style)) {
500 (Style::Solid(color), Buffer::Solid(buffer)) => {
501 Box::new(tessellation::BuffersBuilder::new(
502 buffer,
503 TriangleVertex2DBuilder(color::pack(*color)),
504 ))
505 }
506 (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
507 Box::new(tessellation::BuffersBuilder::new(
508 buffer,
509 GradientVertex2DBuilder {
510 gradient: gradient.pack(),
511 },
512 ))
513 }
514 _ => unreachable!(),
515 }
516 }
517
518 fn get_stroke<'a>(
519 &'a mut self,
520 style: &Style,
521 ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> {
522 match (style, self.get_mut(style)) {
523 (Style::Solid(color), Buffer::Solid(buffer)) => {
524 Box::new(tessellation::BuffersBuilder::new(
525 buffer,
526 TriangleVertex2DBuilder(color::pack(*color)),
527 ))
528 }
529 (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
530 Box::new(tessellation::BuffersBuilder::new(
531 buffer,
532 GradientVertex2DBuilder {
533 gradient: gradient.pack(),
534 },
535 ))
536 }
537 _ => unreachable!(),
538 }
539 }
540
541 fn into_meshes(self, clip_bounds: Rectangle) -> impl Iterator<Item = Mesh> {
542 self.stack
543 .into_iter()
544 .filter_map(move |buffer| match buffer {
545 Buffer::Solid(buffer) if !buffer.indices.is_empty() => {
546 Some(Mesh::Solid {
547 buffers: mesh::Indexed {
548 vertices: buffer.vertices,
549 indices: buffer.indices,
550 },
551 clip_bounds,
552 transformation: Transformation::IDENTITY,
553 })
554 }
555 Buffer::Gradient(buffer) if !buffer.indices.is_empty() => {
556 Some(Mesh::Gradient {
557 buffers: mesh::Indexed {
558 vertices: buffer.vertices,
559 indices: buffer.indices,
560 },
561 clip_bounds,
562 transformation: Transformation::IDENTITY,
563 })
564 }
565 _ => None,
566 })
567 }
568}
569
570#[derive(Debug)]
571struct Transforms {
572 previous: Vec<Transform>,
573 current: Transform,
574}
575
576#[derive(Debug, Clone, Copy)]
577struct Transform(lyon::math::Transform);
578
579impl Transform {
580 fn is_identity(&self) -> bool {
581 self.0 == lyon::math::Transform::identity()
582 }
583
584 fn is_scale_translation(&self) -> bool {
585 self.0.m12.abs() < 2.0 * f32::EPSILON
586 && self.0.m21.abs() < 2.0 * f32::EPSILON
587 }
588
589 fn scale(&self) -> (f32, f32) {
590 (self.0.m11, self.0.m22)
591 }
592
593 fn transform_point(&self, point: Point) -> Point {
594 let transformed = self
595 .0
596 .transform_point(euclid::Point2D::new(point.x, point.y));
597
598 Point {
599 x: transformed.x,
600 y: transformed.y,
601 }
602 }
603
604 fn transform_style(&self, style: Style) -> Style {
605 match style {
606 Style::Solid(color) => Style::Solid(color),
607 Style::Gradient(gradient) => {
608 Style::Gradient(self.transform_gradient(gradient))
609 }
610 }
611 }
612
613 fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
614 match &mut gradient {
615 Gradient::Linear(linear) => {
616 linear.start = self.transform_point(linear.start);
617 linear.end = self.transform_point(linear.end);
618 }
619 }
620
621 gradient
622 }
623
624 fn transform_rectangle(
625 &self,
626 rectangle: Rectangle,
627 ) -> (Rectangle, Radians) {
628 let top_left = self.transform_point(rectangle.position());
629 let top_right = self.transform_point(
630 rectangle.position() + Vector::new(rectangle.width, 0.0),
631 );
632 let bottom_left = self.transform_point(
633 rectangle.position() + Vector::new(0.0, rectangle.height),
634 );
635
636 Rectangle::with_vertices(top_left, top_right, bottom_left)
637 }
638}
639struct GradientVertex2DBuilder {
640 gradient: gradient::Packed,
641}
642
643impl tessellation::FillVertexConstructor<mesh::GradientVertex2D>
644 for GradientVertex2DBuilder
645{
646 fn new_vertex(
647 &mut self,
648 vertex: tessellation::FillVertex<'_>,
649 ) -> mesh::GradientVertex2D {
650 let position = vertex.position();
651
652 mesh::GradientVertex2D {
653 position: [position.x, position.y],
654 gradient: self.gradient,
655 }
656 }
657}
658
659impl tessellation::StrokeVertexConstructor<mesh::GradientVertex2D>
660 for GradientVertex2DBuilder
661{
662 fn new_vertex(
663 &mut self,
664 vertex: tessellation::StrokeVertex<'_, '_>,
665 ) -> mesh::GradientVertex2D {
666 let position = vertex.position();
667
668 mesh::GradientVertex2D {
669 position: [position.x, position.y],
670 gradient: self.gradient,
671 }
672 }
673}
674
675struct TriangleVertex2DBuilder(color::Packed);
676
677impl tessellation::FillVertexConstructor<mesh::SolidVertex2D>
678 for TriangleVertex2DBuilder
679{
680 fn new_vertex(
681 &mut self,
682 vertex: tessellation::FillVertex<'_>,
683 ) -> mesh::SolidVertex2D {
684 let position = vertex.position();
685
686 mesh::SolidVertex2D {
687 position: [position.x, position.y],
688 color: self.0,
689 }
690 }
691}
692
693impl tessellation::StrokeVertexConstructor<mesh::SolidVertex2D>
694 for TriangleVertex2DBuilder
695{
696 fn new_vertex(
697 &mut self,
698 vertex: tessellation::StrokeVertex<'_, '_>,
699 ) -> mesh::SolidVertex2D {
700 let position = vertex.position();
701
702 mesh::SolidVertex2D {
703 position: [position.x, position.y],
704 color: self.0,
705 }
706 }
707}
708
709fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin {
710 match line_join {
711 LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
712 LineJoin::Round => lyon::tessellation::LineJoin::Round,
713 LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
714 }
715}
716
717fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap {
718 match line_cap {
719 LineCap::Butt => lyon::tessellation::LineCap::Butt,
720 LineCap::Square => lyon::tessellation::LineCap::Square,
721 LineCap::Round => lyon::tessellation::LineCap::Round,
722 }
723}
724
725fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule {
726 match rule {
727 fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero,
728 fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
729 }
730}
731
732pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
733 use lyon::algorithms::walk::{
734 RepeatedPattern, WalkerEvent, walk_along_path,
735 };
736 use lyon::path::iterator::PathIterator;
737
738 Path::new(|builder| {
739 let segments_odd = (line_dash.segments.len() % 2 == 1)
740 .then(|| [line_dash.segments, line_dash.segments].concat());
741
742 let mut draw_line = false;
743
744 walk_along_path(
745 path.raw().iter().flattened(
746 lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
747 ),
748 0.0,
749 lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
750 &mut RepeatedPattern {
751 callback: |event: WalkerEvent<'_>| {
752 let point = Point {
753 x: event.position.x,
754 y: event.position.y,
755 };
756
757 if draw_line {
758 builder.line_to(point);
759 } else {
760 builder.move_to(point);
761 }
762
763 draw_line = !draw_line;
764
765 true
766 },
767 index: line_dash.offset,
768 intervals: segments_odd
769 .as_deref()
770 .unwrap_or(line_dash.segments),
771 },
772 );
773 })
774}