Files
endorphin-tessera-gui/src/render/display_list.rs
T

178 lines
5.3 KiB
Rust

//! 面向设备像素的绘制指令集合。它承载矩形、文字、裁剪与 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<crate::scene3d::View>,
},
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<DrawCommand>,
}
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);
}
}