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