cairo_blur/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use cairo::{Format, ImageSurface};
4
5/// Blur a cairo image surface
6pub fn blur_image_surface(surface: &mut ImageSurface, radius: i32) {
7    let mut width = surface.width();
8    let height = surface.height();
9    let temp =
10        ImageSurface::create(Format::ARgb32, width, height).expect("Couldn’t create surface");
11    let mut kernel = [0u8; 17];
12    let size = kernel.len() as i32;
13    let half = size / 2;
14
15    match surface.format() {
16        Format::A1 => return,
17
18        Format::A8 => width /= 4,
19        _ => (),
20    }
21
22    let src_stride = surface.stride();
23    let dst_stride = temp.stride();
24    surface
25        .with_data(move |src| {
26            temp.with_data(move |dst| {
27                let src =
28                    unsafe { std::slice::from_raw_parts_mut(src.as_ptr() as *mut u8, src.len()) };
29                let dst =
30                    unsafe { std::slice::from_raw_parts_mut(dst.as_ptr() as *mut u8, dst.len()) };
31                let mut x: u32;
32                let mut y: u32;
33                let mut z: u32;
34                let mut w: u32;
35                let mut p: u32;
36
37                let mut a: u32 = 0;
38                for i in 0..size {
39                    let f = i - half;
40                    let f = f as f64;
41                    kernel[i as usize] = ((-f * f / 30.0).exp() * 80.0) as u8;
42                    a += kernel[i as usize] as u32;
43                }
44
45                // Horizontally blur from surface -> temp
46                for i in 0..height {
47                    let s: &[u32] = unsafe { src[(i * src_stride) as usize..].align_to::<u32>().1 };
48                    let d: &mut [u32] =
49                        unsafe { dst[(i * dst_stride) as usize..].align_to_mut::<u32>().1 };
50                    for j in 0..width {
51                        if radius < j && j < width - radius {
52                            let j = j as usize;
53                            d[j] = s[j];
54                            continue;
55                        }
56
57                        x = 0;
58                        y = 0;
59                        z = 0;
60                        w = 0;
61                        for k in 0..size {
62                            if j - half + k < 0 || j - half + k >= width {
63                                continue;
64                            }
65
66                            p = s[(j - half + k) as usize];
67                            let k = k as usize;
68
69                            x += ((p >> 24) & 0xff) * kernel[k] as u32;
70                            y += ((p >> 16) & 0xff) * kernel[k] as u32;
71                            z += ((p >> 8) & 0xff) * kernel[k] as u32;
72                            w += (p & 0xff) * kernel[k] as u32;
73                        }
74                        d[j as usize] = (x / a) << 24 | (y / a) << 16 | (z / a) << 8 | (w / a);
75                    }
76                }
77
78                // Then vertically blur from tmp -> surface
79                for i in 0..height {
80                    let mut s: &mut [u32] =
81                        unsafe { dst[(i * dst_stride) as usize..].align_to_mut::<u32>().1 };
82                    let d: &mut [u32] =
83                        unsafe { src[(i * src_stride) as usize..].align_to_mut::<u32>().1 };
84                    for j in 0..width {
85                        if radius < i && i < height - radius {
86                            let j = j as usize;
87                            d[j] = s[j];
88                            continue;
89                        }
90
91                        x = 0;
92                        y = 0;
93                        z = 0;
94                        w = 0;
95                        for k in 0..size {
96                            if i - half + k < 0 || i - half + k >= height {
97                                continue;
98                            }
99
100                            s = unsafe {
101                                dst[((i - half + k) * dst_stride) as usize..]
102                                    .align_to_mut::<u32>()
103                                    .1
104                            };
105                            p = s[j as usize];
106                            let k = k as usize;
107                            x += ((p >> 24) & 0xff) * kernel[k] as u32;
108                            y += ((p >> 16) & 0xff) * kernel[k] as u32;
109                            z += ((p >> 8) & 0xff) * kernel[k] as u32;
110                            w += (p & 0xff) * kernel[k] as u32;
111                        }
112                        d[j as usize] = (x / a) << 24 | (y / a) << 16 | (z / a) << 8 | (w / a);
113                    }
114                }
115            })
116            .unwrap();
117        })
118        .unwrap();
119
120    surface.mark_dirty();
121}