ferroui_base/rendering/composition/server/
server_drop_shadow.rs1use super::{
2 drop_shadow_effect, impl_server_render_resource, paint_composition_brush, IAnimatedServerObject, IServerObject, IServerRenderResource,
3 ServerCompositionVisual, ServerCompositor, ServerRenderResource,
4};
5use crate::media::effects::ImmutableBlurEffect;
6use crate::media::imaging::BitmapBlendingMode;
7use crate::media::{BoxShadow, BoxShadows, Color, RenderOptions};
8use crate::platform::{AlphaFormat, IDrawingContextImpl, PixelFormats, SharedBitmapImpl};
9use crate::rendering::composition::generated::{ServerDropShadowHooks, ServerDropShadowProps};
10use crate::rendering::composition::transport::BatchStreamReader;
11use crate::rendering::composition::CompositionDropShadowSourcePolicy;
12use crate::{Matrix, PixelSize, Rect, RoundedRect, Vector};
13use std::any::{Any, TypeId};
14use std::cell::RefCell;
15use std::rc::{Rc, Weak};
16use std::sync::Arc;
17use std::time::Duration;
18
19const COLOR_BITMAP_SIDE: i32 = 4;
22
23pub struct ServerDropShadow {
32 base: ServerRenderResource,
33 props: ServerDropShadowProps,
34 color_bitmap: RefCell<Option<(Color, Arc<SharedBitmapImpl>)>>,
37}
38
39impl ServerDropShadow {
40 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerDropShadow> {
43 let shadow = Rc::new_cyclic(|this: &Weak<ServerDropShadow>| ServerDropShadow {
44 base: ServerRenderResource::new(
45 compositor,
46 this.clone() as Weak<dyn IAnimatedServerObject>,
47 this.clone() as Weak<dyn IServerRenderResource>,
48 ),
49 props: ServerDropShadowProps::new(),
50 color_bitmap: RefCell::new(None),
51 });
52 shadow.base.object().activate();
53 shadow
54 }
55
56 pub fn props(&self) -> &ServerDropShadowProps {
58 &self.props
59 }
60
61 pub fn is_disposed(&self) -> bool {
62 self.base.is_disposed()
63 }
64
65 pub fn effective_color(&self) -> Color {
67 let color = self.props.color();
68 let opacity = self.props.opacity().clamp(0.0, 1.0);
69 let alpha = (color.a as f32 * opacity).round().clamp(0.0, 255.0) as u8;
70 Color::from_argb(alpha, color.r, color.g, color.b)
71 }
72
73 pub fn plane_offset(&self, visual: Option<&ServerCompositionVisual>) -> Vector {
78 let offset = self.props.offset();
79 let depth = match visual {
80 Some(visual) => visual.depth_offset(offset.z as f32),
81 None => Vector::default(),
82 };
83 Vector::new(offset.x + depth.x, offset.y + depth.y)
84 }
85
86 pub fn box_shadow_in(&self, visual: Option<&ServerCompositionVisual>) -> BoxShadow {
89 let offset = self.plane_offset(visual);
90 BoxShadow {
91 offset_x: offset.x,
92 offset_y: offset.y,
93 blur: self.props.blur_radius().max(0.0) as f64,
94 spread: 0.0,
95 color: self.effective_color(),
96 is_inset: false,
97 }
98 }
99
100 pub fn box_shadow(&self) -> BoxShadow {
102 self.box_shadow_in(None)
103 }
104
105 pub fn box_shadows_in(&self, visual: Option<&ServerCompositionVisual>) -> BoxShadows {
108 let shadow = self.box_shadow_in(visual);
109 if shadow.color.a == 0 {
110 return BoxShadows::default();
111 }
112 BoxShadows::new(shadow)
113 }
114
115 pub fn box_shadows(&self) -> BoxShadows {
117 self.box_shadows_in(None)
118 }
119
120 pub fn bounds_in(&self, visual: Option<&ServerCompositionVisual>, rect: Rect) -> Rect {
124 let shadow = self.box_shadow_in(visual);
125 rect.union(shadow.transform_bounds(rect))
126 }
127
128 pub fn bounds(&self, rect: Rect) -> Rect {
130 self.bounds_in(None, rect)
131 }
132
133 pub fn is_visible(&self) -> bool {
136 self.effective_color().a != 0
137 }
138
139 pub fn inherits_from_visual_content(&self) -> bool {
142 self.props.source_policy() == CompositionDropShadowSourcePolicy::InheritFromVisualContent
143 }
144
145 pub fn effective_mask(&self) -> Option<Rc<dyn IServerObject>> {
148 if self.inherits_from_visual_content() {
149 return None;
150 }
151 self.props.mask()
152 }
153
154 pub fn render_under_rect(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
158 self.render_under_rect_in(canvas, None, rect)
159 }
160
161 pub fn render_with_content(
168 &self,
169 canvas: &mut dyn IDrawingContextImpl,
170 rect: Rect,
171 paint: impl FnOnce(&mut dyn IDrawingContextImpl),
172 ) -> bool {
173 if canvas.as_drawing_context_impl_with_effects().is_none() {
174 return false;
175 }
176 let effect = drop_shadow_effect(self);
177 let bounds = self.bounds(rect);
178 if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
179 effects.push_effect(Some(bounds), &effect);
180 }
181 paint(&mut *canvas);
182 if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
183 effects.pop_effect();
184 }
185 true
186 }
187
188 pub fn render_under_rect_in(
197 &self,
198 canvas: &mut dyn IDrawingContextImpl,
199 visual: Option<&ServerCompositionVisual>,
200 rect: Rect,
201 ) {
202 if rect.width <= 0.0 || rect.height <= 0.0 || !self.is_visible() {
203 return;
204 }
205 if let Some(mask) = self.effective_mask() {
206 if self.render_shadow_of(canvas, visual, rect, |canvas| paint_composition_brush(canvas, &mask, rect)) {
207 return;
208 }
209 }
210 let shadows = self.box_shadows_in(visual);
211 if shadows.count() > 0 {
212 canvas.draw_rectangle(None, None, RoundedRect::from_rect(rect), &shadows);
213 }
214 }
215
216 pub fn render_shadow_of(
231 &self,
232 canvas: &mut dyn IDrawingContextImpl,
233 visual: Option<&ServerCompositionVisual>,
234 rect: Rect,
235 paint: impl FnOnce(&mut dyn IDrawingContextImpl),
236 ) -> bool {
237 if rect.width <= 0.0 || rect.height <= 0.0 {
238 return true;
239 }
240 let color = self.effective_color();
241 if color.a == 0 {
242 return true;
243 }
244 if canvas.as_drawing_context_impl_with_effects().is_none() {
245 return false;
246 }
247 let Some(bitmap) = self.color_bitmap(color) else { return false };
248
249 let offset = self.plane_offset(visual);
250 let blur = self.props.blur_radius().max(0.0) as f64;
251 let bounds = self.bounds_in(visual, rect);
252 let colored = rect.inflate(2.0);
255
256 let blurred = blur > 0.0;
257 if blurred {
258 if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
259 effects.push_effect(Some(bounds), &ImmutableBlurEffect::new(blur));
260 }
261 }
262 let transform = canvas.transform();
263 let moved = offset != Vector::default();
264 if moved {
265 canvas.set_transform(Matrix::create_translation(offset.x, offset.y) * transform);
266 }
267 canvas.push_layer(colored);
268 paint(&mut *canvas);
269 canvas.push_render_options(RenderOptions {
270 bitmap_blending_mode: BitmapBlendingMode::SourceIn,
271 ..RenderOptions::default()
272 });
273 let side = COLOR_BITMAP_SIDE as f64;
274 canvas.draw_bitmap(&*bitmap, 1.0, Rect::new(1.0, 1.0, side - 2.0, side - 2.0), colored);
275 canvas.pop_render_options();
276 canvas.pop_layer();
277 if moved {
278 canvas.set_transform(transform);
279 }
280 if blurred {
281 if let Some(effects) = canvas.as_drawing_context_impl_with_effects() {
282 effects.pop_effect();
283 }
284 }
285 true
286 }
287
288 fn color_bitmap(&self, color: Color) -> Option<Arc<SharedBitmapImpl>> {
292 if let Some((cached, bitmap)) = self.color_bitmap.borrow().as_ref() {
293 if *cached == color {
294 return Some(bitmap.clone());
295 }
296 }
297 let compositor = self.base.compositor()?;
298 let premultiply = |channel: u8| ((channel as u32 * color.a as u32 + 127) / 255) as u8;
299 let pixel = [premultiply(color.r), premultiply(color.g), premultiply(color.b), color.a];
300 let pixels: Vec<u8> = pixel.iter().copied().cycle().take((COLOR_BITMAP_SIDE * COLOR_BITMAP_SIDE * 4) as usize).collect();
301 let size = PixelSize::new(COLOR_BITMAP_SIDE, COLOR_BITMAP_SIDE);
302 let bitmap = compositor.render_interface().with_platform_render_interface(|render_interface| {
303 render_interface.load_bitmap_from_pixels(
304 PixelFormats::RGBA8888,
305 AlphaFormat::Premul,
306 &pixels,
307 size,
308 Vector::new(96.0, 96.0),
309 COLOR_BITMAP_SIDE * 4,
310 )
311 })?;
312 *self.color_bitmap.borrow_mut() = Some((color, bitmap.clone()));
313 Some(bitmap)
314 }
315}
316
317impl_server_render_resource!(
318 ServerDropShadow,
319 base,
320 composition_property: ServerDropShadowProps::get_composition_property
321);
322
323impl ServerDropShadowHooks for ServerDropShadow {}
324
325impl IServerObject for ServerDropShadow {
326 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
327 self.props.deserialize_changes_core(self, reader, committed_at);
328 }
329
330 fn values_invalidated(&self) {
331 self.base.core().invalidated(self);
332 }
333
334 fn dispose(&self) {
335 self.color_bitmap.borrow_mut().take();
336 self.base.core().dispose();
337 }
338
339 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
340 Some(self)
341 }
342
343 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
344 Some(self)
345 }
346
347 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
348 self.props.find_props(type_id)
349 }
350
351 fn as_any(&self) -> &dyn Any {
352 self
353 }
354
355 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
356 self
357 }
358}