Skip to main content

argui_paint/
display_list.rs

1use core::fmt;
2
3use argui_core::Affine2D;
4
5use crate::{ClipChain, ImagePrimitive, LayerStyle, Quad, VectorPrimitive};
6
7#[derive(Clone, Debug, PartialEq)]
8pub enum DisplayCommand {
9    Quad(Quad),
10    Image(ImagePrimitive),
11    Vector(VectorPrimitive),
12    Text {
13        block: usize,
14        transform: Affine2D,
15        clips: ClipChain,
16    },
17    BeginLayer(LayerStyle),
18    EndLayer,
19}
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum DisplayListError {
23    UnexpectedLayerEnd { command: usize },
24    UnclosedLayers { count: usize },
25}
26
27impl fmt::Display for DisplayListError {
28    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::UnexpectedLayerEnd { command } => {
31                write!(
32                    formatter,
33                    "layer ends without a matching begin at command {command}"
34                )
35            }
36            Self::UnclosedLayers { count } => {
37                write!(formatter, "display list has {count} unclosed layers")
38            }
39        }
40    }
41}
42
43impl std::error::Error for DisplayListError {}
44
45#[derive(Clone, Debug, Default, PartialEq)]
46pub struct DisplayList {
47    commands: Vec<DisplayCommand>,
48    quad_count: usize,
49}
50
51impl DisplayList {
52    /// Allocated command capacity, excluding shared and nested command payloads.
53    #[must_use]
54    pub fn storage_bytes(&self) -> usize {
55        self.commands.capacity() * size_of::<DisplayCommand>()
56    }
57    #[must_use]
58    pub const fn new() -> Self {
59        Self {
60            commands: Vec::new(),
61            quad_count: 0,
62        }
63    }
64
65    pub fn push_quad(&mut self, quad: Quad) {
66        self.commands.push(DisplayCommand::Quad(quad));
67        self.quad_count += 1;
68    }
69
70    pub fn push_image(&mut self, image: ImagePrimitive) {
71        self.commands.push(DisplayCommand::Image(image));
72    }
73
74    pub fn push_vector(&mut self, vector: VectorPrimitive) {
75        self.commands.push(DisplayCommand::Vector(vector));
76    }
77
78    pub fn push_text(&mut self, block: usize) {
79        self.push_text_transformed(block, Affine2D::IDENTITY, ClipChain::default());
80    }
81
82    pub fn push_text_transformed(&mut self, block: usize, transform: Affine2D, clips: ClipChain) {
83        self.commands.push(DisplayCommand::Text {
84            block,
85            transform,
86            clips,
87        });
88    }
89
90    pub fn begin_layer(&mut self, style: LayerStyle) {
91        self.commands.push(DisplayCommand::BeginLayer(style));
92    }
93
94    pub fn end_layer(&mut self) {
95        self.commands.push(DisplayCommand::EndLayer);
96    }
97
98    pub fn clear(&mut self) {
99        self.commands.clear();
100        self.quad_count = 0;
101    }
102
103    pub fn extend(&mut self, commands: impl IntoIterator<Item = DisplayCommand>) {
104        for command in commands {
105            if matches!(command, DisplayCommand::Quad(_)) {
106                self.quad_count += 1;
107            }
108            self.commands.push(command);
109        }
110    }
111
112    #[must_use]
113    pub const fn len(&self) -> usize {
114        self.commands.len()
115    }
116
117    #[must_use]
118    pub const fn is_empty(&self) -> bool {
119        self.commands.is_empty()
120    }
121
122    #[must_use]
123    pub fn commands(&self) -> &[DisplayCommand] {
124        &self.commands
125    }
126
127    #[must_use]
128    pub const fn quad_count(&self) -> usize {
129        self.quad_count
130    }
131
132    pub fn validate(&self) -> Result<(), DisplayListError> {
133        let mut depth = 0_usize;
134        for (command, item) in self.commands.iter().enumerate() {
135            match item {
136                DisplayCommand::BeginLayer(_) => depth += 1,
137                DisplayCommand::EndLayer if depth == 0 => {
138                    return Err(DisplayListError::UnexpectedLayerEnd { command });
139                }
140                DisplayCommand::EndLayer => depth -= 1,
141                DisplayCommand::Quad(_)
142                | DisplayCommand::Image(_)
143                | DisplayCommand::Vector(_)
144                | DisplayCommand::Text { .. } => {}
145            }
146        }
147        if depth == 0 {
148            Ok(())
149        } else {
150            Err(DisplayListError::UnclosedLayers { count: depth })
151        }
152    }
153}