1use std::rc::Rc;
2
3pub use cranpose_render_common::graph_scene::{HitRegion, Scene};
4use cranpose_render_common::{layer_shadow::ShadowRRect, primitive_emit::ShapeDrawParams};
5use cranpose_ui::TextStyle;
6use cranpose_ui_graphics::{
7 ArcGeometry, BlendMode, Brush, Color, ColorFilter, ImageBitmap, ImageSampling, LineGeometry,
8 Point, Rect, RoundedCornerShape, Stroke, Trapezoid,
9};
10
11#[derive(Clone, Debug)]
12pub(crate) struct DrawShape {
13 pub rect: Rect,
14 pub snap_anchor: Option<Point>,
15 pub snap_to_pixel_grid: bool,
16 pub brush: Brush,
17 pub shape: Option<RoundedCornerShape>,
18 pub stroke: Option<Stroke>,
19 pub arc: Option<ArcGeometry>,
20 pub line: Option<LineGeometry>,
21 pub trapezoid: Option<Trapezoid>,
22 pub z_index: usize,
23 pub clip: Option<Rect>,
24 pub blend_mode: BlendMode,
25}
26
27fn shifted(point: Point, dx: f32, dy: f32) -> Point {
29 Point::new(point.x + dx, point.y + dy)
30}
31
32impl DrawShape {
33 pub(crate) fn translated(&self, dx: f32, dy: f32) -> Self {
35 Self {
36 rect: self.rect.translate(dx, dy),
37 snap_anchor: self.snap_anchor.map(|anchor| shifted(anchor, dx, dy)),
38 arc: self.arc.map(|arc| ArcGeometry {
39 center: shifted(arc.center, dx, dy),
40 ..arc
41 }),
42 line: self.line.map(|line| {
43 line.placed(shifted(line.start, dx, dy), shifted(line.end, dx, dy), 1.0)
44 }),
45 trapezoid: self.trapezoid.map(|trapezoid| trapezoid.translate(dx, dy)),
46 clip: None,
47 ..self.clone()
48 }
49 }
50
51 pub(crate) fn of_params(params: ShapeDrawParams, z_index: usize) -> Self {
53 Self {
54 rect: params.rect,
55 snap_anchor: None,
56 snap_to_pixel_grid: false,
57 brush: params.brush.into_brush(),
58 shape: params.shape,
59 stroke: params.stroke,
60 arc: params.arc,
61 line: params.line,
62 trapezoid: params.trapezoid,
63 z_index,
64 clip: params.clip,
65 blend_mode: params.blend_mode,
66 }
67 }
68}
69
70#[derive(Clone, Debug)]
71pub(crate) struct TextDraw {
72 pub rect: Rect,
73 pub snap_anchor: Option<Point>,
74 pub text: Rc<cranpose_ui::text::AnnotatedString>,
75 pub color: Color,
76 pub text_style: TextStyle,
77 pub font_size: f32,
78 pub scale: f32,
79 pub z_index: usize,
80 pub clip: Option<Rect>,
81}
82
83#[derive(Clone)]
84pub(crate) struct ImageDraw {
85 pub rect: Rect,
86 pub snap_anchor: Option<Point>,
87 pub image: ImageBitmap,
88 pub alpha: f32,
89 pub color_filter: Option<ColorFilter>,
90 pub sampling: ImageSampling,
91 pub z_index: usize,
92 pub clip: Option<Rect>,
93 pub blend_mode: BlendMode,
94 pub src_rect: Option<Rect>,
95}
96
97#[derive(Clone, Copy, Debug)]
100pub(crate) struct ShadowDraw {
101 pub shadow: ShadowRRect,
102 pub snap_anchor: Option<Point>,
103 pub color: Color,
104 pub z_index: usize,
105 pub clip: Option<Rect>,
106}
107
108impl TextDraw {
109 pub(crate) fn translated(&self, dx: f32, dy: f32) -> Self {
111 Self {
112 rect: self.rect.translate(dx, dy),
113 snap_anchor: self.snap_anchor.map(|anchor| shifted(anchor, dx, dy)),
114 clip: None,
115 ..self.clone()
116 }
117 }
118}
119
120impl ImageDraw {
121 pub(crate) fn translated(&self, dx: f32, dy: f32) -> Self {
123 Self {
124 rect: self.rect.translate(dx, dy),
125 snap_anchor: self.snap_anchor.map(|anchor| shifted(anchor, dx, dy)),
126 clip: None,
127 ..self.clone()
128 }
129 }
130}
131
132impl ShadowDraw {
133 pub(crate) fn translated(&self, dx: f32, dy: f32) -> Self {
135 Self {
136 shadow: self.shadow.translated(dx, dy),
137 snap_anchor: self.snap_anchor.map(|anchor| shifted(anchor, dx, dy)),
138 clip: None,
139 ..*self
140 }
141 }
142}
143
144pub(crate) struct RasterScene {
145 pub shapes: Vec<DrawShape>,
146 pub images: Vec<ImageDraw>,
147 pub texts: Vec<TextDraw>,
148 pub shadows: Vec<ShadowDraw>,
149 pub next_z: usize,
150}
151
152impl RasterScene {
153 pub fn new() -> Self {
154 Self {
155 shapes: Vec::new(),
156 images: Vec::new(),
157 texts: Vec::new(),
158 shadows: Vec::new(),
159 next_z: 0,
160 }
161 }
162
163 pub fn push_shape(
164 &mut self,
165 rect: Rect,
166 brush: Brush,
167 shape: Option<RoundedCornerShape>,
168 clip: Option<Rect>,
169 blend_mode: BlendMode,
170 ) {
171 let z_index = self.next_z;
172 self.next_z += 1;
173 self.shapes.push(DrawShape {
174 rect,
175 snap_anchor: None,
176 snap_to_pixel_grid: false,
177 brush,
178 shape,
179 stroke: None,
180 arc: None,
181 line: None,
182 trapezoid: None,
183 z_index,
184 clip,
185 blend_mode,
186 });
187 }
188
189 pub fn push_shadow(&mut self, shadow: ShadowRRect, color: Color, clip: Option<Rect>) {
190 let z_index = self.next_z;
191 self.next_z += 1;
192 self.shadows.push(ShadowDraw {
193 shadow,
194 snap_anchor: None,
195 color,
196 z_index,
197 clip,
198 });
199 }
200
201 pub fn push_pixel_snapped_shape(
202 &mut self,
203 rect: Rect,
204 brush: Brush,
205 shape: Option<RoundedCornerShape>,
206 clip: Option<Rect>,
207 blend_mode: BlendMode,
208 ) {
209 let z_index = self.next_z;
210 self.next_z += 1;
211 self.shapes.push(DrawShape {
212 rect,
213 snap_anchor: None,
214 snap_to_pixel_grid: true,
215 brush,
216 shape,
217 stroke: None,
218 arc: None,
219 line: None,
220 trapezoid: None,
221 z_index,
222 clip,
223 blend_mode,
224 });
225 }
226
227 pub fn push_shape_params(&mut self, params: ShapeDrawParams) {
230 let z_index = self.next_z;
231 self.next_z += 1;
232 self.shapes.push(DrawShape::of_params(params, z_index));
233 }
234
235 #[expect(clippy::too_many_arguments)]
236 pub fn push_image_with_geometry(
237 &mut self,
238 rect: Rect,
239 _local_rect: Rect,
240 _quad: [[f32; 2]; 4],
241 image: ImageBitmap,
242 alpha: f32,
243 color_filter: Option<ColorFilter>,
244 sampling: ImageSampling,
245 clip: Option<Rect>,
246 src_rect: Option<Rect>,
247 blend_mode: BlendMode,
248 ) {
249 let z_index = self.next_z;
250 self.next_z += 1;
251 self.images.push(ImageDraw {
252 rect,
253 snap_anchor: None,
254 image,
255 alpha: alpha.clamp(0.0, 1.0),
256 color_filter,
257 sampling,
258 z_index,
259 clip,
260 blend_mode,
261 src_rect,
262 });
263 }
264
265 #[expect(clippy::too_many_arguments)]
266 pub fn push_text(
267 &mut self,
268 rect: Rect,
269 text: Rc<cranpose_ui::text::AnnotatedString>,
270 color: Color,
271 text_style: TextStyle,
272 font_size: f32,
273 scale: f32,
274 clip: Option<Rect>,
275 ) {
276 let z_index = self.next_z;
277 self.next_z += 1;
278 self.texts.push(TextDraw {
279 rect,
280 snap_anchor: None,
281 text,
282 color,
283 text_style,
284 font_size,
285 scale,
286 z_index,
287 clip,
288 });
289 }
290}
291
292impl Default for RasterScene {
293 fn default() -> Self {
294 Self::new()
295 }
296}