@@ -114,9 +114,8 @@ impl<T> StatefulWidget for ShaderCanvas<T> {
114114 ) {
115115 let width = area. width ;
116116 let height = area. height ;
117- state. shader_input . resolution = [ width. into ( ) , height. into ( ) ] ;
118- state. shader_input . time = state. creation_time . elapsed ( ) . as_secs_f32 ( ) ;
119- let samples = state. backend . execute ( & state. shader_input , & state. user_data ) ;
117+ let shader_input = ShaderInput :: new ( state. instant . elapsed ( ) . as_secs_f32 ( ) , width, height) ;
118+ let samples = state. backend . execute ( & shader_input, & state. user_data ) ;
120119
121120 for y in 0 ..height {
122121 for x in 0 ..width {
@@ -145,34 +144,45 @@ impl<T> StatefulWidget for ShaderCanvas<T> {
145144}
146145
147146/// State struct for [`ShaderCanvas`], it holds the [`TuiShaderBackend`].
148- pub struct ShaderCanvasState < T > {
147+ pub struct ShaderCanvasState < T = NoUserData > {
149148 backend : Box < dyn TuiShaderBackend < T > > ,
150- shader_input : ShaderInput ,
151149 user_data : T ,
152- creation_time : Instant ,
150+ instant : Instant ,
153151}
154152
155153impl < T > ShaderCanvasState < T > {
156- pub fn new ( backend : Box < dyn TuiShaderBackend < T > > , user_data : T ) -> Self {
154+ pub fn new < B : TuiShaderBackend < T > + ' static > ( backend : B , user_data : T ) -> Self {
155+ let backend = Box :: new ( backend) ;
157156 let creation_time = Instant :: now ( ) ;
158- let shader_input = ShaderInput :: new ( creation_time. elapsed ( ) . as_secs_f32 ( ) , 0 , 0 ) ;
159157 ShaderCanvasState {
160158 backend,
161- shader_input,
162159 user_data,
163- creation_time,
160+ instant : creation_time,
164161 }
165162 }
166163}
167164
168- impl ShaderCanvasState < NoUserData > {
165+ impl ShaderCanvasState {
169166 /// Creates a new [`ShaderCanvasState`] using [`WgpuBackend`] as it's
170167 /// [`TuiShaderBackend`].
171168 pub fn wgpu ( path_to_fragment_shader : & str , entry_point : & str ) -> Self {
172- let backend = Box :: new ( WgpuBackend :: new ( path_to_fragment_shader, entry_point) ) ;
169+ let backend = WgpuBackend :: new ( path_to_fragment_shader, entry_point) ;
173170 let user_data = NoUserData :: default ( ) ;
174171 Self :: new ( backend, user_data)
175172 }
173+
174+ pub fn cpu < F > ( callback : F ) -> Self
175+ where
176+ F : Fn ( u32 , u32 ) -> Pixel + ' static ,
177+ {
178+ let backend = CpuBackend :: new ( callback) ;
179+ let user_data = NoUserData :: default ( ) ;
180+ Self :: new ( backend, user_data)
181+ }
182+
183+ pub fn builder ( ) -> ShaderCanvasStateBuilder {
184+ ShaderCanvasStateBuilder :: default ( )
185+ }
176186}
177187
178188impl < T > ShaderCanvasState < T >
@@ -184,18 +194,7 @@ where
184194 entry_point : & str ,
185195 user_data : T ,
186196 ) -> Self {
187- let backend = Box :: new ( WgpuBackend :: new ( path_to_fragment_shader, entry_point) ) ;
188- Self :: new ( backend, user_data)
189- }
190- }
191-
192- impl ShaderCanvasState < NoUserData > {
193- pub fn cpu < F > ( callback : F ) -> Self
194- where
195- F : Fn ( u32 , u32 ) -> [ u8 ; 4 ] + ' static ,
196- {
197- let backend = Box :: new ( CpuBackend :: new ( callback) ) ;
198- let user_data = NoUserData :: default ( ) ;
197+ let backend = WgpuBackend :: new ( path_to_fragment_shader, entry_point) ;
199198 Self :: new ( backend, user_data)
200199 }
201200}
@@ -206,22 +205,60 @@ where
206205{
207206 pub fn cpu_with_user_data < F > ( callback : F , user_data : T ) -> Self
208207 where
209- F : Fn ( u32 , u32 , & T ) -> [ u8 ; 4 ] + ' static ,
208+ F : Fn ( u32 , u32 , & T ) -> Pixel + ' static ,
210209 {
211- let backend = Box :: new ( CpuBackend :: new_with_user_data ( callback) ) ;
210+ let backend = CpuBackend :: new_with_user_data ( callback) ;
212211 Self :: new ( backend, user_data)
213212 }
214213}
215214
216- impl Default for ShaderCanvasState < NoUserData > {
215+ impl Default for ShaderCanvasState {
217216 /// Creates a new [`ShaderCanvasState`] instance with a [`WgpuBackend`].
218217 fn default ( ) -> Self {
219- let backend = Box :: new ( WgpuBackend :: default ( ) ) ;
218+ let backend = WgpuBackend :: default ( ) ;
220219 let user_data = NoUserData :: default ( ) ;
221220 Self :: new ( backend, user_data)
222221 }
223222}
224223
224+ #[ derive( Default ) ]
225+ pub struct ShaderCanvasStateBuilder < T = NoUserData > {
226+ backend : Option < Box < dyn TuiShaderBackend < T > > > ,
227+ user_data : Option < T > ,
228+ instant : Option < Instant > ,
229+ }
230+
231+ impl < T > ShaderCanvasStateBuilder < T > {
232+ pub fn with_backend < B : TuiShaderBackend < T > + ' static > ( mut self , backend : B ) -> Self {
233+ self . backend = Some ( Box :: new ( backend) ) ;
234+ self
235+ }
236+
237+ pub fn with_user_data ( mut self , user_data : T ) -> Self {
238+ self . user_data = Some ( user_data) ;
239+ self
240+ }
241+
242+ pub fn with_instant ( mut self , instant : Instant ) -> Self {
243+ self . instant = Some ( instant) ;
244+ self
245+ }
246+ }
247+
248+ impl ShaderCanvasStateBuilder {
249+ pub fn build ( self ) -> ShaderCanvasState {
250+ let backend = self
251+ . backend
252+ . unwrap_or_else ( || Box :: new ( WgpuBackend :: default ( ) ) ) ;
253+ let user_data = NoUserData :: default ( ) ;
254+ let instant = self . instant . unwrap_or_else ( || Instant :: now ( ) ) ;
255+ ShaderCanvasState {
256+ backend,
257+ user_data,
258+ instant,
259+ }
260+ }
261+ }
225262#[ repr( C ) ]
226263#[ derive( Copy , Clone , PartialEq , bytemuck:: Pod , bytemuck:: Zeroable ) ]
227264pub struct ShaderInput {
@@ -289,33 +326,33 @@ pub enum StyleRule {
289326/// Primarily used in [`CharacterRule::Map`] and [`StyleRule::Map`], it provides access to a Cells color and position
290327/// allowing to map the output of the shader to more complex behaviour.
291328pub struct Sample {
292- value : [ u8 ; 4 ] ,
329+ pixel : Pixel ,
293330 position : ( u16 , u16 ) ,
294331}
295332
296333impl Sample {
297- fn new ( value : [ u8 ; 4 ] , position : ( u16 , u16 ) ) -> Self {
298- Self { value , position }
334+ fn new ( pixel : Pixel , position : ( u16 , u16 ) ) -> Self {
335+ Self { pixel , position }
299336 }
300337
301338 /// The red channel of the [`Sample`]
302339 pub fn r ( & self ) -> u8 {
303- self . value [ 0 ]
340+ self . pixel [ 0 ]
304341 }
305342
306343 /// The green channel of the [`Sample`]
307344 pub fn g ( & self ) -> u8 {
308- self . value [ 1 ]
345+ self . pixel [ 1 ]
309346 }
310347
311348 /// The blue channel of the [`Sample`]
312349 pub fn b ( & self ) -> u8 {
313- self . value [ 2 ]
350+ self . pixel [ 2 ]
314351 }
315352
316353 /// The alpha channel of the [`Sample`]
317354 pub fn a ( & self ) -> u8 {
318- self . value [ 3 ]
355+ self . pixel [ 3 ]
319356 }
320357
321358 /// The x coordinate of the [`Sample`]
@@ -329,6 +366,8 @@ impl Sample {
329366 }
330367}
331368
369+ pub type Pixel = [ u8 ; 4 ] ;
370+
332371#[ cfg( test) ]
333372mod tests {
334373 use ratatui:: backend:: TestBackend ;
@@ -351,7 +390,7 @@ mod tests {
351390
352391 #[ test]
353392 fn cpu_backend ( ) {
354- fn cb ( _x : u32 , _y : u32 ) -> [ u8 ; 4 ] {
393+ fn cb ( _x : u32 , _y : u32 ) -> Pixel {
355394 [ 255 , 0 , 0 , 255 ]
356395 }
357396 let mut context = CpuBackend :: new ( cb) ;
0 commit comments