use bytemuck::{Pod, Zeroable};
#[inline]
pub const fn pack_rgba8(rgba: [u8; 4]) -> u32 {
(rgba[0] as u32)
| ((rgba[1] as u32) << 8)
| ((rgba[2] as u32) << 16)
| ((rgba[3] as u32) << 24)
}
#[inline]
pub fn pack_rgba_f32(rgba: [f32; 4]) -> u32 {
let q = |x: f32| -> u8 {
let c = x.clamp(0.0, 1.0);
(c * 255.0 + 0.5) as u8
};
pack_rgba8([q(rgba[0]), q(rgba[1]), q(rgba[2]), q(rgba[3])])
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct QuadInstance {
pub pos: [f32; 2],
pub size: [f32; 2],
pub color: u32,
pub border_color: u32,
pub corner_radius: f32,
pub border_width: f32,
pub _pad0: [f32; 2],
pub clip_rect: [f32; 4],
}
impl QuadInstance {
#[inline]
pub fn from_float_color(
pos: [f32; 2],
size: [f32; 2],
color: [f32; 4],
corner_radius: f32,
border_width: f32,
border_color: [f32; 4],
clip_rect: [f32; 4],
) -> Self {
Self {
pos, size,
color: pack_rgba_f32(color),
border_color: pack_rgba_f32(border_color),
corner_radius,
border_width,
_pad0: [0.0; 2],
clip_rect,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct LineInstance {
pub start: [f32; 2],
pub end: [f32; 2],
pub color: u32,
pub width: f32,
pub cap_flags: f32,
pub _pad0: f32,
pub _pad1: [f32; 2],
pub clip_rect: [f32; 4],
}
impl LineInstance {
#[inline]
pub fn from_float_color(
start: [f32; 2],
end: [f32; 2],
color: [f32; 4],
width: f32,
cap_flags: f32,
clip_rect: [f32; 4],
) -> Self {
Self {
start, end,
color: pack_rgba_f32(color),
width, cap_flags,
_pad0: 0.0,
_pad1: [0.0; 2],
clip_rect,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct TriangleInstance {
pub v0: [f32; 2],
pub v1: [f32; 2],
pub v2: [f32; 2],
pub color: u32,
pub _pad0: [f32; 3],
pub clip_rect: [f32; 4],
}
impl TriangleInstance {
#[inline]
pub fn from_float_color(
v0: [f32; 2],
v1: [f32; 2],
v2: [f32; 2],
color: [f32; 4],
clip_rect: [f32; 4],
) -> Self {
Self {
v0, v1, v2,
color: pack_rgba_f32(color),
_pad0: [0.0; 3],
clip_rect,
}
}
}
const _: () = assert!(
std::mem::size_of::<QuadInstance>() % 8 == 0,
"QuadInstance size must be a multiple of 8"
);
const _: () = assert!(
std::mem::size_of::<LineInstance>() % 8 == 0,
"LineInstance size must be a multiple of 8"
);
const _: () = assert!(
std::mem::size_of::<TriangleInstance>() % 8 == 0,
"TriangleInstance size must be a multiple of 8"
);
#[derive(Clone)]
pub enum DrawCmd {
Quad(QuadInstance),
Triangle(TriangleInstance),
Line(LineInstance),
Text(crate::text::TextAreaData),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quad_instance_is_56_bytes() {
assert_eq!(std::mem::size_of::<QuadInstance>(), 56);
}
#[test]
fn line_instance_is_56_bytes() {
assert_eq!(std::mem::size_of::<LineInstance>(), 56);
}
#[test]
fn triangle_instance_is_56_bytes() {
assert_eq!(std::mem::size_of::<TriangleInstance>(), 56);
}
#[test]
fn pack_rgba8_round_trip_endianness() {
let p = pack_rgba8([0x11, 0x22, 0x33, 0x44]);
assert_eq!(p, 0x44332211);
}
#[test]
fn pack_rgba_f32_quantises_midpoints() {
let p = pack_rgba_f32([0.0, 0.5, 1.0, 0.25]);
let bytes = p.to_le_bytes();
assert_eq!(bytes[0], 0);
assert_eq!(bytes[1], 128);
assert_eq!(bytes[2], 255);
assert_eq!(bytes[3], 64);
}
#[test]
fn pack_rgba_f32_clamps_out_of_range() {
let p = pack_rgba_f32([-1.0, 2.0, 0.5, f32::NAN]);
let bytes = p.to_le_bytes();
assert_eq!(bytes[0], 0);
assert_eq!(bytes[1], 255);
assert_eq!(bytes[2], 128);
assert_eq!(bytes[3], 0);
}
}