Skip to main content

maple_render_core/
gif_anim.rs

1use 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    /// Explicit palette frames set by the caller. When `None`, the template's
19    /// own palette (from `template.json`) is used.
20    palette_frames: Option<Vec<i32>>,
21    /// Explicit timing set by the caller. When `None`, the template's own
22    /// delay/hold (from `template.json`) is used.
23    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}