1use super::{paint_composition_brush, ServerCompositionSurfaceBrush};
6use crate::media::immutable::ImmutableSolidColorBrush;
7use crate::media::imaging::BitmapBlendingMode;
8use crate::media::{BoxShadows, Color, ColorMatrix, ColorMatrixAlphaMode, EffectExtensions, IEffect, ImmutableBlurEffect};
9use crate::platform::{AlphaFormat, IDrawingContextImpl, PixelFormat, PixelFormats};
10use crate::rendering::composition::effects::{
11 blur_amount_to_radius, BlendEffectMode, EffectColor, EffectGraph, EffectMatrix, EffectNode, EffectNodeKind,
12 EffectScalar, PixelOp,
13};
14use crate::rendering::composition::generated::{ServerCompositionBrushHooks, ServerCompositionBrushProps};
15use crate::rendering::composition::server::{
16 impl_server_render_resource, IAnimatedServerObject, IServerObject, IServerRenderResource,
17 IServerRenderResourceObserver, ServerCompositionPropertySet, ServerCompositor, ServerObjectId,
18 ServerRenderResource,
19};
20use crate::rendering::composition::transport::BatchStreamReader;
21use crate::{Matrix, PixelSize, Rect, RoundedRect, Thickness, Vector};
22use std::any::{Any, TypeId};
23use std::cell::{Cell, RefCell};
24use std::rc::{Rc, Weak};
25use std::sync::Arc;
26use std::time::Duration;
27
28pub(crate) struct EffectBrushChanges {
32 pub(crate) properties: Option<ServerObjectId>,
33 pub(crate) sources: Option<Vec<(String, Option<ServerObjectId>)>>,
34}
35
36#[derive(Clone, Copy, PartialEq, Eq)]
38enum LayerPhase {
39 Before,
42 After,
45}
46
47fn is_empty(rect: &Rect) -> bool {
48 rect.width <= 0.0 || rect.height <= 0.0
49}
50
51pub struct ServerCompositionEffectBrush {
59 this: Weak<ServerCompositionEffectBrush>,
60 base: ServerRenderResource,
61 props: ServerCompositionBrushProps,
62 graph: Arc<EffectGraph>,
63 properties: RefCell<Option<Rc<ServerCompositionPropertySet>>>,
64 sources: RefCell<Vec<(String, Option<Rc<dyn IServerObject>>)>>,
65 painting: Cell<bool>,
68}
69
70impl ServerCompositionEffectBrush {
71 pub(crate) fn new(compositor: &Rc<ServerCompositor>, graph: Arc<EffectGraph>) -> Rc<ServerCompositionEffectBrush> {
74 let brush = Rc::new_cyclic(|this: &Weak<ServerCompositionEffectBrush>| ServerCompositionEffectBrush {
75 this: this.clone(),
76 base: ServerRenderResource::new(
77 compositor,
78 this.clone() as Weak<dyn IAnimatedServerObject>,
79 this.clone() as Weak<dyn IServerRenderResource>,
80 ),
81 props: ServerCompositionBrushProps::new(),
82 graph,
83 properties: RefCell::new(None),
84 sources: RefCell::new(Vec::new()),
85 painting: Cell::new(false),
86 });
87 brush.base.object().activate();
88 brush
89 }
90
91 pub fn brush_props(&self) -> &ServerCompositionBrushProps {
93 &self.props
94 }
95
96 pub fn is_disposed(&self) -> bool {
97 self.base.is_disposed()
98 }
99
100 pub fn is_loaded(&self) -> bool {
103 self.graph.is_loaded()
104 }
105
106 pub fn properties(&self) -> Option<Rc<ServerCompositionPropertySet>> {
108 self.properties.borrow().clone()
109 }
110
111 pub fn source(&self, name: &str) -> Option<Rc<dyn IServerObject>> {
113 self.sources.borrow().iter().find(|(existing, _)| existing == name).and_then(|(_, source)| source.clone())
114 }
115
116 fn subscription_key(&self) -> usize {
117 self.this.as_ptr() as *const () as usize
118 }
119
120 fn observer(&self) -> Option<Rc<dyn IServerRenderResourceObserver>> {
121 self.this.upgrade().map(|this| this as Rc<dyn IServerRenderResourceObserver>)
122 }
123
124 fn set_properties(&self, properties: Option<Rc<ServerCompositionPropertySet>>) {
127 if let Some(old) = self.properties.borrow().as_ref() {
128 old.changed().remove(self.subscription_key());
129 }
130 if let Some(new) = &properties {
131 let weak = self.this.clone();
132 new.changed().add(
133 self.subscription_key(),
134 Rc::new(move || {
135 if let Some(brush) = weak.upgrade() {
136 brush.base.core().invalidated(&*brush);
137 }
138 }),
139 );
140 }
141 *self.properties.borrow_mut() = properties;
142 }
143
144 fn is_self(&self, source: &Rc<dyn IServerObject>) -> bool {
146 std::ptr::addr_eq(Rc::as_ptr(source), self.this.as_ptr())
147 }
148
149 fn set_sources(&self, sources: Vec<(String, Option<Rc<dyn IServerObject>>)>) {
152 let old = std::mem::replace(&mut *self.sources.borrow_mut(), sources);
153 let Some(observer) = self.observer() else { return };
154 let resources = |sources: &[(String, Option<Rc<dyn IServerObject>>)]| -> Vec<Rc<dyn IServerRenderResource>> {
155 sources
156 .iter()
157 .filter_map(|(_, source)| source.clone())
158 .filter(|source| !self.is_self(source))
159 .filter_map(|source| source.as_render_resource())
160 .collect()
161 };
162 for resource in resources(old.as_slice()) {
163 resource.remove_observer(&observer);
164 }
165 let new = resources(self.sources.borrow().as_slice());
166 for resource in new {
167 resource.add_observer(&observer);
168 }
169 }
170
171 fn scalar(&self, value: &EffectScalar) -> f64 {
174 if let (Some(key), Some(properties)) = (&value.key, self.properties.borrow().as_ref()) {
175 if let Some(current) = properties.get_value(key).try_cast::<f64>() {
176 return current;
177 }
178 }
179 value.value as f64
180 }
181
182 fn color(&self, value: &EffectColor) -> Color {
184 if let (Some(key), Some(properties)) = (&value.key, self.properties.borrow().as_ref()) {
185 if let Some(current) = properties.get_value(key).try_cast::<Color>() {
186 return current;
187 }
188 }
189 value.value
190 }
191
192 fn blur_radius(&self, amount: &EffectScalar) -> f64 {
194 blur_amount_to_radius(self.scalar(amount))
195 }
196
197 fn unit(&self, value: &EffectScalar) -> f64 {
199 let value = self.scalar(value);
200 if value.is_nan() {
201 return 0.0;
202 }
203 value.clamp(0.0, 1.0)
204 }
205
206 fn push_blur(canvas: &mut dyn IDrawingContextImpl, rect: Rect, radius: f64) -> bool {
210 if radius <= 0.0 {
211 return false;
212 }
213 match canvas.as_drawing_context_impl_with_effects() {
214 Some(effects) => {
215 effects.push_effect(Some(rect), &ImmutableBlurEffect::new(radius));
216 true
217 }
218 None => false,
221 }
222 }
223
224 fn pop_blur(canvas: &mut dyn IDrawingContextImpl, pushed: bool) {
225 if pushed {
226 if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
227 effects.pop_effect();
228 }
229 }
230 }
231
232 fn blur_is_pushed(canvas: &mut dyn IDrawingContextImpl, radius: f64) -> bool {
235 radius > 0.0 && canvas.as_drawing_context_impl_with_effects().is_some()
236 }
237
238 fn paint_node_with_opacity(&self, node: &EffectNode, canvas: &mut dyn IDrawingContextImpl, rect: Rect, opacity: f64) {
240 if opacity <= 0.0 {
241 return;
242 }
243 if opacity < 1.0 {
244 canvas.push_opacity(opacity, Some(rect));
245 }
246 self.paint_node(node, canvas, rect);
247 if opacity < 1.0 {
248 canvas.pop_opacity();
249 }
250 }
251
252 fn blur_reach(radius: f64) -> f64 {
255 if radius <= 0.0 {
256 return 0.0;
257 }
258 let blur = ImmutableBlurEffect::new(radius);
259 let padding = EffectExtensions::get_effect_output_padding(Some(&blur as &dyn IEffect));
260 padding.left.max(padding.top).max(padding.right).max(padding.bottom)
261 }
262
263 fn matrix(&self, matrix: &EffectMatrix) -> ColorMatrix {
265 match matrix {
266 EffectMatrix::Fixed(matrix) => *matrix,
267 EffectMatrix::Saturation(saturation) => ColorMatrix::saturation(self.scalar(saturation) as f32),
268 EffectMatrix::Tint(color) => {
269 let color = self.color(color);
270 ColorMatrix::tint([color.r, color.g, color.b, color.a].map(|channel| channel as f32 / 255.0))
271 }
272 }
273 }
274
275 fn node_color(&self, node: &EffectNode) -> [f32; 4] {
277 let EffectNodeKind::ColorSource(color) = &node.kind else { return [0.0; 4] };
278 let color = self.color(color);
279 let alpha = color.a as f32 / 255.0;
280 [color.r as f32 / 255.0 * alpha, color.g as f32 / 255.0 * alpha, color.b as f32 / 255.0 * alpha, alpha]
281 }
282
283 fn arithmetic_matrix(
286 &self,
287 multiply: &EffectScalar,
288 source1: &EffectScalar,
289 source2: &EffectScalar,
290 offset: &EffectScalar,
291 image_is_source1: bool,
292 color: &EffectNode,
293 ) -> ColorMatrix {
294 let c = self.node_color(color);
295 let k1 = self.scalar(multiply) as f32;
296 let (k_image, k_color) = if image_is_source1 {
297 (self.scalar(source1) as f32, self.scalar(source2) as f32)
298 } else {
299 (self.scalar(source2) as f32, self.scalar(source1) as f32)
300 };
301 let k4 = self.scalar(offset) as f32;
302 ColorMatrix::scale_and_offset(c.map(|c| k1 * c + k_image), c.map(|c| k_color * c + k4))
303 }
304
305 fn push_blend(canvas: &mut dyn IDrawingContextImpl, rect: Rect, mode: BitmapBlendingMode) -> bool {
307 match canvas.as_drawing_context_impl_with_layer_effects() {
308 Some(layers) => {
309 layers.push_blend_layer(rect, mode);
310 true
311 }
312 None => false,
315 }
316 }
317
318 fn pop_blend(canvas: &mut dyn IDrawingContextImpl, pushed: bool) {
319 if pushed {
320 if let Some(layers) = canvas.as_drawing_context_impl_with_layer_effects() {
321 layers.pop_blend_layer();
322 }
323 }
324 }
325
326 fn push_matrix(canvas: &mut dyn IDrawingContextImpl, rect: Rect, matrix: &ColorMatrix, alpha_mode: ColorMatrixAlphaMode) -> bool {
329 if matrix.is_identity() {
330 return false;
331 }
332 match canvas.as_drawing_context_impl_with_layer_effects() {
333 Some(layers) => {
334 layers.push_color_matrix_layer(rect, matrix, alpha_mode);
335 true
336 }
337 None => false,
340 }
341 }
342
343 fn pop_matrix(canvas: &mut dyn IDrawingContextImpl, pushed: bool) {
344 if pushed {
345 if let Some(layers) = canvas.as_drawing_context_impl_with_layer_effects() {
346 layers.pop_color_matrix_layer();
347 }
348 }
349 }
350
351 fn paint_node(&self, node: &EffectNode, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
353 match &node.kind {
354 EffectNodeKind::Parameter(name) => {
355 if let Some(source) = self.source(name).filter(|source| !self.is_self(source)) {
356 paint_composition_brush(canvas, &source, rect);
357 }
358 }
359 EffectNodeKind::GaussianBlur { amount, hard, source } => {
360 let radius = self.blur_radius(amount);
361 if *hard && Self::blur_is_pushed(canvas, radius) {
362 let reach = Self::blur_reach(radius);
366 let wide = rect.inflate(reach);
367 canvas.push_clip(rect);
368 let pushed = Self::push_blur(canvas, wide, radius);
369 self.paint_node(source, canvas, wide);
370 Self::pop_blur(canvas, pushed);
371 canvas.pop_clip();
372 } else {
373 let pushed = Self::push_blur(canvas, rect, radius);
374 self.paint_node(source, canvas, rect);
375 Self::pop_blur(canvas, pushed);
376 }
377 }
378 EffectNodeKind::Opacity { opacity, source } => {
379 self.paint_node_with_opacity(source, canvas, rect, self.unit(opacity));
380 }
381 EffectNodeKind::CrossFade { weight, source1, source2 } => {
382 let weight = self.unit(weight);
386 if weight <= 0.0 || weight >= 1.0 {
387 self.paint_node_with_opacity(source1, canvas, rect, 1.0 - weight);
388 self.paint_node_with_opacity(source2, canvas, rect, weight);
389 return;
390 }
391 canvas.push_layer(rect);
392 self.paint_node_with_opacity(source1, canvas, rect, 1.0 - weight);
393 let pushed = Self::push_blend(canvas, rect, BitmapBlendingMode::Plus);
394 self.paint_node_with_opacity(source2, canvas, rect, weight);
395 Self::pop_blend(canvas, pushed);
396 canvas.pop_layer();
397 }
398 EffectNodeKind::Composite { mode, sources } => {
399 if *mode == BitmapBlendingMode::SourceOver {
400 for source in sources {
401 self.paint_node(source, canvas, rect);
402 }
403 return;
404 }
405 canvas.push_layer(rect);
408 for (index, source) in sources.iter().enumerate() {
409 let pushed = index > 0 && Self::push_blend(canvas, rect, *mode);
410 self.paint_node(source, canvas, rect);
411 Self::pop_blend(canvas, pushed);
412 }
413 canvas.pop_layer();
414 }
415 EffectNodeKind::ColorSource(color) => {
416 let color = self.color(color);
417 if color.a > 0 {
418 let brush = ImmutableSolidColorBrush::new(color);
419 canvas.draw_rectangle(Some(&brush), None, RoundedRect::from_rect(rect), &BoxShadows::default());
420 }
421 }
422 EffectNodeKind::Blend { mode, background, foreground } => {
423 canvas.push_layer(rect);
426 self.paint_node(background, canvas, rect);
427 let pushed = Self::push_blend(canvas, rect, *mode);
428 self.paint_node(foreground, canvas, rect);
429 Self::pop_blend(canvas, pushed);
430 canvas.pop_layer();
431 }
432 EffectNodeKind::ColorMatrix { matrix, alpha_mode, source } => {
433 let matrix = self.matrix(matrix);
434 let pushed = Self::push_matrix(canvas, rect, &matrix, *alpha_mode);
435 self.paint_node(source, canvas, rect);
436 Self::pop_matrix(canvas, pushed);
437 }
438 EffectNodeKind::AlphaMask { source, mask } => {
439 canvas.push_layer(rect);
440 self.paint_node(source, canvas, rect);
441 let pushed = Self::push_blend(canvas, rect, BitmapBlendingMode::DestinationIn);
442 if pushed {
443 self.paint_node(mask, canvas, rect);
444 }
445 Self::pop_blend(canvas, pushed);
446 canvas.pop_layer();
447 }
448 EffectNodeKind::Border { tile, source } => {
449 let surface = match &source.kind {
450 EffectNodeKind::Parameter(name) if *tile => self.source(name),
451 _ => None,
452 };
453 match surface.as_ref().and_then(|surface| surface.as_any().downcast_ref::<ServerCompositionSurfaceBrush>()) {
454 Some(surface) => surface.paint_tiled(canvas, rect),
455 None => self.paint_node(source, canvas, rect),
456 }
457 }
458 EffectNodeKind::ArithmeticWithColor { multiply, source1, source2, offset, image_is_source1, image, color } => {
459 let matrix = self.arithmetic_matrix(multiply, source1, source2, offset, *image_is_source1, color);
460 let pushed = Self::push_matrix(canvas, rect, &matrix, ColorMatrixAlphaMode::Straight);
461 self.paint_node(image, canvas, rect);
462 Self::pop_matrix(canvas, pushed);
463 }
464 EffectNodeKind::Shadow { amount, color, source } => {
465 let radius = self.blur_radius(amount);
466 let matrix = shadow_matrix(self.color(color));
467 let blurred = Self::push_blur(canvas, rect, radius);
468 let colored = Self::push_matrix(canvas, rect, &matrix, ColorMatrixAlphaMode::Premultiplied);
469 self.paint_node(source, canvas, rect);
470 Self::pop_matrix(canvas, colored);
471 Self::pop_blur(canvas, blurred);
472 }
473 EffectNodeKind::Pixels { op, sources } => self.paint_pixels(op, sources, canvas, rect),
474 EffectNodeKind::SceneLighting { ambient, diffuse, specular, shine } => {
475 let material = crate::rendering::composition::server::LightingMaterial {
476 ambient: self.scalar(ambient),
477 diffuse: self.scalar(diffuse),
478 specular: self.scalar(specular),
479 shine: self.scalar(shine),
480 };
481 crate::rendering::composition::server::paint_scene_lighting(canvas, rect, &material);
482 }
483 }
484 }
485
486 fn render_node_pixels(&self, node: &EffectNode, canvas: &dyn IDrawingContextImpl, rect: Rect, size: PixelSize) -> Option<Vec<u8>> {
490 let compositor = self.base.compositor()?;
491 let transform = Matrix::create_translation(-rect.x, -rect.y)
492 * Matrix::create_scale(size.width as f64 / rect.width, size.height as f64 / rect.height);
493 let _ = canvas;
494 compositor.render_interface().with_platform_render_interface(|render_interface| {
495 let bitmap = render_interface.create_render_target_bitmap(size, Vector::new(96.0, 96.0));
496 {
497 let mut context = bitmap.create_drawing_context();
498 context.set_transform(transform);
499 self.paint_node(node, &mut *context, rect);
500 context.dispose();
501 }
502 let framebuffer = bitmap.lock();
503 let (width, height) = (size.width as usize, size.height as usize);
504 let row_bytes = framebuffer.row_bytes() as usize;
505 let bgra = framebuffer.format() == PixelFormat::BGRA8888;
506 let premultiplied = framebuffer.alpha_format() != AlphaFormat::Unpremul;
507 let mut rgba = vec![0u8; width * height * 4];
508 framebuffer.with_data(&mut |data| {
509 for y in 0..height {
510 for x in 0..width {
511 let at = y * row_bytes + x * 4;
512 let Some(pixel) = data.get(at..at + 4) else { continue };
513 let (mut r, mut g, mut b, a) =
514 if bgra { (pixel[2], pixel[1], pixel[0], pixel[3]) } else { (pixel[0], pixel[1], pixel[2], pixel[3]) };
515 if !premultiplied {
516 let scale = |c: u8| ((u32::from(c) * u32::from(a) + 127) / 255) as u8;
517 (r, g, b) = (scale(r), scale(g), scale(b));
518 }
519 rgba[(y * width + x) * 4..(y * width + x) * 4 + 4].copy_from_slice(&[r, g, b, a]);
520 }
521 }
522 });
523 framebuffer.dispose();
524 rgba
525 })
526 }
527
528 fn paint_pixels(&self, op: &PixelOp, sources: &[EffectNode], canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
533 if sources.is_empty() || is_empty(&rect) {
534 return;
535 }
536 let transform = canvas.transform();
537 let scale_x = (transform.m11 * transform.m11 + transform.m12 * transform.m12).sqrt().max(1e-6);
538 let scale_y = (transform.m21 * transform.m21 + transform.m22 * transform.m22).sqrt().max(1e-6);
539 let size = PixelSize::new(
540 ((rect.width * scale_x).ceil() as i32).clamp(1, 4096),
541 ((rect.height * scale_y).ceil() as i32).clamp(1, 4096),
542 );
543 let mut images = Vec::with_capacity(sources.len());
544 for source in sources {
545 match self.render_node_pixels(source, canvas, rect, size) {
546 Some(pixels) => images.push(pixels),
547 None => return,
548 }
549 }
550 let mut images = images.into_iter();
551 let Some(mut result) = images.next() else { return };
552 let width = size.width as usize;
553 for (index, other) in images.enumerate() {
554 match op {
555 PixelOp::Blend(mode) => blend_pixels(*mode, &mut result, &other, width),
556 PixelOp::Arithmetic { multiply, source1, source2, offset } => {
557 let k = [self.scalar(multiply), self.scalar(source1), self.scalar(source2), self.scalar(offset)];
558 arithmetic_pixels(k.map(|k| k as f32), &mut result, &other);
559 }
560 PixelOp::MaskInvert => mask_invert_pixels(&mut result, &other),
561 }
562 if !matches!(op, PixelOp::MaskInvert) && index == 0 {
564 break;
565 }
566 }
567 let Some(compositor) = self.base.compositor() else { return };
568 let bitmap = compositor.render_interface().with_platform_render_interface(|render_interface| {
569 render_interface.load_bitmap_from_pixels(
570 PixelFormats::RGBA8888,
571 AlphaFormat::Premul,
572 &result,
573 size,
574 Vector::new(96.0, 96.0),
575 size.width * 4,
576 )
577 });
578 if let Some(bitmap) = bitmap {
579 let source = Rect::new(0.0, 0.0, size.width as f64, size.height as f64);
580 canvas.draw_bitmap(&*bitmap, 1.0, source, rect);
581 }
582 }
583
584 pub fn paint(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
588 let Some(root) = self.graph.root.as_ref() else { return };
589 let opacity = self.props.opacity();
590 if opacity <= 0.0 || is_empty(&rect) || self.painting.replace(true) {
591 return;
592 }
593 if opacity != 1.0 {
594 canvas.push_opacity(opacity, Some(rect));
595 }
596 self.paint_node(root, canvas, rect);
597 if opacity != 1.0 {
598 canvas.pop_opacity();
599 }
600 self.painting.set(false);
601 }
602
603 fn paint_around_layer(
606 &self,
607 node: &EffectNode,
608 path: &[usize],
609 canvas: &mut dyn IDrawingContextImpl,
610 rect: Rect,
611 phase: LayerPhase,
612 ) {
613 let Some((&index, rest)) = path.split_first() else {
614 return;
617 };
618 let before = phase == LayerPhase::Before;
619 let has_layers = canvas.as_drawing_context_impl_with_layer_effects().is_some();
620 match &node.kind {
621 EffectNodeKind::Parameter(_) | EffectNodeKind::ColorSource(_) | EffectNodeKind::SceneLighting { .. } => {}
622 EffectNodeKind::GaussianBlur { amount, source, .. } => {
623 let radius = self.blur_radius(amount);
624 match phase {
625 LayerPhase::Before => {
626 Self::push_blur(canvas, rect, radius);
627 self.paint_around_layer(source, rest, canvas, rect, phase);
628 }
629 LayerPhase::After => {
630 self.paint_around_layer(source, rest, canvas, rect, phase);
631 let pushed = Self::blur_is_pushed(canvas, radius);
632 Self::pop_blur(canvas, pushed);
633 }
634 }
635 }
636 EffectNodeKind::Opacity { opacity, source } => {
637 let opacity = self.unit(opacity);
638 self.wrap_layer_in_opacity(source, rest, canvas, rect, phase, opacity);
639 }
640 EffectNodeKind::CrossFade { weight, source1, source2 } => {
641 let weight = self.unit(weight);
642 if index == 0 {
643 self.wrap_layer_in_opacity(source1, rest, canvas, rect, phase, 1.0 - weight);
644 if phase == LayerPhase::After {
645 self.paint_node_with_opacity(source2, canvas, rect, weight);
646 }
647 } else {
648 if phase == LayerPhase::Before {
649 self.paint_node_with_opacity(source1, canvas, rect, 1.0 - weight);
650 }
651 self.wrap_layer_in_opacity(source2, rest, canvas, rect, phase, weight);
652 }
653 }
654 EffectNodeKind::Composite { mode, sources } => {
655 let Some(layer) = sources.get(index) else { return };
656 let blended = *mode != BitmapBlendingMode::SourceOver && has_layers;
657 if before {
658 if blended {
659 canvas.push_layer(rect);
660 }
661 for (position, source) in sources[..index].iter().enumerate() {
662 let pushed = blended && position > 0 && Self::push_blend(canvas, rect, *mode);
663 self.paint_node(source, canvas, rect);
664 Self::pop_blend(canvas, pushed);
665 }
666 if blended && index > 0 {
667 Self::push_blend(canvas, rect, *mode);
668 }
669 self.paint_around_layer(layer, rest, canvas, rect, phase);
670 } else {
671 self.paint_around_layer(layer, rest, canvas, rect, phase);
672 Self::pop_blend(canvas, blended && index > 0);
673 for source in &sources[index + 1..] {
674 let pushed = blended && Self::push_blend(canvas, rect, *mode);
675 self.paint_node(source, canvas, rect);
676 Self::pop_blend(canvas, pushed);
677 }
678 if blended {
679 canvas.pop_layer();
680 }
681 }
682 }
683 EffectNodeKind::Blend { mode, background, foreground } => {
684 if index == 0 {
685 if before {
687 canvas.push_layer(rect);
688 self.paint_around_layer(background, rest, canvas, rect, phase);
689 } else {
690 self.paint_around_layer(background, rest, canvas, rect, phase);
691 let pushed = Self::push_blend(canvas, rect, *mode);
692 self.paint_node(foreground, canvas, rect);
693 Self::pop_blend(canvas, pushed);
694 canvas.pop_layer();
695 }
696 } else if before {
697 canvas.push_layer(rect);
699 self.paint_node(background, canvas, rect);
700 Self::push_blend(canvas, rect, *mode);
701 self.paint_around_layer(foreground, rest, canvas, rect, phase);
702 } else {
703 self.paint_around_layer(foreground, rest, canvas, rect, phase);
704 Self::pop_blend(canvas, has_layers);
705 canvas.pop_layer();
706 }
707 }
708 EffectNodeKind::ColorMatrix { matrix, alpha_mode, source } => {
709 let matrix = self.matrix(matrix);
710 if before {
711 Self::push_matrix(canvas, rect, &matrix, *alpha_mode);
712 self.paint_around_layer(source, rest, canvas, rect, phase);
713 } else {
714 self.paint_around_layer(source, rest, canvas, rect, phase);
715 Self::pop_matrix(canvas, has_layers && !matrix.is_identity());
716 }
717 }
718 EffectNodeKind::ArithmeticWithColor { multiply, source1, source2, offset, image_is_source1, image, color } => {
719 let image_index = if *image_is_source1 { 0 } else { 1 };
720 if index != image_index {
721 return;
723 }
724 let matrix = self.arithmetic_matrix(multiply, source1, source2, offset, *image_is_source1, color);
725 if before {
726 Self::push_matrix(canvas, rect, &matrix, ColorMatrixAlphaMode::Straight);
727 self.paint_around_layer(image, rest, canvas, rect, phase);
728 } else {
729 self.paint_around_layer(image, rest, canvas, rect, phase);
730 Self::pop_matrix(canvas, has_layers && !matrix.is_identity());
731 }
732 }
733 EffectNodeKind::AlphaMask { source, mask } => {
734 if index == 0 {
735 if before {
737 canvas.push_layer(rect);
738 self.paint_around_layer(source, rest, canvas, rect, phase);
739 } else {
740 self.paint_around_layer(source, rest, canvas, rect, phase);
741 let pushed = Self::push_blend(canvas, rect, BitmapBlendingMode::DestinationIn);
742 if pushed {
743 self.paint_node(mask, canvas, rect);
744 }
745 Self::pop_blend(canvas, pushed);
746 canvas.pop_layer();
747 }
748 } else if before {
749 canvas.push_layer(rect);
751 self.paint_node(source, canvas, rect);
752 Self::push_blend(canvas, rect, BitmapBlendingMode::DestinationIn);
753 self.paint_around_layer(mask, rest, canvas, rect, phase);
754 } else {
755 self.paint_around_layer(mask, rest, canvas, rect, phase);
756 Self::pop_blend(canvas, has_layers);
757 canvas.pop_layer();
758 }
759 }
760 EffectNodeKind::Border { source, .. } => {
761 self.paint_around_layer(source, rest, canvas, rect, phase);
762 }
763 EffectNodeKind::Shadow { amount, color, source } => {
764 let radius = self.blur_radius(amount);
765 let matrix = shadow_matrix(self.color(color));
766 if before {
767 Self::push_blur(canvas, rect, radius);
768 Self::push_matrix(canvas, rect, &matrix, ColorMatrixAlphaMode::Premultiplied);
769 self.paint_around_layer(source, rest, canvas, rect, phase);
770 } else {
771 self.paint_around_layer(source, rest, canvas, rect, phase);
772 Self::pop_matrix(canvas, has_layers && !matrix.is_identity());
773 let pushed = Self::blur_is_pushed(canvas, radius);
774 Self::pop_blur(canvas, pushed);
775 }
776 }
777 EffectNodeKind::Pixels { sources, .. } => {
781 if let Some(layer) = sources.get(index) {
782 self.paint_around_layer(layer, rest, canvas, rect, phase);
783 }
784 }
785 }
786 }
787
788 fn wrap_layer_in_opacity(
791 &self,
792 source: &EffectNode,
793 rest: &[usize],
794 canvas: &mut dyn IDrawingContextImpl,
795 rect: Rect,
796 phase: LayerPhase,
797 opacity: f64,
798 ) {
799 match phase {
800 LayerPhase::Before => {
801 if opacity < 1.0 {
802 canvas.push_opacity(opacity.max(0.0), Some(rect));
803 }
804 self.paint_around_layer(source, rest, canvas, rect, phase);
805 }
806 LayerPhase::After => {
807 self.paint_around_layer(source, rest, canvas, rect, phase);
808 if opacity < 1.0 {
809 canvas.pop_opacity();
810 }
811 }
812 }
813 }
814
815 pub fn push_for_layer(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
826 let opacity = self.props.opacity();
827 if opacity != 1.0 {
828 canvas.push_opacity(opacity.max(0.0), Some(rect));
829 }
830 match (self.graph.root.as_ref(), self.graph.layer_path.as_deref()) {
831 (Some(root), Some(path)) => self.paint_around_layer(root, path, canvas, rect, LayerPhase::Before),
832 (root, _) => {
833 if let Some(root) = root {
834 self.paint_node(root, canvas, rect);
835 }
836 canvas.push_clip(Rect::new(rect.x, rect.y, 0.0, 0.0));
838 }
839 }
840 }
841
842 pub fn pop_for_layer(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
845 match (self.graph.root.as_ref(), self.graph.layer_path.as_deref()) {
846 (Some(root), Some(path)) => self.paint_around_layer(root, path, canvas, rect, LayerPhase::After),
847 _ => canvas.pop_clip(),
848 }
849 if self.props.opacity() != 1.0 {
850 canvas.pop_opacity();
851 }
852 }
853
854 pub fn layer_padding(&self) -> Thickness {
857 let mut padding = Thickness::default();
858 let (Some(mut node), Some(path)) = (self.graph.root.as_ref(), self.graph.layer_path.as_deref()) else {
859 return padding;
860 };
861 for &index in path {
862 if let EffectNodeKind::GaussianBlur { amount, .. } = &node.kind {
863 let radius = self.blur_radius(amount);
864 if radius > 0.0 {
865 let blur = ImmutableBlurEffect::new(radius);
866 padding = padding + EffectExtensions::get_effect_output_padding(Some(&blur as &dyn IEffect));
867 }
868 }
869 match node.sources().get(index).copied() {
870 Some(source) => node = source,
871 None => break,
872 }
873 }
874 padding
875 }
876}
877
878impl_server_render_resource!(
879 ServerCompositionEffectBrush,
880 base,
881 composition_property: ServerCompositionBrushProps::get_composition_property
882);
883
884impl ServerCompositionBrushHooks for ServerCompositionEffectBrush {}
885
886impl IServerObject for ServerCompositionEffectBrush {
887 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
888 self.props.deserialize_changes_core(self, reader, committed_at);
889 let Some(changes) = reader.read_value::<EffectBrushChanges>() else { return };
890 let compositor = self.base.compositor();
891 let resolve = |id: ServerObjectId| compositor.as_ref().and_then(|compositor| compositor.get_object(id));
892 if let Some(id) = changes.properties {
893 let properties = resolve(id).and_then(|set| set.into_any_rc().downcast::<ServerCompositionPropertySet>().ok());
894 self.set_properties(properties);
895 }
896 if let Some(sources) = changes.sources {
897 self.set_sources(sources.into_iter().map(|(name, id)| (name, id.and_then(resolve))).collect());
898 }
899 }
900
901 fn values_invalidated(&self) {
902 self.base.core().invalidated(self);
903 }
904
905 fn dispose(&self) {
906 self.set_properties(None);
907 self.set_sources(Vec::new());
908 self.base.core().dispose();
909 }
910
911 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
912 Some(self)
913 }
914
915 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
916 Some(self)
917 }
918
919 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
920 self.props.find_props(type_id)
921 }
922
923 fn as_any(&self) -> &dyn Any {
924 self
925 }
926
927 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
928 self
929 }
930}
931
932pub struct ServerCompositionBackdropBrush {
938 base: ServerRenderResource,
939 props: ServerCompositionBrushProps,
940}
941
942impl ServerCompositionBackdropBrush {
943 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionBackdropBrush> {
945 let brush = Rc::new_cyclic(|this: &Weak<ServerCompositionBackdropBrush>| ServerCompositionBackdropBrush {
946 base: ServerRenderResource::new(
947 compositor,
948 this.clone() as Weak<dyn IAnimatedServerObject>,
949 this.clone() as Weak<dyn IServerRenderResource>,
950 ),
951 props: ServerCompositionBrushProps::new(),
952 });
953 brush.base.object().activate();
954 brush
955 }
956
957 pub fn brush_props(&self) -> &ServerCompositionBrushProps {
959 &self.props
960 }
961
962 pub fn paint(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
965 let opacity = self.props.opacity();
966 if opacity <= 0.0 {
967 return;
968 }
969 if opacity < 1.0 {
970 canvas.push_opacity(opacity, Some(rect));
971 }
972 crate::rendering::composition::server::draw_backdrop(canvas, rect);
973 if opacity < 1.0 {
974 canvas.pop_opacity();
975 }
976 }
977}
978
979impl_server_render_resource!(
980 ServerCompositionBackdropBrush,
981 base,
982 composition_property: ServerCompositionBrushProps::get_composition_property
983);
984
985impl ServerCompositionBrushHooks for ServerCompositionBackdropBrush {}
986
987impl IServerObject for ServerCompositionBackdropBrush {
988 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
989 self.props.deserialize_changes_core(self, reader, committed_at);
990 }
991
992 fn values_invalidated(&self) {
993 self.base.core().invalidated(self);
994 }
995
996 fn dispose(&self) {
997 self.base.core().dispose();
998 }
999
1000 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
1001 Some(self)
1002 }
1003
1004 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
1005 Some(self)
1006 }
1007
1008 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
1009 self.props.find_props(type_id)
1010 }
1011
1012 fn as_any(&self) -> &dyn Any {
1013 self
1014 }
1015
1016 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
1017 self
1018 }
1019}
1020
1021pub struct ServerCompositionHostBackdropBrush {
1028 base: ServerRenderResource,
1029 props: ServerCompositionBrushProps,
1030}
1031
1032impl ServerCompositionHostBackdropBrush {
1033 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionHostBackdropBrush> {
1035 let brush = Rc::new_cyclic(|this: &Weak<ServerCompositionHostBackdropBrush>| ServerCompositionHostBackdropBrush {
1036 base: ServerRenderResource::new(
1037 compositor,
1038 this.clone() as Weak<dyn IAnimatedServerObject>,
1039 this.clone() as Weak<dyn IServerRenderResource>,
1040 ),
1041 props: ServerCompositionBrushProps::new(),
1042 });
1043 brush.base.object().activate();
1044 brush
1045 }
1046
1047 pub fn brush_props(&self) -> &ServerCompositionBrushProps {
1049 &self.props
1050 }
1051
1052 pub fn paint(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
1054 let transparent = crate::rendering::composition::server::painting_visual()
1055 .and_then(|visual| visual.root())
1056 .is_some_and(|target| target.transparency_level() != crate::rendering::composition::CompositionTransparencyLevel::None);
1057 if !transparent || self.props.opacity() <= 0.0 {
1058 return;
1059 }
1060 canvas.push_clip(rect);
1061 if let Some(layers) = canvas.as_drawing_context_impl_with_layer_effects() {
1062 layers.push_blend_layer(rect, BitmapBlendingMode::Source);
1065 layers.pop_blend_layer();
1066 }
1067 canvas.pop_clip();
1068 }
1069}
1070
1071impl_server_render_resource!(
1072 ServerCompositionHostBackdropBrush,
1073 base,
1074 composition_property: ServerCompositionBrushProps::get_composition_property
1075);
1076
1077impl ServerCompositionBrushHooks for ServerCompositionHostBackdropBrush {}
1078
1079impl IServerObject for ServerCompositionHostBackdropBrush {
1080 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
1081 self.props.deserialize_changes_core(self, reader, committed_at);
1082 }
1083
1084 fn values_invalidated(&self) {
1085 self.base.core().invalidated(self);
1086 }
1087
1088 fn dispose(&self) {
1089 self.base.core().dispose();
1090 }
1091
1092 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
1093 Some(self)
1094 }
1095
1096 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
1097 Some(self)
1098 }
1099
1100 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
1101 self.props.find_props(type_id)
1102 }
1103
1104 fn as_any(&self) -> &dyn Any {
1105 self
1106 }
1107
1108 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
1109 self
1110 }
1111}
1112
1113fn shadow_matrix(color: Color) -> ColorMatrix {
1116 let channel = |value: u8| f32::from(value) / 255.0;
1117 ColorMatrix::new([
1118 [0.0, 0.0, 0.0, 0.0],
1119 [0.0, 0.0, 0.0, 0.0],
1120 [0.0, 0.0, 0.0, 0.0],
1121 [0.0, 0.0, 0.0, channel(color.a)],
1122 [channel(color.r), channel(color.g), channel(color.b), 0.0],
1123 ])
1124}
1125
1126fn unpremultiply(pixel: &[u8]) -> ([f32; 3], f32) {
1128 let alpha = f32::from(pixel[3]) / 255.0;
1129 if alpha <= 0.0 {
1130 return ([0.0; 3], 0.0);
1131 }
1132 let channel = |value: u8| (f32::from(value) / 255.0 / alpha).min(1.0);
1133 ([channel(pixel[0]), channel(pixel[1]), channel(pixel[2])], alpha)
1134}
1135
1136fn store(pixel: &mut [u8], color: [f32; 3], alpha: f32) {
1137 let alpha = alpha.clamp(0.0, 1.0);
1138 for channel in 0..3 {
1139 pixel[channel] = (color[channel].clamp(0.0, 1.0) * alpha * 255.0).round() as u8;
1140 }
1141 pixel[3] = (alpha * 255.0).round() as u8;
1142}
1143
1144fn luminosity(color: [f32; 3]) -> f32 {
1145 0.3 * color[0] + 0.59 * color[1] + 0.11 * color[2]
1146}
1147
1148fn color_dodge(b: f32, s: f32) -> f32 {
1149 if b <= 0.0 {
1150 0.0
1151 } else if s >= 1.0 {
1152 1.0
1153 } else {
1154 (b / (1.0 - s)).min(1.0)
1155 }
1156}
1157
1158fn color_burn(b: f32, s: f32) -> f32 {
1159 if b >= 1.0 {
1160 1.0
1161 } else if s <= 0.0 {
1162 0.0
1163 } else {
1164 1.0 - ((1.0 - b) / s).min(1.0)
1165 }
1166}
1167
1168fn dissolve_noise(index: usize) -> f32 {
1171 let mut x = index as u32 ^ 0x9E37_79B9;
1172 x = x.wrapping_mul(0x85EB_CA6B);
1173 x ^= x >> 13;
1174 x = x.wrapping_mul(0xC2B2_AE35);
1175 x ^= x >> 16;
1176 (x & 0xFFFF) as f32 / 65535.0
1177}
1178
1179fn blend_pixels(mode: BlendEffectMode, background: &mut [u8], foreground: &[u8], width: usize) {
1184 let _ = width;
1185 for (index, (back, fore)) in background.chunks_exact_mut(4).zip(foreground.chunks_exact(4)).enumerate() {
1186 let (cb, ab) = unpremultiply(back);
1187 let (cs, as_) = unpremultiply(fore);
1188 if mode == BlendEffectMode::Dissolve {
1189 if as_ > 0.0 && dissolve_noise(index) < as_ {
1192 store(back, cs, 1.0);
1193 }
1194 continue;
1195 }
1196 let blended: [f32; 3] = match mode {
1197 BlendEffectMode::DarkerColor => {
1198 if luminosity(cs) < luminosity(cb) {
1199 cs
1200 } else {
1201 cb
1202 }
1203 }
1204 BlendEffectMode::LighterColor => {
1205 if luminosity(cs) > luminosity(cb) {
1206 cs
1207 } else {
1208 cb
1209 }
1210 }
1211 _ => std::array::from_fn(|channel| {
1212 let (b, s) = (cb[channel], cs[channel]);
1213 match mode {
1214 BlendEffectMode::LinearBurn => (b + s - 1.0).max(0.0),
1215 BlendEffectMode::VividLight => {
1216 if s <= 0.5 {
1217 color_burn(b, 2.0 * s)
1218 } else {
1219 color_dodge(b, 2.0 * (s - 0.5))
1220 }
1221 }
1222 BlendEffectMode::LinearLight => (b + 2.0 * s - 1.0).clamp(0.0, 1.0),
1223 BlendEffectMode::PinLight => {
1224 if s <= 0.5 {
1225 b.min(2.0 * s)
1226 } else {
1227 b.max(2.0 * s - 1.0)
1228 }
1229 }
1230 BlendEffectMode::HardMix => {
1231 if b + s >= 1.0 {
1232 1.0
1233 } else {
1234 0.0
1235 }
1236 }
1237 BlendEffectMode::Subtract => (b - s).max(0.0),
1238 BlendEffectMode::Division => {
1239 if s <= 0.0 {
1240 if b <= 0.0 {
1241 0.0
1242 } else {
1243 1.0
1244 }
1245 } else {
1246 (b / s).min(1.0)
1247 }
1248 }
1249 _ => s,
1251 }
1252 }),
1253 };
1254 let alpha = as_ + ab - as_ * ab;
1255 if alpha <= 0.0 {
1256 store(back, [0.0; 3], 0.0);
1257 continue;
1258 }
1259 let premultiplied: [f32; 3] = std::array::from_fn(|channel| {
1260 (1.0 - as_) * ab * cb[channel] + (1.0 - ab) * as_ * cs[channel] + as_ * ab * blended[channel]
1261 });
1262 store(back, premultiplied.map(|value| value / alpha), alpha);
1263 }
1264}
1265
1266fn arithmetic_pixels(k: [f32; 4], first: &mut [u8], second: &[u8]) {
1270 for (one, two) in first.chunks_exact_mut(4).zip(second.chunks_exact(4)) {
1271 let mut out = [0.0f32; 4];
1272 for channel in 0..4 {
1273 let (a, b) = (f32::from(one[channel]) / 255.0, f32::from(two[channel]) / 255.0);
1274 out[channel] = (k[0] * a * b + k[1] * a + k[2] * b + k[3]).clamp(0.0, 1.0);
1275 }
1276 for channel in 0..3 {
1277 out[channel] = out[channel].min(out[3]);
1278 }
1279 for channel in 0..4 {
1280 one[channel] = (out[channel] * 255.0).round() as u8;
1281 }
1282 }
1283}
1284
1285fn mask_invert_pixels(destination: &mut [u8], source: &[u8]) {
1290 for (dest, src) in destination.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
1291 let source_alpha = f32::from(src[3]) / 255.0;
1292 let dest_alpha = f32::from(dest[3]) / 255.0;
1293 for channel in 0..3 {
1294 let d = f32::from(dest[channel]) / 255.0;
1295 let out = source_alpha * (dest_alpha - d) + (1.0 - source_alpha) * d;
1296 dest[channel] = (out.clamp(0.0, dest_alpha) * 255.0).round() as u8;
1297 }
1298 }
1299}
1300
1301#[cfg(test)]
1302mod pixel_tests {
1303 use super::*;
1304
1305 #[test]
1306 fn the_extra_blend_modes_follow_their_formulas() {
1307 let opaque = |r: u8, g: u8, b: u8| vec![r, g, b, 255];
1308 let mut back = opaque(200, 100, 50);
1309 blend_pixels(BlendEffectMode::Subtract, &mut back, &opaque(100, 150, 25), 1);
1310 assert_eq!(opaque(100, 0, 25), back);
1311 let mut back = opaque(200, 100, 50);
1312 blend_pixels(BlendEffectMode::LinearBurn, &mut back, &opaque(100, 255, 0), 1);
1313 assert_eq!(opaque(45, 100, 0), back);
1314 let mut back = opaque(100, 100, 100);
1315 blend_pixels(BlendEffectMode::HardMix, &mut back, &opaque(200, 100, 0), 1);
1316 assert_eq!(opaque(255, 0, 0), back);
1317 let mut back = opaque(10, 10, 10);
1318 blend_pixels(BlendEffectMode::LighterColor, &mut back, &opaque(200, 0, 0), 1);
1319 assert_eq!(opaque(200, 0, 0), back);
1320 }
1321
1322 #[test]
1323 fn a_blend_over_transparency_is_the_foreground() {
1324 let mut back = vec![0, 0, 0, 0];
1325 blend_pixels(BlendEffectMode::Division, &mut back, &[100, 50, 0, 255], 1);
1326 assert_eq!(vec![100, 50, 0, 255], back);
1327 }
1328
1329 #[test]
1330 fn the_arithmetic_composite_of_two_images() {
1331 let mut first = vec![255, 0, 128, 255];
1332 arithmetic_pixels([0.0, 0.5, 0.5, 0.0], &mut first, &[0, 255, 128, 255]);
1333 assert_eq!(vec![128, 128, 128, 255], first);
1334 }
1335
1336 #[test]
1337 fn mask_invert_inverts_the_destination_under_the_source() {
1338 let mut destination = vec![255, 0, 64, 255, 255, 0, 64, 255];
1339 mask_invert_pixels(&mut destination, &[0, 0, 0, 255, 0, 0, 0, 0]);
1340 assert_eq!(vec![0, 255, 191, 255, 255, 0, 64, 255], destination);
1341 }
1342}