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 fill_text(&mut self, text: impl Into<geometry::Text>) {
295 let text = text.into();
296
297 let (scale_x, scale_y) = self.transforms.current.scale();
298
299 if self.transforms.current.is_scale_translation()
300 && scale_x == scale_y
301 && scale_x > 0.0
302 && scale_y > 0.0
303 {
304 let (position, size, line_height) =
305 if self.transforms.current.is_identity() {
306 (text.position, text.size, text.line_height)
307 } else {
308 let position =
309 self.transforms.current.transform_point(text.position);
310
311 let size = Pixels(text.size.0 * scale_y);
312
313 let line_height = match text.line_height {
314 LineHeight::Absolute(size) => {
315 LineHeight::Absolute(Pixels(size.0 * scale_y))
316 }
317 LineHeight::Relative(factor) => {
318 LineHeight::Relative(factor)
319 }
320 };
321
322 (position, size, line_height)
323 };
324
325 let bounds = Rectangle {
326 x: position.x,
327 y: position.y,
328 width: f32::INFINITY,
329 height: f32::INFINITY,
330 };
331
332 self.text.push(Text::Cached {
333 content: text.content,
334 bounds,
335 color: text.color,
336 size,
337 line_height: line_height.to_absolute(size),
338 font: text.font,
339 horizontal_alignment: text.horizontal_alignment,
340 vertical_alignment: text.vertical_alignment,
341 shaping: text.shaping,
342 clip_bounds: self.clip_bounds,
343 });
344 } else {
345 text.draw_with(|path, color| self.fill(&path, color));
346 }
347 }
348
349 #[inline]
350 fn translate(&mut self, translation: Vector) {
351 self.transforms.current.0 =
352 self.transforms
353 .current
354 .0
355 .pre_translate(lyon::math::Vector::new(
356 translation.x,
357 translation.y,
358 ));
359 }
360
361 #[inline]
362 fn rotate(&mut self, angle: impl Into<Radians>) {
363 self.transforms.current.0 = self
364 .transforms
365 .current
366 .0
367 .pre_rotate(lyon::math::Angle::radians(angle.into().0));
368 }
369
370 #[inline]
371 fn scale(&mut self, scale: impl Into<f32>) {
372 let scale = scale.into();
373
374 self.scale_nonuniform(Vector { x: scale, y: scale });
375 }
376
377 #[inline]
378 fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
379 let scale = scale.into();
380
381 self.transforms.current.0 =
382 self.transforms.current.0.pre_scale(scale.x, scale.y);
383 }
384
385 fn push_transform(&mut self) {
386 self.transforms.previous.push(self.transforms.current);
387 }
388
389 fn pop_transform(&mut self) {
390 self.transforms.current = self.transforms.previous.pop().unwrap();
391 }
392
393 fn draft(&mut self, clip_bounds: Rectangle) -> Frame {
394 Frame::with_clip(clip_bounds)
395 }
396
397 fn paste(&mut self, frame: Frame) {
398 self.meshes.extend(frame.meshes);
399 self.meshes
400 .extend(frame.buffers.into_meshes(frame.clip_bounds));
401
402 self.images.extend(frame.images);
403 self.text.extend(frame.text);
404 }
405
406 fn into_geometry(mut self) -> Self::Geometry {
407 self.meshes
408 .extend(self.buffers.into_meshes(self.clip_bounds));
409
410 Geometry::Live {
411 meshes: self.meshes,
412 images: self.images,
413 text: self.text,
414 }
415 }
416
417 fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
418 let mut image = image.into();
419
420 let (bounds, external_rotation) =
421 self.transforms.current.transform_rectangle(bounds);
422
423 image.rotation += external_rotation;
424
425 self.images.push(Image::Raster(image, bounds));
426 }
427
428 fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
429 let mut svg = svg.into();
430
431 let (bounds, external_rotation) =
432 self.transforms.current.transform_rectangle(bounds);
433
434 svg.rotation += external_rotation;
435
436 self.images.push(Image::Vector(svg, bounds));
437 }
438}
439
440enum Buffer {
441 Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
442 Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
443}
444
445struct BufferStack {
446 stack: Vec<Buffer>,
447}
448
449impl BufferStack {
450 fn new() -> Self {
451 Self { stack: Vec::new() }
452 }
453
454 fn get_mut(&mut self, style: &Style) -> &mut Buffer {
455 match style {
456 Style::Solid(_) => match self.stack.last() {
457 Some(Buffer::Solid(_)) => {}
458 _ => {
459 self.stack.push(Buffer::Solid(
460 tessellation::VertexBuffers::new(),
461 ));
462 }
463 },
464 Style::Gradient(_) => match self.stack.last() {
465 Some(Buffer::Gradient(_)) => {}
466 _ => {
467 self.stack.push(Buffer::Gradient(
468 tessellation::VertexBuffers::new(),
469 ));
470 }
471 },
472 }
473
474 self.stack.last_mut().unwrap()
475 }
476
477 fn get_fill<'a>(
478 &'a mut self,
479 style: &Style,
480 ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> {
481 match (style, self.get_mut(style)) {
482 (Style::Solid(color), Buffer::Solid(buffer)) => {
483 Box::new(tessellation::BuffersBuilder::new(
484 buffer,
485 TriangleVertex2DBuilder(color::pack(*color)),
486 ))
487 }
488 (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
489 Box::new(tessellation::BuffersBuilder::new(
490 buffer,
491 GradientVertex2DBuilder {
492 gradient: gradient.pack(),
493 },
494 ))
495 }
496 _ => unreachable!(),
497 }
498 }
499
500 fn get_stroke<'a>(
501 &'a mut self,
502 style: &Style,
503 ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> {
504 match (style, self.get_mut(style)) {
505 (Style::Solid(color), Buffer::Solid(buffer)) => {
506 Box::new(tessellation::BuffersBuilder::new(
507 buffer,
508 TriangleVertex2DBuilder(color::pack(*color)),
509 ))
510 }
511 (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
512 Box::new(tessellation::BuffersBuilder::new(
513 buffer,
514 GradientVertex2DBuilder {
515 gradient: gradient.pack(),
516 },
517 ))
518 }
519 _ => unreachable!(),
520 }
521 }
522
523 fn into_meshes(self, clip_bounds: Rectangle) -> impl Iterator<Item = Mesh> {
524 self.stack
525 .into_iter()
526 .filter_map(move |buffer| match buffer {
527 Buffer::Solid(buffer) if !buffer.indices.is_empty() => {
528 Some(Mesh::Solid {
529 buffers: mesh::Indexed {
530 vertices: buffer.vertices,
531 indices: buffer.indices,
532 },
533 clip_bounds,
534 transformation: Transformation::IDENTITY,
535 })
536 }
537 Buffer::Gradient(buffer) if !buffer.indices.is_empty() => {
538 Some(Mesh::Gradient {
539 buffers: mesh::Indexed {
540 vertices: buffer.vertices,
541 indices: buffer.indices,
542 },
543 clip_bounds,
544 transformation: Transformation::IDENTITY,
545 })
546 }
547 _ => None,
548 })
549 }
550}
551
552#[derive(Debug)]
553struct Transforms {
554 previous: Vec<Transform>,
555 current: Transform,
556}
557
558#[derive(Debug, Clone, Copy)]
559struct Transform(lyon::math::Transform);
560
561impl Transform {
562 fn is_identity(&self) -> bool {
563 self.0 == lyon::math::Transform::identity()
564 }
565
566 fn is_scale_translation(&self) -> bool {
567 self.0.m12.abs() < 2.0 * f32::EPSILON
568 && self.0.m21.abs() < 2.0 * f32::EPSILON
569 }
570
571 fn scale(&self) -> (f32, f32) {
572 (self.0.m11, self.0.m22)
573 }
574
575 fn transform_point(&self, point: Point) -> Point {
576 let transformed = self
577 .0
578 .transform_point(euclid::Point2D::new(point.x, point.y));
579
580 Point {
581 x: transformed.x,
582 y: transformed.y,
583 }
584 }
585
586 fn transform_style(&self, style: Style) -> Style {
587 match style {
588 Style::Solid(color) => Style::Solid(color),
589 Style::Gradient(gradient) => {
590 Style::Gradient(self.transform_gradient(gradient))
591 }
592 }
593 }
594
595 fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
596 match &mut gradient {
597 Gradient::Linear(linear) => {
598 linear.start = self.transform_point(linear.start);
599 linear.end = self.transform_point(linear.end);
600 }
601 }
602
603 gradient
604 }
605
606 fn transform_rectangle(
607 &self,
608 rectangle: Rectangle,
609 ) -> (Rectangle, Radians) {
610 let top_left = self.transform_point(rectangle.position());
611 let top_right = self.transform_point(
612 rectangle.position() + Vector::new(rectangle.width, 0.0),
613 );
614 let bottom_left = self.transform_point(
615 rectangle.position() + Vector::new(0.0, rectangle.height),
616 );
617
618 Rectangle::with_vertices(top_left, top_right, bottom_left)
619 }
620}
621struct GradientVertex2DBuilder {
622 gradient: gradient::Packed,
623}
624
625impl tessellation::FillVertexConstructor<mesh::GradientVertex2D>
626 for GradientVertex2DBuilder
627{
628 fn new_vertex(
629 &mut self,
630 vertex: tessellation::FillVertex<'_>,
631 ) -> mesh::GradientVertex2D {
632 let position = vertex.position();
633
634 mesh::GradientVertex2D {
635 position: [position.x, position.y],
636 gradient: self.gradient,
637 }
638 }
639}
640
641impl tessellation::StrokeVertexConstructor<mesh::GradientVertex2D>
642 for GradientVertex2DBuilder
643{
644 fn new_vertex(
645 &mut self,
646 vertex: tessellation::StrokeVertex<'_, '_>,
647 ) -> mesh::GradientVertex2D {
648 let position = vertex.position();
649
650 mesh::GradientVertex2D {
651 position: [position.x, position.y],
652 gradient: self.gradient,
653 }
654 }
655}
656
657struct TriangleVertex2DBuilder(color::Packed);
658
659impl tessellation::FillVertexConstructor<mesh::SolidVertex2D>
660 for TriangleVertex2DBuilder
661{
662 fn new_vertex(
663 &mut self,
664 vertex: tessellation::FillVertex<'_>,
665 ) -> mesh::SolidVertex2D {
666 let position = vertex.position();
667
668 mesh::SolidVertex2D {
669 position: [position.x, position.y],
670 color: self.0,
671 }
672 }
673}
674
675impl tessellation::StrokeVertexConstructor<mesh::SolidVertex2D>
676 for TriangleVertex2DBuilder
677{
678 fn new_vertex(
679 &mut self,
680 vertex: tessellation::StrokeVertex<'_, '_>,
681 ) -> mesh::SolidVertex2D {
682 let position = vertex.position();
683
684 mesh::SolidVertex2D {
685 position: [position.x, position.y],
686 color: self.0,
687 }
688 }
689}
690
691fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin {
692 match line_join {
693 LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
694 LineJoin::Round => lyon::tessellation::LineJoin::Round,
695 LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
696 }
697}
698
699fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap {
700 match line_cap {
701 LineCap::Butt => lyon::tessellation::LineCap::Butt,
702 LineCap::Square => lyon::tessellation::LineCap::Square,
703 LineCap::Round => lyon::tessellation::LineCap::Round,
704 }
705}
706
707fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule {
708 match rule {
709 fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero,
710 fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
711 }
712}
713
714pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
715 use lyon::algorithms::walk::{
716 walk_along_path, RepeatedPattern, WalkerEvent,
717 };
718 use lyon::path::iterator::PathIterator;
719
720 Path::new(|builder| {
721 let segments_odd = (line_dash.segments.len() % 2 == 1)
722 .then(|| [line_dash.segments, line_dash.segments].concat());
723
724 let mut draw_line = false;
725
726 walk_along_path(
727 path.raw().iter().flattened(
728 lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
729 ),
730 0.0,
731 lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
732 &mut RepeatedPattern {
733 callback: |event: WalkerEvent<'_>| {
734 let point = Point {
735 x: event.position.x,
736 y: event.position.y,
737 };
738
739 if draw_line {
740 builder.line_to(point);
741 } else {
742 builder.move_to(point);
743 }
744
745 draw_line = !draw_line;
746
747 true
748 },
749 index: line_dash.offset,
750 intervals: segments_odd
751 .as_deref()
752 .unwrap_or(line_dash.segments),
753 },
754 );
755 })
756}