Skip to content

Commit 5ba3d7b

Browse files
authored
fix: mask before adding instead of after (#179)
1 parent 28bed22 commit 5ba3d7b

2 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/decoder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
418418
return Err(DecodingError::VersionNumberInvalid(version as u8));
419419
}
420420

421-
self.width = (1 + header) & 0x3FFF;
422-
self.height = (1 + (header >> 14)) & 0x3FFF;
421+
self.width = (header & 0x3FFF) + 1;
422+
self.height = ((header >> 14) & 0x3FFF) + 1;
423423
self.chunks
424424
.insert(WebPRiffChunk::VP8L, start..start + chunk_size);
425425
self.kind = ImageKind::Lossless;

tests/decode.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,50 @@ reftest!(
201201
lossless_indexed_2bit_palette,
202202
lossless_indexed_4bit_palette
203203
);
204+
205+
// Builds a minimal RIFF WebP containing only a VP8L header (no image data).
206+
// This is enough to test dimension parsing without needing valid pixel data.
207+
fn vp8l_dimensions_only_webp(width_minus_one: u32, height_minus_one: u32) -> Vec<u8> {
208+
let packed = (width_minus_one & 0x3FFF) | ((height_minus_one & 0x3FFF) << 14);
209+
let packed_le = packed.to_le_bytes();
210+
// VP8L payload: 1-byte signature + 4-byte packed header = 5 bytes
211+
let payload: [u8; 5] = [0x2F, packed_le[0], packed_le[1], packed_le[2], packed_le[3]];
212+
let chunk_size: u32 = payload.len() as u32;
213+
// RIFF file_size = "WEBP"(4) + "VP8L"(4) + size_field(4) + payload(5) + padding(1) = 18
214+
let riff_size: u32 = 4 + 4 + 4 + chunk_size + (chunk_size % 2);
215+
[
216+
b"RIFF".as_slice(),
217+
&riff_size.to_le_bytes(),
218+
b"WEBP",
219+
b"VP8L",
220+
&chunk_size.to_le_bytes(),
221+
&payload,
222+
&[0x00], // padding byte (chunk_size=5 is odd)
223+
]
224+
.concat()
225+
}
226+
227+
#[test]
228+
fn test_vp8l_max_width_dimensions() {
229+
// Regression test: VP8L encodes (width - 1) in bits 0-13 and (height - 1) in bits 14-27.
230+
// The buggy formula `(1 + header) & 0x3FFF` returns 0 instead of 16384 when the encoded
231+
// value is 0x3FFF, because adding 1 carries into bit 14 before the mask is applied.
232+
// The correct formula is `(header & 0x3FFF) + 1`.
233+
let data = vp8l_dimensions_only_webp(16383, 0);
234+
let decoder = image_webp::WebPDecoder::new(Cursor::new(data)).unwrap();
235+
assert_eq!(decoder.dimensions(), (16384, 1));
236+
}
237+
238+
#[test]
239+
fn test_vp8l_max_height_dimensions() {
240+
let data = vp8l_dimensions_only_webp(0, 16383);
241+
let decoder = image_webp::WebPDecoder::new(Cursor::new(data)).unwrap();
242+
assert_eq!(decoder.dimensions(), (1, 16384));
243+
}
244+
245+
#[test]
246+
fn test_vp8l_max_width_and_height_dimensions() {
247+
let data = vp8l_dimensions_only_webp(16383, 16383);
248+
let decoder = image_webp::WebPDecoder::new(Cursor::new(data)).unwrap();
249+
assert_eq!(decoder.dimensions(), (16384, 16384));
250+
}

0 commit comments

Comments
 (0)