1use crate::{
2 encode_fragment_pixels, Fragment, FragmentBytes, FragmentFlip, Frame, GeneralResolution,
3 ImageBuffer, OamShape, WanImage,
4};
5use anyhow::{bail, Context};
6use std::convert::TryInto;
7
8pub fn insert_frame_in_wanimage(
9 image: Vec<u8>,
10 width: u16,
11 height: u16,
12 wanimage: &mut WanImage,
13 pal_id: u16,
14) -> anyhow::Result<Option<usize>> {
15 if height >= 256 {
16 bail!("The height of the image is {}, while only image with a height inferior to 256 can be used", height);
17 }
18 if width >= 512 {
19 bail!(
20 "The width of the image is {}, while only image with a width less than 512 can be used",
21 width
22 );
23 }
24 let position_x = -(width as i32) / 2;
25 let position_y = -(height as i32) / 2;
26 let image_buffer = ImageBuffer::new_from_vec(image, width, height)
27 .context("The input image don't correspond to the dimension of it")?;
28
29 let fragments = if let Some(fragments) =
30 insert_fragment_pos_in_wan_image(wanimage, pal_id, &image_buffer, position_x, position_y)?
31 {
32 fragments
33 } else {
34 return Ok(None);
35 };
36
37 Ok(if !fragments.is_empty() {
38 let frame_id = wanimage.frame_store.frames.len();
39 wanimage.frame_store.frames.push(Frame {
40 fragments,
41 frame_offset: None,
42 });
43 Some(frame_id)
44 } else {
45 None
46 })
47}
48
49fn insert_fragment_pos_in_wan_image(
50 wanimage: &mut WanImage,
51 pal_id: u16,
52 image_buffer: &ImageBuffer,
53 upper_image_x: i32,
54 upper_image_y: i32,
55) -> anyhow::Result<Option<Vec<Fragment>>> {
56 let mut fragments = Vec::new();
57
58 const MAX_META_FRAME_SIZE: u16 = 64;
60 for fragment_segment_x in
61 0..(image_buffer.width() + MAX_META_FRAME_SIZE - 1) / MAX_META_FRAME_SIZE
62 {
63 for fragment_segment_y in
64 0..(image_buffer.height() + MAX_META_FRAME_SIZE - 1) / MAX_META_FRAME_SIZE
65 {
66 let mut fragment_x =
67 upper_image_x + MAX_META_FRAME_SIZE as i32 * fragment_segment_x as i32;
68 let mut fragment_y =
69 upper_image_y + MAX_META_FRAME_SIZE as i32 * fragment_segment_y as i32;
70
71 let mut cut_section = image_buffer.get_fragment(
72 MAX_META_FRAME_SIZE * fragment_segment_x,
73 MAX_META_FRAME_SIZE * fragment_segment_y,
74 MAX_META_FRAME_SIZE,
75 MAX_META_FRAME_SIZE,
76 0,
77 );
78 fragment_y += cut_section.cut_top() as i32;
79 cut_section.cut_bottom();
80 fragment_x += cut_section.cut_left() as i32;
81 cut_section.cut_right();
82
83 if !cut_section.have_pixel() {
84 continue;
85 }
86
87 let fragment_size = OamShape::find_smallest_containing(GeneralResolution::new(
89 cut_section.width().into(),
90 cut_section.height().into(),
91 ))
92 .unwrap();
93
94 let buffer_to_write = cut_section.get_fragment(
95 0,
96 0,
97 fragment_size.size().x as u16,
98 fragment_size.size().y as u16,
99 0,
100 );
101
102 let image_bytes_index = wanimage.fragment_bytes_store.fragment_bytes.len();
103 wanimage
104 .fragment_bytes_store
105 .fragment_bytes
106 .push(FragmentBytes {
107 mixed_pixels: encode_fragment_pixels(
108 buffer_to_write.buffer(),
109 fragment_size.size(),
110 )
111 .context("failed to encode the input byte. This is an internal error")?,
112 z_index: 1,
113 });
114
115 let offset_y = fragment_y.try_into().context("The image is too large")?;
116 fragments.push(Fragment {
117 unk1: 0,
118 unk3_4: None,
119 unk5: false,
120 fragment_bytes_index: image_bytes_index,
121 offset_y,
122 offset_x: fragment_x.try_into().context("The image is too high")?,
123 flip: FragmentFlip::standard(),
124 is_mosaic: false,
125 pal_idx: pal_id,
126 resolution: fragment_size,
127 });
128 }
129 }
130
131 if fragments.is_empty() {
132 return Ok(None);
133 }
134
135 Ok(Some(fragments))
136}
137
138#[test]
139fn imagebuffer_cut_test() {
140 #[rustfmt::skip]
142 #[allow(clippy::type_complexity)]
143 let tests_to_perform: [(Vec<u8>, u16, u16, Vec<u8>, u16, u16, usize, usize, usize, usize); 2] = [
144 (
145 vec![
146 0, 0, 0, 0, 0, 0,
147 0, 0, 0, 0, 0, 0,
148 0, 0, 1, 1, 1, 0,
149 0, 0, 0, 0, 1, 0,
150 0, 0, 0, 0, 0, 0
151 ], 6, 5,
152 vec![
153 1, 1, 1,
154 0, 0, 1
155 ], 3, 2,
156 2, 1, 2, 1
157 ),
158 (
159 vec![
160 1, 1, 1,
161 1, 1, 1,
162 1, 1, 1
163 ], 3, 3,
164 vec![
165 1, 1, 1,
166 1, 1, 1,
167 1, 1, 1
168 ], 3, 3,
169 0, 0, 0, 0
170 )
171 ];
172 for (
173 buffer_src,
174 x_src,
175 y_src,
176 buffer_target,
177 x_target,
178 y_target,
179 cut_top_px,
180 cut_bottom_px,
181 cut_left_px,
182 cut_right_px,
183 ) in tests_to_perform
184 {
185 let mut image_src = ImageBuffer::new_from_vec(buffer_src, x_src, y_src).unwrap();
186 let image_target = ImageBuffer::new_from_vec(buffer_target, x_target, y_target).unwrap();
187 assert_eq!(cut_top_px, image_src.cut_top());
188 assert_eq!(cut_bottom_px, image_src.cut_bottom());
189 assert_eq!(cut_left_px, image_src.cut_left());
190 assert_eq!(cut_right_px, image_src.cut_right());
191 assert_eq!(image_src, image_target);
192 }
193
194 let mut image_src = ImageBuffer::new_from_vec(vec![0; 4], 2, 2).unwrap();
196 image_src.cut_top();
197 image_src.cut_bottom();
198 image_src.cut_right();
199 image_src.cut_right();
200 assert_eq!(image_src, ImageBuffer::new_from_vec(vec![], 0, 0).unwrap());
201}
202
203#[test]
204fn get_image_fragment_test() {
205 let image_buffer = ImageBuffer::new_from_vec(vec![1, 1, 0, 1, 2, 3, 1, 3, 0], 3, 3).unwrap();
206 let fragment = image_buffer.get_fragment(1, 1, 3, 2, 0);
207 assert_eq!(
208 fragment,
209 ImageBuffer::new_from_vec(vec![2, 3, 0, 3, 0, 0], 3, 2).unwrap()
210 );
211}
212
213#[test]
214fn insert_frame_flat_test() {
215 let mut wanimage = WanImage::new(crate::SpriteType::PropsUI);
216 wanimage.palette.palette.push([255, 255, 255, 128]);
217 let frame_id = insert_frame_in_wanimage(vec![1; 36], 6, 6, &mut wanimage, 0)
218 .unwrap()
219 .unwrap();
220 let frame = &wanimage.frame_store.frames[frame_id];
221 let fragment = &frame.fragments[0];
222 assert_eq!(fragment.resolution, OamShape::new(0, 0).unwrap());
223 assert_eq!(fragment.pal_idx, 0);
224}
225
226#[test]
227fn insert_empty_image_test() {
228 let mut wanimage = WanImage::new(crate::SpriteType::PropsUI);
229 assert!(insert_frame_in_wanimage(vec![0; 4], 2, 2, &mut wanimage, 0)
230 .unwrap()
231 .is_none());
232}