use crate::color::Color;
use crate::geom::{Rect, Size};
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum QuadKind {
Solid,
GlyphMask,
GlyphColor,
Image,
GlyphSubpixel,
Shadow,
Segment,
Fragment,
Texture,
Backdrop,
}
impl QuadKind {
pub const ALL: [QuadKind; 10] = [
QuadKind::Solid,
QuadKind::GlyphMask,
QuadKind::GlyphColor,
QuadKind::Image,
QuadKind::GlyphSubpixel,
QuadKind::Shadow,
QuadKind::Segment,
QuadKind::Fragment,
QuadKind::Texture,
QuadKind::Backdrop,
];
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Quad {
pub rect: Rect,
pub color: Color,
pub border_color: Color,
pub radius: [f32; 4],
pub border_w: f32,
pub blur: f32,
pub kind: QuadKind,
pub clip: ClipId,
pub uv: [u32; 4],
}
pub type ClipId = u32;
pub const NO_CLIP_ID: ClipId = 0;
impl Quad {
pub fn segment_ends(&self) -> [f32; 4] {
self.uv.map(f32::from_bits)
}
pub fn segment_uv(ends: [f32; 4]) -> [u32; 4] {
ends.map(f32::to_bits)
}
}
pub const NO_CLIP: Rect = Rect {
x: -1e9,
y: -1e9,
w: 2e9,
h: 2e9,
};
pub const SQUARE: [f32; 4] = [0.0; 4];
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Clip {
pub rect: Rect,
pub radius: [f32; 4],
}
impl Clip {
pub const NONE: Clip = Clip {
rect: NO_CLIP,
radius: SQUARE,
};
pub fn rect(rect: Rect) -> Self {
Self {
rect,
radius: SQUARE,
}
}
pub fn scaled(&self, s: f32) -> Clip {
Clip {
rect: self.rect.scaled(s),
radius: self.radius.map(|r| r * s),
}
}
pub fn intersect(&self, box_rect: Rect, box_radius: [f32; 4]) -> Clip {
let rect = self.rect.intersect(&box_rect);
if self.radius == SQUARE && box_radius == SQUARE {
return Clip::rect(rect);
}
let mut radius = SQUARE;
for (i, r) in radius.iter_mut().enumerate() {
let mine = surviving(rect, self.rect, self.radius[i], i);
let theirs = surviving(rect, box_rect, box_radius[i], i);
*r = mine.max(theirs);
}
Clip { rect, radius }
}
}
fn surviving(rect: Rect, src: Rect, radius: f32, i: usize) -> f32 {
if radius <= 0.0 {
return 0.0;
}
let (dx, dy) = match i {
0 => (rect.x - src.x, rect.y - src.y),
1 => (rect.x + rect.w - src.x - src.w, rect.y - src.y),
2 => (
rect.x + rect.w - src.x - src.w,
rect.y + rect.h - src.y - src.h,
),
_ => (rect.x - src.x, rect.y + rect.h - src.y - src.h),
};
if dx.abs() < 0.01 && dy.abs() < 0.01 {
radius
} else {
0.0
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FragmentDraw {
pub id: crate::resources::FragmentId,
pub params: [f32; 16],
pub image: FragmentImage,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum FragmentImage {
#[default]
None,
Atlas([u32; 4]),
Texture { index: u32, uv: [u32; 4] },
}
impl FragmentImage {
pub fn uv(self) -> [u32; 4] {
match self {
FragmentImage::None => [0; 4],
FragmentImage::Atlas(uv) | FragmentImage::Texture { uv, .. } => uv,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TextureDraw {
pub id: crate::resources::ImageId,
pub uv: [u32; 4],
}
#[derive(Clone, Debug)]
pub struct TexturePixels {
pub width: u32,
pub height: u32,
pub rev: u32,
pub rgba: std::sync::Arc<Vec<u8>>,
}
#[derive(Default)]
pub struct DisplayList {
pub quads: Vec<Quad>,
pub clips: Vec<Clip>,
pub fragments: Vec<FragmentDraw>,
pub fragment_sources: Vec<std::sync::Arc<str>>,
pub textures: Vec<TextureDraw>,
pub texture_pixels: Vec<TexturePixels>,
pub dropped_textures: Vec<crate::resources::ImageId>,
pub dropped_fragments: Vec<crate::resources::FragmentId>,
pub viewport: Size,
pub scale: f32,
pub time: f32,
}
impl DisplayList {
pub fn clear(&mut self) {
self.quads.clear();
self.clips.clear();
self.fragments.clear();
self.fragment_sources.clear();
self.textures.clear();
self.texture_pixels.clear();
self.dropped_textures.clear();
self.dropped_fragments.clear();
}
pub fn clip_of(&self, q: &Quad) -> Clip {
self.clips
.get(q.clip as usize)
.copied()
.unwrap_or(Clip::NONE)
}
pub fn intern_clip(&mut self, clip: Clip) -> ClipId {
intern_clip(&mut self.clips, clip)
}
}
pub fn intern_clip(clips: &mut Vec<Clip>, clip: Clip) -> ClipId {
if let Some(last) = clips.last()
&& *last == clip
{
return clips.len() as ClipId - 1;
}
clips.push(clip);
clips.len() as ClipId - 1
}