rsvg/filters/
composite.rs

1use cssparser::Parser;
2use markup5ever::{expanded_name, local_name, ns};
3
4use crate::document::AcquiredNodes;
5use crate::element::{set_attribute, ElementTrait};
6use crate::error::*;
7use crate::node::{CascadedValues, Node};
8use crate::parse_identifiers;
9use crate::parsers::{Parse, ParseValue};
10use crate::properties::ColorInterpolationFilters;
11use crate::rect::IRect;
12use crate::session::Session;
13use crate::surface_utils::shared_surface::Operator as SurfaceOperator;
14use crate::xml::Attributes;
15
16use super::bounds::BoundsBuilder;
17use super::context::{FilterContext, FilterOutput};
18use super::{
19    FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
20    PrimitiveParams, ResolvedPrimitive,
21};
22
23/// Enumeration of the possible compositing operations.
24#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
25pub enum Operator {
26    #[default]
27    Over,
28    In,
29    Out,
30    Atop,
31    Xor,
32    Arithmetic,
33}
34
35/// The `feComposite` filter primitive.
36#[derive(Default)]
37pub struct FeComposite {
38    base: Primitive,
39    params: Composite,
40}
41
42/// Resolved `feComposite` primitive for rendering.
43#[derive(Clone, Default)]
44pub struct Composite {
45    pub in1: Input,
46    pub in2: Input,
47    pub operator: Operator,
48    pub k1: f64,
49    pub k2: f64,
50    pub k3: f64,
51    pub k4: f64,
52    pub color_interpolation_filters: ColorInterpolationFilters,
53}
54
55impl ElementTrait for FeComposite {
56    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
57        let (in1, in2) = self.base.parse_two_inputs(attrs, session);
58        self.params.in1 = in1;
59        self.params.in2 = in2;
60
61        for (attr, value) in attrs.iter() {
62            match attr.expanded() {
63                expanded_name!("", "operator") => {
64                    set_attribute(&mut self.params.operator, attr.parse(value), session)
65                }
66                expanded_name!("", "k1") => {
67                    set_attribute(&mut self.params.k1, attr.parse(value), session)
68                }
69                expanded_name!("", "k2") => {
70                    set_attribute(&mut self.params.k2, attr.parse(value), session)
71                }
72                expanded_name!("", "k3") => {
73                    set_attribute(&mut self.params.k3, attr.parse(value), session)
74                }
75                expanded_name!("", "k4") => {
76                    set_attribute(&mut self.params.k4, attr.parse(value), session)
77                }
78                _ => (),
79            }
80        }
81    }
82}
83
84impl Composite {
85    pub fn render(
86        &self,
87        bounds_builder: BoundsBuilder,
88        ctx: &FilterContext,
89    ) -> Result<FilterOutput, FilterError> {
90        let input_1 = ctx.get_input(&self.in1, self.color_interpolation_filters)?;
91        let input_2 = ctx.get_input(&self.in2, self.color_interpolation_filters)?;
92        let bounds: IRect = bounds_builder
93            .add_input(&input_1)
94            .add_input(&input_2)
95            .compute(ctx)
96            .clipped
97            .into();
98
99        let surface = if self.operator == Operator::Arithmetic {
100            input_1.surface().compose_arithmetic(
101                input_2.surface(),
102                bounds,
103                self.k1,
104                self.k2,
105                self.k3,
106                self.k4,
107            )?
108        } else {
109            input_1
110                .surface()
111                .compose(input_2.surface(), bounds, self.operator.into())?
112        };
113
114        Ok(FilterOutput { surface, bounds })
115    }
116
117    pub fn get_input_requirements(&self) -> InputRequirements {
118        self.in1
119            .get_requirements()
120            .fold(self.in2.get_requirements())
121    }
122}
123
124impl FilterEffect for FeComposite {
125    fn resolve(
126        &self,
127        _acquired_nodes: &mut AcquiredNodes<'_>,
128        node: &Node,
129    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
130        let cascaded = CascadedValues::new_from_node(node);
131        let values = cascaded.get();
132
133        let mut params = self.params.clone();
134        params.color_interpolation_filters = values.color_interpolation_filters();
135
136        Ok(vec![ResolvedPrimitive {
137            primitive: self.base.clone(),
138            params: PrimitiveParams::Composite(params),
139        }])
140    }
141}
142
143impl Parse for Operator {
144    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
145        Ok(parse_identifiers!(
146            parser,
147            "over" => Operator::Over,
148            "in" => Operator::In,
149            "out" => Operator::Out,
150            "atop" => Operator::Atop,
151            "xor" => Operator::Xor,
152            "arithmetic" => Operator::Arithmetic,
153        )?)
154    }
155}
156
157impl From<Operator> for SurfaceOperator {
158    #[inline]
159    fn from(x: Operator) -> SurfaceOperator {
160        use Operator::*;
161
162        match x {
163            Over => SurfaceOperator::Over,
164            In => SurfaceOperator::In,
165            Out => SurfaceOperator::Out,
166            Atop => SurfaceOperator::Atop,
167            Xor => SurfaceOperator::Xor,
168
169            _ => panic!("can't convert Operator::Arithmetic to a shared_surface::Operator"),
170        }
171    }
172}