//! 面向设备像素的绘制指令集合。它承载矩形、文字、裁剪与 3D 表面等原语,不直接操作 VM。 //! 平移和缩放统一作用于命令几何;文字样式和裁剪也要使用一致的设备比例。 //! use crate::geometry::{Point, Rect}; use crate::text::TextStyle; #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// 设备绘制使用的 RGBA 字节通道,区别于源代码的颜色文本。 pub struct Color { pub red: u8, pub green: u8, pub blue: u8, pub alpha: u8, } impl Color { /// 从 RGB 通道构造不透明颜色值。 pub const fn rgb(red: u8, green: u8, blue: u8) -> Self { Self { red, green, blue, alpha: 255, } } /// 从 RGBA 通道构造带透明度的颜色值。 pub const fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self { Self { red, green, blue, alpha, } } } #[derive(Clone, Debug)] /// 原生画布最终顺序执行的绘制原语,已经使用对应设备比例。 pub enum DrawCommand { Scene3D { rect: Rect, view: std::sync::Arc, }, Clear(Color), PushClip(Rect), PopClip, FillRoundedRect { rect: Rect, radius: f32, color: Color, }, StrokeRoundedRect { rect: Rect, radius: f32, width: f32, color: Color, }, WrappedText { rect: Rect, value: String, style: TextStyle, color: Color, }, Text { origin: Point, value: String, style: TextStyle, color: Color, }, } impl DrawCommand { /// 返回平移后的绘制命令,并同步移动命令中的相关几何。 pub fn translated(mut self, delta: Point) -> Self { match &mut self { Self::WrappedText { rect, .. } | Self::Scene3D { rect, .. } | Self::PushClip(rect) | Self::FillRoundedRect { rect, .. } | Self::StrokeRoundedRect { rect, .. } => { rect.x += delta.x; rect.y += delta.y; } Self::Text { origin, .. } => { origin.x += delta.x; origin.y += delta.y; } _ => {} } self } } #[derive(Clone, Debug, Default)] /// 有序绘制原语容器;命令顺序同时约束叠放和裁剪栈行为。 pub struct DisplayList { commands: Vec, } impl DisplayList { /// 按设备比例缩放命令,文字、边框和矩形不能分别使用不一致比例。 pub fn scale(&mut self, scale: f32) { for command in &mut self.commands { let rect = match command { DrawCommand::WrappedText { rect, style, .. } => { *style = style.scaled(scale); Some(rect) } DrawCommand::PushClip(rect) => Some(rect), DrawCommand::Scene3D { rect, .. } => Some(rect), DrawCommand::FillRoundedRect { rect, radius, .. } => { *radius *= scale; Some(rect) } DrawCommand::StrokeRoundedRect { rect, radius, width, .. } => { *radius *= scale; *width *= scale; Some(rect) } DrawCommand::Text { origin, style, .. } => { origin.x *= scale; origin.y *= scale; *style = style.scaled(scale); None } _ => None, }; if let Some(rect) = rect { rect.x *= scale; rect.y *= scale; rect.width *= scale; rect.height *= scale; } } } /// 按绘制顺序追加一条原语,后加入的内容在合成顺序中位于后面。 pub fn push(&mut self, command: DrawCommand) { self.commands.push(command); } /// 平移当前显示列表中的命令,供整块页面或管理面板放入窗口坐标。 pub fn translate(&mut self, delta: Point) { for command in &mut self.commands { match command { DrawCommand::WrappedText { rect, .. } | DrawCommand::Scene3D { rect, .. } | DrawCommand::PushClip(rect) | DrawCommand::FillRoundedRect { rect, .. } | DrawCommand::StrokeRoundedRect { rect, .. } => { rect.x += delta.x; rect.y += delta.y; } DrawCommand::Text { origin, .. } => { origin.x += delta.x; origin.y += delta.y; } DrawCommand::Clear(_) | DrawCommand::PopClip => {} } } } /// 借用按绘制顺序排列的原生原语,不复制命令集合。 pub fn commands(&self) -> &[DrawCommand] { &self.commands } /// 按原顺序合并另一组绘制命令,不在这里重排层次。 pub fn extend(&mut self, other: DisplayList) { self.commands.extend(other.commands); } }