1use tessera_ui::{Color, DrawCommand, PxPosition, PxSize};
2
3use super::{ShapeUniforms, ShapeVertex};
4
5#[derive(Debug, Clone)]
7pub enum ShapeCommand {
8 Rect {
10 color: Color,
12 corner_radius: f32,
14 g2_k_value: f32,
17 shadow: Option<ShadowProps>,
19 },
20 OutlinedRect {
22 color: Color,
24 corner_radius: f32,
26 g2_k_value: f32,
29 shadow: Option<ShadowProps>,
31 border_width: f32,
33 },
34 RippleRect {
36 color: Color,
38 corner_radius: f32,
40 g2_k_value: f32,
43 shadow: Option<ShadowProps>,
45 ripple: RippleProps,
47 },
48 RippleOutlinedRect {
50 color: Color,
52 corner_radius: f32,
54 g2_k_value: f32,
57 shadow: Option<ShadowProps>,
59 border_width: f32,
61 ripple: RippleProps,
63 },
64 Ellipse {
66 color: Color,
68 shadow: Option<ShadowProps>,
70 },
71 OutlinedEllipse {
73 color: Color,
75 shadow: Option<ShadowProps>,
77 border_width: f32,
79 },
80}
81
82impl DrawCommand for ShapeCommand {
83 fn barrier(&self) -> Option<tessera_ui::BarrierRequirement> {
84 None
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
91pub struct ShadowProps {
92 pub color: Color,
94 pub offset: [f32; 2],
96 pub smoothness: f32,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq)]
102pub struct RippleProps {
103 pub center: [f32; 2],
105 pub radius: f32,
107 pub alpha: f32,
109 pub color: Color,
111}
112
113impl Default for RippleProps {
114 fn default() -> Self {
115 Self {
116 center: [0.0, 0.0],
117 radius: 0.0,
118 alpha: 0.0,
119 color: Color::WHITE,
120 }
121 }
122}
123
124pub struct ShapeCommandComputed {
125 pub(crate) vertices: Vec<ShapeVertex>,
126 pub(crate) uniforms: ShapeUniforms,
127}
128
129impl ShapeCommandComputed {
130 pub fn from_command(command: ShapeCommand, size: PxSize, position: PxPosition) -> Self {
131 match command {
132 ShapeCommand::Rect {
133 color,
134 corner_radius,
135 g2_k_value,
136 shadow,
137 } => rect_to_computed_draw_command(
138 size,
139 position,
140 color, corner_radius,
142 g2_k_value,
143 shadow,
144 0.0, 0.0, ),
147 ShapeCommand::OutlinedRect {
148 color,
149 corner_radius,
150 g2_k_value,
151 shadow,
152 border_width,
153 } => rect_to_computed_draw_command(
154 size,
155 position,
156 color, corner_radius,
158 g2_k_value,
159 shadow,
160 border_width,
161 1.0, ),
163 ShapeCommand::RippleRect {
164 color,
165 corner_radius,
166 g2_k_value,
167 shadow,
168 ripple,
169 } => ripple_rect_to_computed_draw_command(
170 size,
171 position,
172 color,
173 corner_radius,
174 g2_k_value,
175 shadow,
176 0.0, 0.0, ripple,
179 ),
180 ShapeCommand::RippleOutlinedRect {
181 color,
182 corner_radius,
183 g2_k_value,
184 shadow,
185 border_width,
186 ripple,
187 } => ripple_rect_to_computed_draw_command(
188 size,
189 position,
190 color,
191 corner_radius,
192 g2_k_value,
193 shadow,
194 border_width,
195 1.0, ripple,
197 ),
198 ShapeCommand::Ellipse { color, shadow } => rect_to_computed_draw_command(
199 size, position, color,
200 -1.0, 0.0, shadow, 0.0, 0.0, ),
204 ShapeCommand::OutlinedEllipse {
205 color,
206 shadow,
207 border_width,
208 } => rect_to_computed_draw_command(
209 size,
210 position,
211 color,
212 -1.0, 0.0,
214 shadow,
215 border_width,
216 1.0, ),
218 }
219 }
220}
221
222fn rect_to_computed_draw_command(
224 size: PxSize,
225 position: PxPosition,
226 primary_color_rgba: Color,
227 corner_radius: f32,
228 g2_k_value: f32,
229 shadow: Option<ShadowProps>,
230 border_width: f32,
231 render_mode: f32,
232) -> ShapeCommandComputed {
233 let width = size.width;
234 let height = size.height;
235
236 let rect_local_pos = [
237 [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5], ];
242
243 let vertex_color_placeholder_rgb = [0.0, 0.0, 0.0];
244 let top_left = position.to_f32_arr3();
245 let top_right = [top_left[0] + width.to_f32(), top_left[1], top_left[2]];
246 let bottom_right = [
247 top_left[0] + width.to_f32(),
248 top_left[1] + height.to_f32(),
249 top_left[2],
250 ];
251 let bottom_left = [top_left[0], top_left[1] + height.to_f32(), top_left[2]];
252
253 let vertices = vec![
254 ShapeVertex {
255 position: top_left,
256 color: vertex_color_placeholder_rgb,
257 local_pos: rect_local_pos[0],
258 },
259 ShapeVertex {
260 position: top_right,
261 color: vertex_color_placeholder_rgb,
262 local_pos: rect_local_pos[1],
263 },
264 ShapeVertex {
265 position: bottom_right,
266 color: vertex_color_placeholder_rgb,
267 local_pos: rect_local_pos[2],
268 },
269 ShapeVertex {
270 position: bottom_left,
271 color: vertex_color_placeholder_rgb,
272 local_pos: rect_local_pos[3],
273 },
274 ];
275
276 let (shadow_rgba_color, shadow_offset_vec, shadow_smooth_val) = if let Some(s_props) = shadow {
277 (s_props.color, s_props.offset, s_props.smoothness)
278 } else {
279 (Color::TRANSPARENT, [0.0, 0.0], 0.0)
280 };
281
282 let uniforms = ShapeUniforms {
283 size_cr_border_width: [width.to_f32(), height.to_f32(), corner_radius, border_width].into(),
284 primary_color: primary_color_rgba.to_array().into(),
285 shadow_color: shadow_rgba_color.to_array().into(),
286 render_params: [
287 shadow_offset_vec[0],
288 shadow_offset_vec[1],
289 shadow_smooth_val,
290 render_mode,
291 ]
292 .into(),
293 ripple_params: [0.0, 0.0, 0.0, 0.0].into(),
294 ripple_color: [0.0, 0.0, 0.0, 0.0].into(),
295 g2_k_value,
296 };
297
298 ShapeCommandComputed { vertices, uniforms }
299}
300
301fn ripple_rect_to_computed_draw_command(
303 size: PxSize,
304 position: PxPosition,
305 primary_color_rgba: Color,
306 corner_radius: f32,
307 g2_k_value: f32,
308 shadow: Option<ShadowProps>,
309 border_width: f32,
310 render_mode: f32,
311 ripple: RippleProps,
312) -> ShapeCommandComputed {
313 let width = size.width;
314 let height = size.height;
315
316 let rect_local_pos = [
317 [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5], ];
322
323 let vertex_color_placeholder_rgb = [0.0, 0.0, 0.0];
324 let top_left = position.to_f32_arr3();
325 let top_right = [top_left[0] + width.to_f32(), top_left[1], top_left[2]];
326 let bottom_right = [
327 top_left[0] + width.to_f32(),
328 top_left[1] + height.to_f32(),
329 top_left[2],
330 ];
331 let bottom_left = [top_left[0], top_left[1] + height.to_f32(), top_left[2]];
332
333 let vertices = vec![
334 ShapeVertex {
335 position: top_left,
336 color: vertex_color_placeholder_rgb,
337 local_pos: rect_local_pos[0],
338 },
339 ShapeVertex {
340 position: top_right,
341 color: vertex_color_placeholder_rgb,
342 local_pos: rect_local_pos[1],
343 },
344 ShapeVertex {
345 position: bottom_right,
346 color: vertex_color_placeholder_rgb,
347 local_pos: rect_local_pos[2],
348 },
349 ShapeVertex {
350 position: bottom_left,
351 color: vertex_color_placeholder_rgb,
352 local_pos: rect_local_pos[3],
353 },
354 ];
355
356 let (shadow_rgba_color, shadow_offset_vec, shadow_smooth_val) = if let Some(s_props) = shadow {
357 (s_props.color.into(), s_props.offset, s_props.smoothness)
358 } else {
359 ([0.0, 0.0, 0.0, 0.0], [0.0, 0.0], 0.0)
360 };
361
362 let ripple_render_mode = if render_mode == 0.0 { 3.0 } else { 4.0 };
363
364 let uniforms = ShapeUniforms {
365 size_cr_border_width: [width.to_f32(), height.to_f32(), corner_radius, border_width].into(),
366 primary_color: primary_color_rgba.to_array().into(),
367 shadow_color: shadow_rgba_color.into(),
368 render_params: [
369 shadow_offset_vec[0],
370 shadow_offset_vec[1],
371 shadow_smooth_val,
372 ripple_render_mode,
373 ]
374 .into(),
375 ripple_params: [
376 ripple.center[0],
377 ripple.center[1],
378 ripple.radius,
379 ripple.alpha,
380 ]
381 .into(),
382 ripple_color: [ripple.color.r, ripple.color.g, ripple.color.b, 0.0].into(),
383 g2_k_value,
384 };
385
386 ShapeCommandComputed { vertices, uniforms }
387}