maple_render_core/
gif_anim.rs1use std::{
2 fs::File,
3 io::{BufWriter, Cursor},
4 path::Path,
5};
6
7use gif::{Encoder, Frame, Repeat};
8use image::RgbaImage;
9
10use crate::{
11 error::{Error, Result},
12 quantize::{Palette, Quantizer},
13 renders::Renders,
14};
15
16pub struct GifAnim {
17 renders: Renders,
18 palette_frames: Option<Vec<i32>>,
21 period: Option<f64>,
24 hold: Option<f64>,
25 first_frame: i32,
26 blocks: Vec<GifBlock>,
27 palette: Option<Palette>,
28 dither: bool,
29}
30
31struct GifBlock {
32 data: Vec<u8>,
33 delay: u16,
34 width: u16,
35 height: u16,
36}
37
38impl GifAnim {
39 pub fn new(renders: Renders) -> Self {
40 GifAnim {
41 renders,
42 palette_frames: None,
43 period: None,
44 hold: None,
45 first_frame: -1,
46 blocks: Vec::new(),
47 palette: None,
48 dither: false,
49 }
50 }
51
52 pub fn set_dither(&mut self, dither: bool) {
53 self.dither = dither;
54 }
55
56 pub fn set_palette(&mut self, index: i32) {
57 self.palette_frames = Some(vec![index]);
58 }
59
60 pub fn set_palette_frames(&mut self, frames: Vec<i32>) {
61 self.palette_frames = Some(frames);
62 }
63
64 pub fn set_first_frame(&mut self, index: i32) {
65 self.first_frame = index;
66 }
67
68 pub fn set_timing(&mut self, period: f64, hold: f64) {
69 self.period = Some(period);
70 self.hold = Some(hold);
71 }
72
73 pub fn apply(&mut self) -> Result<()> {
74 let palette_frames =
75 self.palette_frames.clone().unwrap_or_else(|| self.renders.repo().get_palette());
76 let period = self.period.unwrap_or_else(|| self.renders.repo().get_period());
77 let hold = self.hold.unwrap_or_else(|| self.renders.repo().get_hold());
78
79 let step = (period * 100.0 + 0.5) as u16;
80 let last_step = (hold * 100.0 + 0.5) as u16;
81
82 let pals = palette_frames.len();
83 if pals < 1 {
84 return Err(Error::MissingData("No palette frames specified".to_string()));
85 }
86
87 let mut pal_image: Option<RgbaImage> = None;
88 let mut pal_index: i32 = -1;
89
90 for (i, &idx) in palette_frames.iter().enumerate() {
91 if pals == 1 {
92 let render = self.renders.get_render(idx)?;
93 pal_image = Some(render.get().clone());
94 pal_index = idx;
95 self.renders.remove_mapping(idx);
96 continue;
97 }
98
99 {
100 let render = self.renders.get_render(idx)?;
101 let img = render.get();
102 let w = img.width() as usize;
103 let h = img.height() as usize;
104
105 if pal_image.is_none() {
106 pal_image = Some(img.clone());
107 } else {
108 let pal_img = pal_image.as_mut().unwrap();
109 for k in (i..(w * h)).step_by(pals) {
110 let xx = (k % w) as u32;
111 let yy = (k / w) as u32;
112 let pixel = *img.get_pixel(xx, yy);
113 pal_img.put_pixel(xx, yy, pixel);
114 }
115 }
116 }
117
118 self.renders.remove_mapping(idx);
119 }
120
121 let pal_image = pal_image.ok_or(Error::MissingData("No palette image".to_string()))?;
122 let ww = pal_image.width() as u16;
123 let hh = pal_image.height() as u16;
124
125 let mut quantizer = Quantizer::new(&pal_image);
126 self.palette = Some(quantizer.palette().clone());
127
128 let mut prev_indices = quantizer.quantize(&pal_image, self.dither);
129
130 let frames = self.renders.length() as i32;
131
132 let mut _emit_ct = 0;
133 let mut step_pending: u16 = 0;
134 let mut pending_frame_number = pal_index;
135
136 for base in 0..frames {
137 let i = if self.first_frame >= 0 { (base + self.first_frame) % frames } else { base };
138
139 step_pending = step_pending.saturating_add(step);
140
141 let curr_indices = if i != pal_index {
142 let quantized = {
143 let render = self.renders.get_render(i)?;
144 quantizer.quantize(render.get(), self.dither)
145 };
146 self.renders.remove_mapping(i);
147 quantized
148 } else {
149 prev_indices.clone()
150 };
151
152 self.renders.remove_render(i);
153
154 let mut change = false;
155 let mut _emitting_frame_number = -1;
156
157 if base > 0 {
158 for (p0, p1) in prev_indices.iter().zip(curr_indices.iter()) {
159 if p0 != p1 {
160 change = true;
161 _emitting_frame_number = pending_frame_number;
162 pending_frame_number = i;
163 break;
164 }
165 }
166 }
167
168 if change {
169 let delay = step_pending.saturating_sub(step);
170 let emitted_indices = std::mem::replace(&mut prev_indices, curr_indices);
171 self.blocks.push(GifBlock { data: emitted_indices, delay, width: ww, height: hh });
172 step_pending = step;
173 _emit_ct += 1;
174 } else {
175 prev_indices = curr_indices;
176 pending_frame_number = i;
177 }
178
179 if i == frames - 1 {
180 step_pending = step_pending.saturating_add(last_step);
181 }
182 }
183
184 if frames > 0 {
185 self.blocks.push(GifBlock {
186 data: prev_indices,
187 delay: step_pending,
188 width: ww,
189 height: hh,
190 });
191 }
192
193 Ok(())
194 }
195
196 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
197 let (width, height, color_table) = self.build_palette_table()?;
198 let file = File::create(path.as_ref())?;
199 let writer = BufWriter::new(file);
200 self.write_blocks(writer, width, height, &color_table)?;
201
202 Ok(())
203 }
204
205 pub fn encode(&self) -> Result<Vec<u8>> {
206 let (width, height, color_table) = self.build_palette_table()?;
207 let mut out = Vec::new();
208 let writer = BufWriter::new(Cursor::new(&mut out));
209 self.write_blocks(writer, width, height, &color_table)?;
210 Ok(out)
211 }
212
213 fn build_palette_table(&self) -> Result<(u16, u16, Vec<u8>)> {
214 if self.blocks.is_empty() {
215 return Err(Error::GifEncode("No frames to save".to_string()));
216 }
217
218 let palette = self
219 .palette
220 .as_ref()
221 .ok_or_else(|| Error::GifEncode("No palette available".to_string()))?;
222
223 let first_block = &self.blocks[0];
224 let width = first_block.width;
225 let height = first_block.height;
226
227 let mut color_table = Vec::with_capacity(palette.colors_total * 3);
228 for i in 0..palette.colors_total {
229 color_table.push(palette.red[i]);
230 color_table.push(palette.green[i]);
231 color_table.push(palette.blue[i]);
232 }
233 while color_table.len() < 768 {
234 color_table.push(0);
235 }
236
237 Ok((width, height, color_table))
238 }
239
240 fn write_blocks<W: std::io::Write>(
241 &self,
242 writer: W,
243 width: u16,
244 height: u16,
245 color_table: &[u8],
246 ) -> Result<()> {
247 let mut encoder = Encoder::new(writer, width, height, color_table)
248 .map_err(|e| Error::GifEncode(e.to_string()))?;
249
250 encoder.set_repeat(Repeat::Infinite).map_err(|e| Error::GifEncode(e.to_string()))?;
251
252 for block in &self.blocks {
253 let mut frame =
254 Frame::from_indexed_pixels(block.width, block.height, block.data.clone(), None);
255 frame.delay = block.delay;
256 frame.dispose = gif::DisposalMethod::Keep;
257
258 encoder.write_frame(&frame).map_err(|e| Error::GifEncode(e.to_string()))?;
259 }
260
261 Ok(())
262 }
263}