1use markup5ever::{expanded_name, local_name, ns};
2
3use crate::document::AcquiredNodes;
4use crate::element::{set_attribute, ElementTrait};
5use crate::node::Node;
6use crate::parsers::ParseValue;
7use crate::properties::ColorInterpolationFilters;
8use crate::rect::IRect;
9use crate::rsvg_log;
10use crate::session::Session;
11use crate::xml::Attributes;
12
13use super::bounds::BoundsBuilder;
14use super::context::{FilterContext, FilterOutput};
15use super::{
16 FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
17 PrimitiveParams, ResolvedPrimitive,
18};
19
20#[derive(Default)]
22pub struct FeOffset {
23 base: Primitive,
24 params: Offset,
25}
26
27#[derive(Clone, Default)]
29pub struct Offset {
30 pub in1: Input,
31 pub dx: f64,
32 pub dy: f64,
33}
34
35impl ElementTrait for FeOffset {
36 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
37 self.params.in1 = self.base.parse_one_input(attrs, session);
38
39 for (attr, value) in attrs.iter() {
40 match attr.expanded() {
41 expanded_name!("", "dx") => {
42 set_attribute(&mut self.params.dx, attr.parse(value), session)
43 }
44 expanded_name!("", "dy") => {
45 set_attribute(&mut self.params.dy, attr.parse(value), session)
46 }
47 _ => (),
48 }
49 }
50 }
51}
52
53impl Offset {
54 pub fn render(
55 &self,
56 bounds_builder: BoundsBuilder,
57 ctx: &FilterContext,
58 ) -> Result<FilterOutput, FilterError> {
59 let input_1 = ctx.get_input(&self.in1, ColorInterpolationFilters::Auto)?;
67 let bounds = bounds_builder.add_input(&input_1).compute(ctx).clipped;
68
69 rsvg_log!(ctx.session(), "(feOffset bounds={:?}", bounds);
70
71 let (dx, dy) = ctx.paffine().transform_distance(self.dx, self.dy);
72
73 let surface = input_1.surface().offset(bounds, dx, dy)?;
74
75 let ibounds: IRect = bounds.into();
76
77 Ok(FilterOutput {
78 surface,
79 bounds: ibounds,
80 })
81 }
82
83 pub fn get_input_requirements(&self) -> InputRequirements {
84 self.in1.get_requirements()
85 }
86}
87
88impl FilterEffect for FeOffset {
89 fn resolve(
90 &self,
91 _acquired_nodes: &mut AcquiredNodes<'_>,
92 _node: &Node,
93 ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
94 Ok(vec![ResolvedPrimitive {
95 primitive: self.base.clone(),
96 params: PrimitiveParams::Offset(self.params.clone()),
97 }])
98 }
99}