1use crate::compositor::LayerId;
2use crate::effects::{BlendMode, ColorFilter, DropShadowEffect, GaussianBlur};
3use crate::geometry::RRect;
4use crate::glyphs::GlyphRun;
5use crate::gradients::Brush;
6use crate::paint::{Border, FillRule, Paint, PaintStyle, Stroke};
7use crate::paths::Path;
8use incular_core::{Color, Rect, Size, Transform};
9use incular_image::ImageHandle;
10use std::sync::Arc;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct SurfacePartitionId {
20 index: u32,
21 generation: u32,
22}
23
24impl SurfacePartitionId {
25 #[must_use]
26 pub const fn from_parts(index: u32, generation: u32) -> Self {
27 Self { index, generation }
28 }
29
30 #[must_use]
31 pub const fn index(self) -> u32 {
32 self.index
33 }
34
35 #[must_use]
36 pub const fn generation(self) -> u32 {
37 self.generation
38 }
39}
40
41#[derive(Clone, Debug, PartialEq)]
42pub enum PaintCommand {
43 PushSurfacePartition {
46 id: SurfacePartitionId,
47 },
48 PopSurfacePartition,
50 Rect {
51 rect: Rect,
52 color: Color,
53 },
54 RRect {
55 rrect: RRect,
56 brush: Brush,
57 },
58 Border {
59 rrect: RRect,
60 border: Border,
61 },
62 FillPath {
63 path: Arc<Path>,
64 brush: Brush,
65 fill_rule: FillRule,
66 },
67 StrokePath {
68 path: Arc<Path>,
69 brush: Brush,
70 stroke: Stroke,
71 },
72 Image {
74 image: ImageHandle,
75 source: Rect,
76 destination: Rect,
77 sampling: ImageSampling,
78 },
79 GlyphRun {
80 run: Arc<GlyphRun>,
81 color: Color,
82 },
83 PushClip {
84 rect: Rect,
85 },
86 PushClipRRect {
87 rrect: RRect,
88 },
89 PushClipOval {
90 rect: Rect,
91 },
92 PushClipPath {
93 path: Arc<Path>,
94 fill_rule: FillRule,
95 },
96 PopClip,
97 PushTransform {
98 transform: Transform,
99 },
100 PopTransform,
101 PushOpacity {
105 layer: LayerId,
106 alpha: f32,
107 generation: u64,
108 bounds: Rect,
109 },
110 PopOpacity,
111 PushBlur {
114 layer: LayerId,
115 blur: GaussianBlur,
116 generation: u64,
117 bounds: Rect,
118 },
119 PushDropShadow {
122 layer: LayerId,
123 shadow: DropShadowEffect,
124 generation: u64,
125 bounds: Rect,
126 },
127 PushColorFilter {
129 layer: LayerId,
130 filter: ColorFilter,
131 generation: u64,
132 bounds: Rect,
133 },
134 PushBlend {
137 layer: LayerId,
138 mode: BlendMode,
139 generation: u64,
140 bounds: Rect,
141 },
142 PushShaderMask {
145 layer: LayerId,
146 shader: Brush,
147 blend_mode: BlendMode,
148 mask_size: Size,
149 mask_transform: Transform,
150 generation: u64,
151 bounds: Rect,
152 },
153 PushBackdropFilter {
157 layer: LayerId,
158 blur: GaussianBlur,
159 blend_mode: BlendMode,
160 enabled: bool,
161 generation: u64,
162 bounds: Rect,
163 },
164 PopEffect,
165}
166#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
169pub enum ImageSampling {
170 #[default]
171 Linear,
172 Nearest,
173}
174#[derive(Clone, Debug, Default, PartialEq)]
175pub struct DisplayList {
176 commands: Vec<PaintCommand>,
177}
178impl DisplayList {
179 #[must_use]
180 pub const fn new() -> Self {
181 Self {
182 commands: Vec::new(),
183 }
184 }
185 pub fn push(&mut self, command: PaintCommand) {
186 if self.commands.len() == self.commands.capacity() {
187 self.commands.reserve_exact(self.commands.len().max(1));
190 }
191 self.commands.push(command);
192 }
193 pub fn extend_from(&mut self, other: &Self) {
194 self.commands.extend(other.commands.iter().cloned());
195 }
196
197 #[must_use]
205 pub fn detach_surface_partitions(
206 &self,
207 selected: &std::collections::HashSet<SurfacePartitionId>,
208 ) -> (Self, std::collections::HashMap<SurfacePartitionId, Self>) {
209 let mut parent = Self::new();
210 let mut detached = std::collections::HashMap::<SurfacePartitionId, Self>::new();
211 let mut stack = Vec::<SurfacePartitionId>::new();
212 for command in &self.commands {
213 match command {
214 PaintCommand::PushSurfacePartition { id } => stack.push(*id),
215 PaintCommand::PopSurfacePartition => {
216 let _ = stack.pop();
217 }
218 command => {
219 let target = stack.iter().rev().copied().find(|id| selected.contains(id));
220 if let Some(id) = target {
221 detached.entry(id).or_default().push(command.clone());
222 } else {
223 parent.push(command.clone());
224 }
225 }
226 }
227 }
228 (parent, detached)
229 }
230
231 #[must_use]
234 pub fn translated(&self, offset: incular_core::Offset) -> Self {
235 if self.is_empty() || offset == incular_core::Offset::ZERO {
236 return self.clone();
237 }
238 let mut output = Self::new();
239 output.push(PaintCommand::PushTransform {
240 transform: Transform::translation(offset),
241 });
242 output.extend_from(self);
243 output.push(PaintCommand::PopTransform);
244 output
245 }
246
247 #[must_use]
255 pub fn begins_with_opaque_surface_rect(&self, surface_size: Size) -> bool {
256 if !surface_size.width.is_finite()
257 || !surface_size.height.is_finite()
258 || surface_size.width <= 0.0
259 || surface_size.height <= 0.0
260 {
261 return false;
262 }
263 let mut transforms = vec![Transform::IDENTITY];
264 for command in &self.commands {
265 match command {
266 PaintCommand::PushTransform { transform } => {
267 let next = transforms
268 .last()
269 .copied()
270 .expect("root transform")
271 .then(*transform);
272 if !next.is_translation() {
273 return false;
274 }
275 transforms.push(next);
276 }
277 PaintCommand::PopTransform => {
278 if transforms.len() <= 1 {
279 return false;
280 }
281 transforms.pop();
282 }
283 PaintCommand::PushSurfacePartition { .. } | PaintCommand::PopSurfacePartition => {}
284 PaintCommand::Rect { rect, color } => {
285 if color.alpha != u8::MAX {
286 return false;
287 }
288 let world = transforms
289 .last()
290 .expect("root transform")
291 .transform_rect_bbox(*rect);
292 const EPSILON: f32 = 0.01;
293 return world.origin.x <= EPSILON
294 && world.origin.y <= EPSILON
295 && world.origin.x + world.size.width >= surface_size.width - EPSILON
296 && world.origin.y + world.size.height >= surface_size.height - EPSILON;
297 }
298 _ => return false,
302 }
303 }
304 false
305 }
306 #[must_use]
307 pub fn commands(&self) -> &[PaintCommand] {
308 &self.commands
309 }
310 #[must_use]
311 pub fn len(&self) -> usize {
312 self.commands.len()
313 }
314 #[must_use]
315 pub fn is_empty(&self) -> bool {
316 self.commands.is_empty()
317 }
318}
319#[derive(Default)]
320pub struct Canvas {
321 list: DisplayList,
322 saves: Vec<SaveKind>,
323}
324#[derive(Clone, Copy)]
325enum SaveKind {
326 Transform,
327 Clip,
328}
329impl Canvas {
330 pub fn rect(&mut self, rect: Rect, color: Color) {
331 self.list.push(PaintCommand::Rect { rect, color });
332 }
333 pub fn rrect(&mut self, rrect: RRect, brush: impl Into<Brush>) {
334 self.list.push(PaintCommand::RRect {
335 rrect,
336 brush: brush.into(),
337 });
338 }
339 pub fn border(&mut self, rrect: RRect, border: Border) {
340 self.list.push(PaintCommand::Border { rrect, border });
341 }
342 pub fn fill_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, fill_rule: FillRule) {
343 self.list.push(PaintCommand::FillPath {
344 path,
345 brush: brush.into(),
346 fill_rule,
347 });
348 }
349 pub fn stroke_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, stroke: Stroke) {
350 self.list.push(PaintCommand::StrokePath {
351 path,
352 brush: brush.into(),
353 stroke,
354 });
355 }
356 pub fn draw_path(&mut self, path: Arc<Path>, paint: &Paint, fill_rule: FillRule) {
362 match paint.style_value() {
363 PaintStyle::Fill => self.fill_path(path, paint.brush_value().clone(), fill_rule),
364 PaintStyle::Stroke => {
365 self.stroke_path(path, paint.brush_value().clone(), paint.stroke_value())
366 }
367 }
368 }
369 pub fn image(&mut self, image: ImageHandle, source: Rect, destination: Rect) {
370 self.image_with_sampling(image, source, destination, ImageSampling::Linear);
371 }
372 pub fn image_with_sampling(
373 &mut self,
374 image: ImageHandle,
375 source: Rect,
376 destination: Rect,
377 sampling: ImageSampling,
378 ) {
379 self.list.push(PaintCommand::Image {
380 image,
381 source,
382 destination,
383 sampling,
384 });
385 }
386 pub fn glyph_run(&mut self, run: Arc<GlyphRun>, color: Color) {
387 self.list.push(PaintCommand::GlyphRun { run, color });
388 }
389 pub fn save_transform(&mut self, transform: Transform) {
390 self.saves.push(SaveKind::Transform);
391 self.list.push(PaintCommand::PushTransform { transform });
392 }
393 pub fn save_clip(&mut self, rect: Rect) {
394 self.saves.push(SaveKind::Clip);
395 self.list.push(PaintCommand::PushClip { rect });
396 }
397 pub fn save_clip_rrect(&mut self, rrect: RRect) {
398 self.saves.push(SaveKind::Clip);
399 self.list.push(PaintCommand::PushClipRRect { rrect });
400 }
401 pub fn save_clip_oval(&mut self, rect: Rect) {
402 self.saves.push(SaveKind::Clip);
403 self.list.push(PaintCommand::PushClipOval { rect });
404 }
405 pub fn save_clip_path(&mut self, path: Arc<Path>, fill_rule: FillRule) {
406 self.saves.push(SaveKind::Clip);
407 self.list
408 .push(PaintCommand::PushClipPath { path, fill_rule });
409 }
410 pub fn restore(&mut self) {
411 match self.saves.pop().expect("unbalanced canvas restore") {
412 SaveKind::Transform => self.list.push(PaintCommand::PopTransform),
413 SaveKind::Clip => self.list.push(PaintCommand::PopClip),
414 }
415 }
416 #[must_use]
417 pub fn finish(self) -> DisplayList {
418 assert!(self.saves.is_empty(), "unbalanced display list");
419 self.list
420 }
421}