1use alloc::borrow::ToOwned;
8use alloc::boxed::Box;
9use alloc::string::String;
10
11use shuyi_platform::{Card, Frame, OneBitLook, Show};
12use shuyi_ui::frame::Rgb;
13use shuyi_ui::frame::glyph_buffer::GlyphBuffer;
14use shuyi_ui::frame::onebit::{self, FRAME_BYTES, ROW_BYTES, RowMask, luminance};
15
16use crate::report::{Measured, Phases};
17use crate::sdk::Sdk;
18
19const NO_LOOK: &str = "This game has no one-bit look: its Look has no one_bit part.";
22
23fn is_light(rgb: Rgb) -> bool {
25 luminance(rgb) >= 128
26}
27
28fn runs(rows: &RowMask) -> impl Iterator<Item = (i32, i32)> + '_ {
30 let mut rows = rows.rows().peekable();
31 core::iter::from_fn(move || {
32 let first = rows.next()?;
33 let mut last = first;
34 while rows.next_if_eq(&(last + 1)).is_some() {
35 last += 1;
36 }
37 Some((first, last))
38 })
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43pub(crate) enum Showing {
44 #[default]
46 Nothing,
47 Card,
49 Errors,
51 Game,
53}
54
55#[derive(Debug)]
57pub struct Screen {
58 drawn: Box<onebit::Frame>,
60 shown: Option<GlyphBuffer>,
63 text: Option<String>,
65 stale: bool,
68 changed: bool,
70 showing: Showing,
71 measured: Option<Measured>,
73}
74
75impl Default for Screen {
76 fn default() -> Self {
77 Self {
78 drawn: Box::new([u8::MAX; FRAME_BYTES]),
79 shown: None,
80 text: None,
81 stale: true,
82 changed: false,
83 showing: Showing::Nothing,
84 measured: None,
85 }
86 }
87}
88
89impl Screen {
90 pub fn present(&mut self, sdk: &impl Sdk, frame: &Frame<'_>) {
92 self.show(sdk, frame);
93 }
94
95 fn show<S: Sdk>(&mut self, sdk: &S, frame: &Frame<'_>) {
97 match frame.show {
98 Show::Card(Card::Text {
99 line, background, ..
100 }) => self.text(sdk, line, is_light(*background), Showing::Card),
101 Show::Card(card) => self.text(sdk, "", is_light(card.background()), Showing::Card),
102 Show::Errors(text) => self.text(sdk, text, true, Showing::Errors),
103 Show::Game { buffer, look, .. } => match look.and_then(|look| look.one_bit) {
104 Some(one_bit) => self.game(sdk, buffer, &one_bit),
105 None => self.text(sdk, NO_LOOK, true, Showing::Errors),
106 },
107 _ => {}
108 }
109 }
110
111 pub(crate) fn showing(&self) -> Showing {
113 self.showing
114 }
115
116 pub(crate) fn measure(&mut self, phases: bool) {
119 self.measured = Some(Measured {
120 phases: phases.then(Phases::default),
121 ..Measured::default()
122 });
123 }
124
125 pub(crate) fn take_measured(&mut self) -> Measured {
128 let Some(measured) = &mut self.measured else {
129 return Measured::default();
130 };
131 let phases = measured.phases.map(|_| Phases::default());
132 core::mem::replace(
133 measured,
134 Measured {
135 phases,
136 ..Measured::default()
137 },
138 )
139 }
140
141 pub fn invalidate(&mut self) {
144 self.stale = true;
145 }
146
147 pub fn take_changed(&mut self) -> bool {
150 core::mem::take(&mut self.changed)
151 }
152
153 fn text(&mut self, sdk: &impl Sdk, text: &str, white: bool, showing: Showing) {
154 self.showing = showing;
155 if self.text.as_deref() == Some(text) && !self.stale {
156 return;
157 }
158 sdk.draw_text(text, white);
159 self.text = Some(text.to_owned());
160 self.shown = None;
161 self.stale = false;
162 self.changed = true;
163 }
164
165 fn game<S: Sdk>(&mut self, sdk: &S, buffer: &GlyphBuffer, look: &OneBitLook<'_>) {
166 self.text = None;
167 self.showing = Showing::Game;
168 if self.stale || self.shown.as_ref() != Some(buffer) {
169 let start = self.measured.is_some().then(S::clock_us);
170 self.draw::<S>(buffer, look);
171 self.copy(sdk);
172 if let (Some(start), Some(measured)) = (start, &mut self.measured) {
173 measured.draw.add(S::clock_us().saturating_sub(start));
174 }
175 }
176 self.stale = false;
177 }
178
179 fn draw<S: Sdk>(&mut self, buffer: &GlyphBuffer, look: &OneBitLook<'_>) {
182 let (font, map) = (look.font, look.map);
183 if let Some(phases) = self.measured.as_mut().and_then(|m| m.phases.as_mut()) {
184 let mut last = S::clock_us();
185 let mark = &mut |phase| {
186 let now = S::clock_us();
187 phases.add(phase, now.saturating_sub(last));
188 last = now;
189 };
190 onebit::render_marked_with(buffer, font, &look.images, map, &mut self.drawn, mark);
191 self.shown = Some(buffer.clone());
192 return;
193 }
194 if let Some(shown) = &mut self.shown {
195 onebit::render_changed_with(shown, buffer, font, &look.images, map, &mut self.drawn);
196 shown.clone_from(buffer);
197 } else {
198 onebit::render_with(buffer, font, &look.images, map, &mut self.drawn);
199 self.shown = Some(buffer.clone());
200 }
201 }
202
203 fn copy(&mut self, sdk: &impl Sdk) {
206 let mut changed = RowMask::default();
207 let drawn = &self.drawn;
208 sdk.with_frame(|screen| {
209 changed = onebit::changed_rows(screen, drawn);
210 for y in changed.rows() {
211 let start = usize::try_from(y).unwrap_or(0) * ROW_BYTES;
212 let row = start..start + ROW_BYTES;
213 screen[row.clone()].copy_from_slice(&drawn[row]);
214 }
215 });
216 for (first, last) in runs(&changed) {
217 sdk.mark_rows(first, last);
218 }
219 self.changed |= !changed.is_empty();
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use alloc::vec;
226 use alloc::vec::Vec;
227
228 use super::*;
229
230 fn mask(rows: &[i32]) -> RowMask {
231 let mut mask = RowMask::default();
232 for &y in rows {
233 mask.insert(y);
234 }
235 mask
236 }
237
238 #[test]
239 fn neighbouring_rows_are_one_run() {
240 let got: Vec<_> = runs(&mask(&[3, 4, 5, 9, 239])).collect();
241 assert_eq!(got, vec![(3, 5), (9, 9), (239, 239)]);
242 assert_eq!(runs(&mask(&[])).count(), 0);
243 }
244}