pixel_sort/render/
solver.rs

1use crate::{Result, Vrellis, VrellisCanvas, VrellisPoint};
2use image::{imageops::FilterType, io::Reader, DynamicImage, GenericImageView, GrayImage, Luma};
3use std::{io::Cursor, path::Path};
4
5impl Vrellis {
6    pub fn render_path(&self, path: impl AsRef<Path>) -> Result<VrellisCanvas> {
7        let img = Reader::open(path)?.decode()?;
8        Ok(self.render(img))
9    }
10    pub fn render_bytes(&self, bytes: &[u8]) -> Result<VrellisCanvas> {
11        let img = Reader::new(Cursor::new(bytes)).decode()?;
12        Ok(self.render(img))
13    }
14    pub fn render(&self, img: DynamicImage) -> VrellisCanvas {
15        let img = img.resize_exact(1000, 1000, FilterType::Triangle);
16        let canvas = match self.inverted_color {
17            true => DynamicImage::new_luma_a8(img.width(), img.height()),
18            false => DynamicImage::new_luma_a8(img.width(), img.height()),
19        };
20        let points_sample = self.convex_shape.sample(self.points, img.width(), img.height());
21        let initial_point = match self.inverted_color {
22            true => points_sample.iter().min_by_key(|p| p.n).unwrap(),
23            false => points_sample.iter().min_by_key(|p| p.n).unwrap(),
24        };
25        let mut current_composite_image = img.to_luma();
26        quantify_color(&mut current_composite_image);
27        VrellisCanvas {
28            algorithm: self.algorithm,
29            min_distance: self.min_distance,
30            inverted_color: self.inverted_color,
31            target_image: img.to_rgb(),
32            current_image: canvas.to_luma_alpha(),
33            current_composite_image,
34            points: points_sample.clone(),
35            path: vec![initial_point.n],
36            path_banned: Default::default(),
37            last_point: initial_point.clone(),
38            line_width: self.line_width,
39        }
40    }
41}
42
43impl Iterator for VrellisCanvas {
44    type Item = DynamicImage;
45    fn next(&mut self) -> Option<Self::Item> {
46        let mut selected = None;
47        let mut max_score = 0.0;
48        for point in self.points.iter() {
49            if self.should_skip(point) {
50                continue;
51            }
52            let score = self.algorithm.line_score(
53                &self.current_composite_image,
54                self.last_point.x,
55                self.last_point.y,
56                point.x,
57                point.y,
58                self.inverted_color,
59            );
60            if score > max_score {
61                max_score = score;
62                selected = Some(point)
63            }
64        }
65        // No legal line segment, no line is selected
66        if let None = selected {
67            return None;
68        };
69        let selected = selected.unwrap().clone();
70        self.algorithm.draw_line(
71            &mut self.current_composite_image,
72            self.last_point.x,
73            self.last_point.y,
74            selected.x,
75            selected.y,
76            self.inverted_color,
77        );
78        self.draw_canvas_line(self.last_point.x, self.last_point.y, selected.x, selected.y, self.inverted_color);
79        self.last_point = selected;
80        let new = selected.n;
81        let old = *self.path.last().unwrap();
82        self.path.push(new);
83        self.path_banned.insert((new, old));
84        self.path_banned.insert((old, new));
85        return Some(DynamicImage::ImageLumaA8(self.current_image.clone()));
86    }
87}
88
89impl VrellisCanvas {
90    fn should_skip(&self, this: &VrellisPoint) -> bool {
91        if self.last_point.x == this.x || self.last_point.y == this.y {
92            return true;
93        }
94        let old = self.last_point.n;
95        let this = this.n;
96        if old == this {
97            true
98        }
99        else if old > this && old - this <= self.min_distance {
100            true
101        }
102        else if self.path_banned.contains(&(old, this)) {
103            true
104        }
105        else {
106            false
107        }
108    }
109}
110
111pub fn quantify_color(img: &mut GrayImage) {
112    for p in img.pixels_mut() {
113        unsafe { nearest_color(p) }
114    }
115}
116
117pub unsafe fn nearest_color(pixel: &mut Luma<u8>) {
118    let colors = &[0u8, 64, 128, 192, 255];
119    let pixel = pixel.0.get_unchecked_mut(0);
120    let raw = *pixel;
121    let mut min_delta = 255;
122    for &item in colors.iter().rev() {
123        let mid = if raw >= item { raw - item } else { item - raw };
124        if mid < min_delta {
125            // print!("{} ", mid);
126            min_delta = mid;
127            *pixel = item
128        }
129    }
130    // println!()
131}