argui_paint/
display_list.rs1use 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 #[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}