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