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