Skip to content

Commit 8d55a5f

Browse files
committed
Rendering abstraction recording WIP
Signed-off-by: Nico Burns <[email protected]>
1 parent 1bd8052 commit 8d55a5f

File tree

2 files changed

+254
-0
lines changed

2 files changed

+254
-0
lines changed

packages/anyrender/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
77
mod wasm_send_sync;
88
pub use wasm_send_sync::*;
99

10+
pub mod recording;
11+
1012
pub type NormalizedCoord = i16;
1113

1214
/// A positioned glyph.

packages/anyrender/src/recording.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
use std::collections::HashMap;
2+
3+
use crate::{Glyph, NormalizedCoord, Scene};
4+
use kurbo::{Affine, BezPath, Rect, Shape, Stroke};
5+
use peniko::{BlendMode, Blob, Brush, BrushRef, Color, Fill, Font, Gradient, Style, StyleRef};
6+
7+
const DEFAULT_TOLERANCE: f64 = 0.1;
8+
9+
#[derive(Clone)]
10+
pub struct ResourceId(pub u64);
11+
12+
#[derive(Clone)]
13+
pub struct Resource {
14+
pub blob: Blob<u8>,
15+
}
16+
17+
#[derive(Clone)]
18+
pub enum RenderCommand {
19+
PushLayer(LayerCmd),
20+
PopLayer,
21+
Stroke(StrokeCmd),
22+
Fill(FillCmd),
23+
GlyphRun(GlyphRunCmd),
24+
BoxShadow(BoxShadowCmd),
25+
}
26+
27+
#[derive(Clone)]
28+
pub enum Paint {
29+
/// Solid color brush.
30+
Solid(Color),
31+
/// Gradient brush.
32+
Gradient(Gradient),
33+
/// Image brush.
34+
Image(ResourceId),
35+
}
36+
37+
#[derive(Clone)]
38+
pub struct LayerCmd {
39+
pub blend: BlendMode,
40+
pub alpha: f32,
41+
pub transform: Affine,
42+
pub clip: BezPath, // TODO: more shape options
43+
}
44+
45+
#[derive(Clone)]
46+
pub struct StrokeCmd {
47+
pub style: Stroke,
48+
pub transform: Affine,
49+
pub brush: Brush, // TODO: review ownership to avoid cloning. Should brushes be a "resource"?
50+
pub brush_transform: Option<Affine>,
51+
pub shape: BezPath, // TODO: more shape options
52+
}
53+
54+
#[derive(Clone)]
55+
pub struct FillCmd {
56+
pub fill: Fill,
57+
pub transform: Affine,
58+
pub brush: Brush, // TODO: review ownership to avoid cloning. Should brushes be a "resource"?
59+
pub brush_transform: Option<Affine>,
60+
pub shape: BezPath, // TODO: more shape options
61+
}
62+
63+
#[derive(Clone)]
64+
pub struct GlyphRunCmd {
65+
pub font_data: ResourceId,
66+
pub font_index: u32,
67+
pub font_size: f32,
68+
pub hint: bool,
69+
pub normalized_coords: Vec<NormalizedCoord>,
70+
pub style: Style,
71+
pub brush: Brush,
72+
pub brush_alpha: f32,
73+
pub transform: Affine,
74+
pub glyph_transform: Option<Affine>,
75+
pub glyphs: Vec<Glyph>,
76+
}
77+
78+
#[derive(Clone)]
79+
pub struct BoxShadowCmd {
80+
pub transform: Affine,
81+
pub rect: Rect,
82+
pub brush: Color,
83+
pub radius: f64,
84+
pub std_dev: f64,
85+
}
86+
87+
pub struct Recording {
88+
pub tolerance: f64,
89+
pub resources: HashMap<u64, Resource>,
90+
pub cmds: Vec<RenderCommand>,
91+
}
92+
93+
impl Default for Recording {
94+
fn default() -> Self {
95+
Self {
96+
tolerance: DEFAULT_TOLERANCE,
97+
resources: HashMap::new(),
98+
cmds: Vec::new(),
99+
}
100+
}
101+
}
102+
103+
impl Recording {
104+
pub fn new() -> Self {
105+
Self::default()
106+
}
107+
108+
pub fn with_tolerance(tolerance: f64) -> Self {
109+
Self {
110+
tolerance,
111+
resources: HashMap::new(),
112+
cmds: Vec::new(),
113+
}
114+
}
115+
116+
pub fn store_resource(&mut self, blob: Blob<u8>) -> ResourceId {
117+
let id = blob.id();
118+
self.resources.entry(id).or_insert(Resource { blob });
119+
ResourceId(id)
120+
}
121+
122+
pub fn store_resource_ref(&mut self, blob: &Blob<u8>) -> ResourceId {
123+
let id = blob.id();
124+
self.resources
125+
.entry(id)
126+
.or_insert_with(|| Resource { blob: blob.clone() });
127+
ResourceId(id)
128+
}
129+
}
130+
131+
impl Scene for Recording {
132+
type Output = Self;
133+
134+
fn reset(&mut self) {
135+
self.cmds.clear()
136+
}
137+
138+
fn push_layer(
139+
&mut self,
140+
blend: impl Into<BlendMode>,
141+
alpha: f32,
142+
transform: Affine,
143+
clip: &impl Shape,
144+
) {
145+
let blend = blend.into();
146+
let clip = clip.into_path(self.tolerance);
147+
let layer = LayerCmd {
148+
blend,
149+
alpha,
150+
transform,
151+
clip,
152+
};
153+
self.cmds.push(RenderCommand::PushLayer(layer));
154+
}
155+
156+
fn pop_layer(&mut self) {
157+
self.cmds.push(RenderCommand::PopLayer);
158+
}
159+
160+
fn stroke<'a>(
161+
&mut self,
162+
style: &Stroke,
163+
transform: Affine,
164+
brush: impl Into<BrushRef<'a>>,
165+
brush_transform: Option<Affine>,
166+
shape: &impl Shape,
167+
) {
168+
let shape = shape.into_path(self.tolerance);
169+
let brush = brush.into().to_owned();
170+
let stroke = StrokeCmd {
171+
style: style.clone(),
172+
transform,
173+
brush,
174+
brush_transform,
175+
shape,
176+
};
177+
self.cmds.push(RenderCommand::Stroke(stroke));
178+
}
179+
180+
fn fill<'a>(
181+
&mut self,
182+
style: Fill,
183+
transform: Affine,
184+
brush: impl Into<BrushRef<'a>>,
185+
brush_transform: Option<Affine>,
186+
shape: &impl Shape,
187+
) {
188+
let shape = shape.into_path(self.tolerance);
189+
let brush = brush.into().to_owned();
190+
let fill = FillCmd {
191+
fill: style,
192+
transform,
193+
brush,
194+
brush_transform,
195+
shape,
196+
};
197+
self.cmds.push(RenderCommand::Fill(fill));
198+
}
199+
200+
fn draw_glyphs<'a, 's: 'a>(
201+
&'a mut self,
202+
font: &'a Font,
203+
font_size: f32,
204+
hint: bool,
205+
normalized_coords: &'a [NormalizedCoord],
206+
style: impl Into<StyleRef<'a>>,
207+
brush: impl Into<BrushRef<'a>>,
208+
brush_alpha: f32,
209+
transform: Affine,
210+
glyph_transform: Option<Affine>,
211+
glyphs: impl Iterator<Item = Glyph>,
212+
) {
213+
let font_index = font.index;
214+
let font_data = self.store_resource_ref(&font.data);
215+
let glyph_run = GlyphRunCmd {
216+
font_data,
217+
font_index,
218+
font_size,
219+
hint,
220+
normalized_coords: normalized_coords.to_vec(),
221+
style: style.into().to_owned(),
222+
brush: brush.into().to_owned(),
223+
brush_alpha,
224+
transform,
225+
glyph_transform,
226+
glyphs: glyphs.into_iter().collect(),
227+
};
228+
self.cmds.push(RenderCommand::GlyphRun(glyph_run));
229+
}
230+
231+
fn draw_box_shadow(
232+
&mut self,
233+
transform: Affine,
234+
rect: Rect,
235+
brush: Color,
236+
radius: f64,
237+
std_dev: f64,
238+
) {
239+
let box_shadow = BoxShadowCmd {
240+
transform,
241+
rect,
242+
brush,
243+
radius,
244+
std_dev,
245+
};
246+
self.cmds.push(RenderCommand::BoxShadow(box_shadow));
247+
}
248+
249+
fn finish(self) -> Self::Output {
250+
self
251+
}
252+
}

0 commit comments

Comments
 (0)