1#![doc = include_str!("readme.md")]
2use crate::{grids::rpg_maker_xp::GridCornerRMXP, GridAtlas, GridCompleteAtlas, GridCornerRMVX, GridEdgeWang};
3use image::{
4 error::{ParameterError, ParameterErrorKind},
5 ColorType, GenericImageView, ImageError, ImageFormat, ImageResult, RgbaImage,
6};
7use std::{
8 collections::BTreeMap,
9 fmt::{Display, Formatter},
10 io::{Error, ErrorKind},
11 path::{Path, PathBuf},
12};
13
14pub fn decompose_image_grid_by_cells<P>(path: P, cols: u32, rows: u32) -> ImageResult<()>
16where
17 P: AsRef<Path>,
18{
19 let path = path.as_ref().canonicalize()?;
20 let dir = path.parent().expect("The path must have a parent directory");
21 let name = path.file_stem().expect("The path must have a file name");
22 let image = image::open(&path)?;
23 let (width, height) = image.dimensions();
24 let cell_width = width / cols;
25 let cell_height = height / rows;
26 for row in 0..rows {
27 for col in 0..cols {
28 let view = image.view(col * cell_width, row * cell_height, cell_width, cell_height);
29 let out = dir.join(format!("{}-{}-{}.png", name.to_str().unwrap(), col, row));
30 view.to_image().save(&out)?;
31 }
32 }
33 Ok(())
34}
35
36#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
46pub struct AnimationSlice {}
47
48pub fn grid_corner_mask(lu: bool, ru: bool, ld: bool, rd: bool) -> u8 {
50 (lu as u8) << 0 | (ru as u8) << 1 | (ld as u8) << 2 | (rd as u8) << 3
51}
52
53#[derive(Debug)]
63pub struct MaskBuilder {
64 map: BTreeMap<u8, (u32, u32)>,
65 defaults: (u32, u32),
66}
67impl Display for MaskBuilder {
68 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
69 writeln!(f, "match mask {{")?;
70 for (mask, (x, y)) in self.map.iter() {
71 writeln!(f, " 0b{:08b} => ({}, {}),", mask, x.saturating_sub(1), y.saturating_sub(1))?;
72 }
73 writeln!(f, " _ => ({}, {}),", self.defaults.0.saturating_sub(1), self.defaults.1.saturating_sub(1))?;
74 writeln!(f, "}}")?;
75 Ok(())
76 }
77}
78
79impl MaskBuilder {
80 pub fn new(x: u32, y: u32) -> MaskBuilder {
82 Self { map: BTreeMap::default(), defaults: (x, y) }
83 }
84 pub fn masks(&self) -> Vec<u8> {
86 self.map.keys().copied().collect()
87 }
88 pub fn has_bits(&mut self, (x, y): (u32, u32), mask: &[u32]) {
90 let s: u32 = mask.iter().map(|i| 2u32.pow(*i)).sum();
91 self.has_mask((x, y), s as u8);
92 }
93 pub fn has_mask(&mut self, (x, y): (u32, u32), mask: u8) {
95 let pop = self.map.insert(mask, (x, y));
96 if let Some((i, j)) = pop {
97 panic!("duplicate mask {}: new {:?}, old {:?}", mask, (x, y), (i, j))
98 }
99 }
100 pub fn complete_set() -> Self {
102 let mut masks = MaskBuilder::new(1, 4);
103 masks.has_bits((1, 1), &[4]);
105 masks.has_bits((2, 1), &[2, 4]);
106 masks.has_bits((3, 1), &[2, 4, 6]);
107 masks.has_bits((4, 1), &[4, 6]);
108 masks.has_bits((1, 2), &[0, 4]);
109 masks.has_bits((2, 2), &[0, 2, 4]);
110 masks.has_bits((3, 2), &[0, 2, 4, 6]);
111 masks.has_bits((4, 2), &[0, 4, 6]);
112 masks.has_bits((1, 3), &[0]);
113 masks.has_bits((2, 3), &[0, 2]);
114 masks.has_bits((3, 3), &[0, 2, 6]);
115 masks.has_bits((4, 3), &[0, 6]);
116 masks.has_bits((2, 4), &[2]);
118 masks.has_bits((3, 4), &[2, 6]);
119 masks.has_bits((4, 4), &[6]);
120 masks.has_bits((5, 1), &[0, 2, 4, 6, 7]);
122 masks.has_bits((6, 1), &[2, 3, 4, 6]);
123 masks.has_bits((7, 1), &[2, 4, 5, 6]);
124 masks.has_bits((8, 1), &[0, 1, 2, 4, 6]);
125 masks.has_bits((5, 2), &[0, 2, 3, 4]);
126 masks.has_bits((6, 2), &[0, 1, 2, 3, 4, 5, 6]);
127 masks.has_bits((7, 2), &[0, 2, 3, 4, 5, 6, 7]);
128 masks.has_bits((8, 2), &[0, 4, 5, 6]);
129 masks.has_bits((5, 3), &[0, 1, 2, 4]);
130 masks.has_bits((6, 3), &[0, 1, 2, 3, 4, 6, 7]);
131 masks.has_bits((7, 3), &[0, 1, 2, 4, 5, 6, 7]);
132 masks.has_bits((8, 3), &[0, 4, 6, 7]);
133 masks.has_bits((5, 4), &[0, 2, 4, 5, 6]);
134 masks.has_bits((6, 4), &[0, 1, 2, 6]);
135 masks.has_bits((7, 4), &[0, 2, 6, 7]);
136 masks.has_bits((8, 4), &[0, 2, 3, 4, 6]);
137 masks.has_bits((9, 1), &[2, 3, 4]);
139 masks.has_bits((10, 1), &[0, 2, 3, 4, 5, 6]);
140 masks.has_bits((11, 1), &[2, 3, 4, 5, 6]);
141 masks.has_bits((12, 1), &[4, 5, 6]);
142 masks.has_bits((9, 2), &[0, 1, 2, 3, 4]);
143 masks.has_bits((10, 2), &[0, 1, 2, 4, 5, 6]);
144 masks.has_bits((11, 2), &[]);
145 masks.has_bits((12, 2), &[0, 2, 4, 5, 6, 7]);
146 masks.has_bits((9, 3), &[0, 1, 2, 3, 4, 6]);
147 masks.has_bits((10, 3), &[0, 1, 2, 3, 4, 5, 6, 7]);
148 masks.has_bits((11, 3), &[0, 2, 3, 4, 6, 7]);
149 masks.has_bits((12, 3), &[0, 4, 5, 6, 7]);
150 masks.has_bits((9, 4), &[0, 1, 2]);
151 masks.has_bits((10, 4), &[0, 1, 2, 6, 7]);
152 masks.has_bits((11, 4), &[0, 1, 2, 4, 6, 7]);
153 masks.has_bits((12, 4), &[0, 6, 7]);
154
155 masks
156 }
157 pub fn blob7x7_set() -> Self {
159 let mut masks = MaskBuilder::new(1, 1);
160 masks.has_mask((2, 1), 4);
163 masks.has_mask((3, 1), 92);
164 masks.has_mask((4, 1), 124);
165 masks.has_mask((5, 1), 116);
166 masks.has_mask((6, 1), 80);
167 masks.has_mask((1, 2), 16);
169 masks.has_mask((2, 2), 20);
170 masks.has_mask((3, 2), 87);
171 masks.has_mask((4, 2), 223);
172 masks.has_mask((5, 2), 241);
173 masks.has_mask((6, 2), 21);
174 masks.has_mask((7, 2), 64);
175
176 masks.has_mask((1, 3), 29);
177 masks.has_mask((2, 3), 117);
178 masks.has_mask((3, 3), 85);
179 masks.has_mask((4, 3), 71);
180 masks.has_mask((5, 3), 221);
181 masks.has_mask((6, 3), 125);
182 masks.has_mask((7, 3), 112);
183
184 masks.has_mask((1, 4), 31);
185 masks.has_mask((2, 4), 253);
186 masks.has_mask((3, 4), 113);
187 masks.has_mask((4, 4), 28);
188 masks.has_mask((5, 4), 127);
189 masks.has_mask((6, 4), 247);
190 masks.has_mask((7, 4), 209);
191
192 masks.has_mask((1, 5), 23);
193 masks.has_mask((2, 5), 199);
194 masks.has_mask((3, 5), 213);
195 masks.has_mask((4, 5), 95);
196 masks.has_mask((5, 5), 255);
197 masks.has_mask((6, 5), 245);
198 masks.has_mask((7, 5), 81);
199
200 masks.has_mask((1, 6), 5);
201 masks.has_mask((2, 6), 84);
202 masks.has_mask((3, 6), 93);
203 masks.has_mask((4, 6), 119);
204 masks.has_mask((5, 6), 215);
205 masks.has_mask((6, 6), 193);
206 masks.has_mask((7, 6), 17);
207
208 masks.has_mask((1, 7), 0);
209 masks.has_mask((2, 7), 1);
210 masks.has_mask((3, 7), 7);
211 masks.has_mask((4, 7), 197);
212 masks.has_mask((5, 7), 69);
213 masks.has_mask((6, 7), 68);
214 masks.has_mask((7, 7), 65);
215
216 masks
217 }
218}
219
220pub fn convert_blob7x7a<P>(image: P) -> ImageResult<()>
222where
223 P: AsRef<Path>,
224{
225 let path = image.as_ref().canonicalize()?;
226 let new_name = path.file_stem().and_then(|s| s.to_str()).map(|s| format!("{}-std.png", s)).unwrap();
227 let raw = image::open(image.as_ref())?.to_rgba8();
228 let new = GridCompleteAtlas::from_blob7x7a(&raw, raw.width() / 7, raw.height() / 7);
229 new.save(path.with_file_name(new_name))
230}
231
232pub fn convert_edge4x4<P>(image: P) -> ImageResult<()>
234where
235 P: AsRef<Path>,
236{
237 let (raw, output) = image_with_new_path(image)?;
238 let new = GridEdgeWang::create(&raw, (0, 0), (raw.width() / 4, raw.height() / 4))?;
239 new.as_complete().save(output)
240}
241
242pub fn convert_rpg4x6<P>(image: P) -> ImageResult<()>
244where
245 P: AsRef<Path>,
246{
247 let (raw, output) = image_with_new_path(image)?;
248 let rpg = GridCornerRMVX::create(&raw, (0, 0), (raw.width() / 4, raw.height() / 6))?;
249 rpg.as_complete().save(output)
250}
251
252pub fn convert_rpg6x8<P>(image: P) -> ImageResult<()>
254where
255 P: AsRef<Path>,
256{
257 let (raw, output) = image_with_new_path(image)?;
258 let rpg = GridCornerRMXP::create(&raw, (0, 0), (raw.width() / 6, raw.height() / 8))?;
259 rpg.as_complete().save(output)
260}
261
262fn image_with_new_path<P>(image: P) -> ImageResult<(RgbaImage, PathBuf)>
263where
264 P: AsRef<Path>,
265{
266 let path = image.as_ref().canonicalize()?;
267 let raw = image::open(image.as_ref())?.to_rgba8();
268 let new_name = path.file_stem().and_then(|s| s.to_str()).map(|s| format!("{}-std.png", s)).unwrap();
269 let new_path = path.with_file_name(new_name);
270 Ok((raw, new_path))
271}
272
273pub(crate) fn save_as_png<P>(image: &RgbaImage, path: P) -> ImageResult<()>
275where
276 P: AsRef<Path>,
277{
278 image::save_buffer_with_format(path, &image, image.width(), image.height(), ColorType::Rgba8, ImageFormat::Png)
279}
280
281pub(crate) fn parameter_error<T, S: ToString>(message: S) -> ImageResult<T> {
282 Err(ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(message.to_string()))))
283}
284
285pub(crate) fn io_error<T, S>(message: S, kind: ErrorKind) -> ImageResult<T>
286where
287 S: ToString,
288{
289 Err(ImageError::IoError(Error::new(kind, message.to_string())))
290}
291
292pub(crate) fn check_image_multiple(image: &RgbaImage, width: u32, height: u32) -> ImageResult<()> {
293 let (w, h) = image.dimensions();
294 if w % width != 0 {
295 parameter_error(format!("Image width {} is not a multiple of {}", w, width))?
296 }
297 if h % height != 0 {
298 parameter_error(format!("Image height {} is not a multiple of {}", h, height))?
299 }
300 Ok(())
301}