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]
301 pub fn lift_surface_partitions(self) -> Self {
302 if !self
303 .commands()
304 .iter()
305 .any(|command| matches!(command, PaintCommand::PushSurfacePartition { .. }))
306 {
307 return self;
308 }
309 Self::lift_partitions_in(self.commands())
310 }
311
312 fn lift_partitions_in(commands: &[PaintCommand]) -> Self {
313 let mut base = Self::new();
314 let mut lifted = Vec::<Self>::new();
315 let mut transforms = Vec::<Transform>::new();
316 let mut index = 0;
317 while index < commands.len() {
318 match &commands[index] {
319 PaintCommand::PushSurfacePartition { .. } => {
320 let start = index;
322 let mut depth = 0usize;
323 while index < commands.len() {
324 match commands[index] {
325 PaintCommand::PushSurfacePartition { .. } => depth += 1,
326 PaintCommand::PopSurfacePartition => {
327 depth -= 1;
328 if depth == 0 {
329 break;
330 }
331 }
332 _ => {}
333 }
334 index += 1;
335 }
336 let end = index.min(commands.len() - 1);
337 let mut entry = Self::new();
338 for transform in &transforms {
339 entry.push(PaintCommand::PushTransform {
340 transform: *transform,
341 });
342 }
343 entry.push(commands[start].clone());
344 entry.extend_from(&Self::lift_partitions_in(&commands[start + 1..end]));
345 if matches!(commands[end], PaintCommand::PopSurfacePartition) && end > start {
346 entry.push(PaintCommand::PopSurfacePartition);
347 }
348 for _ in &transforms {
349 entry.push(PaintCommand::PopTransform);
350 }
351 lifted.push(entry);
352 }
353 command => {
354 match command {
355 PaintCommand::PushTransform { transform } => transforms.push(*transform),
356 PaintCommand::PopTransform => {
357 let _ = transforms.pop();
358 }
359 _ => {}
360 }
361 base.push(command.clone());
362 }
363 }
364 index += 1;
365 }
366 for entry in &lifted {
367 base.extend_from(entry);
368 }
369 base
370 }
371
372 #[must_use]
375 pub fn translated(&self, offset: incular_core::Offset) -> Self {
376 if self.is_empty() || offset == incular_core::Offset::ZERO {
377 return self.clone();
378 }
379 let mut output = Self::new();
380 output.push(PaintCommand::PushTransform {
381 transform: Transform::translation(offset),
382 });
383 output.extend_from(self);
384 output.push(PaintCommand::PopTransform);
385 output
386 }
387
388 #[must_use]
396 pub fn begins_with_opaque_surface_rect(&self, surface_size: Size) -> bool {
397 if !surface_size.width.is_finite()
398 || !surface_size.height.is_finite()
399 || surface_size.width <= 0.0
400 || surface_size.height <= 0.0
401 {
402 return false;
403 }
404 let mut transforms = vec![Transform::IDENTITY];
405 for command in self.commands() {
406 match command {
407 PaintCommand::PushTransform { transform } => {
408 let next = transforms
409 .last()
410 .copied()
411 .expect("root transform")
412 .then(*transform);
413 if !next.is_translation() {
414 return false;
415 }
416 transforms.push(next);
417 }
418 PaintCommand::PopTransform => {
419 if transforms.len() <= 1 {
420 return false;
421 }
422 transforms.pop();
423 }
424 PaintCommand::PushSurfacePartition { .. } | PaintCommand::PopSurfacePartition => {}
425 PaintCommand::Rect { rect, color } => {
426 if color.alpha != u8::MAX {
427 return false;
428 }
429 let world = transforms
430 .last()
431 .expect("root transform")
432 .transform_rect_bbox(*rect);
433 const EPSILON: f32 = 0.01;
434 return world.origin.x <= EPSILON
435 && world.origin.y <= EPSILON
436 && world.origin.x + world.size.width >= surface_size.width - EPSILON
437 && world.origin.y + world.size.height >= surface_size.height - EPSILON;
438 }
439 _ => return false,
443 }
444 }
445 false
446 }
447 #[must_use]
448 pub fn commands(&self) -> &[PaintCommand] {
449 self.commands.as_deref().map_or(&[], Vec::as_slice)
450 }
451 #[must_use]
452 pub fn len(&self) -> usize {
453 self.commands().len()
454 }
455 #[must_use]
456 pub fn is_empty(&self) -> bool {
457 self.commands().is_empty()
458 }
459 pub fn clear(&mut self) {
461 if let Some(commands) = self.commands.as_mut() {
462 match std::sync::Arc::get_mut(commands) {
463 Some(unique) => unique.clear(),
464 None => self.commands = None,
465 }
466 }
467 }
468}
469#[derive(Default)]
470pub struct Canvas {
471 list: DisplayList,
472 saves: Vec<SaveKind>,
473}
474#[derive(Clone, Copy)]
475enum SaveKind {
476 Transform,
477 Clip,
478}
479impl Canvas {
480 pub fn rect(&mut self, rect: Rect, color: Color) {
481 self.list.push(PaintCommand::Rect { rect, color });
482 }
483 pub fn rrect(&mut self, rrect: RRect, brush: impl Into<Brush>) {
484 self.list.push(PaintCommand::RRect {
485 rrect,
486 brush: brush.into(),
487 });
488 }
489 pub fn border(&mut self, rrect: RRect, border: Border) {
490 self.list.push(PaintCommand::Border { rrect, border });
491 }
492 pub fn fill_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, fill_rule: FillRule) {
493 self.list.push(PaintCommand::FillPath {
494 path,
495 brush: brush.into(),
496 fill_rule,
497 });
498 }
499 pub fn stroke_path(&mut self, path: Arc<Path>, brush: impl Into<Brush>, stroke: Stroke) {
500 self.list.push(PaintCommand::StrokePath {
501 path,
502 brush: brush.into(),
503 stroke,
504 });
505 }
506 pub fn draw_path(&mut self, path: Arc<Path>, paint: &Paint, fill_rule: FillRule) {
512 match paint.style_value() {
513 PaintStyle::Fill => self.fill_path(path, paint.brush_value().clone(), fill_rule),
514 PaintStyle::Stroke => {
515 self.stroke_path(path, paint.brush_value().clone(), paint.stroke_value())
516 }
517 }
518 }
519 pub fn image(&mut self, image: ImageHandle, source: Rect, destination: Rect) {
520 self.image_with_sampling(image, source, destination, ImageSampling::Linear);
521 }
522 pub fn image_with_sampling(
523 &mut self,
524 image: ImageHandle,
525 source: Rect,
526 destination: Rect,
527 sampling: ImageSampling,
528 ) {
529 self.list.push(PaintCommand::Image {
530 image,
531 source,
532 destination,
533 sampling,
534 });
535 }
536 pub fn glyph_run(&mut self, run: Arc<GlyphRun>, color: Color) {
537 self.list.push(PaintCommand::GlyphRun { run, color });
538 }
539 pub fn save_transform(&mut self, transform: Transform) {
540 self.saves.push(SaveKind::Transform);
541 self.list.push(PaintCommand::PushTransform { transform });
542 }
543 pub fn save_clip(&mut self, rect: Rect) {
544 self.saves.push(SaveKind::Clip);
545 self.list.push(PaintCommand::PushClip { rect });
546 }
547 pub fn save_clip_rrect(&mut self, rrect: RRect) {
548 self.saves.push(SaveKind::Clip);
549 self.list.push(PaintCommand::PushClipRRect { rrect });
550 }
551 pub fn save_clip_oval(&mut self, rect: Rect) {
552 self.saves.push(SaveKind::Clip);
553 self.list.push(PaintCommand::PushClipOval { rect });
554 }
555 pub fn save_clip_path(&mut self, path: Arc<Path>, fill_rule: FillRule) {
556 self.saves.push(SaveKind::Clip);
557 self.list
558 .push(PaintCommand::PushClipPath { path, fill_rule });
559 }
560 pub fn restore(&mut self) {
561 match self.saves.pop().expect("unbalanced canvas restore") {
562 SaveKind::Transform => self.list.push(PaintCommand::PopTransform),
563 SaveKind::Clip => self.list.push(PaintCommand::PopClip),
564 }
565 }
566 #[must_use]
567 pub fn finish(self) -> DisplayList {
568 assert!(self.saves.is_empty(), "unbalanced display list");
569 self.list
570 }
571}