diff --git a/hui/src/draw.rs b/hui/src/draw.rs index 0253e11..a8b089d 100644 --- a/hui/src/draw.rs +++ b/hui/src/draw.rs @@ -9,7 +9,7 @@ use crate::{ pub(crate) mod atlas; use atlas::TextureAtlasManager; -pub use atlas::{TextureHandle, TextureAtlasMeta}; +pub use atlas::{TextureHandle, TextureAtlasMeta, TextureFormat}; mod corner_radius; pub use corner_radius::RoundedCorners; @@ -85,7 +85,7 @@ pub struct UiVertex { pub uv: Vec2, } -/// Represents a single draw call, should be handled by the render backend +/// Represents a single draw call (vertices + indices), should be handled by the render backend #[derive(Default)] pub struct UiDrawCall { pub vertices: Vec, diff --git a/hui/src/draw/atlas.rs b/hui/src/draw/atlas.rs index 812ebe4..919d08d 100644 --- a/hui/src/draw/atlas.rs +++ b/hui/src/draw/atlas.rs @@ -5,8 +5,25 @@ use rect_packer::DensePacker; use crate::rectangle::Corners; const RGBA_CHANNEL_COUNT: u32 = 4; +//TODO make this work const ALLOW_ROTATION: bool = false; +/// Texture format of the source texture data +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub enum TextureFormat { + /// The data is stored in RGBA format, with 1 byte (8 bits) per channel + #[default] + Rgba, + + /// The data is copied into the Alpha channel, with 1 byte (8 bits) per channel\ + /// Remaining channels are set to 255 (which can be easily shaded to any color) + /// + /// This format is useful for storing grayscale textures such as icons\ + /// (Please note that the internal representation is still RGBA, this is just a convenience feature) + Grayscale, +} + +/// Contains a reference to the texture data, and metadata associated with it pub struct TextureAtlasMeta<'a> { /// Texture data\ /// The data is stored in RGBA format, with 1 byte (8 bits) per channel @@ -157,7 +174,7 @@ impl TextureAtlasManager { /// Allocate a new texture region in the atlas and copy the data into it\ /// This function may resize the atlas as needed, and should never fail under normal circumstances. - pub fn add(&mut self, width: usize, data: &[u8]) -> TextureHandle { + pub(crate) fn add_rgba(&mut self, width: usize, data: &[u8]) -> TextureHandle { let size = uvec2(width as u32, (data.len() / (width * RGBA_CHANNEL_COUNT as usize)) as u32); let handle: TextureHandle = self.allocate(size); let allocation = self.allocations.get(&handle.index).unwrap(); @@ -178,7 +195,7 @@ impl TextureAtlasManager { /// Works the same way as [`TextureAtlasManager::add`], but the input data is assumed to be grayscale (1 channel per pixel)\ /// The data is copied into the alpha channel of the texture, while all the other channels are set to 255\ /// May resize the atlas as needed, and should never fail under normal circumstances. - pub fn add_grayscale(&mut self, width: usize, data: &[u8]) -> TextureHandle { + pub(crate) fn add_grayscale(&mut self, width: usize, data: &[u8]) -> TextureHandle { let size = uvec2(width as u32, (data.len() / width) as u32); let handle = self.allocate(size); let allocation = self.allocations.get(&handle.index).unwrap(); @@ -194,6 +211,13 @@ impl TextureAtlasManager { handle } + pub fn add(&mut self, width: usize, data: &[u8], format: TextureFormat) -> TextureHandle { + match format { + TextureFormat::Rgba => self.add_rgba(width, data), + TextureFormat::Grayscale => self.add_grayscale(width, data), + } + } + pub fn modify(&mut self, handle: TextureHandle) { todo!() } diff --git a/hui/src/element/builtin/container.rs b/hui/src/element/builtin/container.rs index b2f8e64..3674679 100644 --- a/hui/src/element/builtin/container.rs +++ b/hui/src/element/builtin/container.rs @@ -4,9 +4,9 @@ use derive_setters::Setters; use glam::{Vec2, vec2}; use crate::{ background::BackgroundColor, - draw::{RoundedCorners, UiDrawCommand}, + draw::{RoundedCorners, TextureHandle, UiDrawCommand}, element::{ElementList, MeasureContext, ProcessContext, UiElement}, - layout::{Alignment, Alignment2d, LayoutInfo, UiDirection, Size, Size2d}, + layout::{Alignment, Alignment2d, LayoutInfo, Size, Size2d, UiDirection}, measure::{Hints, Response}, rectangle::{Corners, Sides} }; @@ -55,10 +55,19 @@ pub struct Container { #[setters(into)] pub align: Alignment2d, - /// Background color of the container + /// Background color of the container\ + /// + /// If the container has a background texture, it will be multiplied by this color #[setters(into)] pub background: BackgroundColor, + /// Background texture of the container + /// + /// Can be used in conjunction with the background color\ + /// In this case, the texture will be shaded by the color + #[setters(into)] + pub background_image: Option, + /// Corner radius of the background rectangle #[setters(into)] pub corner_radius: Corners, @@ -88,6 +97,7 @@ impl Default for Container { padding: Sides::all(0.), align: Alignment2d::default(), background: Default::default(), + background_image: None, children: ElementList(Vec::new()), wrap: false, corner_radius: Corners::all(0.), diff --git a/hui/src/instance.rs b/hui/src/instance.rs index c5312dd..c595ae2 100644 --- a/hui/src/instance.rs +++ b/hui/src/instance.rs @@ -1,8 +1,7 @@ use glam::Vec2; use crate::{ draw::{ - atlas::{TextureAtlasManager, TextureAtlasMeta}, - UiDrawCall, UiDrawCommandList, + atlas::{TextureAtlasManager, TextureAtlasMeta}, TextureFormat, TextureHandle, UiDrawCall, UiDrawCommandList }, element::{MeasureContext, ProcessContext, UiElement}, event::{EventQueue, UiEvent}, input::UiInputState, layout::{LayoutInfo, UiDirection}, state::StateRepo, text::{FontHandle, TextRenderer} }; @@ -45,7 +44,7 @@ impl UiInstance { atlas: { let mut atlas = TextureAtlasManager::default(); //HACK: Ensure that vec(0, 0) uv is white square - atlas.add(1, &[255, 255, 255, 255]); + atlas.add_rgba(1, &[255, 255, 255, 255]); atlas }, events: EventQueue::new(), @@ -58,10 +57,23 @@ impl UiInstance { /// TrueType (`.ttf`/`.ttc`) and OpenType (`.otf`) fonts are supported\ /// /// Returns a font handle ([`FontHandle`]). + /// + /// ## Panics: + /// If the font data is invalid or corrupt pub fn add_font(&mut self, font: &[u8]) -> FontHandle { self.text_renderer.add_font_from_bytes(font) } + /// Add an image to the texture atlas\ + /// Accepted texture formats are `Rgba` and `Grayscale` + /// + /// Returns a texture handle ([`TextureHandle`])\ + /// This handle can be used to reference the texture in draw commands\ + /// It's a light reference and can be cloned/copied freely, but will not be cleaned up even when dropped + pub fn add_image(&mut self, format: TextureFormat, data: &[u8], width: usize) -> TextureHandle { + self.atlas.add(width, data, format) + } + /// Push a font to the font stack\ /// The font will be used for all text rendering until it is popped /// @@ -88,6 +100,9 @@ impl UiInstance { /// /// Use the `max_size` parameter to specify the maximum size of the element\ /// (usually, the size of the window/screen) + /// + /// ## Panics: + /// If called while the UI is not active (call [`UiInstance::begin`] first) pub fn add(&mut self, element: T, max_size: Vec2) { assert!(self.state, "must call UiInstance::begin before adding elements"); let layout = LayoutInfo { @@ -114,7 +129,7 @@ impl UiInstance { /// Prepare the UI for layout and processing\ /// You must call this function at the beginning of the frame, before adding any elements\ /// - /// # Panics + /// ## Panics: /// If called twice in a row (for example, if you forget to call [`UiInstance::end`])\ /// This is an indication of a bug in your code and should be fixed. pub fn begin(&mut self) { @@ -137,7 +152,7 @@ impl UiInstance { /// End the frame and prepare the UI for rendering\ /// You must call this function at the end of the frame, before rendering the UI /// - /// # Panics + /// ## Panics: /// If called without calling [`UiInstance::begin`] first. (or if called twice)\ /// This is an indication of a bug in your code and should be fixed. pub fn end(&mut self) {