1use std::collections::HashMap;
2use std::rc::Rc;
3
4use crate::bbox::BoundingBox;
5use crate::coord_units::CoordUnits;
6use crate::filter::UserSpaceFilter;
7use crate::parsers::CustomIdent;
8use crate::properties::ColorInterpolationFilters;
9use crate::rect::{IRect, Rect};
10use crate::session::Session;
11use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
12use crate::transform::Transform;
13
14use super::error::FilterError;
15use super::{FilterPlan, Input};
16
17#[derive(Debug, Clone)]
19pub struct FilterOutput {
20 pub surface: SharedImageSurface,
22
23 pub bounds: IRect,
25}
26
27#[derive(Debug, Clone)]
29pub struct FilterResult {
30 pub name: Option<CustomIdent>,
32
33 pub output: FilterOutput,
35}
36
37#[derive(Debug, Clone)]
39pub enum FilterInput {
40 StandardInput(SharedImageSurface),
42 PrimitiveOutput(FilterOutput),
44}
45
46pub struct FilterContext {
52 plan: Rc<FilterPlan>,
54
55 source_surface: SharedImageSurface,
57
58 last_result: Option<FilterOutput>,
60 previous_results: HashMap<CustomIdent, FilterOutput>,
62
63 primitive_units: CoordUnits,
65 effects_region: Rect,
67
68 _affine: Transform,
84
85 paffine: Transform,
89}
90
91impl FilterContext {
92 pub fn new(
94 filter: &UserSpaceFilter,
95 plan: Rc<FilterPlan>,
96 source_surface: SharedImageSurface,
97 node_bbox: BoundingBox,
98 ) -> Result<Self, FilterError> {
99 let bbox_rect = node_bbox.rect.unwrap_or_default();
102
103 let affine = match filter.filter_units {
104 CoordUnits::UserSpaceOnUse => *plan.viewport.transform,
105 CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
106 bbox_rect.width(),
107 0.0,
108 0.0,
109 bbox_rect.height(),
110 bbox_rect.x0,
111 bbox_rect.y0,
112 )
113 .post_transform(&plan.viewport.transform),
114 };
115
116 let paffine = match filter.primitive_units {
117 CoordUnits::UserSpaceOnUse => *plan.viewport.transform,
118 CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
119 bbox_rect.width(),
120 0.0,
121 0.0,
122 bbox_rect.height(),
123 bbox_rect.x0,
124 bbox_rect.y0,
125 )
126 .post_transform(&plan.viewport.transform),
127 };
128
129 if !(affine.is_invertible() && paffine.is_invertible()) {
130 return Err(FilterError::InvalidParameter(
131 "transform is not invertible".to_string(),
132 ));
133 }
134
135 let effects_region = {
136 let mut bbox = BoundingBox::new();
137 let other_bbox = BoundingBox::new()
138 .with_transform(affine)
139 .with_rect(filter.rect);
140
141 bbox.insert(&other_bbox);
144
145 let (width, height) = (source_surface.width(), source_surface.height());
147 let rect = Rect::from_size(f64::from(width), f64::from(height));
148 let other_bbox = BoundingBox::new().with_rect(rect);
149 bbox.clip(&other_bbox);
150
151 bbox.rect.unwrap()
152 };
153
154 Ok(Self {
155 plan,
156 source_surface,
157 last_result: None,
158 previous_results: HashMap::new(),
159 primitive_units: filter.primitive_units,
160 effects_region,
161 _affine: affine,
162 paffine,
163 })
164 }
165
166 pub fn session(&self) -> &Session {
167 &self.plan.session
168 }
169
170 #[inline]
172 pub fn source_graphic(&self) -> &SharedImageSurface {
173 &self.source_surface
174 }
175
176 fn background_image(&self) -> SharedImageSurface {
178 self.plan.background_image.clone().expect(
179 "the calling DrawingCtx should have passed a background_image to the FilterPlan",
180 )
181 }
182
183 fn stroke_paint_image(&self) -> SharedImageSurface {
187 self.plan.stroke_paint_image.clone().expect(
188 "the calling DrawingCtx should have passed a stroke_paint_image to the FilterPlan",
189 )
190 }
191
192 fn fill_paint_image(&self) -> SharedImageSurface {
196 self.plan.fill_paint_image.clone().expect(
197 "the calling DrawingCtx should have passed a fill_paint_image to the FilterPlan",
198 )
199 }
200
201 #[inline]
207 pub fn into_output(self) -> Result<SharedImageSurface, cairo::Error> {
208 match self.last_result {
209 Some(FilterOutput { surface, bounds }) => surface.to_srgb(bounds),
210 None => SharedImageSurface::empty(
211 self.source_surface.width(),
212 self.source_surface.height(),
213 SurfaceType::AlphaOnly,
214 ),
215 }
216 }
217
218 #[inline]
220 pub fn store_result(&mut self, result: FilterResult) {
221 if let Some(name) = result.name {
222 self.previous_results.insert(name, result.output.clone());
223 }
224
225 self.last_result = Some(result.output);
226 }
227
228 #[inline]
230 pub fn paffine(&self) -> Transform {
231 self.paffine
232 }
233
234 #[inline]
236 pub fn primitive_units(&self) -> CoordUnits {
237 self.primitive_units
238 }
239
240 #[inline]
242 pub fn effects_region(&self) -> Rect {
243 self.effects_region
244 }
245
246 fn get_unspecified_input(&self) -> FilterInput {
253 if let Some(output) = self.last_result.as_ref() {
254 FilterInput::PrimitiveOutput(output.clone())
255 } else {
256 FilterInput::StandardInput(self.source_graphic().clone())
257 }
258 }
259
260 fn get_input_raw(&self, in_: &Input) -> Result<FilterInput, FilterError> {
262 match *in_ {
263 Input::Unspecified => Ok(self.get_unspecified_input()),
264
265 Input::SourceGraphic => Ok(FilterInput::StandardInput(self.source_graphic().clone())),
266
267 Input::SourceAlpha => self
268 .source_graphic()
269 .extract_alpha(self.effects_region().into())
270 .map_err(FilterError::CairoError)
271 .map(FilterInput::StandardInput),
272
273 Input::BackgroundImage => Ok(FilterInput::StandardInput(self.background_image())),
274
275 Input::BackgroundAlpha => self
276 .background_image()
277 .extract_alpha(self.effects_region().into())
278 .map_err(FilterError::CairoError)
279 .map(FilterInput::StandardInput),
280
281 Input::FillPaint => Ok(FilterInput::StandardInput(self.fill_paint_image())),
282
283 Input::StrokePaint => Ok(FilterInput::StandardInput(self.stroke_paint_image())),
284
285 Input::FilterOutput(ref name) => {
286 let input = match self.previous_results.get(name).cloned() {
287 Some(filter_output) => {
288 FilterInput::PrimitiveOutput(filter_output)
290 }
291
292 None => {
293 self.get_unspecified_input()
299 }
300 };
301
302 Ok(input)
303 }
304 }
305 }
306
307 pub fn get_input(
311 &self,
312 in_: &Input,
313 color_interpolation_filters: ColorInterpolationFilters,
314 ) -> Result<FilterInput, FilterError> {
315 let raw = self.get_input_raw(in_)?;
316
317 let (surface, bounds) = match raw {
319 FilterInput::StandardInput(ref surface) => (surface, self.effects_region().into()),
320 FilterInput::PrimitiveOutput(FilterOutput {
321 ref surface,
322 ref bounds,
323 }) => (surface, *bounds),
324 };
325
326 let surface = match color_interpolation_filters {
327 ColorInterpolationFilters::Auto => Ok(surface.clone()),
328 ColorInterpolationFilters::LinearRgb => surface.to_linear_rgb(bounds),
329 ColorInterpolationFilters::Srgb => surface.to_srgb(bounds),
330 };
331
332 surface
333 .map_err(FilterError::CairoError)
334 .map(|surface| match raw {
335 FilterInput::StandardInput(_) => FilterInput::StandardInput(surface),
336 FilterInput::PrimitiveOutput(ref output) => {
337 FilterInput::PrimitiveOutput(FilterOutput { surface, ..*output })
338 }
339 })
340 }
341}
342
343impl FilterInput {
344 #[inline]
346 pub fn surface(&self) -> &SharedImageSurface {
347 match *self {
348 FilterInput::StandardInput(ref surface) => surface,
349 FilterInput::PrimitiveOutput(FilterOutput { ref surface, .. }) => surface,
350 }
351 }
352}