use super::{
CompositionProperty, IServerObject, IServerRenderResourceObserver, IServerVisualContent, ServerCompositionEffectBrush,
ServerCompositionVisual, ServerDropShadow, ServerSizeDependantVisual, ServerVisualRenderContext,
};
use crate::media::{Color, EffectExtensions, IEffect, ImmutableDropShadowEffect};
use crate::platform::{IDrawingContextImpl, LtrbRect};
use crate::rendering::composition::generated::{
ServerCompositionContainerVisualProps, ServerLayerVisualHooks, ServerLayerVisualProps,
};
use crate::rendering::composition::transport::BatchStreamReader;
use crate::{Rect, Thickness};
use std::any::{Any, TypeId};
use std::rc::Rc;
use std::time::Duration;
pub(crate) fn observe_visual_resource(
visual: &ServerCompositionVisual,
resource: Option<Rc<dyn IServerObject>>,
observe: bool,
) {
let Some(resource) = resource.and_then(|resource| resource.as_render_resource()) else { return };
let Some(this) = visual.weak().upgrade() else { return };
let observer: Rc<dyn IServerRenderResourceObserver> = this;
if observe {
resource.add_observer(&observer);
} else {
resource.remove_observer(&observer);
}
}
pub fn drop_shadow_effect(shadow: &ServerDropShadow) -> ImmutableDropShadowEffect {
drop_shadow_effect_in(shadow, None)
}
pub fn drop_shadow_effect_in(shadow: &ServerDropShadow, visual: Option<&ServerCompositionVisual>) -> ImmutableDropShadowEffect {
let props = shadow.props();
let offset = shadow.plane_offset(visual);
let color: Color = props.color();
ImmutableDropShadowEffect::new(
offset.x,
offset.y,
props.blur_radius().max(0.0) as f64,
color,
props.opacity().clamp(0.0, 1.0) as f64,
)
}
enum LayerMode {
Shadow(ImmutableDropShadowEffect),
Layer,
}
#[derive(Default)]
pub struct ServerLayerVisual {
props: ServerLayerVisualProps,
}
impl ServerLayerVisual {
pub fn new() -> Self {
Self::default()
}
pub fn props(&self) -> &ServerLayerVisualProps {
&self.props
}
pub fn shadow(&self) -> Option<Rc<ServerDropShadow>> {
self.props.shadow().and_then(|shadow| shadow.into_any_rc().downcast::<ServerDropShadow>().ok())
}
pub fn effect_brush(&self) -> Option<Rc<ServerCompositionEffectBrush>> {
self.props
.effect_brush()
.and_then(|brush| brush.into_any_rc().downcast::<ServerCompositionEffectBrush>().ok())
}
fn visible_shadow(&self) -> Option<Rc<ServerDropShadow>> {
self.shadow().filter(|shadow| shadow.is_visible())
}
fn rect(visual: &ServerCompositionVisual) -> Rect {
let size = visual.size();
Rect::new(0.0, 0.0, size.x, size.y)
}
fn layer_mode(&self, visual: &ServerCompositionVisual, canvas: &mut dyn IDrawingContextImpl) -> Option<(LayerMode, Rect)> {
let bounds = visual.sub_tree_bounds()?.to_rect();
let mode = match self.visible_shadow() {
Some(shadow)
if shadow.effective_mask().is_none() && canvas.as_drawing_context_impl_with_effects().is_some() =>
{
LayerMode::Shadow(drop_shadow_effect_in(&shadow, Some(visual)))
}
_ => LayerMode::Layer,
};
Some((mode, bounds))
}
}
impl ServerLayerVisualHooks for ServerCompositionVisual {
fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
ServerCompositionVisual::base_deserialize_changes_core(self, reader, committed_at);
}
fn on_shadow_changing(&self) {
if let Some(content) = self.content_as::<ServerLayerVisual>() {
observe_visual_resource(self, content.props.shadow(), false);
}
}
fn on_shadow_changed(&self) {
if let Some(content) = self.content_as::<ServerLayerVisual>() {
observe_visual_resource(self, content.props.shadow(), true);
}
self.invalidate_content();
}
fn on_effect_brush_changing(&self) {
if let Some(content) = self.content_as::<ServerLayerVisual>() {
observe_visual_resource(self, content.props.effect_brush(), false);
}
}
fn on_effect_brush_changed(&self) {
if let Some(content) = self.content_as::<ServerLayerVisual>() {
observe_visual_resource(self, content.props.effect_brush(), true);
}
self.invalidate_content();
}
}
impl IServerVisualContent for ServerLayerVisual {
fn deserialize_changes_core(
&self,
visual: &ServerCompositionVisual,
reader: &mut BatchStreamReader<'_>,
committed_at: Duration,
) {
self.props.deserialize_changes_core(visual, reader, committed_at);
}
fn compute_own_content_bounds(&self, visual: &ServerCompositionVisual) -> Option<LtrbRect> {
let shadow = self.visible_shadow()?;
let bounds = ServerSizeDependantVisual::compute_own_content_bounds(visual)?;
Some(LtrbRect::from_rect(shadow.bounds_in(Some(visual), bounds.to_rect())))
}
fn size_changed(&self, visual: &ServerCompositionVisual) {
ServerSizeDependantVisual::size_changed(visual);
}
fn sub_tree_bounds_padding(&self, visual: &ServerCompositionVisual) -> Option<Thickness> {
let shadow = self.visible_shadow().map(|shadow| {
let effect = drop_shadow_effect_in(&shadow, Some(visual));
EffectExtensions::get_effect_output_padding(Some(&effect as &dyn IEffect))
});
let effect = self.effect_brush().map(|effect| effect.layer_padding());
match (shadow, effect) {
(None, None) => None,
(shadow, effect) => Some(shadow.unwrap_or_default() + effect.unwrap_or_default()),
}
}
fn has_effect(&self, visual: &ServerCompositionVisual) -> bool {
visual.effect().is_some() || self.visible_shadow().is_some() || self.effect_brush().is_some()
}
fn render_core(
&self,
visual: &ServerCompositionVisual,
context: &mut ServerVisualRenderContext<'_>,
_current_transformed_clip: LtrbRect,
) {
let canvas = context.canvas();
let Some((mode, bounds)) = self.layer_mode(visual, canvas) else { return };
match mode {
LayerMode::Shadow(effect) => {
if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
effects.push_effect(Some(bounds), &effect);
}
}
LayerMode::Layer => {
if let Some(shadow) = self.visible_shadow() {
shadow.render_under_rect_in(canvas, Some(visual), Self::rect(visual));
}
canvas.push_layer(bounds);
}
}
if let Some(effect) = self.effect_brush() {
effect.push_for_layer(canvas, bounds);
}
}
fn render_after_children(&self, visual: &ServerCompositionVisual, context: &mut ServerVisualRenderContext<'_>) {
let canvas = context.canvas();
let Some((mode, bounds)) = self.layer_mode(visual, canvas) else { return };
if let Some(effect) = self.effect_brush() {
effect.pop_for_layer(canvas, bounds);
}
match mode {
LayerMode::Shadow(_) => {
if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
effects.pop_effect();
}
}
LayerMode::Layer => canvas.pop_layer(),
}
}
fn dependency_queued_invalidate(&self, visual: &ServerCompositionVisual) {
visual.invalidate_content();
}
fn dispose(&self, visual: &ServerCompositionVisual) {
observe_visual_resource(visual, self.props.shadow(), false);
observe_visual_resource(visual, self.props.effect_brush(), false);
}
fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
self.props.find_props(type_id)
}
fn get_composition_property(&self, name: &str) -> Option<&'static CompositionProperty> {
ServerCompositionContainerVisualProps::get_composition_property(name)
}
fn as_any(&self) -> &dyn Any {
self
}
}