use super::{signature::command_signature_part, *};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RenderPhase {
Background,
Content,
Overlay,
Popup,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ScenePrimitive {
Rect {
id: UiId,
rect: UiRect,
style: VisualStyle,
phase: RenderPhase,
},
Ellipse {
id: UiId,
rect: UiRect,
style: VisualStyle,
phase: RenderPhase,
},
Text {
id: UiId,
rect: UiRect,
text: Cow<'static, str>,
style: TextStyle,
phase: RenderPhase,
},
Custom {
id: UiId,
rect: UiRect,
key: &'static str,
style: Option<CustomPaintStyle>,
phase: RenderPhase,
},
Line {
id: UiId,
start: super::Point,
end: super::Point,
stroke: super::Stroke,
phase: RenderPhase,
},
Path {
id: UiId,
rect: UiRect,
path: UiPath,
style: PathStyle,
phase: RenderPhase,
},
Image {
id: UiId,
rect: UiRect,
source: UiImageSource,
request: ImageRequest,
fit: ImageFit,
blur: Option<BlurStyle>,
phase: RenderPhase,
},
Icon {
id: UiId,
rect: UiRect,
key: &'static str,
style: super::IconStyle,
phase: RenderPhase,
},
Glow {
id: UiId,
rect: UiRect,
color: super::Color,
alpha: u8,
phase: RenderPhase,
},
BackdropBlur {
id: UiId,
rect: UiRect,
style: BlurStyle,
phase: RenderPhase,
},
BackdropBlurPath {
id: UiId,
rect: UiRect,
path: UiPath,
style: BlurStyle,
phase: RenderPhase,
},
ContentBlur {
id: UiId,
rect: UiRect,
style: BlurStyle,
commands: Vec<ScenePrimitive>,
child_signature: u64,
phase: RenderPhase,
},
Overlay {
id: UiId,
rect: UiRect,
style: OverlayStyle,
phase: RenderPhase,
},
CompositingLayer {
id: UiId,
rect: UiRect,
spec: CompositingLayerSpec,
commands: Vec<ScenePrimitive>,
content_signature: u64,
phase: RenderPhase,
},
StaticLayer {
id: UiId,
rect: UiRect,
spec: StaticLayerSpec,
commands: Vec<ScenePrimitive>,
child_signature: u64,
phase: RenderPhase,
},
ScrollRaster {
id: UiId,
viewport: UiRect,
spec: ScrollRasterSpec,
commands: Vec<ScenePrimitive>,
child_signature: u64,
phase: RenderPhase,
},
Clip {
id: UiId,
rect: UiRect,
commands: Vec<ScenePrimitive>,
child_signature: u64,
phase: RenderPhase,
},
ClipPath {
id: UiId,
rect: UiRect,
path: UiPath,
commands: Vec<ScenePrimitive>,
child_signature: u64,
phase: RenderPhase,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ScenePrimitiveKind {
Rect,
Ellipse,
Text,
Custom,
Line,
Path,
Image,
Icon,
Glow,
BackdropBlur,
BackdropBlurPath,
ContentBlur,
Overlay,
CompositingLayer,
StaticLayer,
ScrollRaster,
Clip,
ClipPath,
}
impl ScenePrimitiveKind {
pub const ALL: [Self; 18] = [
Self::Rect,
Self::Ellipse,
Self::Text,
Self::Custom,
Self::Line,
Self::Path,
Self::Image,
Self::Icon,
Self::Glow,
Self::BackdropBlur,
Self::BackdropBlurPath,
Self::ContentBlur,
Self::Overlay,
Self::CompositingLayer,
Self::StaticLayer,
Self::ScrollRaster,
Self::Clip,
Self::ClipPath,
];
pub const fn as_str(self) -> &'static str {
match self {
Self::Rect => "Rect",
Self::Ellipse => "Ellipse",
Self::Text => "Text",
Self::Custom => "Custom",
Self::Line => "Line",
Self::Path => "Path",
Self::Image => "Image",
Self::Icon => "Icon",
Self::Glow => "Glow",
Self::BackdropBlur => "BackdropBlur",
Self::BackdropBlurPath => "BackdropBlurPath",
Self::ContentBlur => "ContentBlur",
Self::Overlay => "Overlay",
Self::CompositingLayer => "CompositingLayer",
Self::StaticLayer => "StaticLayer",
Self::ScrollRaster => "ScrollRaster",
Self::Clip => "Clip",
Self::ClipPath => "ClipPath",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollRasterSpec {
pub cache_epoch: u64,
pub content_height: f32,
pub scroll_y: f32,
pub tile_height_px: f32,
pub memory_budget_bytes: usize,
pub background_fill: Option<Color>,
pub visible_tiles: Vec<usize>,
pub prefetch_tiles: Vec<usize>,
pub max_prefetch_tiles_per_frame: usize,
pub max_prefetch_ms_per_frame: u32,
}
impl Eq for ScrollRasterSpec {}
impl Hash for ScrollRasterSpec {
fn hash<H: Hasher>(&self, state: &mut H) {
self.cache_epoch.hash(state);
normalized_f32_bits(self.content_height).hash(state);
normalized_f32_bits(self.scroll_y).hash(state);
normalized_f32_bits(self.tile_height_px).hash(state);
self.memory_budget_bytes.hash(state);
self.background_fill.hash(state);
self.visible_tiles.hash(state);
self.prefetch_tiles.hash(state);
self.max_prefetch_tiles_per_frame.hash(state);
self.max_prefetch_ms_per_frame.hash(state);
}
}
impl ScenePrimitive {
pub(crate) fn contains_shadow(&self) -> bool {
match self {
Self::CompositingLayer { spec, commands, .. } => {
spec.shadow.is_some() || commands.iter().any(Self::contains_shadow)
}
Self::StaticLayer { commands, .. }
| Self::ScrollRaster { commands, .. }
| Self::ContentBlur { commands, .. }
| Self::Clip { commands, .. }
| Self::ClipPath { commands, .. } => commands.iter().any(Self::contains_shadow),
_ => false,
}
}
pub const fn kind(&self) -> ScenePrimitiveKind {
match self {
Self::Rect { .. } => ScenePrimitiveKind::Rect,
Self::Ellipse { .. } => ScenePrimitiveKind::Ellipse,
Self::Text { .. } => ScenePrimitiveKind::Text,
Self::Custom { .. } => ScenePrimitiveKind::Custom,
Self::Line { .. } => ScenePrimitiveKind::Line,
Self::Path { .. } => ScenePrimitiveKind::Path,
Self::Image { .. } => ScenePrimitiveKind::Image,
Self::Icon { .. } => ScenePrimitiveKind::Icon,
Self::Glow { .. } => ScenePrimitiveKind::Glow,
Self::BackdropBlur { .. } => ScenePrimitiveKind::BackdropBlur,
Self::BackdropBlurPath { .. } => ScenePrimitiveKind::BackdropBlurPath,
Self::ContentBlur { .. } => ScenePrimitiveKind::ContentBlur,
Self::Overlay { .. } => ScenePrimitiveKind::Overlay,
Self::CompositingLayer { .. } => ScenePrimitiveKind::CompositingLayer,
Self::StaticLayer { .. } => ScenePrimitiveKind::StaticLayer,
Self::ScrollRaster { .. } => ScenePrimitiveKind::ScrollRaster,
Self::Clip { .. } => ScenePrimitiveKind::Clip,
Self::ClipPath { .. } => ScenePrimitiveKind::ClipPath,
}
}
pub fn id(&self) -> &UiId {
match self {
ScenePrimitive::Rect { id, .. }
| ScenePrimitive::Ellipse { id, .. }
| ScenePrimitive::Text { id, .. }
| ScenePrimitive::Custom { id, .. }
| ScenePrimitive::Line { id, .. }
| ScenePrimitive::Path { id, .. }
| ScenePrimitive::Image { id, .. }
| ScenePrimitive::Icon { id, .. }
| ScenePrimitive::Glow { id, .. }
| ScenePrimitive::BackdropBlur { id, .. }
| ScenePrimitive::BackdropBlurPath { id, .. }
| ScenePrimitive::ContentBlur { id, .. }
| ScenePrimitive::Overlay { id, .. }
| ScenePrimitive::CompositingLayer { id, .. }
| ScenePrimitive::StaticLayer { id, .. }
| ScenePrimitive::ScrollRaster { id, .. }
| ScenePrimitive::Clip { id, .. }
| ScenePrimitive::ClipPath { id, .. } => id,
}
}
pub fn phase(&self) -> RenderPhase {
match self {
ScenePrimitive::Rect { phase, .. }
| ScenePrimitive::Ellipse { phase, .. }
| ScenePrimitive::Text { phase, .. }
| ScenePrimitive::Custom { phase, .. }
| ScenePrimitive::Line { phase, .. }
| ScenePrimitive::Path { phase, .. }
| ScenePrimitive::Image { phase, .. }
| ScenePrimitive::Icon { phase, .. }
| ScenePrimitive::Glow { phase, .. }
| ScenePrimitive::BackdropBlur { phase, .. }
| ScenePrimitive::BackdropBlurPath { phase, .. }
| ScenePrimitive::ContentBlur { phase, .. }
| ScenePrimitive::Overlay { phase, .. }
| ScenePrimitive::CompositingLayer { phase, .. }
| ScenePrimitive::StaticLayer { phase, .. }
| ScenePrimitive::ScrollRaster { phase, .. }
| ScenePrimitive::Clip { phase, .. }
| ScenePrimitive::ClipPath { phase, .. } => *phase,
}
}
pub fn rect(&self) -> UiRect {
match self {
ScenePrimitive::Rect { rect, .. }
| ScenePrimitive::Ellipse { rect, .. }
| ScenePrimitive::Text { rect, .. }
| ScenePrimitive::Custom { rect, .. }
| ScenePrimitive::Image { rect, .. }
| ScenePrimitive::Path { rect, .. }
| ScenePrimitive::Icon { rect, .. }
| ScenePrimitive::Glow { rect, .. }
| ScenePrimitive::BackdropBlur { rect, .. }
| ScenePrimitive::BackdropBlurPath { rect, .. }
| ScenePrimitive::ContentBlur { rect, .. }
| ScenePrimitive::Overlay { rect, .. } => *rect,
ScenePrimitive::CompositingLayer { rect, .. } => *rect,
ScenePrimitive::StaticLayer { rect, spec, .. } => {
rect.translate(spec.offset_x, spec.offset_y)
}
ScenePrimitive::ScrollRaster { viewport, .. } => *viewport,
ScenePrimitive::Clip { rect, .. } | ScenePrimitive::ClipPath { rect, .. } => *rect,
ScenePrimitive::Line { start, end, .. } => UiRect::new(
start.x.min(end.x),
start.y.min(end.y),
start.x.max(end.x) + 1.0,
start.y.max(end.y) + 1.0,
),
}
}
pub fn paint_bounds(&self) -> UiRect {
let rect = match self {
ScenePrimitive::CompositingLayer { rect, spec, .. } => {
spec.transform.transformed_bounds(*rect)
}
_ => self.rect(),
};
let stroke_width = match self {
ScenePrimitive::Rect { style, .. } | ScenePrimitive::Ellipse { style, .. } => {
style.stroke.map(|stroke| stroke.width).unwrap_or(0.0)
}
ScenePrimitive::Line { stroke, .. } => stroke.width,
ScenePrimitive::Path { style, .. } => {
style.stroke.map(|stroke| stroke.width).unwrap_or(0.0)
}
_ => 0.0,
};
let blur_outset = match self {
ScenePrimitive::BackdropBlur { style, .. }
| ScenePrimitive::BackdropBlurPath { style, .. }
| ScenePrimitive::ContentBlur { style, .. } => style.outset(),
ScenePrimitive::Image {
blur: Some(blur), ..
} => blur.outset(),
_ => 0.0,
};
let outset = (stroke_width.max(0.0) + 1.0) / 2.0 + blur_outset;
rect.inflate(outset, outset)
}
pub fn signature(&self) -> u64 {
let mut hasher = DefaultHasher::new();
command_signature_part(self, &mut hasher);
hasher.finish()
}
}