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
813use core:: convert:: Infallible ;
914use 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+ } ;
1624use 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
7576fn 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