From 4e99edf6f5fc75dbdcf396a08f382e0368be2f32 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 10 Jul 2021 15:32:26 -0700 Subject: [PATCH] CellAttributes: split color into thin/fat components The common palette indices are in the main cell attributes. Using true color will allocate fat storage. This allows reducing the Cell size from 32 -> 24 across the implementation of storing 10 bpc color (it peaked at 40 in the last couple of commits). --- termwiz/src/cell.rs | 100 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 9 deletions(-) diff --git a/termwiz/src/cell.rs b/termwiz/src/cell.rs index 392eb96c1..246bcf296 100644 --- a/termwiz/src/cell.rs +++ b/termwiz/src/cell.rs @@ -1,5 +1,5 @@ //! Model a cell in the terminal display -use crate::color::ColorAttribute; +use crate::color::{ColorAttribute, PaletteIndex}; pub use crate::escape::osc::Hyperlink; use crate::image::ImageCell; #[cfg(feature = "use_serde")] @@ -8,6 +8,28 @@ use std::mem; use std::sync::Arc; use unicode_width::UnicodeWidthStr; +#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum SmallColor { + Default, + PaletteIndex(PaletteIndex), +} + +impl Default for SmallColor { + fn default() -> Self { + Self::Default + } +} + +impl Into for SmallColor { + fn into(self) -> ColorAttribute { + match self { + Self::Default => ColorAttribute::Default, + Self::PaletteIndex(idx) => ColorAttribute::PaletteIndex(idx), + } + } +} + /// Holds the attributes for a cell. /// Most style attributes are stored internally as part of a bitfield /// to reduce per-cell overhead. @@ -18,9 +40,9 @@ use unicode_width::UnicodeWidthStr; pub struct CellAttributes { attributes: u16, /// The foreground color - foreground: ColorAttribute, + foreground: SmallColor, /// The background color - background: ColorAttribute, + background: SmallColor, /// Relatively rarely used attributes spill over to a heap /// allocated struct in order to keep CellAttributes /// smaller in the common case. @@ -58,6 +80,8 @@ struct FatAttributes { /// The color of the underline. If None, then /// the foreground color is to be used underline_color: ColorAttribute, + foreground: ColorAttribute, + background: ColorAttribute, } /// Define getter and setter for the attributes bitfield. @@ -227,21 +251,75 @@ impl CellAttributes { /// Set the foreground color for the cell to that specified pub fn set_foreground>(&mut self, foreground: C) -> &mut Self { - self.foreground = foreground.into(); + let foreground: ColorAttribute = foreground.into(); + match foreground { + ColorAttribute::Default => { + self.foreground = SmallColor::Default; + if let Some(fat) = self.fat.as_mut() { + fat.foreground = ColorAttribute::Default; + } + self.deallocate_fat_attributes_if_none(); + } + ColorAttribute::PaletteIndex(idx) => { + self.foreground = SmallColor::PaletteIndex(idx); + if let Some(fat) = self.fat.as_mut() { + fat.foreground = ColorAttribute::Default; + } + self.deallocate_fat_attributes_if_none(); + } + foreground => { + self.foreground = SmallColor::Default; + self.allocate_fat_attributes(); + self.fat.as_mut().unwrap().foreground = foreground; + } + } + self } pub fn foreground(&self) -> ColorAttribute { - self.foreground + if let Some(fat) = self.fat.as_ref() { + if fat.foreground != ColorAttribute::Default { + return fat.foreground; + } + } + self.foreground.into() } pub fn set_background>(&mut self, background: C) -> &mut Self { - self.background = background.into(); + let background: ColorAttribute = background.into(); + match background { + ColorAttribute::Default => { + self.background = SmallColor::Default; + if let Some(fat) = self.fat.as_mut() { + fat.background = ColorAttribute::Default; + } + self.deallocate_fat_attributes_if_none(); + } + ColorAttribute::PaletteIndex(idx) => { + self.background = SmallColor::PaletteIndex(idx); + if let Some(fat) = self.fat.as_mut() { + fat.background = ColorAttribute::Default; + } + self.deallocate_fat_attributes_if_none(); + } + background => { + self.background = SmallColor::Default; + self.allocate_fat_attributes(); + self.fat.as_mut().unwrap().background = background; + } + } + self } pub fn background(&self) -> ColorAttribute { - self.background + if let Some(fat) = self.fat.as_ref() { + if fat.background != ColorAttribute::Default { + return fat.background; + } + } + self.background.into() } fn allocate_fat_attributes(&mut self) { @@ -250,6 +328,8 @@ impl CellAttributes { hyperlink: None, image: None, underline_color: ColorAttribute::Default, + foreground: ColorAttribute::Default, + background: ColorAttribute::Default, })); } } @@ -262,6 +342,8 @@ impl CellAttributes { fat.image.is_none() && fat.hyperlink.is_none() && fat.underline_color == ColorAttribute::Default + && fat.foreground == ColorAttribute::Default + && fat.background == ColorAttribute::Default }) .unwrap_or(false); if deallocate { @@ -640,8 +722,8 @@ mod test { fn memory_usage() { assert_eq!(std::mem::size_of::(), 4); assert_eq!(std::mem::size_of::(), 8); - assert_eq!(std::mem::size_of::(), 32); - assert_eq!(std::mem::size_of::(), 40); + assert_eq!(std::mem::size_of::(), 16); + assert_eq!(std::mem::size_of::(), 24); assert_eq!(std::mem::size_of::>(), 24); assert_eq!(std::mem::size_of::(), 4); assert_eq!(std::mem::size_of::(), 8);