summaryrefslogtreecommitdiff
path: root/rsvg/src/filters/bounds.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsvg/src/filters/bounds.rs')
-rw-r--r--rsvg/src/filters/bounds.rs121
1 files changed, 121 insertions, 0 deletions
diff --git a/rsvg/src/filters/bounds.rs b/rsvg/src/filters/bounds.rs
new file mode 100644
index 00000000..6a6dd9d2
--- /dev/null
+++ b/rsvg/src/filters/bounds.rs
@@ -0,0 +1,121 @@
+//! Filter primitive subregion computation.
+use crate::rect::Rect;
+use crate::transform::Transform;
+
+use super::context::{FilterContext, FilterInput};
+
+/// A helper type for filter primitive subregion computation.
+pub struct BoundsBuilder {
+ /// Filter primitive properties.
+ x: Option<f64>,
+ y: Option<f64>,
+ width: Option<f64>,
+ height: Option<f64>,
+
+ /// The transform to use when generating the rect
+ transform: Transform,
+
+ /// The inverse transform used when adding rects
+ inverse: Transform,
+
+ /// Whether one of the input nodes is standard input.
+ standard_input_was_referenced: bool,
+
+ /// The current bounding rectangle.
+ rect: Option<Rect>,
+}
+
+/// A filter primitive's subregion.
+pub struct Bounds {
+ /// Primitive's subregion, clipped to the filter effects region.
+ pub clipped: Rect,
+
+ /// Primitive's subregion, unclipped.
+ pub unclipped: Rect,
+}
+
+impl BoundsBuilder {
+ /// Constructs a new `BoundsBuilder`.
+ #[inline]
+ pub fn new(
+ x: Option<f64>,
+ y: Option<f64>,
+ width: Option<f64>,
+ height: Option<f64>,
+ transform: Transform,
+ ) -> Self {
+ // We panic if transform is not invertible. This is checked in the caller.
+ Self {
+ x,
+ y,
+ width,
+ height,
+ transform,
+ inverse: transform.invert().unwrap(),
+ standard_input_was_referenced: false,
+ rect: None,
+ }
+ }
+
+ /// Adds a filter primitive input to the bounding box.
+ #[inline]
+ pub fn add_input(mut self, input: &FilterInput) -> Self {
+ // If a standard input was referenced, the default value is the filter effects region
+ // regardless of other referenced inputs. This means we can skip computing the bounds.
+ if self.standard_input_was_referenced {
+ return self;
+ }
+
+ match *input {
+ FilterInput::StandardInput(_) => {
+ self.standard_input_was_referenced = true;
+ }
+ FilterInput::PrimitiveOutput(ref output) => {
+ let input_rect = self.inverse.transform_rect(&Rect::from(output.bounds));
+ self.rect = Some(self.rect.map_or(input_rect, |r| input_rect.union(&r)));
+ }
+ }
+
+ self
+ }
+
+ /// Returns the final exact bounds, both with and without clipping to the effects region.
+ pub fn compute(self, ctx: &FilterContext) -> Bounds {
+ let effects_region = ctx.effects_region();
+
+ // The default value is the filter effects region converted into
+ // the ptimitive coordinate system.
+ let mut rect = match self.rect {
+ Some(r) if !self.standard_input_was_referenced => r,
+ _ => self.inverse.transform_rect(&effects_region),
+ };
+
+ // If any of the properties were specified, we need to respect them.
+ // These replacements are possible because of the primitive coordinate system.
+ if self.x.is_some() || self.y.is_some() || self.width.is_some() || self.height.is_some() {
+ if let Some(x) = self.x {
+ let w = rect.width();
+ rect.x0 = x;
+ rect.x1 = rect.x0 + w;
+ }
+ if let Some(y) = self.y {
+ let h = rect.height();
+ rect.y0 = y;
+ rect.y1 = rect.y0 + h;
+ }
+ if let Some(width) = self.width {
+ rect.x1 = rect.x0 + width;
+ }
+ if let Some(height) = self.height {
+ rect.y1 = rect.y0 + height;
+ }
+ }
+
+ // Convert into the surface coordinate system.
+ let unclipped = self.transform.transform_rect(&rect);
+
+ let clipped = unclipped.intersection(&effects_region).unwrap_or_default();
+
+ Bounds { clipped, unclipped }
+ }
+}