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 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 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}