gif: expose first-frame data byte count

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 19:51:49 +00:00
1 parent 3c51d88330
commit ededdd75c9
2 files changed
+32

No files matched your search

+23
View File
@@ -253,6 +253,29 @@ pub fn first_frame_decoded_pixels(bytes: &[u8]) -> Option<u64> {
Some(pixels)
}
/// Counts compressed image-data bytes in the first frame's sub-blocks, excluding its LZW code-size
/// byte and all later blocks. A truncated final sub-block contributes only the bytes present.
pub fn first_frame_compressed_bytes(bytes: &[u8]) -> Option<usize> {
let parsed = scan(bytes, 1)?;
let frame = parsed.frames.first()?;
let packed = *bytes.get(frame.offset.checked_add(9)?)?;
let mut pos = frame.offset.checked_add(10)?.checked_add(table_len(packed))?.checked_add(1)?;
let mut total = 0usize;
loop {
let length = usize::from(*bytes.get(pos)?);
pos = pos.checked_add(1)?;
if length == 0 {
return Some(total);
}
let end = pos.checked_add(length)?;
total = total.checked_add(end.min(bytes.len()).saturating_sub(pos))?;
if end > bytes.len() {
return Some(total);
}
pos = end;
}
}
/// Whether a file of `file_len` bytes can plausibly hold `declared_bytes` of decoded picture: at
/// most `max_ratio` decoded bytes for each file byte, plus 1 MiB. A decoder that sets up the whole
/// logical screen before it reads a frame should be given only files that pass this.
+9
View File
@@ -54,6 +54,15 @@ fn animated(delays_cs: &[u32], repeat: Option<image::codecs::gif::Repeat>) -> Ve
out
}
#[test]
fn first_frame_compressed_bytes_exclude_extensions_after_the_image_data() {
let mut bytes = gif(&[[1, 2, 3], [4, 5, 6]], None, None);
assert_eq!(first_frame_compressed_bytes(&bytes), Some(2));
bytes.splice(bytes.len() - 1..bytes.len() - 1, [0x21, 0xFE, 1, b'x', 0]);
assert_eq!(first_frame_compressed_bytes(&bytes), Some(2));
assert_eq!(first_frame_compressed_bytes(b"not a gif"), None);
}
#[test]
fn the_first_frames_table_and_transparent_entry() {
let g = [[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 255]];