1use crate::console::drawing;
19use crate::console::graphics::{RasterInfo, RasterOps};
20use crate::console::{CharsXY, PixelsXY, RGB, SizeInPixels};
21use crate::gfx::lcd::fonts::Font;
22use crate::gfx::lcd::{AsByteSlice, Lcd, LcdSize, LcdXY, to_xy_size};
23use std::convert::TryFrom;
24use std::io;
25
26#[cfg(test)]
27mod tests;
28#[cfg(test)]
29mod testutils;
30
31pub struct BufferedLcd<L: Lcd> {
38 lcd: L,
39 font: &'static Font,
40
41 fb: Vec<u8>,
42 stride: usize,
43 sync: bool,
44 damage: Option<(LcdXY, LcdXY)>,
45
46 size_pixels: LcdSize,
47 size_chars: CharsXY,
48
49 draw_color: L::Pixel,
50 row_buffer: Vec<u8>,
51}
52
53impl<L> BufferedLcd<L>
54where
55 L: Lcd,
56{
57 pub fn new(lcd: L, font: &'static Font) -> Self {
59 let (size, stride) = lcd.info();
60
61 let fb = {
62 let pixels = size.width * size.height;
63 vec![0; pixels * stride]
64 };
65
66 let size_chars = CharsXY::new(
67 u16::try_from(size.width / font.glyph_size.width).expect("Must fit"),
68 u16::try_from(size.height / font.glyph_size.height).expect("Must fit"),
69 );
70
71 let draw_color = lcd.encode((255, 255, 255));
72 let row_buffer = Vec::with_capacity(size.width * stride);
73
74 Self {
75 lcd,
76 font,
77 fb,
78 stride,
79 sync: true,
80 damage: None,
81 size_pixels: size,
82 size_chars,
83 draw_color,
84 row_buffer,
85 }
86 }
87
88 fn without_sync<O>(&mut self, ops: O) -> io::Result<()>
90 where
91 O: Fn(&mut BufferedLcd<L>) -> io::Result<()>,
92 {
93 if self.sync {
94 let old_sync = self.sync;
95 self.sync = false;
96
97 let result = ops(self);
98
99 self.sync = old_sync;
100 if self.sync {
101 self.force_present_canvas()?;
102 }
103
104 result
105 } else {
106 ops(self)
107 }
108 }
109
110 fn clip_xy(&self, xy: PixelsXY) -> Option<LcdXY> {
113 fn clamp(value: i16, max: usize) -> Option<usize> {
114 if value < 0 {
115 None
116 } else {
117 let value = usize::try_from(value).expect("Positive value must fit");
118 if value > max { None } else { Some(value) }
119 }
120 }
121
122 let x = clamp(xy.x, self.size_pixels.width - 1);
123 let y = clamp(xy.y, self.size_pixels.height - 1);
124 match (x, y) {
125 (Some(x), Some(y)) => Some(LcdXY { x, y }),
126 _ => None,
127 }
128 }
129
130 fn clamp_xy(&self, xy: PixelsXY) -> LcdXY {
132 fn clamp(value: i16, max: usize) -> usize {
133 if value < 0 {
134 0
135 } else {
136 let value = usize::try_from(value).expect("Positive value must fit");
137 if value > max { max } else { value }
138 }
139 }
140
141 LcdXY {
142 x: clamp(xy.x, self.size_pixels.width - 1),
143 y: clamp(xy.y, self.size_pixels.height - 1),
144 }
145 }
146
147 fn clip_x2y2(&self, xy: PixelsXY, size: SizeInPixels) -> Option<LcdXY> {
150 fn clamp(value: i16, delta: u16, max: usize) -> Option<usize> {
151 let value = i32::from(value);
152 let delta = i32::from(delta);
153
154 let value = value + delta;
155 if value < 0 {
156 None
157 } else {
158 let value = usize::try_from(value).expect("Positive value must fit");
159 if value > max { Some(max) } else { Some(value) }
160 }
161 }
162
163 let x = clamp(xy.x, size.width - 1, self.size_pixels.width - 1);
164 let y = clamp(xy.y, size.height - 1, self.size_pixels.height - 1);
165 match (x, y) {
166 (Some(x), Some(y)) => Some(LcdXY { x, y }),
167 _ => None,
168 }
169 }
170
171 fn assert_xy_in_range(&mut self, xy: PixelsXY) {
177 if cfg!(test) {
178 let x = usize::try_from(xy.x).expect("x must be positive and must fit");
179 let y = usize::try_from(xy.y).expect("y must be positive and must fit");
180 debug_assert!(x < self.size_pixels.width, "x must be within the LCD width");
181 debug_assert!(y < self.size_pixels.height, "y must be within the LCD height");
182 }
183 }
184
185 fn assert_xy_size_in_range(&mut self, xy: PixelsXY, size: SizeInPixels) {
191 if cfg!(test) {
192 self.assert_xy_in_range(xy);
193 let x = xy.x as usize;
194 let y = xy.y as usize;
195
196 let width = usize::from(size.width);
197 let height = usize::from(size.height);
198
199 debug_assert!(
200 x + width - 1 < self.size_pixels.width,
201 "x + width must be within the LCD width"
202 );
203 debug_assert!(
204 y + height - 1 < self.size_pixels.height,
205 "y + height must be within the LCD height"
206 );
207 }
208 }
209
210 fn fb_addr(&self, x: usize, y: usize) -> usize {
212 debug_assert!(x < self.size_pixels.width);
213 debug_assert!(y < self.size_pixels.height);
214 ((y * self.size_pixels.width) + x) * self.stride
215 }
216
217 fn damage(&mut self, x1y1: LcdXY, x2y2: LcdXY) {
223 debug_assert!(!self.sync);
224 debug_assert!(x2y2.x >= x1y1.x);
225 debug_assert!(x2y2.y >= x1y1.y);
226
227 if self.damage.is_none() {
228 self.damage = Some((x1y1, x2y2));
229 return;
230 }
231 let mut damage = self.damage.unwrap();
232
233 if damage.0.x > x1y1.x {
234 damage.0.x = x1y1.x;
235 }
236 if damage.0.y > x1y1.y {
237 damage.0.y = x1y1.y;
238 }
239
240 if damage.1.x < x2y2.x {
241 damage.1.x = x2y2.x;
242 }
243 if damage.1.y < x2y2.y {
244 damage.1.y = x2y2.y;
245 }
246
247 self.damage = Some(damage);
248 }
249
250 fn fill(&mut self, x1y1: LcdXY, x2y2: LcdXY) -> io::Result<()> {
256 let rowlen = {
259 let xlen = x2y2.x - x1y1.x + 1;
260 let rowlen = xlen * self.stride;
261 self.row_buffer.clear();
262 let color = self.draw_color.as_slice();
263 for _ in 0..xlen {
264 self.row_buffer.extend_from_slice(color);
265 }
266 debug_assert_eq!(rowlen, self.row_buffer.len());
267 rowlen
268 };
269
270 if self.sync {
271 let mut data = LcdSize::between(x1y1, x2y2).new_buffer(self.stride);
272 for y in x1y1.y..(x2y2.y + 1) {
273 let offset = self.fb_addr(x1y1.x, y);
274 self.fb[offset..offset + rowlen].copy_from_slice(&self.row_buffer);
275 data.extend(&self.row_buffer);
276 }
277 self.lcd.set_data(x1y1, x2y2, &data)?;
278 } else {
279 for y in x1y1.y..(x2y2.y + 1) {
280 let offset = self.fb_addr(x1y1.x, y);
281 self.fb[offset..offset + rowlen].copy_from_slice(&self.row_buffer);
282 }
283 self.damage(x1y1, x2y2);
284 }
285
286 Ok(())
287 }
288
289 fn force_present_canvas(&mut self) -> io::Result<()> {
291 let (x1y1, x2y2) = match self.damage {
292 None => return Ok(()),
293 Some(damage) => damage,
294 };
295
296 let mut data = LcdSize::between(x1y1, x2y2).new_buffer(self.stride);
297 for y in x1y1.y..(x2y2.y + 1) {
298 for x in x1y1.x..(x2y2.x + 1) {
299 let offset = self.fb_addr(x, y);
300 data.extend_from_slice(&self.fb[offset..offset + self.stride]);
301 }
302 }
303 debug_assert_eq!(
304 {
305 let (_xy, size) = to_xy_size(x1y1, x2y2);
306 size.width * size.height * self.stride
307 },
308 data.len()
309 );
310
311 self.lcd.set_data(x1y1, x2y2, &data)?;
312
313 self.damage = None;
314
315 Ok(())
316 }
317
318 fn write_char(&mut self, pos: LcdXY, ch: char) -> io::Result<()> {
320 let glyph = self.font.glyph(ch);
321 for j in 0..self.font.glyph_size.height {
322 for k in 0..self.font.stride {
323 let row = glyph[j * self.font.stride + k];
324 let mut mask = 0x80;
325 for i in 0..self.font.glyph_size.width {
326 let bit = row & mask;
327 if bit != 0 {
328 let x = pos.x + i + k * 8;
329 if x >= self.size_pixels.width {
330 continue;
331 }
332
333 let y = pos.y + j;
334 if y >= self.size_pixels.height {
335 continue;
336 }
337
338 let xy = LcdXY { x, y };
339 self.fill(xy, xy)?;
341 }
342 mask >>= 1;
343 }
344 }
345 }
346 Ok(())
347 }
348}
349
350impl<L> Drop for BufferedLcd<L>
351where
352 L: Lcd,
353{
354 fn drop(&mut self) {
355 self.set_draw_color((0, 0, 0));
356 self.clear().unwrap();
357 }
358}
359
360impl<L> RasterOps for BufferedLcd<L>
361where
362 L: Lcd,
363{
364 type ID = (Vec<u8>, SizeInPixels);
365
366 fn get_info(&self) -> RasterInfo {
367 RasterInfo {
368 size_pixels: self.size_pixels.into(),
369 glyph_size: self.font.glyph_size.into(),
370 size_chars: self.size_chars,
371 }
372 }
373
374 fn set_draw_color(&mut self, color: RGB) {
375 self.draw_color = self.lcd.encode(color);
376 }
377
378 fn clear(&mut self) -> io::Result<()> {
379 self.fill(
380 LcdXY { x: 0, y: 0 },
381 LcdXY { x: self.size_pixels.width - 1, y: self.size_pixels.height - 1 },
382 )
383 }
384
385 fn set_sync(&mut self, enabled: bool) {
386 self.sync = enabled;
387 }
388
389 fn present_canvas(&mut self) -> io::Result<()> {
390 if self.sync { Ok(()) } else { self.force_present_canvas() }
391 }
392
393 fn read_pixels(&mut self, xy: PixelsXY, size: SizeInPixels) -> io::Result<Self::ID> {
394 self.assert_xy_size_in_range(xy, size);
395 let x1y1 = self.clip_xy(xy).expect("Internal ops must receive valid coordinates");
396 let x2y2 = self.clip_x2y2(xy, size).expect("Internal ops must receive valid coordinates");
397
398 let mut pixels = LcdSize::between(x1y1, x2y2).new_buffer(self.stride);
399
400 for y in x1y1.y..(x2y2.y + 1) {
401 for x in x1y1.x..(x2y2.x + 1) {
402 let offset = self.fb_addr(x, y);
403 pixels.extend_from_slice(&self.fb[offset..offset + self.stride]);
404 }
405 }
406
407 debug_assert_eq!(
408 usize::from(size.width) * usize::from(size.height) * self.stride,
409 pixels.len()
410 );
411 Ok((pixels, size))
412 }
413
414 fn put_pixels(&mut self, xy: PixelsXY, (pixels, size): &Self::ID) -> io::Result<()> {
415 debug_assert_eq!(
416 usize::from(size.width) * usize::from(size.height) * self.stride,
417 pixels.len()
418 );
419
420 self.assert_xy_in_range(xy);
421 let x1y1 = self.clip_xy(xy).expect("Internal ops must receive valid coordinates");
422 let x2y2 = self.clip_x2y2(xy, *size).expect("Internal ops must receive valid coordinates");
423
424 let mut p = 0;
425 for y in x1y1.y..(x2y2.y + 1) {
426 for x in x1y1.x..(x2y2.x + 1) {
427 let offset = self.fb_addr(x, y);
428 self.fb[offset..(offset + self.stride)]
429 .copy_from_slice(&pixels[p..(p + self.stride)]);
430 p += self.stride;
431 }
432 }
433
434 if self.sync {
435 self.lcd.set_data(x1y1, x2y2, pixels)?;
436 } else {
437 self.damage(x1y1, x2y2);
438 }
439
440 Ok(())
441 }
442
443 fn move_pixels(
444 &mut self,
445 x1y1: PixelsXY,
446 x2y2: PixelsXY,
447 size: SizeInPixels,
448 ) -> io::Result<()> {
449 self.assert_xy_size_in_range(x1y1, size);
450 self.assert_xy_size_in_range(x2y2, size);
451
452 let data = self.read_pixels(x1y1, size)?;
453
454 self.without_sync(|self2| {
455 self2.draw_rect_filled(x1y1, size)?;
456 self2.put_pixels(x2y2, &data)
457 })?;
458
459 Ok(())
460 }
461
462 fn write_text(&mut self, xy: PixelsXY, text: &str) -> io::Result<()> {
463 self.assert_xy_in_range(xy);
464
465 let x1y1 = self.clip_xy(xy).expect("Internal ops must receive valid coordinates");
466
467 self.without_sync(|self2| {
468 let mut pos = x1y1;
469 for ch in text.chars() {
470 self2.write_char(pos, ch)?;
471 pos.x += self2.font.glyph_size.width;
472 }
473 Ok(())
474 })
475 }
476
477 fn draw_circle(&mut self, center: PixelsXY, radius: u16) -> io::Result<()> {
478 self.without_sync(|self2| drawing::draw_circle(self2, center, radius))
479 }
480
481 fn draw_circle_filled(&mut self, center: PixelsXY, radius: u16) -> io::Result<()> {
482 self.without_sync(|self2| drawing::draw_circle_filled(self2, center, radius))
483 }
484
485 fn draw_line(&mut self, x1y1: PixelsXY, x2y2: PixelsXY) -> io::Result<()> {
486 self.without_sync(|self2| drawing::draw_line(self2, x1y1, x2y2))
487 }
488
489 fn draw_pixel(&mut self, xy: PixelsXY) -> io::Result<()> {
490 let xy = self.clip_xy(xy);
491 match xy {
492 Some(xy) => self.fill(xy, xy),
493 None => Ok(()),
494 }
495 }
496
497 fn draw_rect(&mut self, xy: PixelsXY, size: SizeInPixels) -> io::Result<()> {
498 self.without_sync(|self2| drawing::draw_rect(self2, xy, size))
499 }
500
501 fn draw_rect_filled(&mut self, xy: PixelsXY, size: SizeInPixels) -> io::Result<()> {
502 let x1y1 = self.clamp_xy(xy);
503 let x2y2 = self.clip_x2y2(xy, size);
504 match x2y2 {
505 Some(x2y2) => self.fill(x1y1, x2y2),
506 _ => Ok(()),
507 }
508 }
509}