From eb04b3fb509eabbb0a71ba1e3af0be2cde40c2c6 Mon Sep 17 00:00:00 2001 From: Joscha Date: Wed, 6 Mar 2024 00:36:18 +0100 Subject: [PATCH] Rip out buffer and widgets I've decided to use the "taffy" crate for layouting. To make the API somewhat bearable to use, I've decided to perform all rendering in a good ol' rgb image buffer (in this case, an ImageBuffer from the "image" crate). The pixels will be converted to their final representation just before being sent to whatever device will be displaying them. I've removed the buffer and the color traits because they're no longer necessary. I've also removed the existing widgets because the widget system will have to be redesigned to work well with taffy. --- Cargo.lock | 1 + Cargo.toml | 1 + showbits-common/Cargo.toml | 1 + showbits-common/src/buffer.rs | 50 --------- showbits-common/src/color.rs | 11 -- showbits-common/src/lib.rs | 6 +- showbits-common/src/vec2.rs | 12 +++ showbits-common/src/view.rs | 40 +++---- showbits-common/src/widget.rs | 122 ---------------------- showbits-common/src/widgets.rs | 5 - showbits-common/src/widgets/background.rs | 34 ------ showbits-common/src/widgets/empty.rs | 23 ---- showbits-thermal-printer/src/color.rs | 32 ------ 13 files changed, 38 insertions(+), 300 deletions(-) delete mode 100644 showbits-common/src/buffer.rs delete mode 100644 showbits-common/src/color.rs delete mode 100644 showbits-common/src/widget.rs delete mode 100644 showbits-common/src/widgets.rs delete mode 100644 showbits-common/src/widgets/background.rs delete mode 100644 showbits-common/src/widgets/empty.rs delete mode 100644 showbits-thermal-printer/src/color.rs diff --git a/Cargo.lock b/Cargo.lock index 9190c60..8d65c3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,6 +1025,7 @@ name = "showbits-common" version = "0.0.0" dependencies = [ "anyhow", + "image", "palette", ] diff --git a/Cargo.toml b/Cargo.toml index 19591e4..fa02f40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [workspace.dependencies] anyhow = "1.0.80" +image = "0.24.9" palette = "0.7.5" showbits-common.path = "./showbits-common" diff --git a/showbits-common/Cargo.toml b/showbits-common/Cargo.toml index 06a1b6f..1f3e630 100644 --- a/showbits-common/Cargo.toml +++ b/showbits-common/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] anyhow.workspace = true +image.workspace = true palette.workspace = true [lints] diff --git a/showbits-common/src/buffer.rs b/showbits-common/src/buffer.rs deleted file mode 100644 index 72944f2..0000000 --- a/showbits-common/src/buffer.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::Vec2; - -#[derive(Clone)] -pub struct Buffer { - size: Vec2, - data: Vec, -} - -impl Buffer { - pub fn new(size: Vec2, color: C) -> Self - where - C: Copy, - { - assert!(size.x >= 0); - assert!(size.y >= 0); - - let len = (size.x * size.y) as usize; - let data = vec![color; len]; - - Self { size, data } - } - - pub fn size(&self) -> Vec2 { - self.size - } - - fn index(&self, pos: Vec2) -> Option { - let in_bounds_x = pos.x >= 0 || pos.x < self.size.x; - let in_bounds_y = pos.y >= 0 || pos.y < self.size.y; - let in_bounds = in_bounds_x && in_bounds_y; - - if in_bounds { - Some((pos.y * self.size.x + pos.x) as usize) - } else { - None - } - } - - pub fn at(&self, pos: Vec2) -> Option<&C> { - let index = self.index(pos)?; - let pixel = self.data.get(index)?; - Some(pixel) - } - - pub fn at_mut(&mut self, pos: Vec2) -> Option<&mut C> { - let index = self.index(pos)?; - let pixel = self.data.get_mut(index)?; - Some(pixel) - } -} diff --git a/showbits-common/src/color.rs b/showbits-common/src/color.rs deleted file mode 100644 index e62a83c..0000000 --- a/showbits-common/src/color.rs +++ /dev/null @@ -1,11 +0,0 @@ -use palette::Srgb; - -pub trait Color: Copy { - /// Convert to an sRGB color. - /// - /// Useful for debugging or dithering. - fn to_srgb(self) -> Srgb; - - /// Combine two colors by putting one "over" the other. - fn over(self, below: Self) -> Self; -} diff --git a/showbits-common/src/lib.rs b/showbits-common/src/lib.rs index ef8c027..fde4c1a 100644 --- a/showbits-common/src/lib.rs +++ b/showbits-common/src/lib.rs @@ -1,9 +1,5 @@ -pub use crate::{buffer::*, color::*, rect::*, vec2::*, view::*, widget::*}; +pub use crate::{rect::*, vec2::*, view::*}; -mod buffer; -mod color; mod rect; mod vec2; mod view; -mod widget; -pub mod widgets; diff --git a/showbits-common/src/vec2.rs b/showbits-common/src/vec2.rs index 26ca229..4e69c1d 100644 --- a/showbits-common/src/vec2.rs +++ b/showbits-common/src/vec2.rs @@ -20,6 +20,18 @@ impl Vec2 { Self { x, y } } + pub fn from_u32(x: u32, y: u32) -> Self { + let x: i32 = x.try_into().expect("x too large"); + let y: i32 = y.try_into().expect("y too large"); + Self::new(x, y) + } + + pub fn to_u32(self) -> (u32, u32) { + let x: u32 = self.x.try_into().expect("x too small"); + let y: u32 = self.y.try_into().expect("y too small"); + (x, y) + } + /// The vector pointing from `self` to `other`. /// /// ``` diff --git a/showbits-common/src/view.rs b/showbits-common/src/view.rs index 0e5e1c0..38dd52c 100644 --- a/showbits-common/src/view.rs +++ b/showbits-common/src/view.rs @@ -1,21 +1,21 @@ -use crate::{Buffer, Color, Rect, Vec2}; +use image::RgbImage; +use palette::Srgb; -// TODO Add Orientation (from inkfo) +use crate::{Rect, Vec2}; -pub struct View<'a, C> { +pub struct View<'a> { area: Rect, - buffer: &'a mut Buffer, + buffer: &'a mut RgbImage, } -impl<'a, C> View<'a, C> { - pub fn new(buffer: &'a mut Buffer) -> Self { - Self { - area: Rect::from_nw(Vec2::ZERO, buffer.size()), - buffer, - } +impl<'a> View<'a> { + pub fn new(buffer: &'a mut RgbImage) -> Self { + let size = Vec2::from_u32(buffer.width(), buffer.height()); + let area = Rect::from_nw(Vec2::ZERO, size); + Self { area, buffer } } - pub fn dup(&mut self) -> View<'_, C> { + pub fn dup(&mut self) -> View<'_> { View { area: self.area, buffer: self.buffer, @@ -35,15 +35,19 @@ impl<'a, C> View<'a, C> { pos + self.area.corner_nw() } - pub fn at(&self, pos: Vec2) -> Option<&C> { - self.buffer.at(self.pos_to_buffer_pos(pos)) + pub fn get(&self, pos: Vec2) -> Option { + let (x, y) = self.pos_to_buffer_pos(pos).to_u32(); + let pixel = self.buffer.get_pixel_checked(x, y)?; + let [r, g, b] = pixel.0; + let color = Srgb::new(r, g, b); + Some(color.into_format()) } -} -impl View<'_, C> { - pub fn set(&mut self, pos: Vec2, color: C) { - if let Some(pixel) = self.buffer.at_mut(self.pos_to_buffer_pos(pos)) { - *pixel = color.over(*pixel); + pub fn set(&mut self, pos: Vec2, color: Srgb) { + let (x, y) = self.pos_to_buffer_pos(pos).to_u32(); + if let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) { + let color = color.into_format::(); + pixel.0 = [color.red, color.green, color.blue]; } } } diff --git a/showbits-common/src/widget.rs b/showbits-common/src/widget.rs deleted file mode 100644 index bf0ac35..0000000 --- a/showbits-common/src/widget.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::{widgets::Background, Rect, Vec2, View}; - -pub trait Widget { - /// Size that the widget wants to be, given the width and height - /// constraints. - fn size(&self, max_width: Option, max_height: Option) -> Vec2; - - /// Recalculate the area (size and position) of all inner widgets given the - /// widget's own area. - /// - /// # Implement if... - /// - /// - There are inner widgets - fn set_area(&mut self, _area: Rect) {} - - /// Perform any updates (e.g. fetching map tiles) that require the widget's - /// area and may fail. - /// - /// # Implement if... - /// - /// - There are inner widgets - /// - This widget needs to perform updates - fn update(&mut self, _area: Rect) -> anyhow::Result<()> { - Ok(()) - } - - fn draw(self, view: &mut View<'_, C>); -} - -/// Extension trait for [`Widget`]s. -pub trait WidgetExt { - fn boxed(self) -> BoxedWidget; -} - -impl WidgetExt for W -where - W: Widget + 'static, -{ - fn boxed(self) -> BoxedWidget { - BoxedWidget::new(self) - } -} - -/// Wrapper trait around [`Widget`] that turns `Box` into a `Self` to get -/// around the "size cannot be statically determined" error with the naïve -/// approach of `Box`. -trait WidgetWrapper { - fn size(&self, max_width: Option, max_height: Option) -> Vec2; - fn set_area(&mut self, _area: Rect); - fn update(&mut self, _area: Rect) -> anyhow::Result<()>; - fn draw(self: Box, view: &mut View<'_, C>); -} - -impl> WidgetWrapper for W { - // These implementations explicitly use `Widget::*` to call the widget - // methods even though they have priority over the `WidgetWrapper::*` - // methods for some reason. Just a bit of rustc magic, I guess. - - fn size(&self, max_width: Option, max_height: Option) -> Vec2 { - Widget::size(self, max_width, max_height) - } - - fn set_area(&mut self, area: Rect) { - // Widget::set_area(self, area); - self.set_area(area); - } - - fn update(&mut self, area: Rect) -> anyhow::Result<()> { - Widget::update(self, area) - } - - fn draw(self: Box, view: &mut View<'_, C>) { - Widget::draw(*self, view); - } -} - -pub struct BoxedWidget { - area: Rect, - widget: Box>, -} - -impl BoxedWidget { - pub fn new(widget: W) -> Self - where - W: Widget + 'static, - { - Self { - area: Rect::ZERO, - widget: Box::new(widget), - } - } - - pub fn area(&self) -> Rect { - self.area - } - - // Widget-like functions - - pub fn size(&self, max_width: Option, max_height: Option) -> Vec2 { - self.widget.size(max_width, max_height) - } - - pub fn set_area(&mut self, area: Rect) { - self.area = area; - self.widget.set_area(area); - } - - pub fn update(&mut self) -> anyhow::Result<()> { - self.widget.update(self.area) - } - - pub fn draw(self, view: &mut View<'_, C>) { - let mut view = view.dup().zoom(self.area); - self.widget.draw(&mut view); - } - - // Widget constructors - - pub fn background(self, color: C) -> Background { - Background::new(self, color) - } -} diff --git a/showbits-common/src/widgets.rs b/showbits-common/src/widgets.rs deleted file mode 100644 index be2843c..0000000 --- a/showbits-common/src/widgets.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use background::*; -pub use empty::*; - -mod background; -mod empty; diff --git a/showbits-common/src/widgets/background.rs b/showbits-common/src/widgets/background.rs deleted file mode 100644 index 47c48d7..0000000 --- a/showbits-common/src/widgets/background.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::{BoxedWidget, Color, Vec2, Widget}; - -pub struct Background { - inner: BoxedWidget, - color: C, -} - -impl Background { - pub fn new(inner: BoxedWidget, color: C) -> Self { - Self { inner, color } - } -} - -impl Widget for Background { - fn size(&self, max_width: Option, max_height: Option) -> crate::Vec2 { - self.inner.size(max_width, max_height) - } - - fn set_area(&mut self, area: crate::Rect) { - self.inner.set_area(area); - } - - fn update(&mut self, _area: crate::Rect) -> anyhow::Result<()> { - Ok(()) - } - - fn draw(self, view: &mut crate::View<'_, C>) { - for y in 0..view.size().y { - for x in 0..view.size().x { - view.set(Vec2::new(x, y), self.color); - } - } - } -} diff --git a/showbits-common/src/widgets/empty.rs b/showbits-common/src/widgets/empty.rs deleted file mode 100644 index d397c65..0000000 --- a/showbits-common/src/widgets/empty.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::{Vec2, View, Widget}; - -pub struct Empty {} - -impl Empty { - pub fn new() -> Self { - Self {} - } -} - -impl Default for Empty { - fn default() -> Self { - Self::new() - } -} - -impl Widget for Empty { - fn size(&self, _max_width: Option, _max_height: Option) -> Vec2 { - Vec2::ZERO - } - - fn draw(self, _view: &mut View<'_, C>) {} -} diff --git a/showbits-thermal-printer/src/color.rs b/showbits-thermal-printer/src/color.rs deleted file mode 100644 index 5183c8b..0000000 --- a/showbits-thermal-printer/src/color.rs +++ /dev/null @@ -1,32 +0,0 @@ -use palette::Srgb; -use showbits_common::Color; - -#[derive(Clone, Copy)] -pub enum PixelBw { - Black, - White, - Transparent, - Invert, -} - -impl Color for PixelBw { - fn to_srgb(self) -> Srgb { - match self { - Self::Black => Srgb::new(0.0, 0.0, 0.0), - Self::White => Srgb::new(1.0, 1.0, 1.0), - _ => Srgb::new(1.0, 0.0, 1.0), - } - } - - fn over(self, other: Self) -> Self { - match (self, other) { - (Self::Black, _) => Self::Black, - (Self::White, _) => Self::White, - (Self::Transparent, p) => p, - (Self::Invert, Self::Black) => Self::White, - (Self::Invert, Self::White) => Self::Black, - (Self::Invert, Self::Invert) => Self::Transparent, - (Self::Invert, Self::Transparent) => Self::Invert, - } - } -}