1#![allow(dead_code)]
4
5use alloc::vec::Vec;
6#[cfg(feature = "libm")]
7use core_maths::CoreFloat;
8
9mod png;
10
11pub fn decode_png(data: &[u8], scratch: &mut Vec<u8>, target: &mut [u8]) -> Option<(u32, u32)> {
13 png::decode(data, scratch, target)
14 .map(|(w, h, _)| (w, h))
15 .ok()
16}
17
18pub fn blit(
19 mask: &[u8],
20 mask_width: u32,
21 mask_height: u32,
22 x: i32,
23 y: i32,
24 color: [u8; 4],
25 target: &mut [u8],
26 target_width: u32,
27 target_height: u32,
28) {
29 if mask_width == 0 || mask_height == 0 || target_width == 0 || target_height == 0 {
30 return;
31 }
32 let source_width = mask_width as usize;
33 let source_height = mask_height as usize;
34 let dest_width = target_width as usize;
35 let dest_height = target_height as usize;
36 let source_x = if x < 0 { -x as usize } else { 0 };
37 let source_y = if y < 0 { -y as usize } else { 0 };
38 if source_x >= source_width || source_y >= source_height {
39 return;
40 }
41 let dest_x = if x < 0 { 0 } else { x as usize };
42 let dest_y = if y < 0 { 0 } else { y as usize };
43 if dest_x >= dest_width || dest_y >= dest_height {
44 return;
45 }
46 let source_end_x = (source_width).min(dest_width - dest_x + source_x);
47 let source_end_y = (source_height).min(dest_height - dest_y + source_y);
48 let source_columns = source_y..source_end_y;
49 let source_rows = source_x..source_end_x;
50 let dest_pitch = target_width as usize * 4;
51 let color_a = color[3] as u32;
52 let mut dy = dest_y;
53 for sy in source_columns {
54 let src_row = &mask[sy * mask_width as usize..];
55 let dst_row = &mut target[dy * dest_pitch..];
56 dy += 1;
57 let mut dx = dest_x * 4;
58 for sx in source_rows.clone() {
59 let a = (src_row[sx] as u32 * color_a) >> 8;
60 if a >= 255 {
61 dst_row[dx + 3] = 255;
62 dst_row[dx..(dx + 3)].copy_from_slice(&color[..3]);
63 dst_row[dx + 3] = 255;
64 } else if a != 0 {
65 let inverse_a = 255 - a;
66 for i in 0..3 {
67 let d = dst_row[dx + i] as u32;
68 let c = ((inverse_a * d) + (a * color[i] as u32)) >> 8;
69 dst_row[dx + i] = c as u8;
70 }
71 let d = dst_row[dx + 3] as u32;
72 let c = ((inverse_a * d) + a * 255) >> 8;
73 dst_row[dx + 3] = c as u8;
74 }
75 dx += 4;
76 }
77 }
78}
79
80#[derive(Copy, Clone, PartialEq)]
81pub enum Filter {
82 Nearest,
83 Bilinear,
84 Bicubic,
85 Mitchell,
86 Lanczos3,
87 Gaussian,
88}
89
90pub fn resize(
91 image: &[u8],
92 width: u32,
93 height: u32,
94 channels: u32,
95 target: &mut [u8],
96 target_width: u32,
97 target_height: u32,
98 filter: Filter,
99 scratch: Option<&mut Vec<u8>>,
100) -> bool {
101 if target_width == 0 || target_height == 0 {
102 return true;
103 }
104 let mut tmp = Vec::new();
105 let scratch = if let Some(scratch) = scratch {
106 scratch
107 } else {
108 &mut tmp
109 };
110 let image_size = (width * height * channels) as usize;
111 if image.len() < image_size {
112 return false;
113 }
114 let target_size = (target_width * target_height * channels) as usize;
115 if target.len() < target_size {
116 return false;
117 }
118 let scratch_size = (target_width * height * channels) as usize;
119 scratch.resize(scratch_size, 0);
120 use Filter::*;
121 match filter {
122 Nearest => resample(
123 image,
124 width,
125 height,
126 channels,
127 target,
128 target_width,
129 target_height,
130 scratch,
131 0.,
132 &nearest,
133 ),
134 Bilinear => resample(
135 image,
136 width,
137 height,
138 channels,
139 target,
140 target_width,
141 target_height,
142 scratch,
143 1.,
144 &bilinear,
145 ),
146 Bicubic => resample(
147 image,
148 width,
149 height,
150 channels,
151 target,
152 target_width,
153 target_height,
154 scratch,
155 2.,
156 &bicubic,
157 ),
158 Mitchell => resample(
159 image,
160 width,
161 height,
162 channels,
163 target,
164 target_width,
165 target_height,
166 scratch,
167 2.,
168 &mitchell,
169 ),
170 Lanczos3 => resample(
171 image,
172 width,
173 height,
174 channels,
175 target,
176 target_width,
177 target_height,
178 scratch,
179 3.,
180 &lanczos3,
181 ),
182 Gaussian => resample(
183 image,
184 width,
185 height,
186 channels,
187 target,
188 target_width,
189 target_height,
190 scratch,
191 3.,
192 &|x| gaussian(x, 0.5),
193 ),
194 }
195}
196
197fn resample<Filter>(
198 image: &[u8],
199 width: u32,
200 height: u32,
201 channels: u32,
202 target: &mut [u8],
203 target_width: u32,
204 target_height: u32,
205 scratch: &mut [u8],
206 support: f32,
207 filter: &Filter,
208) -> bool
209where
210 Filter: Fn(f32) -> f32,
211{
212 let tmp_width = target_width;
213 let tmp_height = height;
214 let s = 1. / 255.;
215 if channels == 1 {
216 sample_dir(
217 &|x, y| [0., 0., 0., image[(y * width + x) as usize] as f32 * s],
218 width,
219 height,
220 target_width,
221 filter,
222 support,
223 &mut |x, y, p| scratch[(y * tmp_width + x) as usize] = (p[3] * 255.) as u8,
224 );
225 sample_dir(
226 &|y, x| [0., 0., 0., scratch[(y * tmp_width + x) as usize] as f32 * s],
227 tmp_height,
228 tmp_width,
229 target_height,
230 filter,
231 support,
232 &mut |y, x, p| target[(y * target_width + x) as usize] = (p[3] * 255.) as u8,
233 );
234 true
235 } else if channels == 4 {
236 sample_dir(
237 &|x, y| {
238 let row = (y * width * channels + x * channels) as usize;
239 [
240 image[row] as f32 * s,
241 image[row + 1] as f32 * s,
242 image[row + 2] as f32 * s,
243 image[row + 3] as f32 * s,
244 ]
245 },
246 width,
247 height,
248 target_width,
249 filter,
250 support,
251 &mut |x, y, p| {
252 let row = (y * target_width * channels + x * channels) as usize;
253 scratch[row] = (p[0] * 255.) as u8;
254 scratch[row + 1] = (p[1] * 255.) as u8;
255 scratch[row + 2] = (p[2] * 255.) as u8;
256 scratch[row + 3] = (p[3] * 255.) as u8;
257 },
258 );
259 sample_dir(
260 &|y, x| {
261 let row = (y * tmp_width * channels + x * channels) as usize;
262 [
263 scratch[row] as f32 * s,
264 scratch[row + 1] as f32 * s,
265 scratch[row + 2] as f32 * s,
266 scratch[row + 3] as f32 * s,
267 ]
268 },
269 tmp_height,
270 tmp_width,
271 target_height,
272 filter,
273 support,
274 &mut |y, x, p| {
275 let row = (y * target_width * channels + x * channels) as usize;
276 target[row] = (p[0] * 255.) as u8;
277 target[row + 1] = (p[1] * 255.) as u8;
278 target[row + 2] = (p[2] * 255.) as u8;
279 target[row + 3] = (p[3] * 255.) as u8;
280 },
281 );
282 true
283 } else {
284 false
285 }
286}
287
288fn sample_dir<Input, Output, Filter>(
289 input: &Input,
290 width: u32,
291 height: u32,
292 new_width: u32,
293 filter: &Filter,
294 support: f32,
295 output: &mut Output,
296) where
297 Input: Fn(u32, u32) -> [f32; 4],
298 Output: FnMut(u32, u32, &[f32; 4]),
299 Filter: Fn(f32) -> f32,
300{
301 const MAX_WEIGHTS: usize = 64;
302 let mut weights = [0f32; MAX_WEIGHTS];
303 let mut num_weights;
304 let ratio = width as f32 / new_width as f32;
305 let sratio = ratio.max(1.);
306 let src_support = support * sratio;
307 let isratio = 1. / sratio;
308 for outx in 0..new_width {
309 let inx = (outx as f32 + 0.5) * ratio;
310 let left = (inx - src_support).floor() as i32;
311 let mut left = left.max(0).min(width as i32 - 1) as usize;
312 let right = (inx + src_support).ceil() as i32;
313 let mut right = right.max(left as i32 + 1).min(width as i32) as usize;
314 let inx = inx - 0.5;
315 while right - left > MAX_WEIGHTS {
316 right -= 1;
317 left += 1;
318 }
319 num_weights = 0;
320 let mut sum = 0.;
321 for i in left..right {
322 let w = filter((i as f32 - inx) * isratio);
323 weights[num_weights] = w;
324 num_weights += 1;
325 sum += w;
326 }
327 let isum = 1. / sum;
328 let weights = &weights[..num_weights];
329 for y in 0..height {
330 let mut accum = [0f32; 4];
331 for (i, w) in weights.iter().enumerate() {
332 let p = input((left + i) as u32, y);
333 let a = p[3];
334 accum[0] += p[0] * w * a;
335 accum[1] += p[1] * w * a;
336 accum[2] += p[2] * w * a;
337 accum[3] += p[3] * w;
338 }
339 if accum[3] != 0. {
340 let a = 1. / accum[3];
341 accum[0] *= a;
342 accum[1] *= a;
343 accum[2] *= a;
344 accum[3] *= isum;
345 }
346 output(outx, y, &accum);
347 }
348 }
349}
350
351fn sinc(t: f32) -> f32 {
352 let a = t * core::f32::consts::PI;
353 if t == 0. {
354 1.
355 } else {
356 a.sin() / a
357 }
358}
359
360fn lanczos3(x: f32) -> f32 {
361 if x.abs() < 3. {
362 (sinc(x) * sinc(x / 3.)).abs()
363 } else {
364 0.
365 }
366}
367
368fn bilinear(x: f32) -> f32 {
369 let x = x.abs();
370 if x < 1. {
371 1. - x
372 } else {
373 0.
374 }
375}
376
377fn bicubic(x: f32) -> f32 {
378 let a = x.abs();
379 let b = 0.;
380 let c = 0.5;
381 let k = if a < 1. {
382 (12. - 9. * b - 6. * c) * a.powi(3) + (-18. + 12. * b + 6. * c) * a.powi(2) + (6. - 2. * b)
383 } else if a < 2. {
384 (-b - 6. * c) * a.powi(3)
385 + (6. * b + 30. * c) * a.powi(2)
386 + (-12. * b - 48. * c) * a
387 + (8. * b + 24. * c)
388 } else {
389 0.
390 };
391 (k / 6.).abs()
392}
393
394fn mitchell(x: f32) -> f32 {
395 let x = x.abs();
396 if x < 1. {
397 ((16. + x * x * (21. * x - 36.)) / 18.).abs()
398 } else if x < 2. {
399 ((32. + x * (-60. + x * (36. - 7. * x))) / 18.).abs()
400 } else {
401 0.
402 }
403}
404
405fn nearest(_x: f32) -> f32 {
406 1.
407}
408
409fn gaussian(x: f32, r: f32) -> f32 {
410 ((2. * core::f32::consts::PI).sqrt() * r).recip() * (-x.powi(2) / (2. * r.powi(2))).exp()
411}