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