density_mesh_image/
lib.rs

1pub mod settings;
2
3use crate::settings::{GenerateDensityImageSettings, ImageDensitySource};
4use density_mesh_core::{
5    map::{DensityMap, DensityMapError},
6    Scalar,
7};
8use image::{imageops::FilterType, DynamicImage, GenericImageView, GrayImage};
9
10/// Generate density map image.
11///
12/// # Arguments
13/// * `image` - Input image to process.
14/// * `settings` - Image processing settings.
15/// * `steepness` - If true produce steepness image, if false produce density image.
16///
17/// # Returns
18/// Processed image or error.
19pub fn generate_densitymap_image(
20    image: DynamicImage,
21    settings: &GenerateDensityImageSettings,
22    steepness: bool,
23) -> Result<DynamicImage, DensityMapError> {
24    let map = generate_densitymap_from_image(image, settings)?;
25    let data = if steepness {
26        map.steepness()
27            .iter()
28            .map(|v| (v * 255.0) as u8)
29            .collect::<Vec<_>>()
30    } else {
31        map.values()
32            .iter()
33            .map(|v| (v * 255.0) as u8)
34            .collect::<Vec<_>>()
35    };
36    Ok(DynamicImage::ImageLuma8(
37        GrayImage::from_raw(map.unscaled_width() as _, map.unscaled_height() as _, data).unwrap(),
38    ))
39}
40
41/// Generate density map from image.
42///
43/// # Arguments
44/// * `image` - Input image to process.
45/// * `settings` - Image processing settings.
46///
47/// # Returns
48/// Density map or error.
49pub fn generate_densitymap_from_image(
50    image: DynamicImage,
51    settings: &GenerateDensityImageSettings,
52) -> Result<DensityMap, DensityMapError> {
53    let scale = settings.scale.max(1);
54    let image = if scale > 1 {
55        image.resize_exact(
56            image.width() / scale as u32,
57            image.height() / scale as u32,
58            FilterType::Lanczos3,
59        )
60    } else {
61        image
62    };
63    match settings.density_source {
64        ImageDensitySource::Luma => {
65            let img = image.to_luma();
66            DensityMap::new(img.width() as _, img.height() as _, scale, img.into_raw())
67        }
68        ImageDensitySource::LumaAlpha => {
69            let w = image.width();
70            let h = image.height();
71            let img = image.to_luma_alpha();
72            let data = img
73                .into_raw()
74                .chunks(2)
75                .map(|c| ((c[0] as Scalar / 255.0) * (c[1] as Scalar / 255.0) * 255.0) as u8)
76                .collect::<Vec<_>>();
77            DensityMap::new(w as _, h as _, scale, data)
78        }
79        ImageDensitySource::Red => {
80            let w = image.width();
81            let h = image.height();
82            let data = image
83                .to_rgba()
84                .into_raw()
85                .chunks(4)
86                .map(|c| c[0])
87                .collect::<Vec<_>>();
88            DensityMap::new(w as _, h as _, scale, data)
89        }
90        ImageDensitySource::Green => {
91            let w = image.width();
92            let h = image.height();
93            let data = image
94                .to_rgba()
95                .into_raw()
96                .chunks(4)
97                .map(|c| c[1])
98                .collect::<Vec<_>>();
99            DensityMap::new(w as _, h as _, scale, data)
100        }
101        ImageDensitySource::Blue => {
102            let w = image.width();
103            let h = image.height();
104            let data = image
105                .to_rgba()
106                .into_raw()
107                .chunks(4)
108                .map(|c| c[2])
109                .collect::<Vec<_>>();
110            DensityMap::new(w as _, h as _, scale, data)
111        }
112        ImageDensitySource::Alpha => {
113            let w = image.width();
114            let h = image.height();
115            let data = image
116                .to_rgba()
117                .into_raw()
118                .chunks(4)
119                .map(|c| c[3])
120                .collect::<Vec<_>>();
121            DensityMap::new(w as _, h as _, scale, data)
122        }
123    }
124}
125
126/// Generate image from density map.
127///
128/// # Arguments
129/// * `map` - Input density map.
130/// * `steepness` - If true produce steepness image, if false produce density image.
131///
132/// # Returns
133/// Grayscale image.
134pub fn generate_image_from_densitymap(map: &DensityMap, steepness: bool) -> DynamicImage {
135    DynamicImage::ImageLuma8(
136        GrayImage::from_raw(
137            map.unscaled_width() as _,
138            map.unscaled_height() as _,
139            if steepness {
140                map.steepness()
141                    .iter()
142                    .map(|v| (v * 255.0) as u8)
143                    .collect::<Vec<_>>()
144            } else {
145                map.values()
146                    .iter()
147                    .map(|v| (v * 255.0) as u8)
148                    .collect::<Vec<_>>()
149            },
150        )
151        .unwrap(),
152    )
153}
154
155pub mod prelude {
156    pub use crate::{
157        generate_densitymap_from_image, generate_densitymap_image, generate_image_from_densitymap,
158        settings::*,
159    };
160}