1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 use std::cmp;
6 use std::rc::Rc;
7
8 use rgb::FromSlice;
9 use log::warn;
10 use usvg::ColorInterpolation as ColorSpace;
11
12 use crate::prelude::*;
13 use crate::filter::{self, Filter, ImageExt, IntoSvgFilters, Error};
14 use crate::ConvTransform;
15 use super::ReCairoContextExt;
16
17 type Image = filter::Image<cairo::ImageSurface>;
18 type FilterInputs<'a> = filter::FilterInputs<'a, cairo::ImageSurface>;
19 type FilterResult = filter::FilterResult<cairo::ImageSurface>;
20
21
apply( filter: &usvg::Filter, bbox: Option<Rect>, ts: &usvg::Transform, opt: &Options, tree: &usvg::Tree, background: Option<&cairo::ImageSurface>, fill_paint: Option<&cairo::ImageSurface>, stroke_paint: Option<&cairo::ImageSurface>, canvas: &mut cairo::ImageSurface, )22 pub fn apply(
23 filter: &usvg::Filter,
24 bbox: Option<Rect>,
25 ts: &usvg::Transform,
26 opt: &Options,
27 tree: &usvg::Tree,
28 background: Option<&cairo::ImageSurface>,
29 fill_paint: Option<&cairo::ImageSurface>,
30 stroke_paint: Option<&cairo::ImageSurface>,
31 canvas: &mut cairo::ImageSurface,
32 ) {
33 CairoFilter::apply(filter, bbox, ts, opt, tree, background, fill_paint, stroke_paint, canvas);
34 }
35
36
37 impl ImageExt for cairo::ImageSurface {
width(&self) -> u3238 fn width(&self) -> u32 {
39 self.get_width() as u32
40 }
41
height(&self) -> u3242 fn height(&self) -> u32 {
43 self.get_height() as u32
44 }
45
try_clone(&self) -> Result<Self, Error>46 fn try_clone(&self) -> Result<Self, Error> {
47 let new_image = create_image(self.width(), self.height())?;
48
49 let cr = cairo::Context::new(&new_image);
50 cr.set_source_surface(self, 0.0, 0.0);
51 cr.paint();
52
53 Ok(new_image)
54 }
55
clip(&mut self, region: ScreenRect)56 fn clip(&mut self, region: ScreenRect) {
57 let cr = cairo::Context::new(self);
58 cr.set_source_rgba(0.0, 0.0, 0.0, 0.0);
59 cr.set_operator(cairo::Operator::Clear);
60
61 cr.rectangle(0.0, 0.0, self.width() as f64, region.y() as f64);
62 cr.rectangle(0.0, 0.0, region.x() as f64, self.height() as f64);
63 cr.rectangle(region.right() as f64, 0.0, self.width() as f64, self.height() as f64);
64 cr.rectangle(0.0, region.bottom() as f64, self.width() as f64, self.height() as f64);
65
66 cr.fill();
67 }
68
clear(&mut self)69 fn clear(&mut self) {
70 let cr = cairo::Context::new(self);
71 cr.set_operator(cairo::Operator::Clear);
72 cr.set_source_rgba(0.0, 0.0, 0.0, 0.0);
73 cr.paint();
74 }
75
into_srgb(&mut self)76 fn into_srgb(&mut self) {
77 if let Ok(ref mut data) = self.get_data() {
78 svgfilters::demultiply_alpha(data.as_bgra_mut());
79 svgfilters::from_linear_rgb(data.as_bgra_mut());
80 svgfilters::multiply_alpha(data.as_bgra_mut());
81 } else {
82 warn!("Cairo surface is already borrowed.");
83 }
84 }
85
into_linear_rgb(&mut self)86 fn into_linear_rgb(&mut self) {
87 if let Ok(ref mut data) = self.get_data() {
88 svgfilters::demultiply_alpha(data.as_bgra_mut());
89 svgfilters::into_linear_rgb(data.as_bgra_mut());
90 svgfilters::multiply_alpha(data.as_bgra_mut());
91 } else {
92 warn!("Cairo surface is already borrowed.");
93 }
94 }
95 }
96
create_image(width: u32, height: u32) -> Result<cairo::ImageSurface, Error>97 fn create_image(width: u32, height: u32) -> Result<cairo::ImageSurface, Error> {
98 cairo::ImageSurface::create(cairo::Format::ARgb32, width as i32, height as i32)
99 .map_err(|_| Error::AllocFailed)
100 }
101
copy_image( image: &cairo::ImageSurface, region: ScreenRect, ) -> Result<cairo::ImageSurface, Error>102 fn copy_image(
103 image: &cairo::ImageSurface,
104 region: ScreenRect,
105 ) -> Result<cairo::ImageSurface, Error> {
106 let x = cmp::max(0, region.x()) as f64;
107 let y = cmp::max(0, region.y()) as f64;
108
109 let new_image = create_image(region.width(), region.height())?;
110
111 let cr = cairo::Context::new(&new_image);
112 cr.set_source_surface(&*image, -x, -y);
113 cr.paint();
114
115 Ok(new_image)
116 }
117
118 struct CairoFilter;
119
120 impl Filter<cairo::ImageSurface> for CairoFilter {
get_input( input: &usvg::FilterInput, region: ScreenRect, inputs: &FilterInputs, results: &[FilterResult], ) -> Result<Image, Error>121 fn get_input(
122 input: &usvg::FilterInput,
123 region: ScreenRect,
124 inputs: &FilterInputs,
125 results: &[FilterResult],
126 ) -> Result<Image, Error> {
127 let convert = |in_image, region| {
128 let image = if let Some(image) = in_image {
129 copy_image(image, region)?
130 } else {
131 create_image(region.width(), region.height())?
132 };
133
134 Ok(Image {
135 image: Rc::new(image),
136 region: region.translate_to(0, 0),
137 color_space: ColorSpace::SRGB,
138 })
139 };
140
141 let convert_alpha = |mut image: cairo::ImageSurface| {
142 // Set RGB to black. Keep alpha as is.
143 if let Ok(ref mut data) = image.get_data() {
144 for p in data.chunks_mut(4) {
145 p[0] = 0;
146 p[1] = 0;
147 p[2] = 0;
148 }
149 } else {
150 warn!("Cairo surface is already borrowed.");
151 }
152
153 Ok(Image {
154 image: Rc::new(image),
155 region: region.translate_to(0, 0),
156 color_space: ColorSpace::SRGB,
157 })
158 };
159
160 match input {
161 usvg::FilterInput::SourceGraphic => {
162 let image = copy_image(inputs.source, region)?;
163
164 Ok(Image {
165 image: Rc::new(image),
166 region: region.translate_to(0, 0),
167 color_space: ColorSpace::SRGB,
168 })
169 }
170 usvg::FilterInput::SourceAlpha => {
171 let image = copy_image(inputs.source, region)?;
172 convert_alpha(image)
173 }
174 usvg::FilterInput::BackgroundImage => {
175 convert(inputs.background, region)
176 }
177 usvg::FilterInput::BackgroundAlpha => {
178 let image = Self::get_input(
179 &usvg::FilterInput::BackgroundImage, region, inputs, results,
180 )?;
181 let image = image.take()?;
182 convert_alpha(image)
183 }
184 usvg::FilterInput::FillPaint => {
185 convert(inputs.fill_paint, region.translate_to(0, 0))
186 }
187 usvg::FilterInput::StrokePaint => {
188 convert(inputs.stroke_paint, region.translate_to(0, 0))
189 }
190 usvg::FilterInput::Reference(ref name) => {
191 if let Some(ref v) = results.iter().rev().find(|v| v.name == *name) {
192 Ok(v.image.clone())
193 } else {
194 // Technically unreachable.
195 warn!("Unknown filter primitive reference '{}'.", name);
196 Self::get_input(&usvg::FilterInput::SourceGraphic, region, inputs, results)
197 }
198 }
199 }
200 }
201
apply_blur( fe: &usvg::FeGaussianBlur, units: usvg::Units, cs: ColorSpace, bbox: Option<Rect>, ts: &usvg::Transform, input: Image, ) -> Result<Image, Error>202 fn apply_blur(
203 fe: &usvg::FeGaussianBlur,
204 units: usvg::Units,
205 cs: ColorSpace,
206 bbox: Option<Rect>,
207 ts: &usvg::Transform,
208 input: Image,
209 ) -> Result<Image, Error> {
210 let (std_dx, std_dy, box_blur)
211 = try_opt_or!(Self::resolve_std_dev(fe, units, bbox, ts), Ok(input));
212
213 let mut buffer = input.into_color_space(cs)?.take()?;
214 let (w, h) = (buffer.width(), buffer.height());
215 if let Ok(ref mut data) = buffer.get_data() {
216 let img = svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h);
217 if box_blur {
218 svgfilters::box_blur(std_dx, std_dy, img);
219 } else {
220 svgfilters::iir_blur(std_dx, std_dy, img);
221 }
222 }
223
224 Ok(Image::from_image(buffer, cs))
225 }
226
apply_offset( fe: &usvg::FeOffset, units: usvg::Units, bbox: Option<Rect>, ts: &usvg::Transform, input: Image, ) -> Result<Image, Error>227 fn apply_offset(
228 fe: &usvg::FeOffset,
229 units: usvg::Units,
230 bbox: Option<Rect>,
231 ts: &usvg::Transform,
232 input: Image,
233 ) -> Result<Image, Error> {
234 let (dx, dy) = try_opt_or!(Self::scale_coordinates(fe.dx, fe.dy, units, bbox, ts), Ok(input));
235 if dx.is_fuzzy_zero() && dy.is_fuzzy_zero() {
236 return Ok(input);
237 }
238
239 // TODO: do not use an additional buffer
240 let buffer = create_image(input.width(), input.height())?;
241
242 let cr = cairo::Context::new(&buffer);
243 cr.set_source_surface(input.as_ref(), dx, dy);
244 cr.paint();
245
246 Ok(Image::from_image(buffer, input.color_space))
247 }
248
apply_blend( fe: &usvg::FeBlend, cs: ColorSpace, region: ScreenRect, input1: Image, input2: Image, ) -> Result<Image, Error>249 fn apply_blend(
250 fe: &usvg::FeBlend,
251 cs: ColorSpace,
252 region: ScreenRect,
253 input1: Image,
254 input2: Image,
255 ) -> Result<Image, Error> {
256 let input1 = input1.into_color_space(cs)?;
257 let input2 = input2.into_color_space(cs)?;
258
259 let buffer = create_image(region.width(), region.height())?;
260 let cr = cairo::Context::new(&buffer);
261
262 cr.set_source_surface(input2.as_ref(), 0.0, 0.0);
263 cr.paint();
264
265 let operator = match fe.mode {
266 usvg::FeBlendMode::Normal => cairo::Operator::Over,
267 usvg::FeBlendMode::Multiply => cairo::Operator::Multiply,
268 usvg::FeBlendMode::Screen => cairo::Operator::Screen,
269 usvg::FeBlendMode::Darken => cairo::Operator::Darken,
270 usvg::FeBlendMode::Lighten => cairo::Operator::Lighten,
271 };
272
273 cr.set_operator(operator);
274 cr.set_source_surface(input1.as_ref(), 0.0, 0.0);
275 cr.paint();
276
277 Ok(Image::from_image(buffer, cs))
278 }
279
apply_composite( fe: &usvg::FeComposite, cs: ColorSpace, region: ScreenRect, input1: Image, input2: Image, ) -> Result<Image, Error>280 fn apply_composite(
281 fe: &usvg::FeComposite,
282 cs: ColorSpace,
283 region: ScreenRect,
284 input1: Image,
285 input2: Image,
286 ) -> Result<Image, Error> {
287 let mut buffer1 = input1.into_color_space(cs)?.take()?;
288 let mut buffer2 = input2.into_color_space(cs)?.take()?;
289
290 let mut buffer = create_image(region.width(), region.height())?;
291
292 if let Operator::Arithmetic { k1, k2, k3, k4 } = fe.operator {
293 let (w, h) = (region.width(), region.height());
294 svgfilters::arithmetic_composite(
295 k1, k2, k3, k4,
296 svgfilters::ImageRef::new(buffer1.get_data().unwrap().as_bgra(), w, h),
297 svgfilters::ImageRef::new(buffer2.get_data().unwrap().as_bgra(), w, h),
298 svgfilters::ImageRefMut::new(buffer.get_data().unwrap().as_bgra_mut(), w, h),
299 );
300
301 return Ok(Image::from_image(buffer, cs));
302 }
303
304 let cr = cairo::Context::new(&buffer);
305
306 cr.set_source_surface(&buffer2, 0.0, 0.0);
307 cr.paint();
308
309 use usvg::FeCompositeOperator as Operator;
310 let operator = match fe.operator {
311 Operator::Over => cairo::Operator::Over,
312 Operator::In => cairo::Operator::In,
313 Operator::Out => cairo::Operator::Out,
314 Operator::Atop => cairo::Operator::Atop,
315 Operator::Xor => cairo::Operator::Xor,
316 Operator::Arithmetic { .. } => cairo::Operator::Over,
317 };
318
319 cr.set_operator(operator);
320 cr.set_source_surface(&buffer1, 0.0, 0.0);
321 cr.paint();
322
323 Ok(Image::from_image(buffer, cs))
324 }
325
apply_merge( fe: &usvg::FeMerge, cs: ColorSpace, region: ScreenRect, inputs: &FilterInputs, results: &[FilterResult], ) -> Result<Image, Error>326 fn apply_merge(
327 fe: &usvg::FeMerge,
328 cs: ColorSpace,
329 region: ScreenRect,
330 inputs: &FilterInputs,
331 results: &[FilterResult],
332 ) -> Result<Image, Error> {
333 let buffer = create_image(region.width(), region.height())?;
334 let cr = cairo::Context::new(&buffer);
335
336 for input in &fe.inputs {
337 let input = Self::get_input(input, region, inputs, results)?;
338 let input = input.into_color_space(cs)?;
339
340 cr.set_source_surface(input.as_ref(), 0.0, 0.0);
341 cr.paint();
342 }
343
344 Ok(Image::from_image(buffer, cs))
345 }
346
apply_flood( fe: &usvg::FeFlood, region: ScreenRect, ) -> Result<Image, Error>347 fn apply_flood(
348 fe: &usvg::FeFlood,
349 region: ScreenRect,
350 ) -> Result<Image, Error> {
351 let buffer = create_image(region.width(), region.height())?;
352
353 let cr = cairo::Context::new(&buffer);
354 cr.set_source_color(fe.color, fe.opacity);
355 cr.paint();
356
357 Ok(Image::from_image(buffer, ColorSpace::SRGB))
358 }
359
apply_tile( input: Image, region: ScreenRect, ) -> Result<Image, Error>360 fn apply_tile(
361 input: Image,
362 region: ScreenRect,
363 ) -> Result<Image, Error> {
364 let buffer = create_image(region.width(), region.height())?;
365
366 let subregion = input.region.translate(-region.x(), -region.y());
367
368 let tile = copy_image(&input.image, subregion)?;
369 let brush_ts = usvg::Transform::new_translate(subregion.x() as f64, subregion.y() as f64);
370
371 let patt = cairo::SurfacePattern::create(&tile);
372 patt.set_extend(cairo::Extend::Repeat);
373 patt.set_filter(cairo::Filter::Best);
374
375 let cr = cairo::Context::new(&buffer);
376 let mut m: cairo::Matrix = brush_ts.to_native();
377 m.invert();
378 patt.set_matrix(m);
379
380 cr.set_source(&patt);
381 cr.rectangle(0.0, 0.0, region.width() as f64, region.height() as f64);
382 cr.fill();
383
384 Ok(Image::from_image(buffer, ColorSpace::SRGB))
385 }
386
apply_image( fe: &usvg::FeImage, region: ScreenRect, subregion: ScreenRect, opt: &Options, tree: &usvg::Tree, ts: &usvg::Transform, ) -> Result<Image, Error>387 fn apply_image(
388 fe: &usvg::FeImage,
389 region: ScreenRect,
390 subregion: ScreenRect,
391 opt: &Options,
392 tree: &usvg::Tree,
393 ts: &usvg::Transform,
394 ) -> Result<Image, Error> {
395 let buffer = create_image(region.width(), region.height())?;
396
397 match fe.data {
398 usvg::FeImageKind::Image(ref data, format) => {
399 let cr = cairo::Context::new(&buffer);
400
401 let dx = (subregion.x() - region.x()) as f64;
402 let dy = (subregion.y() - region.y()) as f64;
403 cr.translate(dx, dy);
404
405 let view_box = usvg::ViewBox {
406 rect: subregion.translate_to(0, 0).to_rect(),
407 aspect: fe.aspect,
408 };
409
410 if format == usvg::ImageFormat::SVG {
411 super::image::draw_svg(data, view_box, opt, &cr);
412 } else {
413 super::image::draw_raster(format, data, view_box, fe.rendering_mode, opt, &cr);
414 }
415 }
416 usvg::FeImageKind::Use(ref id) => {
417 if let Some(ref node) = tree.defs_by_id(id).or(tree.node_by_id(id)) {
418 let mut layers = super::create_layers(region.size());
419 let cr = cairo::Context::new(&buffer);
420
421 let (sx, sy) = ts.get_scale();
422 cr.scale(sx, sy);
423 cr.transform(node.transform().to_native());
424
425 super::render_node(node, opt, &mut crate::RenderState::Ok, &mut layers, &cr);
426 }
427 }
428 }
429
430 Ok(Image::from_image(buffer, ColorSpace::SRGB))
431 }
432
apply_component_transfer( fe: &usvg::FeComponentTransfer, cs: ColorSpace, input: Image, ) -> Result<Image, Error>433 fn apply_component_transfer(
434 fe: &usvg::FeComponentTransfer,
435 cs: ColorSpace,
436 input: Image,
437 ) -> Result<Image, Error> {
438 let mut buffer = input.into_color_space(cs)?.take()?;
439 let (w, h) = (buffer.width(), buffer.height());
440 if let Ok(ref mut data) = buffer.get_data() {
441 svgfilters::demultiply_alpha(data.as_bgra_mut());
442
443 svgfilters::component_transfer(
444 fe.func_b.into_svgf(),
445 fe.func_g.into_svgf(),
446 fe.func_r.into_svgf(),
447 fe.func_a.into_svgf(),
448 svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h),
449 );
450
451 svgfilters::multiply_alpha(data.as_bgra_mut());
452 }
453
454 Ok(Image::from_image(buffer, cs))
455 }
456
apply_color_matrix( fe: &usvg::FeColorMatrix, cs: ColorSpace, input: Image, ) -> Result<Image, Error>457 fn apply_color_matrix(
458 fe: &usvg::FeColorMatrix,
459 cs: ColorSpace,
460 input: Image,
461 ) -> Result<Image, Error> {
462 let mut buffer = input.into_color_space(cs)?.take()?;
463 let (w, h) = (buffer.width(), buffer.height());
464 if let Ok(ref mut data) = buffer.get_data() {
465 svgfilters::demultiply_alpha(data.as_bgra_mut());
466 svgfilters::color_matrix(
467 fe.kind.into_svgf(), svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h),
468 );
469 svgfilters::multiply_alpha(data.as_bgra_mut());
470 }
471
472 Ok(Image::from_image(buffer, cs))
473 }
474
apply_convolve_matrix( fe: &usvg::FeConvolveMatrix, cs: ColorSpace, input: Image, ) -> Result<Image, Error>475 fn apply_convolve_matrix(
476 fe: &usvg::FeConvolveMatrix,
477 cs: ColorSpace,
478 input: Image,
479 ) -> Result<Image, Error> {
480 let mut buffer = input.into_color_space(cs)?.take()?;
481
482 if fe.preserve_alpha {
483 if let Ok(ref mut data) = buffer.get_data() {
484 svgfilters::demultiply_alpha(data.as_bgra_mut());
485 }
486 }
487
488 let (w, h) = (buffer.width(), buffer.height());
489 if let Ok(ref mut data) = buffer.get_data() {
490 svgfilters::convolve_matrix(
491 fe.matrix.into_svgf(), fe.divisor.value(), fe.bias,
492 fe.edge_mode.into_svgf(), fe.preserve_alpha,
493 svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h),
494 );
495 }
496
497 Ok(Image::from_image(buffer, cs))
498 }
499
apply_morphology( fe: &usvg::FeMorphology, units: usvg::Units, cs: ColorSpace, bbox: Option<Rect>, ts: &usvg::Transform, input: Image, ) -> Result<Image, Error>500 fn apply_morphology(
501 fe: &usvg::FeMorphology,
502 units: usvg::Units,
503 cs: ColorSpace,
504 bbox: Option<Rect>,
505 ts: &usvg::Transform,
506 input: Image,
507 ) -> Result<Image, Error> {
508 let mut buffer = input.into_color_space(cs)?.take()?;
509 let (rx, ry) = try_opt_or!(
510 Self::scale_coordinates(fe.radius_x.value(), fe.radius_y.value(), units, bbox, ts),
511 Ok(Image::from_image(buffer, cs))
512 );
513
514 if !(rx > 0.0 && ry > 0.0) {
515 buffer.clear();
516 return Ok(Image::from_image(buffer, cs));
517 }
518
519 let (w, h) = (buffer.width(), buffer.height());
520 if let Ok(ref mut data) = buffer.get_data() {
521 svgfilters::morphology(
522 fe.operator.into_svgf(), rx, ry,
523 svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h),
524 );
525 }
526
527 Ok(Image::from_image(buffer, cs))
528 }
529
apply_displacement_map( fe: &usvg::FeDisplacementMap, region: ScreenRect, units: usvg::Units, cs: ColorSpace, bbox: Option<Rect>, ts: &usvg::Transform, input1: Image, input2: Image, ) -> Result<Image, Error>530 fn apply_displacement_map(
531 fe: &usvg::FeDisplacementMap,
532 region: ScreenRect,
533 units: usvg::Units,
534 cs: ColorSpace,
535 bbox: Option<Rect>,
536 ts: &usvg::Transform,
537 input1: Image,
538 input2: Image,
539 ) -> Result<Image, Error> {
540 let mut buffer1 = input1.into_color_space(cs)?.take()?;
541 let mut buffer2 = input2.into_color_space(cs)?.take()?;
542 let (sx, sy) = try_opt_or!(
543 Self::scale_coordinates(fe.scale, fe.scale, units, bbox, ts),
544 Ok(Image::from_image(buffer1, cs))
545 );
546
547 let mut buffer = create_image(region.width(), region.height())?;
548
549 let (w, h) = (buffer.width(), buffer.height());
550 if let (Ok(buffer1), Ok(buffer2), Ok(mut buffer))
551 = (buffer1.get_data(), buffer2.get_data(), buffer.get_data())
552 {
553 svgfilters::displacement_map(
554 fe.x_channel_selector.into_svgf(),
555 fe.y_channel_selector.into_svgf(),
556 sx, sy,
557 svgfilters::ImageRef::new(buffer1.as_bgra(), w, h),
558 svgfilters::ImageRef::new(buffer2.as_bgra(), w, h),
559 svgfilters::ImageRefMut::new(buffer.as_bgra_mut(), w, h),
560 );
561 }
562
563 Ok(Image::from_image(buffer, cs))
564 }
565
apply_turbulence( fe: &usvg::FeTurbulence, region: ScreenRect, cs: ColorSpace, ts: &usvg::Transform, ) -> Result<Image, Error>566 fn apply_turbulence(
567 fe: &usvg::FeTurbulence,
568 region: ScreenRect,
569 cs: ColorSpace,
570 ts: &usvg::Transform,
571 ) -> Result<Image, Error> {
572 let mut buffer = create_image(region.width(), region.height())?;
573 let (sx, sy) = ts.get_scale();
574 if sx.is_fuzzy_zero() || sy.is_fuzzy_zero() {
575 return Ok(Image::from_image(buffer, cs));
576 }
577
578 let (w, h) = (buffer.width(), buffer.height());
579 if let Ok(ref mut data) = buffer.get_data() {
580 svgfilters::turbulence(
581 region.x() as f64, region.y() as f64,
582 sx, sy,
583 fe.base_frequency.x.value().into(), fe.base_frequency.y.value().into(),
584 fe.num_octaves,
585 fe.seed,
586 fe.stitch_tiles,
587 fe.kind == usvg::FeTurbulenceKind::FractalNoise,
588 svgfilters::ImageRefMut::new(data.as_bgra_mut(), w, h),
589 );
590
591 svgfilters::multiply_alpha(data.as_bgra_mut());
592 }
593
594 Ok(Image::from_image(buffer, cs))
595 }
596
apply_diffuse_lighting( fe: &usvg::FeDiffuseLighting, region: ScreenRect, cs: ColorSpace, ts: &usvg::Transform, input: Image, ) -> Result<Image, Error>597 fn apply_diffuse_lighting(
598 fe: &usvg::FeDiffuseLighting,
599 region: ScreenRect,
600 cs: ColorSpace,
601 ts: &usvg::Transform,
602 input: Image,
603 ) -> Result<Image, Error> {
604 let mut input = input.take()?;
605 let mut buffer = create_image(region.width(), region.height())?;
606
607 let light_source = crate::filter::transform_light_source(region, ts, fe.light_source);
608
609 let (w, h) = (buffer.width(), buffer.height());
610 if let (Ok(ref buf_in), Ok(ref mut buf_out)) = (input.get_data(), buffer.get_data()) {
611 svgfilters::diffuse_lighting(
612 fe.surface_scale,
613 fe.diffuse_constant,
614 fe.lighting_color.into_svgf(),
615 light_source.into_svgf(),
616 svgfilters::ImageRef::new(buf_in.as_bgra(), w, h),
617 svgfilters::ImageRefMut::new(buf_out.as_bgra_mut(), w, h),
618 );
619 }
620
621 Ok(Image::from_image(buffer, cs))
622 }
623
apply_specular_lighting( fe: &usvg::FeSpecularLighting, region: ScreenRect, cs: ColorSpace, ts: &usvg::Transform, input: Image, ) -> Result<Image, Error>624 fn apply_specular_lighting(
625 fe: &usvg::FeSpecularLighting,
626 region: ScreenRect,
627 cs: ColorSpace,
628 ts: &usvg::Transform,
629 input: Image,
630 ) -> Result<Image, Error> {
631 let mut input = input.take()?;
632 let mut buffer = create_image(region.width(), region.height())?;
633
634 let light_source = crate::filter::transform_light_source(region, ts, fe.light_source);
635
636 let (w, h) = (buffer.width(), buffer.height());
637 if let (Ok(ref buf_in), Ok(ref mut buf_out)) = (input.get_data(), buffer.get_data()) {
638 svgfilters::specular_lighting(
639 fe.surface_scale,
640 fe.specular_constant,
641 fe.specular_exponent,
642 fe.lighting_color.into_svgf(),
643 light_source.into_svgf(),
644 svgfilters::ImageRef::new(buf_in.as_bgra(), w, h),
645 svgfilters::ImageRefMut::new(buf_out.as_bgra_mut(), w, h),
646 );
647 }
648
649 Ok(Image::from_image(buffer, cs))
650 }
651
apply_to_canvas( input: Image, region: ScreenRect, canvas: &mut cairo::ImageSurface, ) -> Result<(), Error>652 fn apply_to_canvas(
653 input: Image,
654 region: ScreenRect,
655 canvas: &mut cairo::ImageSurface,
656 ) -> Result<(), Error> {
657 let input = input.into_color_space(ColorSpace::SRGB)?;
658
659 let cr = cairo::Context::new(canvas);
660
661 cr.set_operator(cairo::Operator::Clear);
662 cr.set_source_rgba(0.0, 0.0, 0.0, 0.0);
663 cr.paint();
664
665 cr.set_operator(cairo::Operator::Over);
666 cr.set_source_surface(input.as_ref(), region.x() as f64, region.y() as f64);
667 cr.paint();
668
669 Ok(())
670 }
671 }
672