1use super::{signature::command_signature_part, *};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4pub enum RenderPhase {
5 Background,
6 Content,
7 Overlay,
8 Popup,
9}
10
11#[derive(Clone, Debug, PartialEq)]
12pub enum ScenePrimitive {
13 Rect {
14 id: UiId,
15 rect: UiRect,
16 style: VisualStyle,
17 phase: RenderPhase,
18 },
19 Ellipse {
20 id: UiId,
21 rect: UiRect,
22 style: VisualStyle,
23 phase: RenderPhase,
24 },
25 Text {
26 id: UiId,
27 rect: UiRect,
28 text: Cow<'static, str>,
29 style: TextStyle,
30 phase: RenderPhase,
31 },
32 Custom {
33 id: UiId,
34 rect: UiRect,
35 key: &'static str,
36 style: Option<CustomPaintStyle>,
39 phase: RenderPhase,
40 },
41 Line {
42 id: UiId,
43 start: super::Point,
44 end: super::Point,
45 stroke: super::Stroke,
46 phase: RenderPhase,
47 },
48 Path {
49 id: UiId,
50 rect: UiRect,
51 path: UiPath,
52 style: PathStyle,
53 phase: RenderPhase,
54 },
55 Image {
56 id: UiId,
57 rect: UiRect,
58 source: UiImageSource,
59 request: ImageRequest,
60 fit: ImageFit,
61 blur: Option<BlurStyle>,
62 phase: RenderPhase,
63 },
64 Icon {
65 id: UiId,
66 rect: UiRect,
67 key: &'static str,
68 style: super::IconStyle,
69 phase: RenderPhase,
70 },
71 Glow {
72 id: UiId,
73 rect: UiRect,
74 color: super::Color,
75 alpha: u8,
76 phase: RenderPhase,
77 },
78 BackdropBlur {
79 id: UiId,
80 rect: UiRect,
81 style: BlurStyle,
82 phase: RenderPhase,
83 },
84 BackdropBlurPath {
85 id: UiId,
86 rect: UiRect,
87 path: UiPath,
88 style: BlurStyle,
89 phase: RenderPhase,
90 },
91 ContentBlur {
92 id: UiId,
93 rect: UiRect,
94 style: BlurStyle,
95 commands: Vec<ScenePrimitive>,
96 child_signature: u64,
97 phase: RenderPhase,
98 },
99 Overlay {
100 id: UiId,
101 rect: UiRect,
102 style: OverlayStyle,
103 phase: RenderPhase,
104 },
105 CompositingLayer {
106 id: UiId,
107 rect: UiRect,
108 spec: CompositingLayerSpec,
109 commands: Vec<ScenePrimitive>,
111 content_signature: u64,
112 phase: RenderPhase,
113 },
114 StaticLayer {
115 id: UiId,
116 rect: UiRect,
117 spec: StaticLayerSpec,
118 commands: Vec<ScenePrimitive>,
119 child_signature: u64,
120 phase: RenderPhase,
121 },
122 ScrollRaster {
123 id: UiId,
124 viewport: UiRect,
125 spec: ScrollRasterSpec,
126 commands: Vec<ScenePrimitive>,
127 child_signature: u64,
128 phase: RenderPhase,
129 },
130 Clip {
131 id: UiId,
132 rect: UiRect,
133 commands: Vec<ScenePrimitive>,
134 child_signature: u64,
135 phase: RenderPhase,
136 },
137 ClipPath {
138 id: UiId,
139 rect: UiRect,
140 path: UiPath,
141 commands: Vec<ScenePrimitive>,
142 child_signature: u64,
143 phase: RenderPhase,
144 },
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
148pub enum ScenePrimitiveKind {
149 Rect,
150 Ellipse,
151 Text,
152 Custom,
153 Line,
154 Path,
155 Image,
156 Icon,
157 Glow,
158 BackdropBlur,
159 BackdropBlurPath,
160 ContentBlur,
161 Overlay,
162 CompositingLayer,
163 StaticLayer,
164 ScrollRaster,
165 Clip,
166 ClipPath,
167}
168
169impl ScenePrimitiveKind {
170 pub const ALL: [Self; 18] = [
171 Self::Rect,
172 Self::Ellipse,
173 Self::Text,
174 Self::Custom,
175 Self::Line,
176 Self::Path,
177 Self::Image,
178 Self::Icon,
179 Self::Glow,
180 Self::BackdropBlur,
181 Self::BackdropBlurPath,
182 Self::ContentBlur,
183 Self::Overlay,
184 Self::CompositingLayer,
185 Self::StaticLayer,
186 Self::ScrollRaster,
187 Self::Clip,
188 Self::ClipPath,
189 ];
190
191 pub const fn as_str(self) -> &'static str {
192 match self {
193 Self::Rect => "Rect",
194 Self::Ellipse => "Ellipse",
195 Self::Text => "Text",
196 Self::Custom => "Custom",
197 Self::Line => "Line",
198 Self::Path => "Path",
199 Self::Image => "Image",
200 Self::Icon => "Icon",
201 Self::Glow => "Glow",
202 Self::BackdropBlur => "BackdropBlur",
203 Self::BackdropBlurPath => "BackdropBlurPath",
204 Self::ContentBlur => "ContentBlur",
205 Self::Overlay => "Overlay",
206 Self::CompositingLayer => "CompositingLayer",
207 Self::StaticLayer => "StaticLayer",
208 Self::ScrollRaster => "ScrollRaster",
209 Self::Clip => "Clip",
210 Self::ClipPath => "ClipPath",
211 }
212 }
213}
214
215#[derive(Clone, Debug, PartialEq)]
216pub struct ScrollRasterSpec {
217 pub cache_epoch: u64,
218 pub content_height: f32,
219 pub scroll_y: f32,
220 pub tile_height_px: f32,
221 pub memory_budget_bytes: usize,
222 pub background_fill: Option<Color>,
223 pub visible_tiles: Vec<usize>,
224 pub prefetch_tiles: Vec<usize>,
225 pub max_prefetch_tiles_per_frame: usize,
226 pub max_prefetch_ms_per_frame: u32,
227}
228
229impl Eq for ScrollRasterSpec {}
230
231impl Hash for ScrollRasterSpec {
232 fn hash<H: Hasher>(&self, state: &mut H) {
233 self.cache_epoch.hash(state);
234 normalized_f32_bits(self.content_height).hash(state);
235 normalized_f32_bits(self.scroll_y).hash(state);
236 normalized_f32_bits(self.tile_height_px).hash(state);
237 self.memory_budget_bytes.hash(state);
238 self.background_fill.hash(state);
239 self.visible_tiles.hash(state);
240 self.prefetch_tiles.hash(state);
241 self.max_prefetch_tiles_per_frame.hash(state);
242 self.max_prefetch_ms_per_frame.hash(state);
243 }
244}
245
246impl ScenePrimitive {
247 pub(crate) fn contains_shadow(&self) -> bool {
248 match self {
249 Self::CompositingLayer { spec, commands, .. } => {
250 spec.shadow.is_some() || commands.iter().any(Self::contains_shadow)
251 }
252 Self::StaticLayer { commands, .. }
253 | Self::ScrollRaster { commands, .. }
254 | Self::ContentBlur { commands, .. }
255 | Self::Clip { commands, .. }
256 | Self::ClipPath { commands, .. } => commands.iter().any(Self::contains_shadow),
257 _ => false,
258 }
259 }
260
261 pub const fn kind(&self) -> ScenePrimitiveKind {
262 match self {
263 Self::Rect { .. } => ScenePrimitiveKind::Rect,
264 Self::Ellipse { .. } => ScenePrimitiveKind::Ellipse,
265 Self::Text { .. } => ScenePrimitiveKind::Text,
266 Self::Custom { .. } => ScenePrimitiveKind::Custom,
267 Self::Line { .. } => ScenePrimitiveKind::Line,
268 Self::Path { .. } => ScenePrimitiveKind::Path,
269 Self::Image { .. } => ScenePrimitiveKind::Image,
270 Self::Icon { .. } => ScenePrimitiveKind::Icon,
271 Self::Glow { .. } => ScenePrimitiveKind::Glow,
272 Self::BackdropBlur { .. } => ScenePrimitiveKind::BackdropBlur,
273 Self::BackdropBlurPath { .. } => ScenePrimitiveKind::BackdropBlurPath,
274 Self::ContentBlur { .. } => ScenePrimitiveKind::ContentBlur,
275 Self::Overlay { .. } => ScenePrimitiveKind::Overlay,
276 Self::CompositingLayer { .. } => ScenePrimitiveKind::CompositingLayer,
277 Self::StaticLayer { .. } => ScenePrimitiveKind::StaticLayer,
278 Self::ScrollRaster { .. } => ScenePrimitiveKind::ScrollRaster,
279 Self::Clip { .. } => ScenePrimitiveKind::Clip,
280 Self::ClipPath { .. } => ScenePrimitiveKind::ClipPath,
281 }
282 }
283
284 pub fn id(&self) -> &UiId {
285 match self {
286 ScenePrimitive::Rect { id, .. }
287 | ScenePrimitive::Ellipse { id, .. }
288 | ScenePrimitive::Text { id, .. }
289 | ScenePrimitive::Custom { id, .. }
290 | ScenePrimitive::Line { id, .. }
291 | ScenePrimitive::Path { id, .. }
292 | ScenePrimitive::Image { id, .. }
293 | ScenePrimitive::Icon { id, .. }
294 | ScenePrimitive::Glow { id, .. }
295 | ScenePrimitive::BackdropBlur { id, .. }
296 | ScenePrimitive::BackdropBlurPath { id, .. }
297 | ScenePrimitive::ContentBlur { id, .. }
298 | ScenePrimitive::Overlay { id, .. }
299 | ScenePrimitive::CompositingLayer { id, .. }
300 | ScenePrimitive::StaticLayer { id, .. }
301 | ScenePrimitive::ScrollRaster { id, .. }
302 | ScenePrimitive::Clip { id, .. }
303 | ScenePrimitive::ClipPath { id, .. } => id,
304 }
305 }
306
307 pub fn phase(&self) -> RenderPhase {
308 match self {
309 ScenePrimitive::Rect { phase, .. }
310 | ScenePrimitive::Ellipse { phase, .. }
311 | ScenePrimitive::Text { phase, .. }
312 | ScenePrimitive::Custom { phase, .. }
313 | ScenePrimitive::Line { phase, .. }
314 | ScenePrimitive::Path { phase, .. }
315 | ScenePrimitive::Image { phase, .. }
316 | ScenePrimitive::Icon { phase, .. }
317 | ScenePrimitive::Glow { phase, .. }
318 | ScenePrimitive::BackdropBlur { phase, .. }
319 | ScenePrimitive::BackdropBlurPath { phase, .. }
320 | ScenePrimitive::ContentBlur { phase, .. }
321 | ScenePrimitive::Overlay { phase, .. }
322 | ScenePrimitive::CompositingLayer { phase, .. }
323 | ScenePrimitive::StaticLayer { phase, .. }
324 | ScenePrimitive::ScrollRaster { phase, .. }
325 | ScenePrimitive::Clip { phase, .. }
326 | ScenePrimitive::ClipPath { phase, .. } => *phase,
327 }
328 }
329
330 pub fn rect(&self) -> UiRect {
331 match self {
332 ScenePrimitive::Rect { rect, .. }
333 | ScenePrimitive::Ellipse { rect, .. }
334 | ScenePrimitive::Text { rect, .. }
335 | ScenePrimitive::Custom { rect, .. }
336 | ScenePrimitive::Image { rect, .. }
337 | ScenePrimitive::Path { rect, .. }
338 | ScenePrimitive::Icon { rect, .. }
339 | ScenePrimitive::Glow { rect, .. }
340 | ScenePrimitive::BackdropBlur { rect, .. }
341 | ScenePrimitive::BackdropBlurPath { rect, .. }
342 | ScenePrimitive::ContentBlur { rect, .. }
343 | ScenePrimitive::Overlay { rect, .. } => *rect,
344 ScenePrimitive::CompositingLayer { rect, .. } => *rect,
345 ScenePrimitive::StaticLayer { rect, spec, .. } => {
346 rect.translate(spec.offset_x, spec.offset_y)
347 }
348 ScenePrimitive::ScrollRaster { viewport, .. } => *viewport,
349 ScenePrimitive::Clip { rect, .. } | ScenePrimitive::ClipPath { rect, .. } => *rect,
350 ScenePrimitive::Line { start, end, .. } => UiRect::new(
351 start.x.min(end.x),
352 start.y.min(end.y),
353 start.x.max(end.x) + 1.0,
354 start.y.max(end.y) + 1.0,
355 ),
356 }
357 }
358
359 pub fn paint_bounds(&self) -> UiRect {
360 let rect = match self {
361 ScenePrimitive::CompositingLayer { rect, spec, .. } => {
362 spec.transform.transformed_bounds(*rect)
363 }
364 _ => self.rect(),
365 };
366 let stroke_width = match self {
367 ScenePrimitive::Rect { style, .. } | ScenePrimitive::Ellipse { style, .. } => {
368 style.stroke.map(|stroke| stroke.width).unwrap_or(0.0)
369 }
370 ScenePrimitive::Line { stroke, .. } => stroke.width,
371 ScenePrimitive::Path { style, .. } => {
372 style.stroke.map(|stroke| stroke.width).unwrap_or(0.0)
373 }
374 _ => 0.0,
375 };
376 let blur_outset = match self {
377 ScenePrimitive::BackdropBlur { style, .. }
378 | ScenePrimitive::BackdropBlurPath { style, .. }
379 | ScenePrimitive::ContentBlur { style, .. } => style.outset(),
380 ScenePrimitive::Image {
381 blur: Some(blur), ..
382 } => blur.outset(),
383 _ => 0.0,
384 };
385 let outset = (stroke_width.max(0.0) + 1.0) / 2.0 + blur_outset;
386 rect.inflate(outset, outset)
387 }
388
389 pub fn signature(&self) -> u64 {
390 let mut hasher = DefaultHasher::new();
391 command_signature_part(self, &mut hasher);
392 hasher.finish()
393 }
394}