Skip to main content

fx/
object.rs

1// object -- effect group "object" (Objekt): drop_shadow                    [src/fx/fx_object.s]
2//
3//   FXTAB            the group table
4//
5//   drop_shadow  colour, angle (deg, 0 = right, 90 = down), distance d (0..100), blur radius r
6//                (0..100), opacity (0..100), shadow only: offset o = (rint(d cos a), rint(d sin a));
7//                shadow alpha S = Gaussian blur (Kern::gauss(2 r (r + 2)), W16) of the source
8//                alpha moved by o (transparent outside the image); shadow pixel = colour with
9//                alpha (S opacity colour.a + 816000) / 1632000; result = source over shadow
10//                (Normal blend), or the shadow alone
11
12use alloc::vec;
13use alloc::vec::Vec;
14
15use gfx::blendmode::{bm_row, BM_NORMAL, BM_OPAC_SHIFT};
16use rt::fx::cvtd;
17
18use crate::conv::*;
19use crate::*;
20
21struct Shadow<'d, 'e, 's> {
22    dst: &'d mut Dst<'e>,
23    a: &'s FxArgs<'s>,
24    ox: i32,
25    oy: i32,
26    rgb: u32,
27    k: u32,
28    only: bool,
29    tmp: Vec<u8>,
30}
31
32impl SepIo for Shadow<'_, '_, '_> {
33    /// the source moved by the offset, transparent outside  [fxo_shadow_load]
34    fn load(&mut self, sep: &Sep, w16: &mut [i16], y: i32, x0: i32, x1: i32) {
35        load_row(w16, sep.src, y - self.oy, x0 - self.ox, x1 - self.ox, FXL_ZERO);
36    }
37
38    /// shadow pixels, then source over shadow  [fxo_shadow_emit]
39    fn emit(&mut self, sep: &Sep, y: i32, row: &[i16]) {
40        let n = (sep.x1 - sep.x0) as usize;
41        for i in 0..n {
42            let s = (row[i * 4 + 3] as i32).clamp(0, 16320) as u32;
43            let al = (s.wrapping_mul(self.k).wrapping_add(816000)) / 1632000;
44            put(&mut self.tmp, i, al << 24 | self.rgb);
45        }
46        let d = dst_px(self.dst, y, sep.x0, n as i32);
47        if self.only {
48            d.copy_from_slice(&self.tmp[..n * 4]);
49        } else {
50            let s = &self.a.src_row(y)[sep.x0 as usize * 4..sep.x1 as usize * 4];
51            bm_row(d, &self.tmp[..n * 4], s, BM_NORMAL | 255 << BM_OPAC_SHIFT);
52        }
53    }
54}
55
56fn shadow_render(a: &FxArgs, dst: &mut Dst) -> i32 {
57    let col = a.params.0[0] as u32;
58    let k = pint(a, 4, 0, 100) as u32 * (col >> 24);
59    let only = pint(a, 5, 0, 1) != 0;
60    let (s, c) = gfx::xform::sincos(pdbl(a, 1) * 0.017453292519943295);
61    let d = pint(a, 2, 0, 100) as f64;
62    let ox = cvtd(c * d);
63    let oy = cvtd(s * d);
64    let kern = Kern::gauss(var12(pint(a, 3, 0, 100)));
65    let Some(mut sep) = Sep::new(a, &kern) else {
66        return 0;
67    };
68    sep.custom_load = true;
69    let n = (sep.x1 - sep.x0) as usize;
70    let mut io = Shadow { dst, a, ox, oy, rgb: col & 0x00FF_FFFF, k, only, tmp: vec![0u8; n * 4 + 64] };
71    sep_run(&sep, &mut io)
72}
73
74static P_SHADOW: [FxProp; 6] = [
75    FxProp::color(0, "color", 0xFF00_0000),
76    FxProp::new(FP_ANGLE, 2, 1, 0, "angle", -180.0, 180.0, 45.0),
77    FxProp::new(FP_INT, 0, 2, 0, "distance", 0.0, 100.0, 10.0),
78    FxProp::new(FP_INT, 0, 3, 0, "blur_radius", 0.0, 100.0, 10.0),
79    FxProp::new(FP_INT, 0, 4, 0, "opacity", 0.0, 100.0, 50.0),
80    FxProp::new(FP_BOOL, 0, 5, 0, "shadow_only", 0.0, 1.0, 0.0),
81];
82
83/// the group table  [fxtab_object]
84pub static FXTAB: [FxDesc; 1] = [FxDesc {
85    id: FX_DROP_SHADOW,
86    flags: 0,
87    name: "drop_shadow",
88    prepare: None,
89    render: shadow_render,
90    props: &P_SHADOW,
91    category: FXC_OBJECT,
92    user: 0,
93}];