ici_files/
scaling.rs

1use crate::errors::IndexedImageError;
2use crate::errors::IndexedImageError::{InvalidScaleParams, TooBigPostScale};
3use crate::image::IndexedImage;
4use crate::scaling::Scaling::*;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::num::NonZeroUsize;
8
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Debug, Clone, Copy, Eq, PartialEq)]
11pub enum Scaling {
12    /// Increase size of image by x_scale and y_scale
13    /// Where {2,2} doubles the size
14    NearestNeighbour {
15        x_scale: NonZeroUsize,
16        y_scale: NonZeroUsize,
17    },
18    Epx2x,
19    Epx4x,
20}
21
22impl Scaling {
23    pub fn nearest_neighbour(x: usize, y: usize) -> Result<Scaling, IndexedImageError> {
24        Ok(NearestNeighbour {
25            x_scale: NonZeroUsize::new(x).ok_or(InvalidScaleParams(x, y))?,
26            y_scale: NonZeroUsize::new(y).ok_or(InvalidScaleParams(x, y))?,
27        })
28    }
29
30    /// Double image size using nearest neighbour
31    pub fn nn_double() -> Scaling {
32        NearestNeighbour {
33            x_scale: NonZeroUsize::new(2).expect("2 is 0?"),
34            y_scale: NonZeroUsize::new(2).expect("2 is 0?"),
35        }
36    }
37}
38
39pub(crate) fn scale_nearest_neighbor(
40    image: &IndexedImage,
41    x_scale: usize,
42    y_scale: usize,
43) -> Result<IndexedImage, IndexedImageError> {
44    let new_width = image.width() as usize * x_scale;
45    let new_height = image.height() as usize * y_scale;
46    if new_height > 255 || new_width > 255 {
47        return Err(TooBigPostScale(new_width, new_height));
48    }
49    let new_width = new_width as u8;
50    let new_height = new_height as u8;
51    let mut new_image = IndexedImage::blank(new_width, new_height, image.get_palette().to_vec());
52    let x_scale = 1.0 / x_scale as f32;
53    let y_scale = 1.0 / y_scale as f32;
54    for y in 0..new_height {
55        for x in 0..new_width {
56            let px = (x as f32 * x_scale).floor() as u8;
57            let py = (y as f32 * y_scale).floor() as u8;
58            let target_i = new_image.get_pixel_index(x, y)?;
59            let pi = image.get_pixel_index(px, py)?;
60            new_image.set_pixel(target_i, image.get_pixel(pi)?)?;
61        }
62    }
63    Ok(new_image)
64}
65
66pub(crate) unsafe fn scale_nearest_neighbor_unchecked(
67    image: &IndexedImage,
68    x_scale: usize,
69    y_scale: usize,
70) -> IndexedImage {
71    let new_width = (image.width() as usize * x_scale) as u8;
72    let new_height = (image.height() as usize * y_scale) as u8;
73    let mut new_image = IndexedImage::blank(new_width, new_height, image.get_palette().to_vec());
74    let x_scale = 1.0 / x_scale as f32;
75    let y_scale = 1.0 / y_scale as f32;
76    for y in 0..new_height {
77        for x in 0..new_width {
78            let px = (x as f32 * x_scale).floor() as u8;
79            let py = (y as f32 * y_scale).floor() as u8;
80            let target_i = new_image.get_pixel_index_unchecked(x, y);
81            let pi = image.get_pixel_index_unchecked(px, py);
82            new_image.set_pixel_unchecked(target_i, image.get_pixel_unchecked(pi));
83        }
84    }
85    new_image
86}
87
88pub(crate) fn scale_epx(image: &IndexedImage) -> Result<IndexedImage, IndexedImageError> {
89    let new_width = image.width() as usize * 2;
90    let new_height = image.height() as usize * 2;
91    if new_height > 255 || new_width > 255 {
92        return Err(TooBigPostScale(new_width, new_height));
93    }
94    let new_width = new_width as u8;
95    let new_height = new_height as u8;
96    let mut new_image = IndexedImage::blank(new_width, new_height, image.get_palette().to_vec());
97    for x in 0..image.width() {
98        for y in 0..image.height() {
99            let mut p1 = image.get_pixel(image.get_pixel_index(x, y)?)?;
100            let mut p2 = p1;
101            let mut p3 = p1;
102            let mut p4 = p1;
103            let a = image.get_pixel(image.get_pixel_index(x, if y > 0 { y - 1 } else { y })?)?;
104            let c = image.get_pixel(image.get_pixel_index(if x > 0 { x - 1 } else { x }, y)?)?;
105            let b = image.get_pixel(
106                image.get_pixel_index(if x < image.width() - 2 { x + 1 } else { x }, y)?,
107            )?;
108            let d = image.get_pixel(
109                image.get_pixel_index(x, if y < image.height() - 2 { y + 1 } else { y })?,
110            )?;
111
112            if c == a && c != d && a != b {
113                p1 = a
114            }
115            if a == b && a != c && b != d {
116                p2 = b
117            }
118            if d == c && d != b && c != a {
119                p3 = c
120            }
121            if b == d && b != a && d != c {
122                p4 = d
123            }
124
125            let nx = x * 2;
126            let ny = y * 2;
127            new_image.set_pixel(new_image.get_pixel_index(nx, ny)?, p1)?;
128            new_image.set_pixel(new_image.get_pixel_index(nx + 1, ny)?, p2)?;
129            new_image.set_pixel(new_image.get_pixel_index(nx, ny + 1)?, p3)?;
130            new_image.set_pixel(new_image.get_pixel_index(nx + 1, ny + 1)?, p4)?;
131        }
132    }
133    Ok(new_image)
134}
135
136pub(crate) unsafe fn scale_epx_unchecked(image: &IndexedImage) -> IndexedImage {
137    let new_width = (image.width() as usize * 2) as u8;
138    let new_height = (image.height() as usize * 2) as u8;
139    let mut new_image = IndexedImage::blank(new_width, new_height, image.get_palette().to_vec());
140    for x in 0..image.width() {
141        for y in 0..image.height() {
142            let mut p1 = image.get_pixel_unchecked(image.get_pixel_index_unchecked(x, y));
143            let mut p2 = p1;
144            let mut p3 = p1;
145            let mut p4 = p1;
146            let a = image.get_pixel_unchecked(
147                image.get_pixel_index_unchecked(x, if y > 0 { y - 1 } else { y }),
148            );
149            let c = image.get_pixel_unchecked(
150                image.get_pixel_index_unchecked(if x > 0 { x - 1 } else { x }, y),
151            );
152            let b = image.get_pixel_unchecked(
153                image.get_pixel_index_unchecked(if x < image.width() - 2 { x + 1 } else { x }, y),
154            );
155            let d = image.get_pixel_unchecked(
156                image.get_pixel_index_unchecked(x, if y < image.height() - 2 { y + 1 } else { y }),
157            );
158
159            if c == a && c != d && a != b {
160                p1 = a
161            }
162            if a == b && a != c && b != d {
163                p2 = b
164            }
165            if d == c && d != b && c != a {
166                p3 = c
167            }
168            if b == d && b != a && d != c {
169                p4 = d
170            }
171
172            let nx = x * 2;
173            let ny = y * 2;
174            new_image.set_pixel_unchecked(new_image.get_pixel_index_unchecked(nx, ny), p1);
175            new_image.set_pixel_unchecked(new_image.get_pixel_index_unchecked(nx + 1, ny), p2);
176            new_image.set_pixel_unchecked(new_image.get_pixel_index_unchecked(nx, ny + 1), p3);
177            new_image.set_pixel_unchecked(new_image.get_pixel_index_unchecked(nx + 1, ny + 1), p4);
178        }
179    }
180    new_image
181}