use std::rc::Rc;
use std::sync::Arc;
use crate::geometry::{Color, Corners, Point, Rect};
#[derive(Clone)]
pub struct FontBlob {
pub id: u64,
pub data: Arc<dyn AsRef<[u8]> + Send + Sync>,
pub index: u32,
}
impl PartialEq for FontBlob {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.index == other.index
}
}
impl std::fmt::Debug for FontBlob {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FontBlob")
.field("id", &self.id)
.field("index", &self.index)
.field("len", &self.data.as_ref().as_ref().len())
.finish()
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PositionedGlyph {
pub id: u32,
pub x: f32,
pub y: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GlyphRun {
pub font: FontBlob,
pub size: f32,
pub color: Color,
pub glyphs: Vec<PositionedGlyph>,
pub ink_bounds: Rect,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum PaintOp {
FillRect {
rect: Rect,
radius: Corners,
color: Color,
},
StrokeRect {
rect: Rect,
radius: Corners,
width: f32,
color: Color,
},
Glyphs(Rc<GlyphRun>),
Image {
rect: Rect,
radius: Corners,
image: crate::image::Image,
},
}
impl PaintOp {
pub fn bounds(&self) -> Rect {
match self {
PaintOp::FillRect { rect, .. } | PaintOp::StrokeRect { rect, .. } => *rect,
PaintOp::Glyphs(run) => run.ink_bounds,
PaintOp::Image { rect, .. } => *rect,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum DisplayItem {
Op(PaintOp),
PushClip(Rect),
PopClip,
PushOffset(Point),
PopOffset,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct DisplayList {
items: Vec<DisplayItem>,
}
impl DisplayList {
pub fn new() -> DisplayList {
DisplayList::default()
}
pub fn clear(&mut self) {
self.items.clear();
}
pub fn push_op(&mut self, op: PaintOp) {
self.items.push(DisplayItem::Op(op));
}
pub fn extend_ops(&mut self, ops: &[PaintOp]) {
self.items.extend(ops.iter().cloned().map(DisplayItem::Op));
}
pub fn push_clip(&mut self, rect: Rect) {
self.items.push(DisplayItem::PushClip(rect));
}
pub fn pop_clip(&mut self) {
self.items.push(DisplayItem::PopClip);
}
pub fn push_offset(&mut self, offset: Point) {
self.items.push(DisplayItem::PushOffset(offset));
}
pub fn pop_offset(&mut self) {
self.items.push(DisplayItem::PopOffset);
}
pub fn items(&self) -> &[DisplayItem] {
&self.items
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn finish(&mut self) {
#[cfg(debug_assertions)]
self.debug_assert_balanced();
}
#[cfg(debug_assertions)]
fn debug_assert_balanced(&self) {
let mut open = 0usize;
let mut open_offsets = 0usize;
for item in &self.items {
match item {
DisplayItem::PushClip(_) => open += 1,
DisplayItem::PopClip => {
debug_assert!(open > 0, "PopClip with no open clip");
open = open.saturating_sub(1);
}
DisplayItem::PushOffset(_) => open_offsets += 1,
DisplayItem::PopOffset => {
debug_assert!(open_offsets > 0, "PopOffset with no open offset");
open_offsets = open_offsets.saturating_sub(1);
}
DisplayItem::Op(_) => {}
}
}
debug_assert_eq!(open, 0, "PushClip with no matching PopClip");
debug_assert_eq!(open_offsets, 0, "PushOffset with no matching PopOffset");
}
}
#[cfg(test)]
#[path = "paint_tests.rs"]
mod tests;