Skip to main content

lumen_engine/node/processing/
hue_saturation.rs

1use crate::node::{NodeId, NodeParamEvalContext, NodeParams, PortRef};
2
3use crate::gpu::{
4    BoundFrame, CompiledOutput, FrameBindContext, GpuCompileNode, GpuCompiledNode, RasterHandle,
5    compiler,
6};
7
8pub(crate) const SHADER: &str = include_str!("hue_saturation.wgsl");
9
10/// Adjusts raster hue, saturation, and lightness.
11#[derive(Debug, Clone, lumen_macros::Delegate)]
12pub struct HueSaturationParams {
13    /// Hue rotation in degrees.
14    #[meta(name = "Hue", step = 1)]
15    pub hue_degrees: f64,
16    /// Saturation multiplier.
17    #[meta(step = 0.01)]
18    pub saturation: f64,
19    /// Lightness offset.
20    #[meta(step = 0.01)]
21    pub lightness: f64,
22}
23
24impl Default for HueSaturationParams {
25    fn default() -> Self {
26        Self {
27            hue_degrees: 0.0,
28            saturation: 1.0,
29            lightness: 0.0,
30        }
31    }
32}
33
34/// Adjusts raster hue, saturation, and lightness.
35#[derive(Debug, Clone, lumen_macros::Node)]
36#[node(
37    kind = "hue_saturation",
38    name = "Hue Saturation",
39    category = "processing"
40)]
41pub struct HueSaturation {
42    pub id: NodeId,
43    #[params]
44    pub params: HueSaturationParamsDelegate,
45
46    #[input()]
47    pub source: PortRef,
48}
49
50impl Default for HueSaturation {
51    fn default() -> Self {
52        Self {
53            id: NodeId::new(0),
54            params: HueSaturationParamsDelegate::default(),
55            source: PortRef::empty(),
56        }
57    }
58}
59
60#[derive(Debug, Clone)]
61struct CompiledHueSaturation {
62    node_id: NodeId,
63    params: HueSaturationParamsDelegate,
64    buffer: lumen_gpu::BufferId,
65}
66
67impl GpuCompiledNode for CompiledHueSaturation {
68    fn node_id(&self) -> NodeId {
69        self.node_id
70    }
71
72    fn bind(&self, ctx: &FrameBindContext<'_>, bound: &mut BoundFrame) -> crate::Result<()> {
73        let params = self.params.eval(&NodeParamEvalContext {
74            node_id: self.node_id,
75            expr: &ctx.expr_context(self.node_id, "params"),
76        })?;
77        let gpu_params = compiler::HueSaturationParams {
78            hue_offset: (params.hue_degrees / 360.0) as f32,
79            saturation: params.saturation as f32,
80            lightness: params.lightness as f32,
81            _pad: 0.0,
82        };
83        bound.write_buffer(self.buffer, 0, bytemuck::bytes_of(&gpu_params));
84        Ok(())
85    }
86}
87
88impl GpuCompileNode for HueSaturation {
89    fn compile_gpu(
90        &self,
91        ctx: &mut crate::gpu::CompileContext<'_>,
92        port: &PortRef,
93    ) -> crate::Result<CompiledOutput> {
94        let (source, texture, params) = ctx.compile_unary_filter(
95            self.id,
96            &self.source,
97            port,
98            "hue-saturation",
99            SHADER,
100            std::mem::size_of::<compiler::HueSaturationParams>() as u64,
101        )?;
102        ctx.register_compiled_node(CompiledHueSaturation {
103            node_id: self.id,
104            params: self.params.clone(),
105            buffer: params,
106        });
107        Ok(CompiledOutput::Raster(RasterHandle {
108            texture,
109            domain: source.domain,
110            metadata: source.metadata,
111        }))
112    }
113}