color_gradient/helpers/sampler/
mod.rs

1use color_core::{HSVA32, RGBA32};
2use image::{GenericImageView, ImageResult, Rgba32FImage};
3use std::{collections::BTreeMap, error::Error, io::Write, path::Path};
4
5/// A color interpolator that interpolates between colors in the [RGB Color Space](https://en.wikipedia.org/wiki/RGB_color_space).
6#[derive(Debug)]
7pub struct GradientSampler {
8    /// The number of points to sample from the gradient.
9    pub points: usize,
10    /// The number of pixels to add to the left of the gradient.
11    pub margin_left: u32,
12    /// The number of pixels to add to the right of the gradient.
13    pub margin_right: u32,
14    /// The number of pixels to add to the top of the gradient.
15    pub margin_top: u32,
16    /// The number of pixels to add to the bottom of the gradient.     
17    pub margin_bottom: u32,
18    /// The gradient maps.
19    pub maps: Vec<(String, BTreeMap<u32, RGBA32>)>,
20}
21
22impl GradientSampler {
23    ///  # Arguments
24    ///
25    /// # Arguments
26    ///
27    /// * `margin`:
28    ///
29    /// returns: GradientSampler
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// # use color_gradient::helpers::GradientSampler;
35    /// ```
36    pub fn new(points: usize) -> Self {
37        Self { points, margin_left: 0, margin_right: 0, margin_top: 0, margin_bottom: 0, maps: vec![] }
38    }
39    ///  # Arguments
40    ///
41    /// # Arguments
42    ///
43    /// * `margin`:
44    ///
45    /// returns: GradientSampler
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// # use color_gradient::helpers::GradientSampler;
51    /// ```
52    pub fn with_margin(mut self, margin: u32) -> Self {
53        self.margin_left = margin;
54        self.margin_right = margin;
55        self.margin_top = margin;
56        self.margin_bottom = margin;
57        self
58    }
59    ///  # Arguments
60    ///
61    /// # Arguments
62    ///
63    /// * `margin`:
64    ///
65    /// returns: GradientSampler
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// # use color_gradient::helpers::GradientSampler;
71    /// ```
72    pub fn sample(&self, image: &Rgba32FImage) -> BTreeMap<u32, RGBA32> {
73        let width = image.width() - self.margin_left - self.margin_right;
74        let height = image.height() - self.margin_top - self.margin_bottom;
75        let view = image.view(self.margin_left, self.margin_top, width, height);
76        let mut output = BTreeMap::new();
77        let step = width as usize / self.points;
78        for i in (0..=width).step_by(step) {
79            let color = view.get_pixel(i.saturating_sub(1), 0);
80            output.insert(i, RGBA32::new(color.0[0], color.0[1], color.0[2], color.0[3]));
81        }
82        // let last = view.get_pixel(width, 0);
83        // output.insert(width - 1, RGBA32::new(last.0[0], last.0[1], last.0[2], last.0[3]));
84        output
85    }
86    ///  # Arguments
87    ///
88    /// # Arguments
89    ///
90    /// * `margin`:
91    ///
92    /// returns: GradientSampler
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use color_gradient::helpers::GradientSampler;
98    /// ```
99    pub fn sample_file<P>(&mut self, path: P, name: &str) -> Result<(), Box<dyn Error>>
100    where
101        P: AsRef<Path>,
102    {
103        let image_path = path.as_ref();
104        let image = image::open(image_path)?.to_rgba32f();
105        let map = self.sample(&image);
106        self.maps.push((name.to_string(), map));
107        Ok(())
108    }
109    ///  # Arguments
110    ///
111    /// # Arguments
112    ///
113    /// * `margin`:
114    ///
115    /// returns: GradientSampler
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// # use color_gradient::helpers::GradientSampler;
121    /// ```
122    pub fn export_hsv<P>(&self, path: P) -> ImageResult<()>
123    where
124        P: AsRef<Path>,
125    {
126        let mut file = std::fs::File::create(path)?;
127        writeln!(file, "impl HsvGradient {{")?;
128        for (name, map) in self.maps.iter() {
129            let width = *map.last_key_value().expect("Empty map").0;
130            writeln!(file, "/// {} color map in HSV color space.", name)?;
131            writeln!(file, "/// - step:")?;
132            writeln!(
133                file,
134                "/// ![{name}-step](https://raw.githubusercontent.com/oovm/color-rs/dev/projects/color-gradient/assets/hsv/{name}-step.png)",
135                name = name.to_lowercase()
136            )?;
137            writeln!(file, "/// - linear:")?;
138            writeln!(
139                file,
140                "/// ![{name}-linear](https://raw.githubusercontent.com/oovm/color-rs/dev/projects/color-gradient/assets/hsv/{name}-linear.png)",
141                name = name.to_lowercase()
142            )?;
143            writeln!(file, "pub fn {}(min: f32, max: f32) -> HsvGradient {{", name.to_lowercase())?;
144            writeln!(file, "let mut grad = HsvGradient::new(0.0, {:.2});", width as f32)?;
145            for (x, color) in map.iter() {
146                let hsva = HSVA32::from(*color);
147                writeln!(file, "grad.insert_hue({:.2}, {:.2});", *x as f32, hsva.h)?;
148                writeln!(file, "grad.insert_saturation({:.2}, {:.2});", *x as f32, hsva.s)?;
149                writeln!(file, "grad.insert_brightness({:.2}, {:.2});", *x as f32, hsva.v)?;
150            }
151            writeln!(file, "grad.rescale(min, max);")?;
152            writeln!(file, "grad")?;
153            writeln!(file, "}}")?;
154        }
155        writeln!(file, "}}")?;
156        Ok(())
157    }
158}