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::{
11 Arc,
12 atomic::{AtomicU64, Ordering},
13};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub struct BackdropKey(u64);
21
22impl BackdropKey {
23 #[must_use]
25 pub fn new() -> Self {
26 static NEXT: AtomicU64 = AtomicU64::new(1);
27 Self(NEXT.fetch_add(1, Ordering::Relaxed))
28 }
29}
30
31impl Default for BackdropKey {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
44pub struct SurfacePartitionId {
45 index: u32,
46 generation: u32,
47}
48
49impl SurfacePartitionId {
50 #[must_use]
51 pub const fn from_parts(index: u32, generation: u32) -> Self {
52 Self { index, generation }
53 }
54
55 #[must_use]
56 pub const fn index(self) -> u32 {
57 self.index
58 }
59
60 #[must_use]
61 pub const fn generation(self) -> u32 {
62 self.generation
63 }
64}
65
66#[derive(Clone, Debug, PartialEq)]
67pub enum PaintCommand {
68 PushSurfacePartition {
71 id: SurfacePartitionId,
72 },
73 PopSurfacePartition,
75 Rect {
76 rect: Rect,
77 color: Color,
78 },
79 RRect {
80 rrect: RRect,
81 brush: Brush,
82 },
83 Border {
84 rrect: RRect,
85 border: Border,
86 },
87 FillPath {
88 path: Arc<Path>,
89 brush: Brush,
90 fill_rule: FillRule,
91 },
92 StrokePath {
93 path: Arc<Path>,
94 brush: Brush,
95 stroke: Stroke,
96 },
97 Image {
99 image: ImageHandle,
100 source: Rect,
101 destination: Rect,
102 sampling: ImageSampling,
103 },
104 GlyphRun {
105 run: Arc<GlyphRun>,
106 color: Color,
107 },
108 PushClip {
109 rect: Rect,
110 },
111 PushClipRRect {
112 rrect: RRect,
113 },
114 PushClipOval {
115 rect: Rect,
116 },
117 PushClipPath {
118 path: Arc<Path>,
119 fill_rule: FillRule,
120 },
121 PopClip,
122 PushTransform {
123 transform: Transform,
124 },
125 PopTransform,
126 PushOpacity {
130 layer: LayerId,
131 alpha: f32,
132 generation: u64,
133 bounds: Rect,
134 },
135 PopOpacity,
136 PushBlur {
139 layer: LayerId,
140 blur: GaussianBlur,
141 generation: u64,
142 bounds: Rect,
143 },
144 PushDropShadow {
147 layer: LayerId,
148 shadow: DropShadowEffect,
149 generation: u64,
150 bounds: Rect,
151 },
152 PushColorFilter {
154 layer: LayerId,
155 filter: ColorFilter,
156 generation: u64,
157 bounds: Rect,
158 },
159 PushBlend {
162 layer: LayerId,
163 mode: BlendMode,
164 generation: u64,
165 bounds: Rect,
166 },
167 PushShaderMask {
170 layer: LayerId,
171 shader: Brush,
172 blend_mode: BlendMode,
173 mask_size: Size,
174 mask_transform: Transform,
175 generation: u64,
176 bounds: Rect,
177 },
178 PushBackdropFilter {
182 layer: LayerId,
183 blur: GaussianBlur,
184 blend_mode: BlendMode,
185 enabled: bool,
186 matrix: Option<Transform>,
189 backdrop_key: Option<BackdropKey>,
191 generation: u64,
192 bounds: Rect,
193 },
194 PopEffect,
195}
196#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
199pub enum ImageSampling {
200 #[default]
201 Linear,
202 Nearest,
203}
204#[derive(Clone, Default)]
207pub struct DisplayList {
208 commands: Option<std::sync::Arc<Vec<PaintCommand>>>,
209}
210impl PartialEq for DisplayList {
211 fn eq(&self, other: &Self) -> bool {
212 self.commands() == other.commands()
213 }
214}
215impl std::fmt::Debug for DisplayList {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 f.debug_struct("DisplayList")
218 .field("commands", &self.commands())
219 .finish()
220 }
221}
222impl DisplayList {
223 #[must_use]
224 pub const fn new() -> Self {
225 Self { commands: None }
226 }
227 fn commands_mut(&mut self) -> &mut Vec<PaintCommand> {
228 std::sync::Arc::make_mut(
229 self.commands
230 .get_or_insert_with(|| std::sync::Arc::new(Vec::new())),
231 )
232 }
233 pub(crate) fn deep_copy(&self) -> Self {
235 Self {
236 commands: Some(std::sync::Arc::new(self.commands().to_vec())),
237 }
238 }
239 pub fn push(&mut self, command: PaintCommand) {
240 let commands = self.commands_mut();
241 if commands.len() == commands.capacity() {
242 commands.reserve_exact(commands.len().max(1));
245 }
246 commands.push(command);
247 }
248 pub fn extend_from(&mut self, other: &Self) {
249 if other.is_empty() {
250 return;
251 }
252 self.commands_mut().extend(other.commands().iter().cloned());
253 }
254
255 #[must_use]
263 pub fn detach_surface_partitions(
264 &self,
265 selected: &std::collections::HashSet<SurfacePartitionId>,
266 ) -> (Self, std::collections::HashMap<SurfacePartitionId, Self>) {
267 let mut parent = Self::new();
268 let mut detached = std::collections::HashMap::<SurfacePartitionId, Self>::new();
269 let mut stack = Vec::<SurfacePartitionId>::new();
270 for command in self.commands() {
271 match command {
272 PaintCommand::PushSurfacePartition { id } => stack.push(*id),
273 PaintCommand::PopSurfacePartition => {
274 let _ = stack.pop();
275 }
276 command => {
277 let target = stack.iter().rev().copied().find(|id| selected.contains(id));
278 if let Some(id) = target {
279 detached.entry(id).or_default().push(command.clone());
280 } else {
281 parent.push(command.clone());
282 }
283 }
284 }
285 }
286 (parent, detached)
287 }
288
289 #[must_use]
292 pub fn translated(&self, offset: incular_core::Offset) -> Self {
293 if self.is_empty() || offset == incular_core::Offset::ZERO {
294 return self.clone();
295 }
296 let mut output = Self::new();
297 output.push(PaintCommand::PushTransform {
298 transform: Transform::translation(offset),
299 });
300 output.extend_from(self);
301 output.push(PaintCommand::PopTransform);
302 output
303 }
304
305 #[must_use]
313 pub fn begins_with_opaque_surface_rect(&self, surface_size: Size) -> bool {
314 if !surface_size.width.is_finite()
315 || !surface_size.height.is_finite()
316 || surface_size.width <= 0.0
317 || surface_size.height <= 0.0
318 {
319 return false;
320 }
321 let mut transforms = vec![Transform::IDENTITY];
322 for command in self.commands() {
323 match command {
324 PaintCommand::PushTransform { transform } => {
325 let next = transforms
326 .last()
327 .copied()
328 .expect("root transform")
329 .then(*transform);
330 if !next.is_translation() {
331 return false;
332 }
333 transforms.push(next);
334 }
335 PaintCommand::PopTransform => {
336 if transforms.len() <= 1 {
337 return false;
338 }
339 transforms.pop();
340 }
341 PaintCommand::PushSurfacePartition { .. } | PaintCommand::PopSurfacePartition => {}
342 PaintCommand::Rect { rect, color } => {
343 if color.alpha != u8::MAX {
344 return false;
345 }
346 let world = transforms
347 .last()
348 .expect("root transform")
349 .transform_rect_bbox(*rect);
350 const EPSILON: f32 = 0.01;
351 return world.origin.x <= EPSILON
352 && world.origin.y <= EPSILON
353 && world.origin.x + world.size.width >= surface_size.width - EPSILON
354 && world.origin.y + world.size.height >= surface_size.height - EPSILON;
355 }
356 _ => return false,
360 }
361 }
362 false
363 }
364 #[must_use]
365 pub fn commands(&self) -> &[PaintCommand] {
366 self.commands.as_deref().map_or(&[], Vec::as_slice)
367 }
368 #[must_use]
369 pub fn len(&self) -> usize {
370 self.commands().len()
371 }
372 #[must_use]
373 pub fn is_empty(&self) -> bool {
374 self.commands().is_empty()
375 }
376 pub fn clear(&mut self) {
378 if let Some(commands) = self.commands.as_mut() {
379 match std::sync::Arc::get_mut(commands) {
380 Some(unique) => unique.clear(),
381 None => self.commands = None,
382 }
383 }
384 }
385}
386#[derive(Default)]
387pub struct Canvas {
388 list: DisplayList,
389 saves: Vec<SaveKind>,
390}
391#[derive(Clone, Copy)]
392enum SaveKind {
393 Transform,
394 Clip,
395}
396impl Canvas {
397 pub fn rect(&mut self, rect: Rect, color: Color) {
398 self.list.push(PaintCommand::Rect { rect, color });
399 }
400 pub fn rrect(&mut self, rrect: RRect, brush: impl Into<Brush>) {
401 self.list.push(PaintCommand::RRect {
402 rrect,
403 brush: brush.into(),
404 });
405 }
406 pub fn border(&mut self, rrect: RRect, border: Border) {
407 self.list.push(PaintCommand::Border { rrect, border });
408 }
409 pub fn fill_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, fill_rule: FillRule) {
410 self.list.push(PaintCommand::FillPath {
411 path,
412 brush: brush.into(),
413 fill_rule,
414 });
415 }
416 pub fn stroke_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, stroke: Stroke) {
417 self.list.push(PaintCommand::StrokePath {
418 path,
419 brush: brush.into(),
420 stroke,
421 });
422 }
423 pub fn draw_path(&mut self, path: Arc<Path>, paint: &Paint, fill_rule: FillRule) {
429 match paint.style_value() {
430 PaintStyle::Fill => self.fill_path(path, paint.brush_value().clone(), fill_rule),
431 PaintStyle::Stroke => {
432 self.stroke_path(path, paint.brush_value().clone(), paint.stroke_value())
433 }
434 }
435 }
436 pub fn image(&mut self, image: ImageHandle, source: Rect, destination: Rect) {
437 self.image_with_sampling(image, source, destination, ImageSampling::Linear);
438 }
439 pub fn image_with_sampling(
440 &mut self,
441 image: ImageHandle,
442 source: Rect,
443 destination: Rect,
444 sampling: ImageSampling,
445 ) {
446 self.list.push(PaintCommand::Image {
447 image,
448 source,
449 destination,
450 sampling,
451 });
452 }
453 pub fn glyph_run(&mut self, run: Arc<GlyphRun>, color: Color) {
454 self.list.push(PaintCommand::GlyphRun { run, color });
455 }
456 pub fn save_transform(&mut self, transform: Transform) {
457 self.saves.push(SaveKind::Transform);
458 self.list.push(PaintCommand::PushTransform { transform });
459 }
460 pub fn save_clip(&mut self, rect: Rect) {
461 self.saves.push(SaveKind::Clip);
462 self.list.push(PaintCommand::PushClip { rect });
463 }
464 pub fn save_clip_rrect(&mut self, rrect: RRect) {
465 self.saves.push(SaveKind::Clip);
466 self.list.push(PaintCommand::PushClipRRect { rrect });
467 }
468 pub fn save_clip_oval(&mut self, rect: Rect) {
469 self.saves.push(SaveKind::Clip);
470 self.list.push(PaintCommand::PushClipOval { rect });
471 }
472 pub fn save_clip_path(&mut self, path: Arc<Path>, fill_rule: FillRule) {
473 self.saves.push(SaveKind::Clip);
474 self.list
475 .push(PaintCommand::PushClipPath { path, fill_rule });
476 }
477 pub fn restore(&mut self) {
478 match self.saves.pop().expect("unbalanced canvas restore") {
479 SaveKind::Transform => self.list.push(PaintCommand::PopTransform),
480 SaveKind::Clip => self.list.push(PaintCommand::PopClip),
481 }
482 }
483 #[must_use]
484 pub fn finish(self) -> DisplayList {
485 assert!(self.saves.is_empty(), "unbalanced display list");
486 self.list
487 }
488}