1use std::collections::HashMap;
3
4fn read(data: &[u8], index: usize, depth: u8) -> f32 {
6 match depth {
7 1 => data[index] as f32,
8 2 => u16::from_ne_bytes([data[index * 2], data[index * 2 + 1]]) as f32,
9 _ => f32::from_ne_bytes(data[index * 4..index * 4 + 4].try_into().unwrap()),
10 }
11}
12
13fn write(output: &mut [u8], index: usize, value: f32, depth: u8) {
15 match depth {
16 1 => output[index] = value.round_ties_even().clamp(0., 255.) as u8,
17 2 => output[index * 2..index * 2 + 2]
18 .copy_from_slice(&(value.round_ties_even().clamp(0., 65535.) as u16).to_ne_bytes()),
19 _ => output[index * 4..index * 4 + 4].copy_from_slice(&value.to_ne_bytes()),
20 }
21}
22
23#[allow(clippy::too_many_arguments)]
25pub fn resize(
26 data: &[u8],
27 width: usize,
28 height: usize,
29 channels: usize,
30 depth: u8,
31 target_w: usize,
32 target_h: usize,
33 mode: u8,
34 xindices: &[Vec<usize>],
35 xweights: &[Vec<f32>],
36 yindices: &[Vec<usize>],
37 yweights: &[Vec<f32>],
38) -> Result<Vec<u8>, String> {
39 if data.len()
40 != width
41 .checked_mul(height)
42 .and_then(|v| v.checked_mul(channels))
43 .and_then(|v| v.checked_mul(depth as usize))
44 .ok_or("image size overflow")?
45 || xindices.len()
46 != if mode == 4 {
47 target_w * channels
48 } else {
49 target_w
50 }
51 || yindices.len() != target_h
52 || xweights.len() != xindices.len()
53 || yweights.len() != target_h
54 || xindices.iter().flatten().any(|&x| x >= width)
55 || yindices.iter().flatten().any(|&y| y >= height)
56 || xindices
57 .iter()
58 .zip(xweights)
59 .any(|(i, w)| i.len() != w.len())
60 || yindices
61 .iter()
62 .zip(yweights)
63 .any(|(i, w)| i.len() != w.len())
64 {
65 return Err("invalid resize buffer or coefficient table".into());
66 }
67 let output_size = target_w
68 .checked_mul(target_h)
69 .and_then(|v| v.checked_mul(channels))
70 .and_then(|v| v.checked_mul(depth as usize))
71 .ok_or("output size overflow")?;
72 let mut output = vec![0; output_size];
73 if depth == 1 && [4, 5].contains(&mode) {
74 let bits = if mode == 4 { 8 } else { 7 };
75 let rounding = 1i32 << (bits - 1);
76 for y in 0..target_h {
77 for x in 0..target_w {
78 for c in 0..channels {
79 let coefficient = if mode == 4 { x * channels + c } else { x };
80 let vertical = |column: usize| -> i32 {
81 let a = data[(yindices[y][0] * width + xindices[coefficient][column])
82 * channels
83 + c] as i32;
84 let b = data[(yindices[y][1] * width + xindices[coefficient][column])
85 * channels
86 + c] as i32;
87 (a * yweights[y][0] as i32 + b * yweights[y][1] as i32 + rounding) >> bits
88 };
89 output[(y * target_w + x) * channels + c] =
90 ((vertical(0) * xweights[coefficient][0] as i32
91 + vertical(1) * xweights[coefficient][1] as i32
92 + rounding)
93 >> bits) as u8;
94 }
95 }
96 }
97 return Ok(output);
98 }
99 let mut rows: HashMap<usize, Vec<f32>> = HashMap::new();
100 for y in 0..target_h {
101 rows.retain(|key, _| yindices[y].contains(key));
102 for &source_y in &yindices[y] {
103 rows.entry(source_y).or_insert_with(|| {
104 let mut row = vec![0f32; target_w * channels];
105 for x in 0..target_w {
106 for channel in 0..channels {
107 let mut sum = 0f32;
108 for (&index, &weight) in xindices[x].iter().zip(&xweights[x]) {
109 let pixel =
110 read(data, (source_y * width + index) * channels + channel, depth);
111 sum += pixel * weight;
112 }
113 row[x * channels + channel] = sum;
114 }
115 }
116 row
117 });
118 }
119 let vertical_rows: Vec<&[f32]> = yindices[y]
121 .iter()
122 .map(|index| rows[index].as_slice())
123 .collect();
124 for x in 0..target_w * channels {
125 if depth == 1 && mode == 1 {
126 let mut sum = 0i32;
127 for (tap, &weight) in yweights[y].iter().enumerate() {
128 sum += (((vertical_rows[tap][x] as i64 >> 4) * weight as i64) >> 16) as i32;
129 }
130 output[(y * target_w * channels) + x] = ((sum + 2) >> 2).clamp(0, 255) as u8;
131 } else if depth == 1 && mode == 6 && x < target_w * channels / 8 * 8 {
132 let mut sum = vertical_rows[3][x] * (yweights[y][3] / (1u32 << 22) as f32);
133 for index in (0..3).rev() {
134 sum = vertical_rows[index][x]
135 .mul_add(yweights[y][index] / (1u32 << 22) as f32, sum);
136 }
137 write(&mut output, y * target_w * channels + x, sum, depth);
138 } else if depth == 1 && [2, 6].contains(&mode) {
139 let mut sum = 0i64;
140 for (tap, &weight) in yweights[y].iter().enumerate() {
141 sum += vertical_rows[tap][x] as i64 * weight as i64;
142 }
143 output[y * target_w * channels + x] = ((sum + (1 << 21)) >> 22).clamp(0, 255) as u8;
144 } else {
145 let mut sum = 0f32;
146 for (tap, &weight) in yweights[y].iter().enumerate() {
147 sum += vertical_rows[tap][x] * weight;
148 }
149 if mode == 7 {
150 sum = (sum + 0.5).floor();
151 }
152 write(&mut output, y * target_w * channels + x, sum, depth);
153 }
154 }
155 }
156 Ok(output)
157}
158
159fn find(parents: &mut [usize], mut index: usize) -> usize {
161 while parents[index] != index {
162 parents[index] = parents[parents[index]];
163 index = parents[index];
164 }
165 index
166}
167
168pub fn components(
170 data: &[u8],
171 width: usize,
172 height: usize,
173) -> Result<(Vec<u8>, Vec<[i32; 5]>), String> {
174 if width.checked_mul(height) != Some(data.len())
175 || width > i32::MAX as usize
176 || height > i32::MAX as usize
177 {
178 return Err("invalid component mask".into());
179 }
180 let mut labels = vec![0usize; data.len()];
181 let mut parents = vec![0usize];
182 for y in 0..height {
183 for x in 0..width {
184 if data[y * width + x] == 0 {
185 continue;
186 }
187 let mut roots = [0usize; 4];
189 let neighbors = [
190 if x > 0 { labels[y * width + x - 1] } else { 0 },
191 if y > 0 && x > 0 {
192 labels[(y - 1) * width + x - 1]
193 } else {
194 0
195 },
196 if y > 0 {
197 labels[(y - 1) * width + x]
198 } else {
199 0
200 },
201 if y > 0 && x + 1 < width {
202 labels[(y - 1) * width + x + 1]
203 } else {
204 0
205 },
206 ];
207 let mut minimum = usize::MAX;
208 for (i, neighbor) in neighbors.into_iter().enumerate() {
209 if neighbor != 0 {
210 roots[i] = find(&mut parents, neighbor);
211 minimum = minimum.min(roots[i]);
212 }
213 }
214 let label = if minimum != usize::MAX {
215 for root in roots {
216 if root != 0 {
217 parents[root] = minimum;
218 }
219 }
220 minimum
221 } else {
222 let index = parents.len();
223 parents.push(index);
224 index
225 };
226 labels[y * width + x] = label;
227 }
228 }
229 let roots: Vec<usize> = (0..parents.len()).map(|v| find(&mut parents, v)).collect();
230 let mut mapping = vec![0usize; parents.len()];
231 let mut count = 1;
232 for y in (0..height).step_by(2) {
234 for x in (0..width).step_by(2) {
235 for dy in 0..2 {
236 for dx in 0..2 {
237 if y + dy < height && x + dx < width {
238 let root = roots[labels[(y + dy) * width + x + dx]];
239 if root != 0 && mapping[root] == 0 {
240 mapping[root] = count;
241 count += 1;
242 }
243 }
244 }
245 }
246 }
247 }
248 let mut stats = vec![[i32::MAX, i32::MAX, -1, -1, 0]; count];
249 let mut output = Vec::with_capacity(data.len() * 4);
250 for (index, label) in labels.into_iter().enumerate() {
251 let id = mapping[roots[label]];
252 output.extend_from_slice(&(id as i32).to_ne_bytes());
253 let x = (index % width) as i32;
254 let y = (index / width) as i32;
255 let stat = &mut stats[id];
256 stat[0] = stat[0].min(x);
257 stat[1] = stat[1].min(y);
258 stat[2] = stat[2].max(x);
259 stat[3] = stat[3].max(y);
260 stat[4] += 1;
261 }
262 for stat in &mut stats {
263 if stat[4] == 0 {
264 *stat = [-1, -1, 0, 0, 0];
265 } else {
266 stat[2] -= stat[0] - 1;
267 stat[3] -= stat[1] - 1;
268 }
269 }
270 Ok((output, stats))
271}
272
273pub fn contours(
275 data: &[u8],
276 width: usize,
277 height: usize,
278 external: bool,
279) -> Result<Vec<Vec<[i32; 2]>>, String> {
280 if width.checked_mul(height) != Some(data.len()) {
281 return Err("invalid contour mask".into());
282 }
283 let stride = width + 2;
284 let mut pixels = vec![0i16; stride * (height + 2)];
285 for y in 0..height {
286 for x in 0..width {
287 pixels[(y + 1) * stride + x + 1] = i16::from(data[y * width + x] != 0);
288 }
289 }
290 let deltas: [isize; 8] = [
291 1,
292 1 - stride as isize,
293 -(stride as isize),
294 -1 - stride as isize,
295 -1,
296 stride as isize - 1,
297 stride as isize,
298 stride as isize + 1,
299 ];
300 let mut result = Vec::new();
301 for y in 1..=height {
302 let mut previous = 0i16;
303 let mut last_boundary = y * stride;
304 for x in 1..=width {
305 let index = y * stride + x;
306 let current = pixels[index];
307 if current == previous {
308 continue;
309 }
310 let outer = previous == 0 && current == 1;
311 let hole = current == 0 && previous >= 1;
312 if !outer && !hole {
313 previous = current;
314 if previous & -2 != 0 {
315 last_boundary = index;
316 }
317 continue;
318 }
319 if hole && previous & -2 != 0 {
320 last_boundary = index - 1;
321 }
322 if external && (hole || pixels[last_boundary] > 0) {
323 previous = current;
324 continue;
325 }
326 let start = index - usize::from(hole);
327 let mut direction = if hole { 0 } else { 4 };
328 let end = direction;
329 let mut first;
330 loop {
331 direction = (direction + 7) & 7;
332 first = (start as isize + deltas[direction]) as usize;
333 if pixels[first] != 0 || direction == end {
334 break;
335 }
336 }
337 let mut points = Vec::new();
338 if direction == end {
339 pixels[start] = -126;
340 points.push([(start % stride) as i32 - 1, (start / stride) as i32 - 1]);
341 } else {
342 let mut position = start;
343 let mut previous_direction = direction ^ 4;
344 let mut terminated = false;
345 for _ in 0..pixels.len() * 8 {
346 let end = direction;
347 let mut next_direction = direction + 1;
348 let mut following;
349 loop {
350 following = (position as isize + deltas[next_direction & 7]) as usize;
351 if pixels[following] != 0 {
352 break;
353 }
354 next_direction += 1;
355 }
356 direction = next_direction & 7;
357 if (direction as u32).wrapping_sub(1) < end as u32 {
358 pixels[position] = -126;
359 } else if pixels[position] == 1 {
360 pixels[position] = 2;
361 }
362 if direction != previous_direction {
363 points.push([
364 (position % stride) as i32 - 1,
365 (position / stride) as i32 - 1,
366 ]);
367 previous_direction = direction;
368 }
369 if following == start && position == first {
370 terminated = true;
371 break;
372 }
373 position = following;
374 direction = (direction + 4) & 7;
375 }
376 if !terminated {
377 return Err("contour traversal did not terminate".into());
378 }
379 }
380 result.push(points);
381 last_boundary = start;
382 previous = pixels[index];
383 }
384 }
385 result.reverse();
386 Ok(result)
387}
388
389fn half_round(value: f64) -> f32 {
391 let bits = value.to_bits();
392 let exponent = ((bits >> 52) & 2047) as i32 - 1023;
393 if exponent < -14 {
394 return ((value * 16777216.).round_ties_even() / 16777216.) as f32;
395 }
396 if exponent >= 16 {
397 return if value.is_sign_negative() {
398 f32::NEG_INFINITY
399 } else {
400 f32::INFINITY
401 };
402 }
403 let mask = (1u64 << 42) - 1;
404 let remainder = bits & mask;
405 let mut rounded = bits & !mask;
406 if remainder > 1u64 << 41 || (remainder == 1u64 << 41 && (rounded >> 42) & 1 != 0) {
407 rounded += 1u64 << 42;
408 }
409 f64::from_bits(rounded) as f32
410}
411
412fn half_fma(a: f32, b: f32, c: f32) -> f32 {
414 half_round(a as f64 * b as f64 + c as f64)
415}
416
417fn cubic_weights(x: f32, half: bool) -> [f32; 4] {
419 if half {
420 let x = half_round(x as f64);
421 let a2 = half_round((x * x) as f64);
422 let b = half_round((1. - x) as f64);
423 let b2 = half_round((b * b) as f64);
424 let w0 = half_round((-0.75 * half_round((x * b2) as f64)) as f64);
425 let w3 = half_round((-0.75 * half_round((a2 * b) as f64)) as f64);
426 let w1 = half_fma(a2, half_fma(1.25, x, -2.25), 1.);
427 let w2 = half_round((half_round((half_round((1. - w0) as f64) - w1) as f64) - w3) as f64);
428 [w0, w1, w2, w3]
429 } else {
430 let a2 = x * x;
431 let b = 1. - x;
432 let w0 = -0.75 * (x * (b * b));
433 let w3 = -0.75 * (a2 * b);
434 let w1 = a2.mul_add(1.25f32.mul_add(x, -2.25), 1.);
435 [w0, w1, 1. - w0 - w1 - w3, w3]
436 }
437}
438
439#[allow(clippy::too_many_arguments)]
441fn sample(
442 data: &[u8],
443 width: usize,
444 height: usize,
445 channels: usize,
446 depth: u8,
447 x: i64,
448 y: i64,
449 c: usize,
450 replicate: bool,
451 value: f32,
452) -> f32 {
453 if !replicate && (x < 0 || y < 0 || x >= width as i64 || y >= height as i64) {
454 return value;
455 }
456 let x = x.clamp(0, width as i64 - 1) as usize;
457 let y = y.clamp(0, height as i64 - 1) as usize;
458 read(data, (y * width + x) * channels + c, depth)
459}
460
461#[allow(clippy::too_many_arguments)]
463pub fn warp(
464 data: &[u8],
465 width: usize,
466 height: usize,
467 channels: usize,
468 depth: u8,
469 target_w: usize,
470 target_h: usize,
471 mode: u8,
472 replicate: bool,
473 value: f32,
474 inverse: &[f64; 9],
475 affine: bool,
476) -> Result<Vec<u8>, String> {
477 if width == 0
478 || height == 0
479 || data.len()
480 != width
481 .checked_mul(height)
482 .and_then(|v| v.checked_mul(channels))
483 .and_then(|v| v.checked_mul(depth as usize))
484 .ok_or("image size overflow")?
485 {
486 return Err("invalid warp image".into());
487 }
488 let length = target_w
489 .checked_mul(target_h)
490 .and_then(|v| v.checked_mul(channels))
491 .and_then(|v| v.checked_mul(depth as usize))
492 .ok_or("output size overflow")?;
493 let mut output = vec![0u8; length];
494 let half = depth == 1 && channels != 2;
495 let m: Vec<f32> = inverse.iter().map(|&v| v as f32).collect();
496 for y in 0..target_h {
497 let row: Vec<f32> = (0..3)
498 .map(|i| {
499 if depth == 4 {
500 (y as f32).mul_add(m[i * 3 + 1], m[i * 3 + 2])
501 } else {
502 (y as f64 * inverse[i * 3 + 1] + inverse[i * 3 + 2]) as f32
503 }
504 })
505 .collect();
506 let cubic_row: Vec<f32> = (0..3)
507 .map(|i| (y as f32).mul_add(m[i * 3 + 1], m[i * 3 + 2]))
508 .collect();
509 for x in 0..target_w {
510 let (sx, sy) = if mode == 2 {
511 if affine {
512 (
513 (x as f32).mul_add(m[0], cubic_row[0]),
514 (x as f32).mul_add(m[3], cubic_row[1]),
515 )
516 } else {
517 let d = cubic_row[2] as f64 + x as f64 * m[6] as f64;
518 if d == 0. {
519 (0., 0.)
520 } else {
521 (
522 ((cubic_row[0] as f64 + x as f64 * m[0] as f64) / d) as f32,
523 ((cubic_row[1] as f64 + x as f64 * m[3] as f64) / d) as f32,
524 )
525 }
526 }
527 } else if x < target_w / 8 * 8 {
528 let d = (x as f32).mul_add(m[6], row[2]);
529 if d == 0. {
530 (0., 0.)
531 } else {
532 (
533 (x as f32).mul_add(m[0], row[0]) / d,
534 (x as f32).mul_add(m[3], row[1]) / d,
535 )
536 }
537 } else {
538 let d = (x as f64 * inverse[6] + y as f64 * inverse[7] + inverse[8]) as f32;
539 if d == 0. {
540 (0., 0.)
541 } else {
542 (
543 ((x as f64 * inverse[0] + y as f64 * inverse[1] + inverse[2]) / d as f64)
544 as f32,
545 ((x as f64 * inverse[3] + y as f64 * inverse[4] + inverse[5]) / d as f64)
546 as f32,
547 )
548 }
549 };
550 let bx = sx.floor() as i64;
551 let by = sy.floor() as i64;
552 let alpha = sx - bx as f32;
553 let beta = sy - by as f32;
554 let wx = cubic_weights(alpha, half);
555 let wy = cubic_weights(beta, half);
556 for c in 0..channels {
557 let pixel = |ix: i64, iy: i64| {
558 sample(
559 data, width, height, channels, depth, ix, iy, c, replicate, value,
560 )
561 };
562 let result = if mode == 0 {
563 pixel(sx.round_ties_even() as i64, sy.round_ties_even() as i64)
564 } else if mode == 1 {
565 let p00 = pixel(bx, by);
566 let p01 = pixel(bx + 1, by);
567 let p10 = pixel(bx, by + 1);
568 let p11 = pixel(bx + 1, by + 1);
569 let top = alpha.mul_add(p01 - p00, p00);
570 let bottom = alpha.mul_add(p11 - p10, p10);
571 beta.mul_add(bottom - top, top)
572 } else {
573 let mut sum = 0f32;
574 for (j, &yweight) in wy.iter().enumerate() {
575 let mut horizontal = pixel(bx - 1, by + j as i64 - 1) * wx[0];
576 if half {
577 horizontal = half_round(horizontal as f64);
578 }
579 for (i, &xweight) in wx.iter().enumerate().skip(1) {
580 let p = pixel(bx + i as i64 - 1, by + j as i64 - 1);
581 horizontal = if half {
582 half_fma(p, xweight, horizontal)
583 } else {
584 p.mul_add(xweight, horizontal)
585 };
586 }
587 sum = if half {
588 half_fma(horizontal, yweight, sum)
589 } else {
590 horizontal.mul_add(yweight, sum)
591 };
592 }
593 sum
594 };
595 write(
596 &mut output,
597 (y * target_w + x) * channels + c,
598 result,
599 depth,
600 );
601 }
602 }
603 }
604 Ok(output)
605}
606
607pub fn minimum_rectangle(hull: &[[f32; 2]]) -> [f32; 5] {
609 let n = hull.len();
610 if n == 0 {
611 return [0., 0., 0., 0., -90.];
612 }
613 if n == 1 {
614 return [hull[0][0], hull[0][1], 0., 0., -90.];
615 }
616 if n == 2 {
617 let dx = (hull[0][0] - hull[1][0]) as f64;
618 let dy = (hull[0][1] - hull[1][1]) as f64;
619 let mut w = 0.;
620 let mut h = dx.hypot(dy) as f32;
621 let mut angle = -90.;
622 if dx == 0. {
623 std::mem::swap(&mut w, &mut h);
624 } else if dy < 0. {
625 angle = dy.atan2(dx).to_degrees() as f32;
626 std::mem::swap(&mut w, &mut h);
627 } else if dy > 0. {
628 angle = -dx.atan2(dy).to_degrees() as f32;
629 }
630 return [
631 (hull[0][0] + hull[1][0]) * 0.5,
632 (hull[0][1] + hull[1][1]) * 0.5,
633 w,
634 h,
635 angle,
636 ];
637 }
638 let vectors: Vec<[f32; 2]> = (0..n)
639 .map(|i| {
640 [
641 hull[(i + 1) % n][0] - hull[i][0],
642 hull[(i + 1) % n][1] - hull[i][1],
643 ]
644 })
645 .collect();
646 let lengths: Vec<f32> = vectors
647 .iter()
648 .map(|v| (1. / (v[0] as f64).hypot(v[1] as f64)) as f32)
649 .collect();
650 let extremum = |axis: usize, maximum: bool| {
651 let mut idx = 0;
652 for i in 1..n {
653 if if maximum {
654 hull[i][axis] > hull[idx][axis]
655 } else {
656 hull[i][axis] < hull[idx][axis]
657 } {
658 idx = i;
659 }
660 }
661 idx
662 };
663 let mut seq = [
664 extremum(1, false),
665 extremum(0, true),
666 extremum(1, true),
667 extremum(0, false),
668 ];
669 let mut area = f32::INFINITY;
670 let (mut left, mut a, mut b, mut width, mut height, mut bottom) =
671 (0, 0f32, 0f32, 0f32, 0f32, 0);
672 for _ in 0..n {
673 let v0 = vectors[seq[0]];
674 let v1 = vectors[seq[1]];
675 let v2 = vectors[seq[2]];
676 let v3 = vectors[seq[3]];
677 let rot = [v0, [v1[1], -v1[0]], [-v2[0], -v2[1]], [-v3[1], v3[0]]];
678 let mut main = 0;
679 for i in 1..4 {
680 if rot[i][1].mul_add(rot[main][0], -rot[i][0] * rot[main][1]) < 0. {
681 main = i;
682 }
683 }
684 let lead_x = vectors[seq[main]][0] * lengths[seq[main]];
685 let lead_y = vectors[seq[main]][1] * lengths[seq[main]];
686 let (ba, bb) = [
687 (lead_x, lead_y),
688 (lead_y, -lead_x),
689 (-lead_x, -lead_y),
690 (-lead_y, lead_x),
691 ][main];
692 seq[main] = (seq[main] + 1) % n;
693 let dx = hull[seq[1]][0] - hull[seq[3]][0];
694 let dy = hull[seq[1]][1] - hull[seq[3]][1];
695 let bw = dx.mul_add(ba, dy * bb);
696 let dx = hull[seq[2]][0] - hull[seq[0]][0];
697 let dy = hull[seq[2]][1] - hull[seq[0]][1];
698 let bh = (-dx).mul_add(bb, dy * ba);
699 if bw * bh <= area {
700 area = bw * bh;
701 left = seq[3];
702 a = ba;
703 b = bb;
704 width = bw;
705 height = bh;
706 bottom = seq[0];
707 }
708 }
709 let c1 = a.mul_add(hull[left][0], hull[left][1] * b);
710 let c2 = (-b).mul_add(hull[bottom][0], hull[bottom][1] * a);
711 let inverse = 1. / a.mul_add(a, b * b);
712 let px = c1.mul_add(a, -c2 * b) * inverse;
713 let py = a.mul_add(c2, b * c1) * inverse;
714 let edge1 = [a * width, b * width];
715 let edge2 = [-b * height, a * height];
716 let mut w = (edge2[0] as f64).hypot(edge2[1] as f64) as f32;
717 let mut h = (edge1[0] as f64).hypot(edge1[1] as f64) as f32;
718 let angle = if edge1[0] == 0. && edge1[1] > 0. {
719 std::mem::swap(&mut w, &mut h);
720 -90.
721 } else {
722 -(edge1[0] as f64).atan2(edge1[1] as f64).to_degrees() as f32
723 };
724 [
725 px + (edge1[0] + edge2[0]) * 0.5,
726 py + (edge1[1] + edge2[1]) * 0.5,
727 w,
728 h,
729 angle,
730 ]
731}
732
733pub fn morphology(
735 data: &[u8],
736 width: usize,
737 height: usize,
738 kw: usize,
739 kh: usize,
740 dilate: bool,
741) -> Result<Vec<u8>, String> {
742 if width.checked_mul(height) != Some(data.len()) || kw == 0 || kh == 0 {
743 return Err("invalid morphology buffer".into());
744 }
745 let border = if dilate { 0 } else { 255 };
746 let pass = |input: &[u8], vertical: bool, kernel: usize| {
747 let length = if vertical { height } else { width };
748 let lines = if vertical { width } else { height };
749 let mut output = vec![0u8; data.len()];
750 for line in 0..lines {
751 let mut queue = std::collections::VecDeque::<(usize, u8)>::new();
752 for p in 0..length + kernel - 1 {
753 let coordinate = p as isize - (kernel / 2) as isize;
754 let value = if coordinate < 0 || coordinate >= length as isize {
755 border
756 } else {
757 input[if vertical {
758 coordinate as usize * width + line
759 } else {
760 line * width + coordinate as usize
761 }]
762 };
763 while queue
764 .back()
765 .is_some_and(|v| if dilate { v.1 <= value } else { v.1 >= value })
766 {
767 queue.pop_back();
768 }
769 queue.push_back((p, value));
770 while queue.front().is_some_and(|v| v.0 + kernel <= p) {
771 queue.pop_front();
772 }
773 if p + 1 >= kernel {
774 let position = p + 1 - kernel;
775 output[if vertical {
776 position * width + line
777 } else {
778 line * width + position
779 }] = queue.front().unwrap().1;
780 }
781 }
782 }
783 output
784 };
785 Ok(pass(&pass(data, false, kw), true, kh))
786}
787
788pub fn convex_hull(points: &[[f32; 2]]) -> Vec<[f32; 2]> {
790 let mut values: Vec<([f32; 2], usize)> = points
791 .iter()
792 .copied()
793 .enumerate()
794 .map(|(i, p)| (p, i))
795 .collect();
796 values.sort_unstable_by(|a, b| {
797 a.0[0]
798 .total_cmp(&b.0[0])
799 .then_with(|| a.0[1].total_cmp(&b.0[1]))
800 .then(a.1.cmp(&b.1))
801 });
802 values.dedup_by(|a, b| a.0 == b.0);
803 if values.len() < 2 {
804 return values.into_iter().map(|v| v.0).collect();
805 }
806 let cross = |a: ([f32; 2], usize), b: ([f32; 2], usize), c: ([f32; 2], usize)| {
807 (b.0[0] - a.0[0]) as f64 * (c.0[1] - a.0[1]) as f64
808 - (b.0[1] - a.0[1]) as f64 * (c.0[0] - a.0[0]) as f64
809 };
810 let mut lower = Vec::new();
811 let mut upper = Vec::new();
812 for &point in &values {
813 while lower.len() >= 2 && cross(lower[lower.len() - 2], lower[lower.len() - 1], point) <= 0.
814 {
815 lower.pop();
816 }
817 lower.push(point);
818 }
819 for &point in values.iter().rev() {
820 while upper.len() >= 2 && cross(upper[upper.len() - 2], upper[upper.len() - 1], point) <= 0.
821 {
822 upper.pop();
823 }
824 upper.push(point);
825 }
826 lower.pop();
827 upper.pop();
828 lower.extend(upper);
829 let n = lower.len();
830 let mut start = 0;
831 for i in 1..n {
832 if lower[i].0[0] > lower[start].0[0]
833 || (lower[i].0[0] == lower[start].0[0] && lower[i].0[1] > lower[start].0[1])
834 {
835 start = i;
836 }
837 }
838 lower.rotate_left(start);
839 if n >= 3 {
840 for increasing in [true, false] {
841 let start = if increasing {
842 lower.iter().enumerate().min_by_key(|(_, v)| v.1).unwrap().0
843 } else {
844 lower.iter().enumerate().max_by_key(|(_, v)| v.1).unwrap().0
845 };
846 if (0..n - 1)
847 .all(|i| (lower[(start + i) % n].1 < lower[(start + i + 1) % n].1) == increasing)
848 {
849 lower.rotate_left(start);
850 break;
851 }
852 }
853 }
854 lower.into_iter().map(|v| v.0).collect()
855}
856
857pub fn linear_solve(matrix: &[Vec<f64>], rhs: &[f64]) -> Option<Vec<f64>> {
859 let n = matrix.len();
860 if n == 0 || n > 16 || rhs.len() != n || matrix.iter().any(|row| row.len() != n) {
861 return None;
862 }
863 let mut a = matrix.to_vec();
864 let mut b = rhs.to_vec();
865 for i in 0..n {
866 let mut pivot = i;
867 for j in i + 1..n {
868 if a[j][i].abs() > a[pivot][i].abs() {
869 pivot = j;
870 }
871 }
872 if a[pivot][i].abs() < f64::EPSILON * 100. {
873 return None;
874 }
875 if pivot != i {
876 a.swap(i, pivot);
878 b.swap(i, pivot);
879 }
880 let reciprocal = -1. / a[i][i];
881 let (prior, following) = a.split_at_mut(i + 1);
882 let pivot_row = &prior[i];
883 for (offset, row) in following.iter_mut().enumerate() {
884 let alpha = row[i] * reciprocal;
885 for (value, &coefficient) in row[i + 1..].iter_mut().zip(&pivot_row[i + 1..]) {
886 *value = alpha.mul_add(coefficient, *value);
887 }
888 let j = i + 1 + offset;
889 b[j] = alpha.mul_add(b[i], b[j]);
890 }
891 }
892 for i in (0..n).rev() {
893 let mut sum = b[i];
894 for (k, &value) in b.iter().enumerate().skip(i + 1) {
895 sum = (-a[i][k]).mul_add(value, sum);
896 }
897 b[i] = sum / a[i][i];
898 }
899 Some(b)
900}