From fb1deaabe55f83eed81fd89eca7fe739bebefa3c Mon Sep 17 00:00:00 2001 From: Joscha Date: Thu, 7 Mar 2024 14:29:20 +0100 Subject: [PATCH] Render tree of Widgets as RgbImage --- showbits-common/src/lib.rs | 3 +- showbits-common/src/rect.rs | 6 +-- showbits-common/src/render.rs | 79 +++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 showbits-common/src/render.rs diff --git a/showbits-common/src/lib.rs b/showbits-common/src/lib.rs index fefc724..7cca6f2 100644 --- a/showbits-common/src/lib.rs +++ b/showbits-common/src/lib.rs @@ -1,7 +1,8 @@ -pub use crate::{node::*, rect::*, vec2::*, view::*, widget::*}; +pub use crate::{node::*, rect::*, render::*, vec2::*, view::*, widget::*}; mod node; mod rect; +mod render; mod vec2; mod view; mod widget; diff --git a/showbits-common/src/rect.rs b/showbits-common/src/rect.rs index 798bfa0..5d29d4f 100644 --- a/showbits-common/src/rect.rs +++ b/showbits-common/src/rect.rs @@ -56,19 +56,19 @@ impl Rect { } /// Construct a rectangle from its north-east corner and size. - pub fn from_corner_ne(ne: Vec2, size: Vec2) -> Self { + pub fn from_ne(ne: Vec2, size: Vec2) -> Self { let sw = ne + (size - 1).neg_x(); Self::from_ne_sw(ne, sw) } /// Construct a rectangle from its south-west corner and size. - pub fn from_corner_sw(sw: Vec2, size: Vec2) -> Self { + pub fn from_sw(sw: Vec2, size: Vec2) -> Self { let ne = sw + (size - 1).neg_y(); Self::from_ne_sw(ne, sw) } /// Construct a rectangle from its south-east corner and size. - pub fn from_corner_se(se: Vec2, size: Vec2) -> Self { + pub fn from_se(se: Vec2, size: Vec2) -> Self { let nw = se - (size - 1); Self::from_nw_se(nw, se) } diff --git a/showbits-common/src/render.rs b/showbits-common/src/render.rs new file mode 100644 index 0000000..af30def --- /dev/null +++ b/showbits-common/src/render.rs @@ -0,0 +1,79 @@ +use image::RgbImage; +use taffy::{AvailableSpace, NodeId, Point, Size, TaffyResult, TaffyTree}; + +use crate::{Rect, Vec2, View, Widget}; + +fn point_to_vec2(point: Point) -> Vec2 { + Vec2::new(point.x as i32, point.y as i32) +} + +fn size_to_vec2(size: Size) -> Vec2 { + Vec2::new(size.width as i32, size.height as i32) +} + +fn layout( + tree: &mut TaffyTree>, + root: NodeId, + available: Size, +) -> TaffyResult<()> { + tree.enable_rounding(); // Just to make sure + tree.compute_layout_with_measure(root, available, |known, available, _node, context| { + if let Some(widget) = context { + widget.size(known, available) + } else { + Size::ZERO + } + }) +} + +fn render_to_view( + tree: &mut TaffyTree>, + node: NodeId, + view: &mut View<'_>, +) -> anyhow::Result<()> { + let layout = tree.layout(node)?; + let area = Rect::from_nw(point_to_vec2(layout.location), size_to_vec2(layout.size)); + let mut view = view.dup().zoom(area); + + // First pass + if let Some(ctx) = tree.get_node_context_mut(node) { + ctx.draw_below(&mut view)?; + } + + // Render children + let mut children = vec![]; + for child in tree.children(node)? { + let order = tree.layout(child)?.order; + children.push((order, child)); + } + children.sort_unstable_by_key(|(order, _)| *order); + for (_, child) in children { + render_to_view(tree, child, &mut view)?; + } + + // Second pass + if let Some(ctx) = tree.get_node_context_mut(node) { + ctx.draw_above(&mut view)?; + } + + Ok(()) +} + +pub fn render( + tree: &mut TaffyTree>, + root: NodeId, + available: Size, +) -> anyhow::Result { + layout(tree, root, available)?; + + let layout = tree.layout(root)?; + assert_eq!(layout.location.x, 0.0); + assert_eq!(layout.location.y, 0.0); + // TODO Check how taffy treats the border? + + let (width, height) = size_to_vec2(layout.size).to_u32(); + let mut image = RgbImage::new(width, height); + render_to_view(tree, root, &mut View::new(&mut image))?; + + Ok(image) +}