1#[cfg(feature = "image")]
23use crate::color::Color;
24use crate::color::Color16;
25use crate::error::MulReaderResult;
26#[cfg(feature = "image")]
27use crate::error::ToImageError;
28use crate::mul::MulReader;
29use byteorder::{LittleEndian, ReadBytesExt};
30#[cfg(feature = "image")]
31use image::error::{DecodingError, ImageError, ImageFormatHint};
32#[cfg(feature = "image")]
33use image::{Delay, Frame, Frames, Rgba, RgbaImage};
34use std::fs::File;
35use std::io::{Cursor, Read, Seek, SeekFrom};
36use std::path::Path;
37#[cfg(feature = "image")]
38use std::time::Duration;
39
40const PALETTE_SIZE: usize = 256;
41const IMAGE_COMPLETE: u32 = 0x7FFF7FFF;
42
43const OFFSET_MASK: i32 = (0x200 << 22) | (0x200 << 12);
44
45#[derive(Debug, PartialEq, Eq, Clone)]
47pub struct Row {
48 pub header: u32,
56 pub image_data: Vec<u8>,
58}
59
60impl Row {
61 pub fn x_offset(&self, image_center_x: i16) -> i32 {
64 (((self.header as i32 ^ OFFSET_MASK) >> 22) & 0x3FF) + image_center_x as i32 - 0x200
65 }
66
67 pub fn y_offset(&self, image_center_y: i16, height: u32) -> i32 {
70 (((self.header as i32 ^ OFFSET_MASK) >> 12) & 0x3FF) + image_center_y as i32 + height as i32
71 - 0x200
72 }
73
74 #[cfg(feature = "image")]
75 pub fn plot(
79 &self,
80 image_center_x: i16,
81 image_center_y: i16,
82 width: u16,
83 height: u16,
84 palette: &[u16],
85 buffer: &mut RgbaImage,
86 ) -> Result<(), ImageError> {
87 let x = self.x_offset(image_center_x);
88 let y = self.y_offset(image_center_y, height as u32);
89 if x < 0 || y < 0 || y >= height as i32 || x as u16 + self.image_data.len() as u16 > width {
90 return Err(ImageError::Decoding(DecodingError::new(
91 ImageFormatHint::Name("UO AnimFrame".to_string()),
92 ToImageError::PixelOutOfBounds {
93 x: x as i64,
94 y: y as i64,
95 },
96 )));
97 }
98 for i in 0..self.image_data.len() {
99 let target_x = x + i as i32;
100 if target_x > width as i32 {
101 return Err(ImageError::Decoding(DecodingError::new(
102 ImageFormatHint::Name("UO AnimFrame".to_string()),
103 ToImageError::PixelOutOfBounds {
104 x: target_x as i64,
105 y: y as i64,
106 },
107 )));
108 }
109 let (r, g, b, a) = palette[self.image_data[i] as usize].to_rgba();
110 buffer.put_pixel(target_x as u32, y as u32, Rgba([r, g, b, a]));
111 }
112 Ok(())
113 }
114}
115
116#[derive(Debug, PartialEq, Eq, Clone)]
118pub struct AnimFrame {
119 pub image_center_x: i16,
120 pub image_center_y: i16,
121 pub width: u16,
122 pub height: u16,
123 pub data: Vec<Row>,
124}
125
126#[cfg(feature = "image")]
127impl AnimFrame {
128 pub fn to_frame(&self, palette: &[u16]) -> Result<Frame, ImageError> {
130 if self.width == 0 || self.height == 0 {
131 return Err(ImageError::Decoding(DecodingError::from_format_hint(
132 ImageFormatHint::Name("UO AnimFrame".to_string()),
133 )));
134 }
135 let mut buffer = RgbaImage::new(self.width as u32, self.height as u32);
136 for row in &self.data {
137 row.plot(
138 self.image_center_x,
139 self.image_center_y,
140 self.width,
141 self.height,
142 palette,
143 &mut buffer,
144 )?;
145 }
146 Ok(Frame::from_parts(
147 buffer,
148 0,
149 0,
150 Delay::from_saturating_duration(Duration::from_millis(0)),
151 ))
152 }
153}
154
155#[derive(Debug, PartialEq, Eq, Clone)]
157pub struct AnimGroup {
158 pub palette: [Color16; 256],
159 pub frame_count: u32,
160 pub frames: Vec<AnimFrame>,
161}
162
163impl AnimGroup {
164 #[cfg(feature = "image")]
165 pub fn to_frames(&self) -> Frames<'_> {
169 Frames::new(Box::new(
170 self.frames
171 .iter()
172 .map(move |anim_frame| anim_frame.to_frame(&self.palette)),
173 ))
174 }
175}
176
177#[derive(Debug)]
179pub struct AnimReader<T: Read + Seek> {
180 mul_reader: MulReader<T>,
181}
182
183fn read_frame<T: Read + Seek>(reader: &mut T) -> MulReaderResult<AnimFrame> {
184 let image_center_x = reader.read_i16::<LittleEndian>()?;
185 let image_center_y = reader.read_i16::<LittleEndian>()?;
186 let width = reader.read_u16::<LittleEndian>()?;
187 let height = reader.read_u16::<LittleEndian>()?;
188
189 let mut data = vec![];
190 loop {
191 let header = reader.read_u32::<LittleEndian>()?;
192 if header == IMAGE_COMPLETE {
193 break;
194 }
195 let run_length = header & 0xFFF;
196 let mut image_data = vec![];
197 for _i in 0..run_length {
198 image_data.push(reader.read_u8()?);
199 }
200 data.push(Row { header, image_data });
201 }
202
203 Ok(AnimFrame {
205 image_center_x,
206 image_center_y,
207 width,
208 height,
209 data,
210 })
211}
212
213impl AnimReader<File> {
214 pub fn new(index_path: &Path, mul_path: &Path) -> MulReaderResult<AnimReader<File>> {
216 let mul_reader = MulReader::new(index_path, mul_path)?;
217 Ok(AnimReader { mul_reader })
218 }
219}
220
221impl<T: Read + Seek> AnimReader<T> {
222 pub fn from_mul(reader: MulReader<T>) -> AnimReader<T> {
224 AnimReader { mul_reader: reader }
225 }
226
227 pub fn read(&mut self, id: u32) -> MulReaderResult<AnimGroup> {
229 let raw = self.mul_reader.read(id)?;
230 let mut reader = Cursor::new(raw.data);
231 let mut palette = [0; PALETTE_SIZE];
233 for cell in &mut palette {
234 *cell = reader.read_u16::<LittleEndian>()?;
235 }
236
237 let frame_count = reader.read_u32::<LittleEndian>()?;
238 let mut frame_offsets = vec![];
239 for _ in 0..frame_count {
240 frame_offsets.push(reader.read_u32::<LittleEndian>()?);
241 }
242
243 let mut frames = vec![];
244 for offset in frame_offsets {
245 reader.seek(SeekFrom::Start((PALETTE_SIZE as u32 * 2 + offset) as u64))?;
246 frames.push(read_frame(&mut reader)?);
247 }
248
249 Ok(AnimGroup {
250 palette,
251 frame_count,
252 frames,
253 })
254 }
255}