Skip to content

Commit be90bf2

Browse files
committed
feat(examples): upgrade all basic showcases to interactive SDL2 window simulators
1 parent 43caec3 commit be90bf2

3 files changed

Lines changed: 519 additions & 240 deletions

File tree

Lines changed: 189 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
//! Showcase: Draw Task Graph & Partial Band-Buffer Rendering
1+
//! Showcase: Interactive Draw Task Graph & Partial Band-Buffer Rendering
22
//!
33
//! Demonstrates:
44
//! 1. Queuing retained-mode drawing commands into a fixed-capacity `DrawTaskQueue`.
55
//! 2. Dispatching drawing tasks through pluggable `DrawUnit` hardware/software units.
6-
//! 3. Band-buffered rendering using `PartialBandBuffer` on microcontrollers with constrained RAM.
6+
//! 3. Band-buffered rendering using `PartialBandBuffer` (narrow horizontal slices) directly
7+
//! to an interactive SDL2 desktop window.
8+
//!
9+
//! ### Controls:
10+
//! - **Space**: Add / cycle draw tasks in the queue
11+
//! - **Esc / Q**: Exit
712
813
use core::convert::Infallible;
914
use embedded_graphics_core::{
@@ -13,58 +18,54 @@ use embedded_graphics_core::{
1318
pixelcolor::{Rgb565, WebColors},
1419
primitives::Rectangle,
1520
};
21+
use embedded_graphics_simulator::{
22+
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window, sdl2::Keycode,
23+
};
1624
use embedded_gui::{
1725
geometry::Rect,
1826
render::{DrawTask, DrawTaskQueue, PartialBandBuffer, TextStyle, WindowedDrawTarget},
1927
style::Border,
2028
};
2129

22-
/// Mock LCD controller simulating a hardware display with set_window address windowing.
23-
struct MockWindowedLcd {
24-
width: u32,
25-
height: u32,
26-
pixels: [Rgb565; 320 * 240],
30+
const W: u32 = 320;
31+
const H: u32 = 240;
32+
33+
/// Frame buffer wrapping SimulatorDisplay that supports WindowedDrawTarget.
34+
struct WindowedSimulator {
35+
display: SimulatorDisplay<Rgb565>,
2736
window_count: usize,
2837
active_window: Option<Rectangle>,
2938
}
3039

31-
impl MockWindowedLcd {
32-
fn new(width: u32, height: u32) -> Self {
40+
impl WindowedSimulator {
41+
fn new() -> Self {
3342
Self {
34-
width,
35-
height,
36-
pixels: [Rgb565::CSS_BLACK; 320 * 240],
43+
display: SimulatorDisplay::<Rgb565>::new(Size::new(W, H)),
3744
window_count: 0,
3845
active_window: None,
3946
}
4047
}
4148
}
4249

43-
impl OriginDimensions for MockWindowedLcd {
50+
impl OriginDimensions for WindowedSimulator {
4451
fn size(&self) -> Size {
45-
Size::new(self.width, self.height)
52+
Size::new(W, H)
4653
}
4754
}
4855

49-
impl DrawTarget for MockWindowedLcd {
56+
impl DrawTarget for WindowedSimulator {
5057
type Color = Rgb565;
5158
type Error = Infallible;
5259

5360
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
5461
where
5562
I: IntoIterator<Item = Pixel<Self::Color>>,
5663
{
57-
for Pixel(pt, color) in pixels {
58-
if pt.x >= 0 && pt.y >= 0 && (pt.x as u32) < self.width && (pt.y as u32) < self.height {
59-
let idx = pt.y as usize * self.width as usize + pt.x as usize;
60-
self.pixels[idx] = color;
61-
}
62-
}
63-
Ok(())
64+
self.display.draw_iter(pixels)
6465
}
6566
}
6667

67-
impl WindowedDrawTarget for MockWindowedLcd {
68+
impl WindowedDrawTarget for WindowedSimulator {
6869
fn set_window(&mut self, rect: &Rectangle) -> Result<(), Self::Error> {
6970
self.active_window = Some(*rect);
7071
self.window_count += 1;
@@ -73,82 +74,169 @@ impl WindowedDrawTarget for MockWindowedLcd {
7374
}
7475

7576
fn main() {
76-
println!("=== embedded-gui: Draw Task Graph & Band Buffer Showcase ===");
77-
78-
// 1. Build a queue of draw tasks
79-
let mut queue = DrawTaskQueue::<16>::new();
80-
81-
// Background fill task
82-
queue
83-
.push(DrawTask::Fill {
84-
rect: Rect::new(0, 0, 320, 240),
85-
color: Rgb565::new(3, 6, 8),
86-
radius: 0,
87-
opacity: 255,
88-
})
89-
.expect("Task queued");
90-
91-
// Card panel task
92-
queue
93-
.push(DrawTask::Fill {
94-
rect: Rect::new(20, 20, 280, 80),
95-
color: Rgb565::new(8, 16, 24),
96-
radius: 8,
97-
opacity: 255,
98-
})
99-
.expect("Task queued");
100-
101-
// Border task
102-
queue
103-
.push(DrawTask::Border {
104-
rect: Rect::new(20, 20, 280, 80),
105-
border: Border::one(Rgb565::CSS_CYAN),
106-
radius: 8,
107-
})
108-
.expect("Task queued");
109-
110-
// Label task
111-
queue
112-
.push(DrawTask::Label {
113-
rect: Rect::new(35, 35, 250, 20),
114-
text: "BANDED MEMORY ENGINE",
115-
style: TextStyle::new(Rgb565::CSS_WHITE),
116-
})
117-
.expect("Task queued");
118-
119-
// Progress bar task
120-
queue
121-
.push(DrawTask::Fill {
122-
rect: Rect::new(35, 65, 250, 12),
123-
color: Rgb565::new(5, 10, 15),
124-
radius: 3,
125-
opacity: 255,
126-
})
127-
.expect("Task queued");
128-
129-
queue
130-
.push(DrawTask::Fill {
131-
rect: Rect::new(35, 65, 175, 12),
132-
color: Rgb565::CSS_GREEN,
133-
radius: 3,
134-
opacity: 255,
135-
})
136-
.expect("Task queued");
137-
138-
println!("Queued {} draw tasks in task graph.", queue.len());
139-
140-
// 2. Render using PartialBandBuffer (20 lines per band = only 12.8 KB RAM for a 320x240 screen)
141-
let screen = Rect::new(0, 0, 320, 240);
142-
let mut lcd = MockWindowedLcd::new(320, 240);
143-
let mut band_buffer = PartialBandBuffer::<{ 320 * 20 }>::new(320, 20);
144-
145-
band_buffer
146-
.render_tasks_banded(screen, screen, &queue, &mut lcd, Some(Rgb565::new(3, 6, 8)))
147-
.expect("Render succeeded");
148-
149-
println!(
150-
"Rendered 320x240 screen in bands using {} window transfers.",
151-
lcd.window_count
152-
);
153-
println!("Band buffer rendering demonstration completed successfully!");
77+
println!("=== embedded-gui: Interactive Draw Task Graph & Band Buffer Showcase ===");
78+
println!("Controls:");
79+
println!(" [Space] - Animate / cycle draw task progress");
80+
println!(" [Esc/Q] - Exit");
81+
82+
let mut sim = WindowedSimulator::new();
83+
let settings = OutputSettingsBuilder::new().scale(3).build();
84+
let mut window = Window::new("Draw Task Graph & Band Buffer (320x240)", &settings);
85+
86+
let mut progress: u32 = 120;
87+
let mut dir: i32 = 2;
88+
89+
'running: loop {
90+
for event in window.events() {
91+
match event {
92+
SimulatorEvent::Quit => break 'running,
93+
SimulatorEvent::KeyDown { keycode, .. } => match keycode {
94+
Keycode::Escape | Keycode::Q => break 'running,
95+
Keycode::Space => {
96+
progress = (progress + 20) % 250;
97+
}
98+
_ => {}
99+
},
100+
_ => {}
101+
}
102+
}
103+
104+
// Animate progress smoothly
105+
if progress >= 240 {
106+
dir = -2;
107+
} else if progress <= 20 {
108+
dir = 2;
109+
}
110+
progress = (progress as i32 + dir).max(0) as u32;
111+
112+
// 1. Build a queue of draw tasks
113+
let mut queue = DrawTaskQueue::<16>::new();
114+
115+
// Background fill task
116+
queue
117+
.push(DrawTask::Fill {
118+
rect: Rect::new(0, 0, W, H),
119+
color: Rgb565::new(2, 4, 6),
120+
radius: 0,
121+
opacity: 255,
122+
})
123+
.expect("Task queued");
124+
125+
// Top Status Header
126+
queue
127+
.push(DrawTask::Fill {
128+
rect: Rect::new(0, 0, W, 22),
129+
color: Rgb565::new(4, 8, 14),
130+
radius: 0,
131+
opacity: 255,
132+
})
133+
.expect("Task queued");
134+
135+
queue
136+
.push(DrawTask::Label {
137+
rect: Rect::new(12, 5, 200, 14),
138+
text: "BAND BUFFER RENDER ENGINE",
139+
style: TextStyle::new(Rgb565::CSS_CYAN),
140+
})
141+
.expect("Task queued");
142+
143+
// Main Panel Card
144+
queue
145+
.push(DrawTask::Fill {
146+
rect: Rect::new(20, 36, 280, 90),
147+
color: Rgb565::new(6, 12, 18),
148+
radius: 6,
149+
opacity: 255,
150+
})
151+
.expect("Task queued");
152+
153+
queue
154+
.push(DrawTask::Border {
155+
rect: Rect::new(20, 36, 280, 90),
156+
border: Border::one(Rgb565::CSS_DARK_CYAN),
157+
radius: 6,
158+
})
159+
.expect("Task queued");
160+
161+
// Card Label
162+
queue
163+
.push(DrawTask::Label {
164+
rect: Rect::new(35, 48, 250, 16),
165+
text: "SRAM CONSUMPTION: 12.8 KB",
166+
style: TextStyle::new(Rgb565::CSS_WHITE),
167+
})
168+
.expect("Task queued");
169+
170+
queue
171+
.push(DrawTask::Label {
172+
rect: Rect::new(35, 66, 250, 14),
173+
text: "Transfers 12 bands of 20 lines each",
174+
style: TextStyle::new(Rgb565::new(15, 30, 20)),
175+
})
176+
.expect("Task queued");
177+
178+
// Progress Bar Background & Active Fill
179+
queue
180+
.push(DrawTask::Fill {
181+
rect: Rect::new(35, 94, 250, 14),
182+
color: Rgb565::new(3, 6, 9),
183+
radius: 3,
184+
opacity: 255,
185+
})
186+
.expect("Task queued");
187+
188+
queue
189+
.push(DrawTask::Fill {
190+
rect: Rect::new(35, 94, progress, 14),
191+
color: Rgb565::CSS_GREEN,
192+
radius: 3,
193+
opacity: 255,
194+
})
195+
.expect("Task queued");
196+
197+
// Secondary Info Card
198+
queue
199+
.push(DrawTask::Fill {
200+
rect: Rect::new(20, 138, 280, 80),
201+
color: Rgb565::new(6, 12, 18),
202+
radius: 6,
203+
opacity: 255,
204+
})
205+
.expect("Task queued");
206+
207+
queue
208+
.push(DrawTask::Border {
209+
rect: Rect::new(20, 138, 280, 80),
210+
border: Border::one(Rgb565::new(12, 24, 18)),
211+
radius: 6,
212+
})
213+
.expect("Task queued");
214+
215+
queue
216+
.push(DrawTask::Label {
217+
rect: Rect::new(35, 152, 250, 16),
218+
text: "HARDWARE DMA STREAMING",
219+
style: TextStyle::new(Rgb565::CSS_GOLD),
220+
})
221+
.expect("Task queued");
222+
223+
queue
224+
.push(DrawTask::Label {
225+
rect: Rect::new(35, 172, 250, 14),
226+
text: "Windowed SPI/8080 transfers direct to LCD",
227+
style: TextStyle::new(Rgb565::new(20, 40, 30)),
228+
})
229+
.expect("Task queued");
230+
231+
// 2. Render using PartialBandBuffer (20 lines per band = 12.8 KB RAM)
232+
let screen = Rect::new(0, 0, W, H);
233+
let mut band_buffer = PartialBandBuffer::<{ 320 * 20 }>::new(320, 20);
234+
235+
band_buffer
236+
.render_tasks_banded(screen, screen, &queue, &mut sim, Some(Rgb565::new(2, 4, 6)))
237+
.expect("Render succeeded");
238+
239+
window.update(&sim.display);
240+
std::thread::sleep(std::time::Duration::from_millis(16));
241+
}
154242
}

0 commit comments

Comments
 (0)