maple_render_core/
pixer.rs1use 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
234pub fn sample_weakly(img: &RgbaImage, x: f64, y: f64) -> Pixer {
235 Pixer::from_rgba(&safe_pixel(img, x as i32, y as i32))
236}
237
238#[inline(always)]
239pub fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
240 ((x1 - x2).powi(2) + (y1 - y2).powi(2)).sqrt()
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_pixer_operations() {
249 let mut p = Pixer { r: 100.0, g: 150.0, b: 200.0, a: 255.0 };
250 p.scale(0.5);
251 assert_eq!(p.r, 50.0);
252 assert_eq!(p.g, 75.0);
253 assert_eq!(p.b, 100.0);
254 }
255
256 #[test]
257 fn test_clamp_u8() {
258 assert_eq!(clamp_u8(-10.0), 0);
259 assert_eq!(clamp_u8(128.0), 128);
260 assert_eq!(clamp_u8(300.0), 255);
261 }
262}