1use super::LayerGroup;
6use crate::color::Color;
7use crate::drawing::{
8 linebreak_text, measure_text_size, path_for_rectangle, path_for_rounded_rectangle, FontFace,
9 GlyphMap, Text,
10};
11use crate::render::rive::{load_rive, RenderCache as RiveRenderCache};
12use crate::render::{
13 BlendMode, Context as RenderContext, Fill, FillRule, Layer, Raster, Shed, Style,
14};
15use crate::scene::scene::SceneOrder;
16use crate::{Coord, IdFromRaw, IdGenerator2, Point, Rect, Size, ViewAssistantContext};
17use anyhow::Error;
18use euclid::default::Transform2D;
19use euclid::{size2, vec2};
20use fuchsia_trace::duration;
21use rive_rs::{self as rive};
22use std::any::Any;
23use std::collections::BTreeMap;
24use std::path::PathBuf;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct FacetId(u64);
29
30pub(crate) struct FacetEntry {
31 pub facet: FacetPtr,
32 pub location: Point,
33 pub size: Size,
34}
35
36pub(crate) type FacetMap = BTreeMap<FacetId, FacetEntry>;
37
38impl FacetId {
39 pub(crate) fn new() -> Self {
40 IdGenerator2::<FacetId>::next().expect("facet ID")
41 }
42}
43
44impl IdFromRaw for FacetId {
45 fn from_raw(id: u64) -> FacetId {
46 FacetId(id)
47 }
48}
49
50#[derive(Debug)]
51pub struct SetColorMessage {
53 pub color: Color,
55}
56
57#[derive(Debug)]
58pub struct SetBackgroundColorMessage {
60 pub color: Option<Color>,
62}
63
64pub struct SetTextMessage {
66 pub text: String,
68}
69
70pub struct SetSizeMessage {
72 pub size: Size,
74}
75
76pub struct SetCornerRadiusMessage {
78 pub corner_radius: Option<Coord>,
80}
81
82pub trait Facet {
85 #[allow(unused)]
86 fn associate_facet_id(&mut self, facet_id: FacetId) {}
89
90 fn update_layers(
95 &mut self,
96 size: Size,
97 layer_group: &mut dyn LayerGroup,
98 render_context: &mut RenderContext,
99 view_context: &ViewAssistantContext,
100 ) -> Result<(), Error>;
101
102 fn handle_message(&mut self, msg: Box<dyn Any>) {
104 println!("Unhandled message {:#?}", msg);
105 }
106
107 fn calculate_size(&self, available: Size) -> Size;
109}
110
111pub type FacetPtr = Box<dyn Facet>;
113
114pub struct RectangleFacet {
116 size: Size,
117 color: Color,
118 corner_radius: Option<Coord>,
119 raster: Option<Raster>,
120}
121
122impl RectangleFacet {
123 pub fn new(size: Size, color: Color) -> FacetPtr {
125 Box::new(Self { size, color, corner_radius: None, raster: None })
126 }
127
128 pub fn new_rounded(size: Size, corner: Coord, color: Color) -> FacetPtr {
130 Box::new(Self { size, color, corner_radius: Some(corner), raster: None })
131 }
132
133 pub fn h_line(width: Coord, thickness: Coord, color: Color) -> FacetPtr {
135 Self::new(size2(width, thickness), color)
136 }
137
138 pub fn v_line(height: Coord, thickness: Coord, color: Color) -> FacetPtr {
140 Self::new(size2(thickness, height), color)
141 }
142}
143
144impl Facet for RectangleFacet {
145 fn update_layers(
146 &mut self,
147 _size: Size,
148 layer_group: &mut dyn LayerGroup,
149 render_context: &mut RenderContext,
150 _view_context: &ViewAssistantContext,
151 ) -> Result<(), Error> {
152 let rectangle_raster = self.raster.take().unwrap_or_else(|| {
153 let rectangle_path = if let Some(corner) = self.corner_radius.as_ref() {
154 path_for_rounded_rectangle(&Rect::from_size(self.size), *corner, render_context)
155 } else {
156 path_for_rectangle(&Rect::from_size(self.size), render_context)
157 };
158 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
159 raster_builder.add(&rectangle_path, None);
160 raster_builder.build()
161 });
162 let raster = rectangle_raster.clone();
163 self.raster = Some(rectangle_raster);
164 layer_group.insert(
165 SceneOrder::default(),
166 Layer {
167 raster,
168 clip: None,
169 style: Style {
170 fill_rule: FillRule::NonZero,
171 fill: Fill::Solid(self.color),
172 blend_mode: BlendMode::Over,
173 },
174 },
175 );
176 Ok(())
177 }
178
179 fn handle_message(&mut self, msg: Box<dyn Any>) {
180 if let Some(set_color) = msg.downcast_ref::<SetColorMessage>() {
181 self.color = set_color.color;
182 } else if let Some(set_size) = msg.downcast_ref::<SetSizeMessage>() {
183 self.size = set_size.size;
184 self.raster = None;
185 } else if let Some(set_corner) = msg.downcast_ref::<SetCornerRadiusMessage>() {
186 self.corner_radius = set_corner.corner_radius;
187 self.raster = None;
188 }
189 }
190
191 fn calculate_size(&self, _available: Size) -> Size {
192 self.size
193 }
194}
195
196#[derive(Clone, Copy)]
198pub enum TextHorizontalAlignment {
199 Left,
202 Right,
205 Center,
208}
209
210impl Default for TextHorizontalAlignment {
211 fn default() -> Self {
212 Self::Left
213 }
214}
215
216#[derive(Clone, Copy)]
218pub enum TextVerticalAlignment {
219 Top,
222 Bottom,
225 Center,
228}
229
230impl Default for TextVerticalAlignment {
231 fn default() -> Self {
232 Self::Top
233 }
234}
235
236#[derive(Default, Clone, Copy)]
237pub struct TextFacetOptions {
239 pub horizontal_alignment: TextHorizontalAlignment,
241 pub vertical_alignment: TextVerticalAlignment,
243 pub visual: bool,
245 pub color: Color,
247 pub background_color: Option<Color>,
249 pub max_width: Option<f32>,
252}
253
254pub struct TextFacet {
256 face: FontFace,
257 lines: Vec<String>,
258 font_size: f32,
259 size: Size,
260 options: TextFacetOptions,
261 rendered_text: Option<Text>,
262 rendered_background: Option<Raster>,
263 rendered_background_size: Option<Size>,
264 glyphs: GlyphMap,
265}
266
267impl TextFacet {
268 fn set_text(&mut self, text: &str) {
269 let lines = Self::wrap_lines(&self.face, self.font_size, text, &self.options.max_width);
270 let size = Self::calculate_size(&self.face, &lines, self.font_size, self.options.visual);
271 self.lines = lines;
272 self.size = size;
273 self.rendered_background_size = None;
274 self.rendered_background = None;
275 self.rendered_text = None;
276 }
277
278 fn wrap_lines(face: &FontFace, size: f32, text: &str, max_width: &Option<f32>) -> Vec<String> {
279 let lines: Vec<String> = text.lines().map(|line| String::from(line)).collect();
280 if let Some(max_width) = max_width {
281 let wrapped_lines = lines
282 .iter()
283 .map(|line| linebreak_text(face, size, line, *max_width))
284 .flatten()
285 .collect();
286 wrapped_lines
287 } else {
288 lines
289 }
290 }
291
292 fn calculate_size(face: &FontFace, lines: &[String], font_size: f32, visual: bool) -> Size {
293 let ascent = face.ascent(font_size);
294 let descent = face.descent(font_size);
295 if lines.len() > 1 {
296 let measured_width = lines
297 .iter()
298 .map(|line| measure_text_size(&face, font_size, line, false).width)
299 .reduce(f32::max)
300 .unwrap();
301 size2(measured_width, lines.len() as f32 * (ascent - descent))
302 } else {
303 if lines.len() == 0 {
304 Size::zero()
305 } else {
306 if visual {
307 measure_text_size(&face, font_size, &lines[0], true)
308 } else {
309 let measured_size = measure_text_size(&face, font_size, &lines[0], false);
310 let new_size = size2(measured_size.width, ascent - descent);
311 new_size
312 }
313 }
314 }
315 }
316
317 pub fn new(face: FontFace, text: &str, size: f32) -> FacetPtr {
319 Self::with_options(face, text, size, TextFacetOptions::default())
320 }
321
322 pub fn with_options(
324 face: FontFace,
325 text: &str,
326 font_size: f32,
327 options: TextFacetOptions,
328 ) -> FacetPtr {
329 let lines = Self::wrap_lines(&face, font_size, text, &options.max_width);
330 let size = Self::calculate_size(&face, &lines, font_size, options.visual);
331
332 Box::new(Self {
333 face,
334 lines,
335 font_size,
336 size,
337 options,
338 rendered_text: None,
339 rendered_background: None,
340 rendered_background_size: None,
341 glyphs: GlyphMap::new(),
342 })
343 }
344}
345
346impl Facet for TextFacet {
347 fn update_layers(
348 &mut self,
349 size: Size,
350 layer_group: &mut dyn LayerGroup,
351 render_context: &mut RenderContext,
352 _view_context: &ViewAssistantContext,
353 ) -> Result<(), Error> {
354 const BACKGROUND_LAYER_ORDER: SceneOrder = SceneOrder::from_u16(0);
355 const TEXT_LAYER_ORDER: SceneOrder = SceneOrder::from_u16(1);
356
357 if self.rendered_background_size != Some(size) {
358 self.rendered_background = None;
359 }
360
361 let rendered_text = self.rendered_text.take().unwrap_or_else(|| {
362 Text::new_with_lines(
363 render_context,
364 &self.lines,
365 self.font_size,
366 &self.face,
367 &mut self.glyphs,
368 )
369 });
370 let mut x = match self.options.horizontal_alignment {
371 TextHorizontalAlignment::Left => 0.0,
372 TextHorizontalAlignment::Center => (size.width - self.size.width) / 2.0,
373 TextHorizontalAlignment::Right => size.width - self.size.width,
374 };
375 if self.options.visual {
376 x -= rendered_text.bounding_box.origin.x;
377 }
378 let mut y = match self.options.vertical_alignment {
379 TextVerticalAlignment::Top => 0.0,
380 TextVerticalAlignment::Bottom => size.height - self.size.height,
381 TextVerticalAlignment::Center => (size.height - self.size.height) / 2.0,
382 };
383 if self.options.visual {
384 y -= rendered_text.bounding_box.origin.y;
385 }
386 let translation = vec2(x, y);
387 let raster = rendered_text.raster.clone().translate(translation.to_i32());
388 self.rendered_text = Some(rendered_text);
389
390 layer_group.insert(
391 TEXT_LAYER_ORDER,
392 Layer {
393 raster,
394 clip: None,
395 style: Style {
396 fill_rule: FillRule::NonZero,
397 fill: Fill::Solid(self.options.color),
398 blend_mode: BlendMode::Over,
399 },
400 },
401 );
402
403 if let Some(background_color) = self.options.background_color.as_ref() {
404 let rendered_background = self.rendered_background.take().unwrap_or_else(|| {
405 let bg_bounds = Rect::from_size(size);
406 let rect_path = path_for_rectangle(&bg_bounds, render_context);
407 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
408 raster_builder.add(&rect_path, None);
409 raster_builder.build()
410 });
411 let raster = rendered_background.clone();
412 self.rendered_background = Some(rendered_background);
413 self.rendered_background_size = Some(size);
414 layer_group.insert(
415 BACKGROUND_LAYER_ORDER,
416 Layer {
417 raster,
418 clip: None,
419 style: Style {
420 fill_rule: FillRule::NonZero,
421 fill: Fill::Solid(*background_color),
422 blend_mode: BlendMode::Over,
423 },
424 },
425 );
426 } else {
427 layer_group.remove(BACKGROUND_LAYER_ORDER)
428 }
429 Ok(())
430 }
431
432 fn handle_message(&mut self, msg: Box<dyn Any>) {
433 if let Some(set_text) = msg.downcast_ref::<SetTextMessage>() {
434 self.set_text(&set_text.text);
435 } else if let Some(set_color) = msg.downcast_ref::<SetColorMessage>() {
436 self.options.color = set_color.color;
437 } else if let Some(set_background_color) = msg.downcast_ref::<SetBackgroundColorMessage>() {
438 self.options.background_color = set_background_color.color;
439 self.rendered_background = None;
440 }
441 }
442
443 fn calculate_size(&self, _available: Size) -> Size {
444 self.size
445 }
446}
447
448pub struct RasterFacet {
450 raster: Raster,
451 style: Style,
452 size: Size,
453}
454
455impl RasterFacet {
456 pub fn new(raster: Raster, style: Style, size: Size) -> Self {
458 Self { raster, style, size }
459 }
460}
461
462impl Facet for RasterFacet {
463 fn update_layers(
464 &mut self,
465 _size: Size,
466 layer_group: &mut dyn LayerGroup,
467 _render_context: &mut RenderContext,
468 _view_context: &ViewAssistantContext,
469 ) -> Result<(), Error> {
470 layer_group.insert(
471 SceneOrder::default(),
472 Layer { raster: self.raster.clone(), clip: None, style: self.style.clone() },
473 );
474 Ok(())
475 }
476
477 fn calculate_size(&self, _available: Size) -> Size {
478 self.size
479 }
480}
481
482pub struct ShedFacet {
484 path: PathBuf,
485 size: Size,
486 rasters: Option<Vec<(Raster, Style)>>,
487}
488
489impl ShedFacet {
490 pub fn new(path: PathBuf, size: Size) -> Self {
492 Self { path, size, rasters: None }
493 }
494}
495
496impl Facet for ShedFacet {
497 fn update_layers(
498 &mut self,
499 _size: Size,
500 layer_group: &mut dyn LayerGroup,
501 render_context: &mut RenderContext,
502 _view_context: &ViewAssistantContext,
503 ) -> Result<(), Error> {
504 let rasters = self.rasters.take().unwrap_or_else(|| {
505 if let Some(shed) = Shed::open(&self.path).ok() {
506 let shed_size = shed.size();
507 let scale_factor: Size =
508 size2(self.size.width / shed_size.width, self.size.height / shed_size.height);
509 let transform =
510 Transform2D::translation(-shed_size.width / 2.0, -shed_size.height / 2.0)
511 .then_scale(scale_factor.width, scale_factor.height);
512
513 shed.rasters(render_context, Some(&transform))
514 } else {
515 let placeholder_rect =
516 Rect::from_size(self.size).translate(self.size.to_vector() / -2.0);
517 let rect_path = path_for_rectangle(&placeholder_rect, render_context);
518 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
519 raster_builder.add(&rect_path, None);
520 let raster = raster_builder.build();
521 vec![(
522 raster,
523 Style {
524 fill_rule: FillRule::NonZero,
525 fill: Fill::Solid(Color::red()),
526 blend_mode: BlendMode::Over,
527 },
528 )]
529 }
530 });
531 for (i, (raster, style)) in rasters.iter().rev().enumerate() {
532 layer_group.insert(
533 SceneOrder::try_from(i).unwrap_or_else(|e| panic!("{}", e)),
534 Layer { raster: raster.clone(), clip: None, style: style.clone() },
535 );
536 }
537 self.rasters = Some(rasters);
538 Ok(())
539 }
540
541 fn calculate_size(&self, _available: Size) -> Size {
542 self.size
543 }
544}
545
546pub struct SpacingFacet {
548 size: Size,
549}
550
551impl SpacingFacet {
552 pub fn new(size: Size) -> Self {
554 Self { size }
555 }
556}
557
558impl Facet for SpacingFacet {
559 fn update_layers(
560 &mut self,
561 _size: Size,
562 _layer_group: &mut dyn LayerGroup,
563 _render_context: &mut RenderContext,
564 _view_context: &ViewAssistantContext,
565 ) -> Result<(), Error> {
566 Ok(())
567 }
568
569 fn calculate_size(&self, _available: Size) -> Size {
570 self.size
571 }
572}
573
574pub struct RiveFacet {
576 size: Size,
577 artboard: rive::Object<rive::Artboard>,
578 render_cache: RiveRenderCache,
579}
580
581impl RiveFacet {
582 pub fn new(size: Size, artboard: rive::Object<rive::Artboard>) -> Self {
584 Self { size, artboard, render_cache: RiveRenderCache::new() }
585 }
586
587 pub fn new_from_file(
590 size: Size,
591 file: &rive::File,
592 artboard_name: Option<&str>,
593 ) -> Result<Self, Error> {
594 let artboard = if let Some(artboard_name) = artboard_name {
595 file.get_artboard(artboard_name).ok_or_else(|| {
596 anyhow::anyhow!("artboard {} not found in rive file {:?}", artboard_name, file)
597 })?
598 } else {
599 file.artboard().ok_or_else(|| anyhow::anyhow!("no artboard in rive file {:?}", file))?
600 };
601 let facet = RiveFacet::new(size, artboard.clone());
602 let artboard_ref = artboard.as_ref();
603 artboard_ref.advance(0.0);
604 Ok(facet)
605 }
606
607 pub fn new_from_path<P: AsRef<std::path::Path> + std::fmt::Debug>(
610 size: Size,
611 path: P,
612 artboard_name: Option<&str>,
613 ) -> Result<Self, Error> {
614 let file = load_rive(&path)?;
615 Self::new_from_file(size, &file, artboard_name)
616 }
617}
618
619impl Facet for RiveFacet {
620 fn update_layers(
621 &mut self,
622 _size: Size,
623 layer_group: &mut dyn LayerGroup,
624 render_context: &mut RenderContext,
625 _view_context: &ViewAssistantContext,
626 ) -> Result<(), Error> {
627 duration!(c"gfx", c"render::RiveFacet::update_layers");
628
629 let artboard_ref = self.artboard.as_ref();
630 let width = self.size.width as f32;
631 let height = self.size.height as f32;
632 self.render_cache.with_renderer(render_context, |renderer| {
633 artboard_ref.draw(
634 renderer,
635 rive::layout::align(
636 rive::layout::Fit::Contain,
637 rive::layout::Alignment::center(),
638 rive::math::Aabb::new(0.0, 0.0, width, height),
639 artboard_ref.bounds(),
640 ),
641 );
642 });
643
644 let layers = self.render_cache.layers.drain(..).filter(|Layer { style, .. }|
645 match &style.fill {
648 Fill::Solid(color) => color.a != 0 || style.blend_mode != BlendMode::Over,
649 _ => true
650 });
651
652 layer_group.clear();
653 for (i, layer) in layers.enumerate() {
654 layer_group.insert(SceneOrder::try_from(i).unwrap_or_else(|e| panic!("{}", e)), layer);
655 }
656
657 Ok(())
658 }
659
660 fn calculate_size(&self, _available: Size) -> Size {
661 self.size
662 }
663
664 fn handle_message(&mut self, msg: Box<dyn Any>) {
665 if let Some(set_size) = msg.downcast_ref::<SetSizeMessage>() {
666 self.size = set_size.size;
667 }
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use crate::drawing::FontFace;
674 use crate::scene::facets::{TextFacet, TextFacetOptions};
675 use once_cell::sync::Lazy;
676
677 static FONT_DATA: &'static [u8] = include_bytes!(
678 "../../../../../../prebuilt/third_party/fonts/robotoslab/RobotoSlab-Regular.ttf"
679 );
680 static FONT_FACE: Lazy<FontFace> =
681 Lazy::new(|| FontFace::new(&FONT_DATA).expect("Failed to create font"));
682
683 const SAMPLE_TEXT: &'static str = "Lorem ipsum dolor sit amet, consectetur \
684 adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna \
685 aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris \
686 nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit \
687 in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint \
688 occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim \
689 id est laborum.";
690
691 #[test]
692 fn test_text_facet() {
693 let _ = TextFacet::with_options(FONT_FACE.clone(), "", 32.0, TextFacetOptions::default());
694 let _ = TextFacet::with_options(
695 FONT_FACE.clone(),
696 SAMPLE_TEXT,
697 32.0,
698 TextFacetOptions::default(),
699 );
700 let max_width_options =
701 TextFacetOptions { max_width: Some(200.0), ..TextFacetOptions::default() };
702 let _ = TextFacet::with_options(FONT_FACE.clone(), "", 32.0, max_width_options);
703 let _ = TextFacet::with_options(FONT_FACE.clone(), SAMPLE_TEXT, 32.0, max_width_options);
704 }
705}