Coverage Report

Created: 2026-10-17 08:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/gif-0.14.2/src/reader/converter.rs
Line
Count
Source
1
use alloc::borrow::Cow;
2
use alloc::vec::Vec;
3
use core::iter;
4
use core::mem;
5
6
use super::decoder::{DecodingError, OutputBuffer, PLTE_CHANNELS};
7
use crate::common::Frame;
8
use crate::MemoryLimit;
9
10
pub(crate) const N_CHANNELS: usize = 4;
11
12
/// Output mode for the image data
13
#[derive(Clone, Copy, Debug, PartialEq)]
14
#[repr(u8)]
15
pub enum ColorOutput {
16
    /// The decoder expands the image data to 32bit RGBA.
17
    /// This affects:
18
    ///
19
    ///  - The buffer buffer of the `Frame` returned by [`Decoder::read_next_frame`].
20
    ///  - `Decoder::fill_buffer`, `Decoder::buffer_size` and `Decoder::line_length`.
21
    RGBA = 0,
22
    /// The decoder returns the raw indexed data.
23
    Indexed = 1,
24
}
25
26
pub(crate) type FillBufferCallback<'a> =
27
    &'a mut dyn FnMut(&mut OutputBuffer<'_>) -> Result<usize, DecodingError>;
28
29
/// Deinterlaces and expands to RGBA if needed
30
pub(crate) struct PixelConverter {
31
    color_output: ColorOutput,
32
    buffer: Vec<u8>,
33
    global_palette: Option<Vec<u8>>,
34
}
35
36
impl PixelConverter {
37
7.49k
    pub(crate) const fn new(color_output: ColorOutput) -> Self {
38
7.49k
        Self {
39
7.49k
            color_output,
40
7.49k
            buffer: Vec::new(),
41
7.49k
            global_palette: None,
42
7.49k
        }
43
7.49k
    }
44
45
0
    pub(crate) fn check_buffer_size(
46
0
        &self,
47
0
        frame: &Frame<'_>,
48
0
        memory_limit: &MemoryLimit,
49
0
    ) -> Result<usize, DecodingError> {
50
0
        let pixel_bytes = memory_limit
51
0
            .buffer_size(self.color_output, frame.width, frame.height)
52
0
            .ok_or_else(|| DecodingError::OutOfMemory)?;
53
54
0
        debug_assert_eq!(
55
            pixel_bytes,
56
0
            self.buffer_size(frame).unwrap(),
57
0
            "Checked computation diverges from required buffer size"
58
        );
59
0
        Ok(pixel_bytes)
60
0
    }
61
62
    #[inline]
63
0
    pub(crate) fn read_frame(
64
0
        &mut self,
65
0
        frame: &mut Frame<'_>,
66
0
        data_callback: FillBufferCallback<'_>,
67
0
        memory_limit: &MemoryLimit,
68
0
    ) -> Result<(), DecodingError> {
69
0
        let pixel_bytes = self.check_buffer_size(frame, memory_limit)?;
70
0
        let mut vec = match mem::replace(&mut frame.buffer, Cow::Borrowed(&[])) {
71
            // reuse buffer if possible without reallocating
72
0
            Cow::Owned(mut vec) if vec.capacity() >= pixel_bytes => {
73
0
                vec.resize(pixel_bytes, 0);
74
0
                vec
75
            }
76
            // resizing would realloc anyway, and 0-init is faster than a copy
77
0
            _ => vec![0; pixel_bytes],
78
        };
79
0
        self.read_into_buffer(frame, &mut vec, data_callback)?;
80
0
        frame.buffer = Cow::Owned(vec);
81
0
        frame.interlaced = false;
82
0
        Ok(())
83
0
    }
84
85
    #[inline]
86
1.63k
    pub(crate) const fn buffer_size(&self, frame: &Frame<'_>) -> Option<usize> {
87
1.63k
        self.line_length(frame).checked_mul(frame.height as usize)
88
1.63k
    }
89
90
    #[inline]
91
5.87k
    pub(crate) const fn line_length(&self, frame: &Frame<'_>) -> usize {
92
        use self::ColorOutput::{Indexed, RGBA};
93
5.87k
        match self.color_output {
94
5.87k
            RGBA => frame.width as usize * N_CHANNELS,
95
0
            Indexed => frame.width as usize,
96
        }
97
5.87k
    }
98
99
    /// Use `read_into_buffer` to deinterlace
100
    #[inline(never)]
101
205k
    pub(crate) fn fill_buffer(
102
205k
        &mut self,
103
205k
        current_frame: &Frame<'_>,
104
205k
        mut buf: &mut [u8],
105
205k
        data_callback: FillBufferCallback<'_>,
106
205k
    ) -> Result<bool, DecodingError> {
107
        loop {
108
398k
            let decode_into = match self.color_output {
109
                // When decoding indexed data, LZW can write the pixels directly
110
0
                ColorOutput::Indexed => &mut buf[..],
111
                // When decoding RGBA, the pixel data will be expanded by a factor of 4,
112
                // and it's simpler to decode indexed pixels to another buffer first
113
                ColorOutput::RGBA => {
114
398k
                    let buffer_size = buf.len() / N_CHANNELS;
115
398k
                    if buffer_size == 0 {
116
3
                        return Err(DecodingError::format("odd-sized buffer"));
117
398k
                    }
118
398k
                    if self.buffer.len() < buffer_size {
119
2.29k
                        self.buffer.resize(buffer_size, 0);
120
396k
                    }
121
398k
                    &mut self.buffer[..buffer_size]
122
                }
123
            };
124
398k
            match data_callback(&mut OutputBuffer::Slice(decode_into))? {
125
133
                0 => return Ok(false),
126
397k
                bytes_decoded => {
127
397k
                    match self.color_output {
128
                        ColorOutput::RGBA => {
129
397k
                            let transparent = current_frame.transparent;
130
397k
                            let palette: &[u8] = current_frame
131
397k
                                .palette
132
397k
                                .as_deref()
133
397k
                                .or(self.global_palette.as_deref())
134
397k
                                .unwrap_or_default(); // next_frame_info already checked it won't happen
135
136
397k
                            let (pixels, rest) = buf.split_at_mut(bytes_decoded * N_CHANNELS);
137
397k
                            buf = rest;
138
139
10.6M
                            for (rgba, idx) in pixels
140
397k
                                .chunks_exact_mut(N_CHANNELS)
141
397k
                                .zip(self.buffer.iter().copied().take(bytes_decoded))
142
                            {
143
10.6M
                                let plte_offset = PLTE_CHANNELS * idx as usize;
144
10.1M
                                if let Some(colors) =
145
10.6M
                                    palette.get(plte_offset..plte_offset + PLTE_CHANNELS)
146
                                {
147
10.1M
                                    rgba[0] = colors[0];
148
10.1M
                                    rgba[1] = colors[1];
149
10.1M
                                    rgba[2] = colors[2];
150
10.1M
                                    rgba[3] = if let Some(t) = transparent {
151
3.23M
                                        if t == idx {
152
1.02k
                                            0x00
153
                                        } else {
154
3.22M
                                            0xFF
155
                                        }
156
                                    } else {
157
6.96M
                                        0xFF
158
                                    };
159
485k
                                }
160
                            }
161
                        }
162
0
                        ColorOutput::Indexed => {
163
0
                            buf = &mut buf[bytes_decoded..];
164
0
                        }
165
                    }
166
397k
                    if buf.is_empty() {
167
204k
                        return Ok(true);
168
193k
                    }
169
                }
170
            }
171
        }
172
205k
    }
173
174
7.81k
    pub(crate) fn global_palette(&self) -> Option<&[u8]> {
175
7.81k
        self.global_palette.as_deref()
176
7.81k
    }
177
178
7.37k
    pub(crate) fn set_global_palette(&mut self, palette: Vec<u8>) {
179
7.37k
        self.global_palette = if !palette.is_empty() {
180
1.67k
            Some(palette)
181
        } else {
182
5.69k
            None
183
        };
184
7.37k
    }
185
186
    /// Applies deinterlacing
187
    ///
188
    /// Set `frame.interlaced = false` afterwards if you're putting the buffer back into the `Frame`
189
5.87k
    pub(crate) fn read_into_buffer(
190
5.87k
        &mut self,
191
5.87k
        frame: &Frame<'_>,
192
5.87k
        buf: &mut [u8],
193
5.87k
        data_callback: FillBufferCallback<'_>,
194
5.87k
    ) -> Result<(), DecodingError> {
195
5.87k
        if frame.interlaced {
196
4.24k
            let width = self.line_length(frame);
197
203k
            for row in (InterlaceIterator {
198
4.24k
                len: frame.height,
199
                next: 0,
200
                pass: 0,
201
            }) {
202
                // this can't overflow 32-bit, because row never equals (maximum) height
203
203k
                let start = row * width;
204
                // Handle a too-small buffer and 32-bit usize overflow without panicking
205
203k
                let line = buf
206
203k
                    .get_mut(start..)
207
203k
                    .and_then(|b| b.get_mut(..width))
208
203k
                    .ok_or_else(|| DecodingError::format("buffer too small"))?;
209
203k
                if !self.fill_buffer(frame, line, data_callback)? {
210
100
                    return Err(DecodingError::format("image truncated"));
211
203k
                }
212
            }
213
        } else {
214
1.63k
            let buf = self
215
1.63k
                .buffer_size(frame)
216
1.63k
                .and_then(|buffer_size| buf.get_mut(..buffer_size))
217
1.63k
                .ok_or_else(|| DecodingError::format("buffer too small"))?;
218
1.63k
            if !self.fill_buffer(frame, buf, data_callback)? {
219
33
                return Err(DecodingError::format("image truncated"));
220
940
            }
221
        };
222
4.55k
        Ok(())
223
5.87k
    }
224
}
225
226
struct InterlaceIterator {
227
    len: u16,
228
    next: usize,
229
    pass: usize,
230
}
231
232
impl iter::Iterator for InterlaceIterator {
233
    type Item = usize;
234
235
    #[inline]
236
207k
    fn next(&mut self) -> Option<Self::Item> {
237
207k
        if self.len == 0 {
238
3.57k
            return None;
239
203k
        }
240
        // although the pass never goes out of bounds thanks to len==0,
241
        // the optimizer doesn't see it. get()? avoids costlier panicking code.
242
203k
        let mut next = self.next + *[8, 8, 4, 2].get(self.pass)?;
243
204k
        while next >= self.len as usize {
244
407
            debug_assert!(self.pass < 4);
245
407
            next = *[4, 2, 1, 0].get(self.pass)?;
246
407
            self.pass += 1;
247
        }
248
203k
        mem::swap(&mut next, &mut self.next);
249
203k
        Some(next)
250
207k
    }
251
}
252
253
#[cfg(test)]
254
mod test {
255
    use alloc::vec::Vec;
256
257
    use super::InterlaceIterator;
258
259
    #[rustfmt::skip]
260
    #[test]
261
    fn test_interlace_iterator() {
262
        for &(len, expect) in &[
263
            (0, &[][..]),
264
            (1, &[0][..]),
265
            (2, &[0, 1][..]),
266
            (3, &[0, 2, 1][..]),
267
            (4, &[0, 2, 1, 3][..]),
268
            (5, &[0, 4, 2, 1, 3][..]),
269
            (6, &[0, 4, 2, 1, 3, 5][..]),
270
            (7, &[0, 4, 2, 6, 1, 3, 5][..]),
271
            (8, &[0, 4, 2, 6, 1, 3, 5, 7][..]),
272
            (9, &[0, 8, 4, 2, 6, 1, 3, 5, 7][..]),
273
            (10, &[0, 8, 4, 2, 6, 1, 3, 5, 7, 9][..]),
274
            (11, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9][..]),
275
            (12, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]),
276
            (13, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]),
277
            (14, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11, 13][..]),
278
            (15, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13][..]),
279
            (16, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]),
280
            (17, &[0, 8, 16, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]),
281
        ] {
282
            let iter = InterlaceIterator { len, next: 0, pass: 0 };
283
            let lines = iter.collect::<Vec<_>>();
284
            assert_eq!(lines, expect);
285
        }
286
    }
287
288
    #[test]
289
    fn interlace_max() {
290
        let iter = InterlaceIterator {
291
            len: 0xFFFF,
292
            next: 0,
293
            pass: 0,
294
        };
295
        assert_eq!(65533, iter.last().unwrap());
296
    }
297
}