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