figif_core/decoders/
streaming.rs1use crate::error::{FigifError, Result};
4use crate::traits::GifDecoder;
5use crate::types::{DecodedFrame, DisposalMethod, GifMetadata, LoopCount};
6use gif::{DecodeOptions, Decoder};
7use image::{Rgba, RgbaImage};
8use std::fs::File;
9use std::io::{BufReader, Cursor, Read};
10use std::path::Path;
11
12#[derive(Debug, Clone, Default)]
31pub struct StreamingDecoder;
32
33impl StreamingDecoder {
34 pub fn new() -> Self {
36 Self
37 }
38}
39
40pub struct StreamingFrameIter<R: Read> {
42 decoder: Decoder<R>,
43 canvas: RgbaImage,
44 previous_canvas: Option<RgbaImage>,
45 index: usize,
46 width: u32,
47 height: u32,
48}
49
50impl<R: Read> StreamingFrameIter<R> {
51 fn new(decoder: Decoder<R>) -> Self {
52 let width = decoder.width() as u32;
53 let height = decoder.height() as u32;
54 let canvas = RgbaImage::from_pixel(width, height, Rgba([0, 0, 0, 0]));
55
56 Self {
57 decoder,
58 canvas,
59 previous_canvas: None,
60 index: 0,
61 width,
62 height,
63 }
64 }
65}
66
67impl<R: Read> Iterator for StreamingFrameIter<R> {
68 type Item = Result<DecodedFrame>;
69
70 fn next(&mut self) -> Option<Self::Item> {
71 let frame = match self.decoder.read_next_frame() {
72 Ok(Some(frame)) => frame,
73 Ok(None) => return None,
74 Err(e) => return Some(Err(e.into())),
75 };
76
77 let delay = frame.delay;
78 let disposal = DisposalMethod::from(frame.dispose);
79 let left = frame.left;
80 let top = frame.top;
81 let frame_width = frame.width as u32;
82 let frame_height = frame.height as u32;
83
84 if matches!(disposal, DisposalMethod::Previous) {
86 self.previous_canvas = Some(self.canvas.clone());
87 }
88
89 let frame_buffer = &frame.buffer;
91 for y in 0..frame_height {
92 for x in 0..frame_width {
93 let src_idx = ((y * frame_width + x) * 4) as usize;
94 if src_idx + 3 < frame_buffer.len() {
95 let pixel = Rgba([
96 frame_buffer[src_idx],
97 frame_buffer[src_idx + 1],
98 frame_buffer[src_idx + 2],
99 frame_buffer[src_idx + 3],
100 ]);
101
102 let canvas_x = left as u32 + x;
103 let canvas_y = top as u32 + y;
104
105 if canvas_x < self.width && canvas_y < self.height && pixel[3] > 0 {
106 self.canvas.put_pixel(canvas_x, canvas_y, pixel);
107 }
108 }
109 }
110 }
111
112 let decoded = DecodedFrame {
114 index: self.index,
115 image: self.canvas.clone(),
116 delay_centiseconds: delay,
117 disposal,
118 left,
119 top,
120 };
121
122 match disposal {
124 DisposalMethod::Background => {
125 for y in 0..frame_height {
126 for x in 0..frame_width {
127 let canvas_x = left as u32 + x;
128 let canvas_y = top as u32 + y;
129 if canvas_x < self.width && canvas_y < self.height {
130 self.canvas
131 .put_pixel(canvas_x, canvas_y, Rgba([0, 0, 0, 0]));
132 }
133 }
134 }
135 }
136 DisposalMethod::Previous => {
137 if let Some(ref prev) = self.previous_canvas {
138 self.canvas = prev.clone();
139 }
140 }
141 DisposalMethod::Keep | DisposalMethod::None => {}
142 }
143
144 self.index += 1;
145 Some(Ok(decoded))
146 }
147}
148
149pub enum StreamingIterWrapper {
151 File(StreamingFrameIter<BufReader<File>>),
153 Bytes(StreamingFrameIter<Cursor<Vec<u8>>>),
155}
156
157impl Iterator for StreamingIterWrapper {
158 type Item = Result<DecodedFrame>;
159
160 fn next(&mut self) -> Option<Self::Item> {
161 match self {
162 StreamingIterWrapper::File(iter) => iter.next(),
163 StreamingIterWrapper::Bytes(iter) => iter.next(),
164 }
165 }
166}
167
168impl GifDecoder for StreamingDecoder {
169 type FrameIter = StreamingIterWrapper;
170
171 fn decode_file(&self, path: impl AsRef<Path>) -> Result<Self::FrameIter> {
172 let path = path.as_ref();
173 let file = File::open(path).map_err(|e| FigifError::FileRead {
174 path: path.to_path_buf(),
175 source: e,
176 })?;
177 let reader = BufReader::new(file);
178
179 let mut options = DecodeOptions::new();
180 options.set_color_output(gif::ColorOutput::RGBA);
181 let decoder = options.read_info(reader)?;
182
183 Ok(StreamingIterWrapper::File(StreamingFrameIter::new(decoder)))
184 }
185
186 fn decode_bytes(&self, data: &[u8]) -> Result<Self::FrameIter> {
187 if data.is_empty() {
188 return Err(FigifError::EmptyData);
189 }
190
191 let reader = Cursor::new(data.to_vec());
192
193 let mut options = DecodeOptions::new();
194 options.set_color_output(gif::ColorOutput::RGBA);
195 let decoder = options.read_info(reader)?;
196
197 Ok(StreamingIterWrapper::Bytes(StreamingFrameIter::new(
198 decoder,
199 )))
200 }
201
202 fn decode_reader<R: Read + Send>(&self, reader: R) -> Result<Self::FrameIter> {
203 let mut buffer = Vec::new();
205 let mut reader = reader;
206 reader
207 .read_to_end(&mut buffer)
208 .map_err(|e| FigifError::DecodeError {
209 reason: format!("failed to read GIF data: {}", e),
210 })?;
211
212 self.decode_bytes(&buffer)
213 }
214
215 fn metadata_from_bytes(&self, data: &[u8]) -> Result<GifMetadata> {
216 if data.is_empty() {
217 return Err(FigifError::EmptyData);
218 }
219
220 let reader = Cursor::new(data);
221 let mut options = DecodeOptions::new();
222 options.set_color_output(gif::ColorOutput::RGBA);
223 let mut decoder = options.read_info(reader)?;
224
225 let width = decoder.width();
226 let height = decoder.height();
227 let global_palette = decoder.global_palette().map(|p| p.to_vec());
228
229 let mut frame_count = 0;
230 let mut total_duration_cs: u64 = 0;
231
232 while let Some(frame) = decoder.read_next_frame()? {
233 frame_count += 1;
234 total_duration_cs += frame.delay as u64;
235 }
236
237 Ok(GifMetadata {
238 width,
239 height,
240 frame_count,
241 total_duration_ms: total_duration_cs * 10,
242 has_transparency: global_palette.is_some(),
243 loop_count: LoopCount::Infinite,
244 global_palette,
245 })
246 }
247
248 fn metadata_from_file(&self, path: impl AsRef<Path>) -> Result<GifMetadata> {
249 let path = path.as_ref();
250 let data = std::fs::read(path).map_err(|e| FigifError::FileRead {
251 path: path.to_path_buf(),
252 source: e,
253 })?;
254 self.metadata_from_bytes(&data)
255 }
256
257 fn name(&self) -> &'static str {
258 "streaming"
259 }
260}