1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#![crate_name = "distance_field"]
extern crate image;
extern crate spiral;
use image::*;
use spiral::ManhattanIterator;
pub struct Options {
pub size: (u32, u32),
pub max_distance: u16,
pub image_treshold: u8,
}
impl Default for Options {
fn default() -> Self {
Options {
size: (64, 64),
max_distance: 512,
image_treshold: 127
}
}
}
pub trait DistanceFieldExt {
fn distance_field(&self, options: Options) -> ImageBuffer<Luma<u8>, Vec<u8>>;
}
impl DistanceFieldExt for DynamicImage {
fn distance_field(&self, options: Options) -> ImageBuffer<Luma<u8>, Vec<u8>> {
ImageBuffer::from_fn(options.size.0, options.size.1, |x, y| {
Luma([get_nearest_pixel_distance(self, x, y, &options)])
})
}
}
fn get_nearest_pixel_distance(input: &DynamicImage, out_x: u32, out_y: u32, options: &Options) -> u8 {
let orig_size = input.dimensions();
let center = ((out_x * orig_size.0) / options.size.0, (out_y * orig_size.1) / options.size.1);
let is_inside = input.get_pixel(center.0, center.1).to_luma().data[0] > options.image_treshold;
let mut closest_distance = options.max_distance as f32;
for (x, y) in ManhattanIterator::new(center.0 as i32, center.1 as i32, options.max_distance as u16) {
if x < 0 || y < 0 || x >= orig_size.0 as i32 || y >= orig_size.1 as i32 {
continue;
}
let p = input.get_pixel(x as u32, y as u32).to_luma().data[0];
if (p >= options.image_treshold) == is_inside {
continue;
}
let dx = (center.0 as i32 - x).abs();
let dy = (center.1 as i32 - y).abs();
closest_distance = ((dx * dx + dy * dy) as f32).sqrt();
break;
}
let distance_fraction = if is_inside {
0.5 + (closest_distance / 2.0) / options.max_distance as f32
}else{
0.5 - (closest_distance / 2.0) / options.max_distance as f32
};
(distance_fraction * u8::max_value() as f32) as u8
}