Skip to content

Commit 98b4bac

Browse files
committed
cache most recently created filter
- seems better than recomputing every time a paint is used (though I haven't profiled so who actually knows)
1 parent d586b13 commit 98b4bac

File tree

2 files changed

+152
-116
lines changed

2 files changed

+152
-116
lines changed

src/context/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ impl Context2D{
489489
color.to_color()
490490
}
491491

492-
pub fn paint_for_drawing(&self, style:PaintStyle) -> Paint{
492+
pub fn paint_for_drawing(&mut self, style:PaintStyle) -> Paint{
493493
let mut paint = self.state.paint.clone();
494494
self.state.filter.mix_into(&mut paint, self.state.matrix, false);
495495
self.state.dye(style).mix_into(&mut paint, self.state.global_alpha, self.state.image_filter);
@@ -519,7 +519,7 @@ impl Context2D{
519519
paint
520520
}
521521

522-
pub fn paint_for_image(&self) -> Paint {
522+
pub fn paint_for_image(&mut self) -> Paint {
523523
let mut paint = self.state.paint.clone();
524524
self.state.filter.mix_into(&mut paint, self.state.matrix, true)
525525
.set_alpha_f(self.state.global_alpha);

src/filter.rs

Lines changed: 150 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#![allow(unused_imports)]
33
#![allow(unused_variables)]
44
#![allow(dead_code)]
5-
use skia_safe::{FilterMode, MipmapMode, SamplingOptions, CubicResampler};
6-
use skia_safe::{Paint, Matrix, Point, Color, TileMode, MaskFilter, BlurStyle,
5+
use skia_safe::{Paint, Matrix, Point, Color, MaskFilter, ImageFilter as SkImageFilter,
6+
BlurStyle, FilterMode, MipmapMode, SamplingOptions, TileMode,
77
image_filters, color_filters, table_color_filter};
88

99
use crate::utils::*;
@@ -17,139 +17,175 @@ pub enum FilterSpec{
1717
#[derive(Clone, Debug)]
1818
pub struct Filter {
1919
pub css: String,
20-
specs: Vec<FilterSpec>
20+
specs: Vec<FilterSpec>,
21+
_raster: Option<LastFilter>,
22+
_vector: Option<LastFilter>
23+
}
24+
25+
#[derive(Clone, Debug)]
26+
pub struct LastFilter {
27+
matrix: Matrix,
28+
mask: Option<MaskFilter>,
29+
image: Option<SkImageFilter>
30+
}
31+
32+
impl LastFilter {
33+
fn match_scale(&self, matrix:Matrix) -> Option<Self> {
34+
if self.matrix.scale_x() == matrix.scale_x() && self.matrix.scale_y() == matrix.scale_y(){
35+
Some(self.clone())
36+
}else{
37+
None
38+
}
39+
}
2140
}
2241

2342
impl Default for Filter{
2443
fn default() -> Self {
25-
Filter{ css:"none".to_string(), specs:vec![] }
44+
Filter{ css:"none".to_string(), specs:vec![], _raster:None, _vector:None }
2645
}
2746
}
2847

2948
impl Filter {
3049
pub fn new(css:&str, specs:&[FilterSpec]) -> Self {
3150
let css = css.to_string();
3251
let specs = specs.to_vec();
33-
Filter{ css, specs }
52+
Filter{ css, specs, _raster:None, _vector:None }
3453
}
3554

3655
pub fn to_string(&self) -> String {
3756
self.css.clone()
3857
}
3958

40-
pub fn mix_into<'a>(&self, paint:&'a mut Paint, matrix:Matrix, raster:bool) -> &'a mut Paint {
41-
let image_filter = self.specs.iter().fold(None, |chain, next_filter|
42-
match next_filter {
43-
FilterSpec::Shadow{ offset, blur, color } => {
44-
let scale = Point{x:matrix.scale_x(), y:matrix.scale_y()};
45-
let point = (offset.x / scale.x, offset.y / scale.y);
46-
let sigma = ( blur / scale.x, blur / scale.y);
47-
image_filters::drop_shadow(point, sigma, *color, chain, None)
48-
},
49-
FilterSpec::Plain{ name, value } => match name.as_ref() {
50-
"blur" => {
51-
if raster {
52-
let sigma_x = value / (2.0 * matrix.scale_x());
53-
let sigma_y = value / (2.0 * matrix.scale_y());
54-
image_filters::blur((sigma_x, sigma_y), TileMode::Decal, chain, None)
55-
} else {
56-
paint.set_mask_filter(MaskFilter::blur(BlurStyle::Normal, *value, false));
57-
chain
58-
}
59-
},
59+
pub fn mix_into<'a>(&mut self, paint:&'a mut Paint, matrix:Matrix, raster:bool) -> &'a mut Paint {
60+
let filters = self.filters_for(matrix, raster);
61+
paint.set_image_filter(filters.image)
62+
.set_mask_filter(filters.mask)
63+
}
6064

61-
//
62-
// matrices and formulæ taken from: https://www.w3.org/TR/filter-effects-1/
63-
//
64-
"brightness" => {
65-
let amt = value.max(0.0);
66-
let color_matrix = color_filters::matrix_row_major(&[
67-
amt, 0.0, 0.0, 0.0, 0.0,
68-
0.0, amt, 0.0, 0.0, 0.0,
69-
0.0, 0.0, amt, 0.0, 0.0,
70-
0.0, 0.0, 0.0, 1.0, 0.0
71-
]);
72-
image_filters::color_filter(color_matrix, chain, None)
73-
},
74-
"contrast" => {
75-
let amt = value.max(0.0);
76-
let mut ramp = [0u8; 256];
77-
for (i, val) in ramp.iter_mut().take(256).enumerate() {
78-
let orig = i as f32;
79-
*val = (127.0 + amt * orig - (127.0 * amt )) as u8;
80-
}
81-
let table = Some(&ramp);
82-
let color_table = table_color_filter::from_argb(None, table, table, table);
83-
image_filters::color_filter(color_table, chain, None)
84-
},
85-
"grayscale" => {
86-
let amt = 1.0 - value.max(0.0).min(1.0);
87-
let color_matrix = color_filters::matrix_row_major(&[
88-
(0.2126 + 0.7874 * amt), (0.7152 - 0.7152 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
89-
(0.2126 - 0.2126 * amt), (0.7152 + 0.2848 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
90-
(0.2126 - 0.2126 * amt), (0.7152 - 0.7152 * amt), (0.0722 + 0.9278 * amt), 0.0, 0.0,
91-
0.0, 0.0, 0.0, 1.0, 0.0
92-
]);
93-
image_filters::color_filter(color_matrix, chain, None)
94-
},
95-
"invert" => {
96-
let amt = value.max(0.0).min(1.0);
97-
let mut ramp = [0u8; 256];
98-
for (i, val) in ramp.iter_mut().take(256).enumerate().map(|(i,v)| (i as f32, v)) {
99-
let (orig, inv) = (i, 255.0-i);
100-
*val = (orig * (1.0 - amt) + inv * amt) as u8;
101-
}
102-
let table = Some(&ramp);
103-
let color_table = table_color_filter::from_argb(None, table, table, table);
104-
image_filters::color_filter(color_table, chain, None)
105-
},
106-
"opacity" => {
107-
let amt = value.max(0.0).min(1.0);
108-
let color_matrix = color_filters::matrix_row_major(&[
109-
1.0, 0.0, 0.0, 0.0, 0.0,
110-
0.0, 1.0, 0.0, 0.0, 0.0,
111-
0.0, 0.0, 1.0, 0.0, 0.0,
112-
0.0, 0.0, 0.0, amt, 0.0
113-
]);
114-
image_filters::color_filter(color_matrix, chain, None)
115-
},
116-
"saturate" => {
117-
let amt = value.max(0.0);
118-
let color_matrix = color_filters::matrix_row_major(&[
119-
(0.2126 + 0.7874 * amt), (0.7152 - 0.7152 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
120-
(0.2126 - 0.2126 * amt), (0.7152 + 0.2848 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
121-
(0.2126 - 0.2126 * amt), (0.7152 - 0.7152 * amt), (0.0722 + 0.9278 * amt), 0.0, 0.0,
122-
0.0, 0.0, 0.0, 1.0, 0.0
123-
]);
124-
image_filters::color_filter(color_matrix, chain, None)
125-
},
126-
"sepia" => {
127-
let amt = 1.0 - value.max(0.0).min(1.0);
128-
let color_matrix = color_filters::matrix_row_major(&[
129-
(0.393 + 0.607 * amt), (0.769 - 0.769 * amt), (0.189 - 0.189 * amt), 0.0, 0.0,
130-
(0.349 - 0.349 * amt), (0.686 + 0.314 * amt), (0.168 - 0.168 * amt), 0.0, 0.0,
131-
(0.272 - 0.272 * amt), (0.534 - 0.534 * amt), (0.131 + 0.869 * amt), 0.0, 0.0,
132-
0.0, 0.0, 0.0, 1.0, 0.0
133-
]);
134-
image_filters::color_filter(color_matrix, chain, None)
135-
},
136-
"hue-rotate" => {
137-
let cos = to_radians(*value).cos();
138-
let sin = to_radians(*value).sin();
139-
let color_matrix = color_filters::matrix_row_major(&[
140-
(0.213 + cos*0.787 - sin*0.213), (0.715 - cos*0.715 - sin*0.715), (0.072 - cos*0.072 + sin*0.928), 0.0, 0.0,
141-
(0.213 - cos*0.213 + sin*0.143), (0.715 + cos*0.285 + sin*0.140), (0.072 - cos*0.072 - sin*0.283), 0.0, 0.0,
142-
(0.213 - cos*0.213 - sin*0.787), (0.715 - cos*0.715 + sin*0.715), (0.072 + cos*0.928 + sin*0.072), 0.0, 0.0,
143-
0.0, 0.0, 0.0, 1.0, 0.0
144-
]);
145-
image_filters::color_filter(color_matrix, chain, None)
65+
fn filters_for(&mut self, matrix:Matrix, raster:bool) -> LastFilter {
66+
let cached = match (raster, &self._raster, &self._vector) {
67+
(true, Some(cached), _) | (false, _, Some(cached)) => cached.match_scale(matrix),
68+
_ => None
69+
};
70+
71+
cached.or_else(|| {
72+
let mut mask_filter = None;
73+
let image_filter = self.specs.iter().fold(None, |chain, next_filter|
74+
match next_filter {
75+
FilterSpec::Shadow{ offset, blur, color } => {
76+
let scale = Point{x:matrix.scale_x(), y:matrix.scale_y()};
77+
let point = (offset.x / scale.x, offset.y / scale.y);
78+
let sigma = ( blur / scale.x, blur / scale.y);
79+
image_filters::drop_shadow(point, sigma, *color, chain, None)
14680
},
147-
_ => chain
81+
FilterSpec::Plain{ name, value } => match name.as_ref() {
82+
"blur" => {
83+
if raster {
84+
let sigma_x = value / (2.0 * matrix.scale_x());
85+
let sigma_y = value / (2.0 * matrix.scale_y());
86+
image_filters::blur((sigma_x, sigma_y), TileMode::Decal, chain, None)
87+
} else {
88+
mask_filter = MaskFilter::blur(BlurStyle::Normal, *value, false);
89+
chain
90+
}
91+
},
92+
93+
//
94+
// matrices and formulæ taken from: https://www.w3.org/TR/filter-effects-1/
95+
//
96+
"brightness" => {
97+
let amt = value.max(0.0);
98+
let color_matrix = color_filters::matrix_row_major(&[
99+
amt, 0.0, 0.0, 0.0, 0.0,
100+
0.0, amt, 0.0, 0.0, 0.0,
101+
0.0, 0.0, amt, 0.0, 0.0,
102+
0.0, 0.0, 0.0, 1.0, 0.0
103+
]);
104+
image_filters::color_filter(color_matrix, chain, None)
105+
},
106+
"contrast" => {
107+
let amt = value.max(0.0);
108+
let mut ramp = [0u8; 256];
109+
for (i, val) in ramp.iter_mut().take(256).enumerate() {
110+
let orig = i as f32;
111+
*val = (127.0 + amt * orig - (127.0 * amt )) as u8;
112+
}
113+
let table = Some(&ramp);
114+
let color_table = table_color_filter::from_argb(None, table, table, table);
115+
image_filters::color_filter(color_table, chain, None)
116+
},
117+
"grayscale" => {
118+
let amt = 1.0 - value.max(0.0).min(1.0);
119+
let color_matrix = color_filters::matrix_row_major(&[
120+
(0.2126 + 0.7874 * amt), (0.7152 - 0.7152 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
121+
(0.2126 - 0.2126 * amt), (0.7152 + 0.2848 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
122+
(0.2126 - 0.2126 * amt), (0.7152 - 0.7152 * amt), (0.0722 + 0.9278 * amt), 0.0, 0.0,
123+
0.0, 0.0, 0.0, 1.0, 0.0
124+
]);
125+
image_filters::color_filter(color_matrix, chain, None)
126+
},
127+
"invert" => {
128+
let amt = value.max(0.0).min(1.0);
129+
let mut ramp = [0u8; 256];
130+
for (i, val) in ramp.iter_mut().take(256).enumerate().map(|(i,v)| (i as f32, v)) {
131+
let (orig, inv) = (i, 255.0-i);
132+
*val = (orig * (1.0 - amt) + inv * amt) as u8;
133+
}
134+
let table = Some(&ramp);
135+
let color_table = table_color_filter::from_argb(None, table, table, table);
136+
image_filters::color_filter(color_table, chain, None)
137+
},
138+
"opacity" => {
139+
let amt = value.max(0.0).min(1.0);
140+
let color_matrix = color_filters::matrix_row_major(&[
141+
1.0, 0.0, 0.0, 0.0, 0.0,
142+
0.0, 1.0, 0.0, 0.0, 0.0,
143+
0.0, 0.0, 1.0, 0.0, 0.0,
144+
0.0, 0.0, 0.0, amt, 0.0
145+
]);
146+
image_filters::color_filter(color_matrix, chain, None)
147+
},
148+
"saturate" => {
149+
let amt = value.max(0.0);
150+
let color_matrix = color_filters::matrix_row_major(&[
151+
(0.2126 + 0.7874 * amt), (0.7152 - 0.7152 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
152+
(0.2126 - 0.2126 * amt), (0.7152 + 0.2848 * amt), (0.0722 - 0.0722 * amt), 0.0, 0.0,
153+
(0.2126 - 0.2126 * amt), (0.7152 - 0.7152 * amt), (0.0722 + 0.9278 * amt), 0.0, 0.0,
154+
0.0, 0.0, 0.0, 1.0, 0.0
155+
]);
156+
image_filters::color_filter(color_matrix, chain, None)
157+
},
158+
"sepia" => {
159+
let amt = 1.0 - value.max(0.0).min(1.0);
160+
let color_matrix = color_filters::matrix_row_major(&[
161+
(0.393 + 0.607 * amt), (0.769 - 0.769 * amt), (0.189 - 0.189 * amt), 0.0, 0.0,
162+
(0.349 - 0.349 * amt), (0.686 + 0.314 * amt), (0.168 - 0.168 * amt), 0.0, 0.0,
163+
(0.272 - 0.272 * amt), (0.534 - 0.534 * amt), (0.131 + 0.869 * amt), 0.0, 0.0,
164+
0.0, 0.0, 0.0, 1.0, 0.0
165+
]);
166+
image_filters::color_filter(color_matrix, chain, None)
167+
},
168+
"hue-rotate" => {
169+
let cos = to_radians(*value).cos();
170+
let sin = to_radians(*value).sin();
171+
let color_matrix = color_filters::matrix_row_major(&[
172+
(0.213 + cos*0.787 - sin*0.213), (0.715 - cos*0.715 - sin*0.715), (0.072 - cos*0.072 + sin*0.928), 0.0, 0.0,
173+
(0.213 - cos*0.213 + sin*0.143), (0.715 + cos*0.285 + sin*0.140), (0.072 - cos*0.072 - sin*0.283), 0.0, 0.0,
174+
(0.213 - cos*0.213 - sin*0.787), (0.715 - cos*0.715 + sin*0.715), (0.072 + cos*0.928 + sin*0.072), 0.0, 0.0,
175+
0.0, 0.0, 0.0, 1.0, 0.0
176+
]);
177+
image_filters::color_filter(color_matrix, chain, None)
178+
},
179+
_ => chain
180+
}
148181
}
149-
}
150-
);
182+
);
151183

152-
paint.set_image_filter(image_filter)
184+
let filters = Some(LastFilter{matrix:matrix, mask:mask_filter, image:image_filter});
185+
if raster{ self._raster = filters.clone(); }
186+
else{ self._vector = filters.clone(); }
187+
filters
188+
}).expect("Could not create filter")
153189
}
154190
}
155191

0 commit comments

Comments
 (0)