1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
use std::cell::Cell;
use cairo::{self, ImageSurface};
use attributes::Attribute;
use error::NodeError;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
use parsers;
use property_bag::PropertyBag;
use surface_utils::shared_surface::SharedImageSurface;
use util::clamp;
use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::{make_result, Filter, FilterError, PrimitiveWithInput};
/// The `feOffset` filter primitive.
pub struct Offset {
base: PrimitiveWithInput,
dx: Cell<f64>,
dy: Cell<f64>,
}
impl Offset {
/// Constructs a new `Offset` with empty properties.
#[inline]
pub fn new() -> Offset {
Offset {
base: PrimitiveWithInput::new::<Self>(),
dx: Cell::new(0f64),
dy: Cell::new(0f64),
}
}
}
impl NodeTrait for Offset {
fn set_atts(
&self,
node: &RsvgNode,
handle: *const RsvgHandle,
pbag: &PropertyBag,
) -> NodeResult {
self.base.set_atts(node, handle, pbag)?;
for (_key, attr, value) in pbag.iter() {
match attr {
Attribute::Dx => self
.dx
.set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
Attribute::Dy => self
.dy
.set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
_ => (),
}
}
Ok(())
}
#[inline]
fn get_c_impl(&self) -> *const RsvgCNodeImpl {
self.base.get_c_impl()
}
}
impl Filter for Offset {
fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
let input = make_result(self.base.get_input(ctx))?;
let bounds = self.base.get_bounds(ctx).add_input(&input).into_irect();
let dx = self.dx.get();
let dy = self.dy.get();
let paffine = ctx.paffine();
let ox = paffine.xx * dx + paffine.xy * dy;
let oy = paffine.yx * dx + paffine.yy * dy;
// output_bounds contains all pixels within bounds,
// for which (x - ox) and (y - oy) also lie within bounds.
let output_bounds = IRect {
x0: clamp(bounds.x0 + ox as i32, bounds.x0, bounds.x1),
y0: clamp(bounds.y0 + oy as i32, bounds.y0, bounds.y1),
x1: clamp(bounds.x1 + ox as i32, bounds.x0, bounds.x1),
y1: clamp(bounds.y1 + oy as i32, bounds.y0, bounds.y1),
};
let output_surface = ImageSurface::create(
cairo::Format::ARgb32,
ctx.source_graphic().width(),
ctx.source_graphic().height(),
).map_err(FilterError::OutputSurfaceCreation)?;
{
let cr = cairo::Context::new(&output_surface);
cr.rectangle(
output_bounds.x0 as f64,
output_bounds.y0 as f64,
(output_bounds.x1 - output_bounds.x0) as f64,
(output_bounds.y1 - output_bounds.y0) as f64,
);
cr.clip();
input.surface().set_as_source_surface(&cr, ox, oy);
cr.paint();
}
Ok(FilterResult {
name: self.base.result.borrow().clone(),
output: FilterOutput {
surface: SharedImageSurface::new(output_surface).unwrap(),
bounds,
},
})
}
#[inline]
fn is_affected_by_color_interpolation_filters() -> bool {
false
}
}
|