orinium_browser/engine/renderer_model/
draw_command.rs1use std::sync::Arc;
8use std::sync::atomic::{AtomicU64, Ordering};
9
10use anyhow::{Context, Result};
11use smol_str::SmolStr;
12
13use crate::engine::layouter::types::{Color, Gradient, TextFlowStyle, TextStyle};
14use crate::engine::renderer_model::geom::{AffineTransform, Rect};
15use crate::engine::renderer_model::path::Path;
16
17#[derive(Debug, Clone, Copy)]
23pub enum FillRule {
24 NonZero,
25 EvenOdd,
26}
27
28#[derive(Debug, Clone, PartialEq)]
30pub enum Brush {
31 Solid(Color),
32 Gradient(Gradient),
33 Image(Image),
35}
36
37#[derive(Debug, Clone, PartialEq)]
39pub struct Image {
40 id: u64,
41 width: u32,
42 height: u32,
43 rgba: Arc<[u8]>,
44}
45
46impl Image {
47 pub fn decode(bytes: &[u8]) -> Result<Self> {
51 if let Ok(decoded) = image::load_from_memory(bytes) {
52 let rgba = decoded.to_rgba8();
53 let (width, height) = rgba.dimensions();
54 return Self::from_rgba(width, height, rgba.into_raw());
55 }
56
57 Self::decode_svg(bytes).context("failed to decode image")
58 }
59
60 fn decode_svg(bytes: &[u8]) -> Result<Self> {
61 let raster = crate::engine::svg::rasterize_from_bytes(bytes, None)
62 .map_err(|error| anyhow::anyhow!("invalid SVG image: {error}"))?;
63 Self::from_rgba(raster.width, raster.height, raster.rgba)
64 }
65
66 pub fn from_rgba(width: u32, height: u32, rgba: Vec<u8>) -> Result<Self> {
70 static NEXT_IMAGE_ID: AtomicU64 = AtomicU64::new(1);
71
72 let expected_len = (width as usize)
73 .checked_mul(height as usize)
74 .and_then(|pixels| pixels.checked_mul(4))
75 .context("image dimensions exceed addressable memory")?;
76 if rgba.len() != expected_len {
77 anyhow::bail!(
78 "invalid RGBA byte length: expected {expected_len}, got {}",
79 rgba.len()
80 );
81 }
82 Ok(Self {
83 id: NEXT_IMAGE_ID.fetch_add(1, Ordering::Relaxed),
84 width,
85 height,
86 rgba: Arc::from(rgba),
87 })
88 }
89
90 pub fn id(&self) -> u64 {
92 self.id
93 }
94
95 pub fn width(&self) -> u32 {
97 self.width
98 }
99
100 pub fn height(&self) -> u32 {
102 self.height
103 }
104
105 pub fn rgba(&self) -> &[u8] {
107 &self.rgba
108 }
109}
110
111#[derive(Debug, Clone)]
113pub struct Paint {
114 pub brush: Brush,
115 pub opacity: f32,
116}
117
118#[derive(Debug, Clone)]
120pub enum DrawCommand {
121 Fill {
125 path: Path,
126 paint: Paint,
127 rule: FillRule,
128 },
129 DrawText {
131 x: f32,
132 y: f32,
133 text: SmolStr,
134 style: TextStyle,
135 flow_style: TextFlowStyle,
136 },
137 PushClip {
141 path: Path,
142 rule: FillRule,
143 },
144 PopClip,
145 PushTransform {
147 transform: AffineTransform,
148 },
149 PopTransform,
150
151 SystemUi {
157 kind: SystemUiKind,
158 rect: Rect,
159 },
160}
161
162#[derive(Debug, Clone)]
165pub enum SystemUiKind {
166 WebView { surface_id: usize },
168 Input {
170 value: SmolStr,
171 placeholder: SmolStr,
172 },
173}
174
175#[cfg(test)]
176mod image_tests {
177 use super::Image;
178
179 #[test]
180 fn image_decode_rasterizes_svg_assets() {
181 let image = Image::decode(
182 br##"<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8">
183 <rect width="12" height="8" fill="#ff0000"/>
184 </svg>"##,
185 )
186 .expect("SVG decodes");
187 assert_eq!(image.width(), 12);
188 assert_eq!(image.height(), 8);
189 assert_eq!(&image.rgba()[0..4], &[255, 0, 0, 255]);
190 }
191}