1use image::{Rgba, RgbaImage};
2
3#[derive(Debug, Clone, Copy, Default)]
4pub struct Pixer {
5 pub r: f32,
6 pub g: f32,
7 pub b: f32,
8 pub a: f32,
9}
10
11impl Pixer {
12 #[inline(always)]
13 pub fn new() -> Self {
14 Pixer::default()
15 }
16
17 #[inline(always)]
18 pub fn from_rgba(pixel: &Rgba<u8>) -> Self {
19 Pixer { r: pixel[0] as f32, g: pixel[1] as f32, b: pixel[2] as f32, a: pixel[3] as f32 }
20 }
21
22 #[inline(always)]
23 pub fn to_rgba(&self) -> Rgba<u8> {
24 Rgba([clamp_u8(self.r), clamp_u8(self.g), clamp_u8(self.b), clamp_u8(self.a)])
25 }
26
27 #[inline(always)]
28 pub fn preblend(&mut self) {
29 self.r *= self.a;
30 self.g *= self.a;
31 self.b *= self.a;
32 }
33
34 #[inline(always)]
35 pub fn postblend(&mut self, scale: f32) {
36 if scale > 0.0001 {
37 self.r /= scale;
38 self.g /= scale;
39 self.b /= scale;
40 } else {
41 self.r = 0.0;
42 self.g = 0.0;
43 self.b = 0.0;
44 }
45 }
46
47 pub fn add(&mut self, other: &Pixer) {
48 self.r += other.r;
49 self.g += other.g;
50 self.b += other.b;
51 self.a += other.a;
52 }
53
54 pub fn add_rgba(&mut self, pixel: &Rgba<u8>) {
55 self.r += pixel[0] as f32;
56 self.g += pixel[1] as f32;
57 self.b += pixel[2] as f32;
58 self.a += pixel[3] as f32;
59 }
60
61 pub fn scale(&mut self, factor: f32) {
62 self.r *= factor;
63 self.g *= factor;
64 self.b *= factor;
65 self.a *= factor;
66 }
67
68 pub fn div(&mut self, factor: f32) {
69 if factor.abs() > 0.0001 {
70 self.r /= factor;
71 self.g /= factor;
72 self.b /= factor;
73 self.a /= factor;
74 }
75 }
76}
77
78impl std::ops::Add for Pixer {
79 type Output = Pixer;
80
81 fn add(self, other: Pixer) -> Pixer {
82 Pixer { r: self.r + other.r, g: self.g + other.g, b: self.b + other.b, a: self.a + other.a }
83 }
84}
85
86impl std::ops::AddAssign for Pixer {
87 fn add_assign(&mut self, other: Pixer) {
88 self.r += other.r;
89 self.g += other.g;
90 self.b += other.b;
91 self.a += other.a;
92 }
93}
94
95impl std::ops::Mul<f32> for Pixer {
96 type Output = Pixer;
97
98 fn mul(self, factor: f32) -> Pixer {
99 Pixer { r: self.r * factor, g: self.g * factor, b: self.b * factor, a: self.a * factor }
100 }
101}
102
103impl std::ops::Div<f32> for Pixer {
104 type Output = Pixer;
105
106 fn div(self, factor: f32) -> Pixer {
107 if factor.abs() > 0.0001 {
108 Pixer { r: self.r / factor, g: self.g / factor, b: self.b / factor, a: self.a / factor }
109 } else {
110 Pixer::new()
111 }
112 }
113}
114
115#[inline]
116fn clamp_u8(v: f32) -> u8 {
117 if v <= 0.0 {
118 0
119 } else if v >= 255.0 {
120 255
121 } else {
122 v as u8
123 }
124}
125
126#[inline(always)]
127pub fn safe_pixel(img: &RgbaImage, x: i32, y: i32) -> Rgba<u8> {
128 let w = img.width() as i32;
129 let h = img.height() as i32;
130
131 if x < 0 || y < 0 || x >= w || y >= h {
132 Rgba([0, 0, 0, 0])
133 } else {
134 *img.get_pixel(x as u32, y as u32)
135 }
136}
137
138#[inline(always)]
145pub fn sample_linear(img: &RgbaImage, x: f64, y: f64) -> Pixer {
146 let xx = x.floor() as i32;
147 let yy = y.floor() as i32;
148 let fx = x - xx as f64;
149 let fy = y - yy as f64;
150
151 let w00 = ((1.0 - fx) * (1.0 - fy)) as f32;
152 let w10 = (fx * (1.0 - fy)) as f32;
153 let w01 = ((1.0 - fx) * fy) as f32;
154 let w11 = (fx * fy) as f32;
155
156 let w = img.width() as i32;
157 let h = img.height() as i32;
158
159 if xx >= 0 && yy >= 0 && xx + 1 < w && yy + 1 < h {
160 let raw = img.as_raw();
161 let w_us = w as usize;
162 let row_stride = w_us * 4;
163
164 let row0 = yy as usize * row_stride;
165 let row1 = row0 + row_stride;
166 let col = xx as usize * 4;
167
168 let i00 = row0 + col;
169 let i10 = i00 + 4;
170 let i01 = row1 + col;
171 let i11 = i01 + 4;
172
173 let a00 = raw[i00 + 3] as f32;
174 let a10 = raw[i10 + 3] as f32;
175 let a01 = raw[i01 + 3] as f32;
176 let a11 = raw[i11 + 3] as f32;
177
178 let aa = w00 * a00 + w10 * a10 + w01 * a01 + w11 * a11;
179 let aa_safe = if aa < 0.0001 { 0.0001 } else { aa };
180
181 return Pixer {
182 r: (w00 * raw[i00] as f32 * a00
183 + w10 * raw[i10] as f32 * a10
184 + w01 * raw[i01] as f32 * a01
185 + w11 * raw[i11] as f32 * a11)
186 / aa_safe,
187 g: (w00 * raw[i00 + 1] as f32 * a00
188 + w10 * raw[i10 + 1] as f32 * a10
189 + w01 * raw[i01 + 1] as f32 * a01
190 + w11 * raw[i11 + 1] as f32 * a11)
191 / aa_safe,
192 b: (w00 * raw[i00 + 2] as f32 * a00
193 + w10 * raw[i10 + 2] as f32 * a10
194 + w01 * raw[i01 + 2] as f32 * a01
195 + w11 * raw[i11 + 2] as f32 * a11)
196 / aa_safe,
197 a: aa,
198 };
199 }
200
201 let p00 = safe_pixel(img, xx, yy);
202 let p10 = safe_pixel(img, xx + 1, yy);
203 let p01 = safe_pixel(img, xx, yy + 1);
204 let p11 = safe_pixel(img, xx + 1, yy + 1);
205
206 let a00 = p00[3] as f32;
207 let a10 = p10[3] as f32;
208 let a01 = p01[3] as f32;
209 let a11 = p11[3] as f32;
210
211 let aa = w00 * a00 + w10 * a10 + w01 * a01 + w11 * a11;
212 let aa_safe = if aa < 0.0001 { 0.0001 } else { aa };
213
214 Pixer {
215 r: (w00 * p00[0] as f32 * a00
216 + w10 * p10[0] as f32 * a10
217 + w01 * p01[0] as f32 * a01
218 + w11 * p11[0] as f32 * a11)
219 / aa_safe,
220 g: (w00 * p00[1] as f32 * a00
221 + w10 * p10[1] as f32 * a10
222 + w01 * p01[1] as f32 * a01
223 + w11 * p11[1] as f32 * a11)
224 / aa_safe,
225 b: (w00 * p00[2] as f32 * a00
226 + w10 * p10[2] as f32 * a10
227 + w01 * p01[2] as f32 * a01
228 + w11 * p11[2] as f32 * a11)
229 / aa_safe,
230 a: aa,
231 }
232}
233
234#[inline(always)]
240pub(crate) fn sample_linear_premultiplied(img: &RgbaImage, x: f64, y: f64) -> Pixer {
241 let xx = x.floor() as i32;
242 let yy = y.floor() as i32;
243 let fx = x - xx as f64;
244 let fy = y - yy as f64;
245
246 let w00 = ((1.0 - fx) * (1.0 - fy)) as f32;
247 let w10 = (fx * (1.0 - fy)) as f32;
248 let w01 = ((1.0 - fx) * fy) as f32;
249 let w11 = (fx * fy) as f32;
250
251 let w = img.width() as i32;
252 let h = img.height() as i32;
253
254 if xx >= 0 && yy >= 0 && xx + 1 < w && yy + 1 < h {
255 let raw = img.as_raw();
256 let row_stride = w as usize * 4;
257
258 let i00 = yy as usize * row_stride + xx as usize * 4;
259 let i10 = i00 + 4;
260 let i01 = i00 + row_stride;
261 let i11 = i01 + 4;
262
263 let a00 = raw[i00 + 3] as f32;
264 let a10 = raw[i10 + 3] as f32;
265 let a01 = raw[i01 + 3] as f32;
266 let a11 = raw[i11 + 3] as f32;
267
268 return Pixer {
269 r: w00 * raw[i00] as f32 * a00
270 + w10 * raw[i10] as f32 * a10
271 + w01 * raw[i01] as f32 * a01
272 + w11 * raw[i11] as f32 * a11,
273 g: w00 * raw[i00 + 1] as f32 * a00
274 + w10 * raw[i10 + 1] as f32 * a10
275 + w01 * raw[i01 + 1] as f32 * a01
276 + w11 * raw[i11 + 1] as f32 * a11,
277 b: w00 * raw[i00 + 2] as f32 * a00
278 + w10 * raw[i10 + 2] as f32 * a10
279 + w01 * raw[i01 + 2] as f32 * a01
280 + w11 * raw[i11 + 2] as f32 * a11,
281 a: w00 * a00 + w10 * a10 + w01 * a01 + w11 * a11,
282 };
283 }
284
285 let p00 = safe_pixel(img, xx, yy);
286 let p10 = safe_pixel(img, xx + 1, yy);
287 let p01 = safe_pixel(img, xx, yy + 1);
288 let p11 = safe_pixel(img, xx + 1, yy + 1);
289
290 let a00 = p00[3] as f32;
291 let a10 = p10[3] as f32;
292 let a01 = p01[3] as f32;
293 let a11 = p11[3] as f32;
294
295 Pixer {
296 r: w00 * p00[0] as f32 * a00
297 + w10 * p10[0] as f32 * a10
298 + w01 * p01[0] as f32 * a01
299 + w11 * p11[0] as f32 * a11,
300 g: w00 * p00[1] as f32 * a00
301 + w10 * p10[1] as f32 * a10
302 + w01 * p01[1] as f32 * a01
303 + w11 * p11[1] as f32 * a11,
304 b: w00 * p00[2] as f32 * a00
305 + w10 * p10[2] as f32 * a10
306 + w01 * p01[2] as f32 * a01
307 + w11 * p11[2] as f32 * a11,
308 a: w00 * a00 + w10 * a10 + w01 * a01 + w11 * a11,
309 }
310}
311
312#[inline(always)]
317pub(crate) fn sample_linear_opaque(img: &RgbaImage, x: f64, y: f64) -> Pixer {
318 let xx = x.floor() as i32;
319 let yy = y.floor() as i32;
320 let fx = x - xx as f64;
321 let fy = y - yy as f64;
322
323 let w00 = ((1.0 - fx) * (1.0 - fy)) as f32;
324 let w10 = (fx * (1.0 - fy)) as f32;
325 let w01 = ((1.0 - fx) * fy) as f32;
326 let w11 = (fx * fy) as f32;
327
328 let w = img.width() as i32;
329 let h = img.height() as i32;
330
331 if xx >= 0 && yy >= 0 && xx + 1 < w && yy + 1 < h {
332 let raw = img.as_raw();
333 let row_stride = w as usize * 4;
334
335 let i00 = yy as usize * row_stride + xx as usize * 4;
336 let i10 = i00 + 4;
337 let i01 = i00 + row_stride;
338 let i11 = i01 + 4;
339
340 let alpha = 255.0;
341
342 return Pixer {
343 r: w00 * raw[i00] as f32 * alpha
344 + w10 * raw[i10] as f32 * alpha
345 + w01 * raw[i01] as f32 * alpha
346 + w11 * raw[i11] as f32 * alpha,
347 g: w00 * raw[i00 + 1] as f32 * alpha
348 + w10 * raw[i10 + 1] as f32 * alpha
349 + w01 * raw[i01 + 1] as f32 * alpha
350 + w11 * raw[i11 + 1] as f32 * alpha,
351 b: w00 * raw[i00 + 2] as f32 * alpha
352 + w10 * raw[i10 + 2] as f32 * alpha
353 + w01 * raw[i01 + 2] as f32 * alpha
354 + w11 * raw[i11 + 2] as f32 * alpha,
355 a: w00 * alpha + w10 * alpha + w01 * alpha + w11 * alpha,
356 };
357 }
358
359 let p00 = safe_pixel(img, xx, yy);
360 let p10 = safe_pixel(img, xx + 1, yy);
361 let p01 = safe_pixel(img, xx, yy + 1);
362 let p11 = safe_pixel(img, xx + 1, yy + 1);
363 let a00 = p00[3] as f32;
364 let a10 = p10[3] as f32;
365 let a01 = p01[3] as f32;
366 let a11 = p11[3] as f32;
367
368 Pixer {
369 r: w00 * p00[0] as f32 * a00
370 + w10 * p10[0] as f32 * a10
371 + w01 * p01[0] as f32 * a01
372 + w11 * p11[0] as f32 * a11,
373 g: w00 * p00[1] as f32 * a00
374 + w10 * p10[1] as f32 * a10
375 + w01 * p01[1] as f32 * a01
376 + w11 * p11[1] as f32 * a11,
377 b: w00 * p00[2] as f32 * a00
378 + w10 * p10[2] as f32 * a10
379 + w01 * p01[2] as f32 * a01
380 + w11 * p11[2] as f32 * a11,
381 a: w00 * a00 + w10 * a10 + w01 * a01 + w11 * a11,
382 }
383}
384
385pub fn sample_weakly(img: &RgbaImage, x: f64, y: f64) -> Pixer {
386 Pixer::from_rgba(&safe_pixel(img, x as i32, y as i32))
387}
388
389#[inline(always)]
390pub fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
391 ((x1 - x2).powi(2) + (y1 - y2).powi(2)).sqrt()
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397
398 #[test]
399 fn test_pixer_operations() {
400 let mut p = Pixer { r: 100.0, g: 150.0, b: 200.0, a: 255.0 };
401 p.scale(0.5);
402 assert_eq!(p.r, 50.0);
403 assert_eq!(p.g, 75.0);
404 assert_eq!(p.b, 100.0);
405 }
406
407 #[test]
408 fn test_clamp_u8() {
409 assert_eq!(clamp_u8(-10.0), 0);
410 assert_eq!(clamp_u8(128.0), 128);
411 assert_eq!(clamp_u8(300.0), 255);
412 }
413
414 #[test]
415 fn opaque_sampler_matches_general_premultiplied_sampler() {
416 let mut image = RgbaImage::new(3, 2);
417 for (i, pixel) in image.pixels_mut().enumerate() {
418 let value = u8::try_from(i * 31).expect("test value fits in u8");
419 *pixel = Rgba([value, value.wrapping_add(17), value.wrapping_add(83), 255]);
420 }
421
422 for (x, y) in [
423 (-1.25, -0.5),
424 (-0.25, 0.25),
425 (0.0, 0.0),
426 (0.3, 0.7),
427 (1.5, 0.25),
428 (2.0, 1.0),
429 (2.75, 1.75),
430 (4.0, 4.0),
431 ] {
432 let general = sample_linear_premultiplied(&image, x, y);
433 let opaque = sample_linear_opaque(&image, x, y);
434 assert_eq!(general.r.to_bits(), opaque.r.to_bits());
435 assert_eq!(general.g.to_bits(), opaque.g.to_bits());
436 assert_eq!(general.b.to_bits(), opaque.b.to_bits());
437 assert_eq!(general.a.to_bits(), opaque.a.to_bits());
438 }
439 }
440}