1use crate::color::Rgba8;
10
11pub const LINE_SUBPIXEL_SHIFT: u32 = 8;
13pub const LINE_SUBPIXEL_SCALE: i32 = 1 << LINE_SUBPIXEL_SHIFT;
14pub const LINE_SUBPIXEL_MASK: i32 = LINE_SUBPIXEL_SCALE - 1;
15
16pub trait PatternFilter {
21 fn dilation() -> u32;
23
24 fn pixel_low_res(buf: &[Vec<Rgba8>], x: i32, y: i32) -> Rgba8;
26
27 fn pixel_high_res(buf: &[Vec<Rgba8>], p: &mut Rgba8, x: i32, y: i32);
30}
31
32pub struct PatternFilterNn;
37
38impl PatternFilter for PatternFilterNn {
39 fn dilation() -> u32 {
40 0
41 }
42
43 #[inline]
44 fn pixel_low_res(buf: &[Vec<Rgba8>], x: i32, y: i32) -> Rgba8 {
45 buf[y as usize][x as usize]
46 }
47
48 #[inline]
49 fn pixel_high_res(buf: &[Vec<Rgba8>], p: &mut Rgba8, x: i32, y: i32) {
50 *p = buf[(y >> LINE_SUBPIXEL_SHIFT) as usize][(x >> LINE_SUBPIXEL_SHIFT) as usize];
51 }
52}
53
54pub struct PatternFilterBilinearRgba;
59
60impl PatternFilter for PatternFilterBilinearRgba {
61 fn dilation() -> u32 {
62 1
63 }
64
65 #[inline]
66 fn pixel_low_res(buf: &[Vec<Rgba8>], x: i32, y: i32) -> Rgba8 {
67 buf[y as usize][x as usize]
68 }
69
70 #[inline]
71 fn pixel_high_res(buf: &[Vec<Rgba8>], p: &mut Rgba8, x: i32, y: i32) {
72 let x_lr = x >> LINE_SUBPIXEL_SHIFT;
73 let y_lr = y >> LINE_SUBPIXEL_SHIFT;
74
75 let x_hr = x & LINE_SUBPIXEL_MASK;
76 let y_hr = y & LINE_SUBPIXEL_MASK;
77
78 let row0 = &buf[y_lr as usize];
79 let row1 = &buf[y_lr as usize + 1];
80
81 let p00 = &row0[x_lr as usize];
82 let p01 = &row0[x_lr as usize + 1];
83 let p10 = &row1[x_lr as usize];
84 let p11 = &row1[x_lr as usize + 1];
85
86 let weight = LINE_SUBPIXEL_SCALE;
87
88 let r = (p00.r as i32 * (weight - x_hr) * (weight - y_hr)
89 + p01.r as i32 * x_hr * (weight - y_hr)
90 + p10.r as i32 * (weight - x_hr) * y_hr
91 + p11.r as i32 * x_hr * y_hr)
92 >> (LINE_SUBPIXEL_SHIFT * 2);
93
94 let g = (p00.g as i32 * (weight - x_hr) * (weight - y_hr)
95 + p01.g as i32 * x_hr * (weight - y_hr)
96 + p10.g as i32 * (weight - x_hr) * y_hr
97 + p11.g as i32 * x_hr * y_hr)
98 >> (LINE_SUBPIXEL_SHIFT * 2);
99
100 let b = (p00.b as i32 * (weight - x_hr) * (weight - y_hr)
101 + p01.b as i32 * x_hr * (weight - y_hr)
102 + p10.b as i32 * (weight - x_hr) * y_hr
103 + p11.b as i32 * x_hr * y_hr)
104 >> (LINE_SUBPIXEL_SHIFT * 2);
105
106 let a = (p00.a as i32 * (weight - x_hr) * (weight - y_hr)
107 + p01.a as i32 * x_hr * (weight - y_hr)
108 + p10.a as i32 * (weight - x_hr) * y_hr
109 + p11.a as i32 * x_hr * y_hr)
110 >> (LINE_SUBPIXEL_SHIFT * 2);
111
112 *p = Rgba8::new(r as u32, g as u32, b as u32, a as u32);
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 fn make_row(colors: &[Rgba8]) -> Vec<Rgba8> {
121 colors.to_vec()
122 }
123
124 #[test]
125 fn test_nn_low_res() {
126 let row0 = make_row(&[Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 255, 0, 255)]);
127 let row1 = make_row(&[Rgba8::new(0, 0, 255, 255), Rgba8::new(255, 255, 0, 255)]);
128 let buf: Vec<Vec<Rgba8>> = vec![row0, row1];
129
130 let p = PatternFilterNn::pixel_low_res(&buf, 0, 0);
131 assert_eq!(p.r, 255);
132 assert_eq!(p.g, 0);
133
134 let p = PatternFilterNn::pixel_low_res(&buf, 1, 0);
135 assert_eq!(p.r, 0);
136 assert_eq!(p.g, 255);
137
138 let p = PatternFilterNn::pixel_low_res(&buf, 0, 1);
139 assert_eq!(p.b, 255);
140 }
141
142 #[test]
143 fn test_nn_high_res() {
144 let row0 = make_row(&[Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 255, 0, 255)]);
145 let row1 = make_row(&[Rgba8::new(0, 0, 255, 255), Rgba8::new(255, 255, 0, 255)]);
146 let buf: Vec<Vec<Rgba8>> = vec![row0, row1];
147
148 let mut p = Rgba8::new(0, 0, 0, 0);
150 PatternFilterNn::pixel_high_res(&buf, &mut p, 128, 0);
151 assert_eq!(p.r, 255);
152
153 PatternFilterNn::pixel_high_res(&buf, &mut p, 256, 0);
155 assert_eq!(p.r, 0);
156 assert_eq!(p.g, 255);
157 }
158
159 #[test]
160 fn test_bilinear_at_integer_coord() {
161 let row0 = make_row(&[
162 Rgba8::new(255, 0, 0, 255),
163 Rgba8::new(0, 255, 0, 255),
164 Rgba8::new(0, 0, 0, 255),
165 ]);
166 let row1 = make_row(&[
167 Rgba8::new(0, 0, 255, 255),
168 Rgba8::new(255, 255, 0, 255),
169 Rgba8::new(0, 0, 0, 255),
170 ]);
171 let row2 = make_row(&[
172 Rgba8::new(0, 0, 0, 255),
173 Rgba8::new(0, 0, 0, 255),
174 Rgba8::new(0, 0, 0, 255),
175 ]);
176 let buf: Vec<Vec<Rgba8>> = vec![row0, row1, row2];
177
178 let mut p = Rgba8::new(0, 0, 0, 0);
180 PatternFilterBilinearRgba::pixel_high_res(&buf, &mut p, 0, 0);
181 assert_eq!(p.r, 255);
182 assert_eq!(p.g, 0);
183 assert_eq!(p.b, 0);
184 }
185
186 #[test]
187 fn test_bilinear_midpoint() {
188 let white = Rgba8::new(255, 255, 255, 255);
190 let black = Rgba8::new(0, 0, 0, 255);
191 let row0 = make_row(&[white, black]);
192 let row1 = make_row(&[black, black]);
193 let buf: Vec<Vec<Rgba8>> = vec![row0, row1];
194
195 let mut p = Rgba8::new(0, 0, 0, 0);
197 PatternFilterBilinearRgba::pixel_high_res(&buf, &mut p, 128, 128);
198 assert!(p.r > 50 && p.r < 80, "r={}", p.r);
200 }
201}