1use crate::rect::Rect;
3use crate::transform::Transform;
4
5use super::context::{FilterContext, FilterInput};
6
7pub struct BoundsBuilder {
9 x: Option<f64>,
11 y: Option<f64>,
12 width: Option<f64>,
13 height: Option<f64>,
14
15 transform: Transform,
17
18 inverse: Transform,
20
21 standard_input_was_referenced: bool,
23
24 rect: Option<Rect>,
26}
27
28#[derive(Debug)]
30pub struct Bounds {
31 pub x: Option<f64>,
33 pub y: Option<f64>,
34 pub width: Option<f64>,
35 pub height: Option<f64>,
36
37 pub clipped: Rect,
39
40 pub unclipped: Rect,
42}
43
44impl BoundsBuilder {
45 #[inline]
47 pub fn new(
48 x: Option<f64>,
49 y: Option<f64>,
50 width: Option<f64>,
51 height: Option<f64>,
52 transform: Transform,
53 ) -> Self {
54 Self {
56 x,
57 y,
58 width,
59 height,
60 transform,
61 inverse: transform.invert().unwrap(),
62 standard_input_was_referenced: false,
63 rect: None,
64 }
65 }
66
67 #[inline]
69 pub fn add_input(mut self, input: &FilterInput) -> Self {
70 if self.standard_input_was_referenced {
73 return self;
74 }
75
76 match *input {
77 FilterInput::StandardInput(_) => {
78 self.standard_input_was_referenced = true;
79 }
80 FilterInput::PrimitiveOutput(ref output) => {
81 let input_rect = self.inverse.transform_rect(&Rect::from(output.bounds));
82 self.rect = Some(self.rect.map_or(input_rect, |r| input_rect.union(&r)));
83 }
84 }
85
86 self
87 }
88
89 pub fn compute(self, ctx: &FilterContext) -> Bounds {
91 let effects_region = ctx.effects_region();
92
93 let mut rect = match self.rect {
96 Some(r) if !self.standard_input_was_referenced => r,
97 _ => self.inverse.transform_rect(&effects_region),
98 };
99
100 if self.x.is_some() || self.y.is_some() || self.width.is_some() || self.height.is_some() {
103 if let Some(x) = self.x {
104 let w = rect.width();
105 rect.x0 = x;
106 rect.x1 = rect.x0 + w;
107 }
108 if let Some(y) = self.y {
109 let h = rect.height();
110 rect.y0 = y;
111 rect.y1 = rect.y0 + h;
112 }
113 if let Some(width) = self.width {
114 rect.x1 = rect.x0 + width;
115 }
116 if let Some(height) = self.height {
117 rect.y1 = rect.y0 + height;
118 }
119 }
120
121 let unclipped = self.transform.transform_rect(&rect);
123
124 let clipped = unclipped.intersection(&effects_region).unwrap_or_default();
125
126 Bounds {
127 x: self.x,
128 y: self.y,
129 width: self.width,
130 height: self.height,
131 clipped,
132 unclipped,
133 }
134 }
135}