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