Skip to content

Commit

Permalink
Fix color indexing transform
Browse files Browse the repository at this point in the history
  • Loading branch information
fintelia committed Dec 25, 2023
1 parent daf97b1 commit 3471c62
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 83 deletions.
4 changes: 2 additions & 2 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ impl<R: Read + Seek> WebPDecoder<R> {
result?;
} else if let Some(range) = self.chunks.get(&WebPRiffChunk::VP8L) {
let mut frame = LosslessDecoder::new(range_reader(&mut self.r, range.clone())?);
let frame = frame.decode_frame()?;
let frame = frame.decode_frame(None)?;
if u32::from(frame.width) != self.width || u32::from(frame.height) != self.height {
return Err(DecodingError::InconsistentImageSizes);
}
Expand Down Expand Up @@ -666,7 +666,7 @@ impl<R: Read + Seek> WebPDecoder<R> {
WebPRiffChunk::VP8L => {
let reader = (&mut self.r).take(chunk_size as u64);
let mut lossless_decoder = LosslessDecoder::new(reader);
let frame = lossless_decoder.decode_frame()?;
let frame = lossless_decoder.decode_frame(None)?;
if frame.width as u32 != frame_width || frame.height as u32 != frame_height {
return Err(DecodingError::InconsistentImageSizes);
}
Expand Down
2 changes: 1 addition & 1 deletion src/extended.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ pub(crate) fn read_alpha_chunk<R: Read>(
let height: u16 = height
.try_into()
.map_err(|_| DecodingError::ImageTooLarge)?;
let frame = decoder.decode_frame_implicit_dims(width, height)?;
let frame = decoder.decode_frame(Some((width, height)))?;

let mut data = vec![0u8; usize::from(width) * usize::from(height)];

Expand Down
120 changes: 40 additions & 80 deletions src/lossless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,105 +83,69 @@ impl<R: Read> LosslessDecoder<R> {
}
}

/// Reads the frame
pub(crate) fn decode_frame(&mut self) -> Result<&LosslessFrame, DecodingError> {
let signature = self.r.read_u8()?;

if signature != 0x2f {
return Err(DecodingError::LosslessSignatureInvalid(signature));
}

/// Decodes a frame.
///
/// In an alpha chunk the width and height are not included in the header, so they should be
/// provided by setting the `implicit_dimensions` argument. Otherwise that argument should be
/// `None` and the frame dimensions will be determined by reading the VP8L header.
pub(crate) fn decode_frame(
&mut self,
implicit_dimensions: Option<(u16, u16)>,
) -> Result<&LosslessFrame, DecodingError> {
let mut buf = Vec::new();
self.r.read_to_end(&mut buf)?;
self.bit_reader.init(buf);

self.frame.width = self.bit_reader.read_bits::<u16>(14)? + 1;
self.frame.height = self.bit_reader.read_bits::<u16>(14)? + 1;

let _alpha_used = self.bit_reader.read_bits::<u8>(1)?;

let version_num = self.bit_reader.read_bits::<u8>(3)?;

if version_num != 0 {
return Err(DecodingError::VersionNumberInvalid(version_num));
}

let mut data = self.decode_image_stream(self.frame.width, self.frame.height, true)?;
match implicit_dimensions {
Some((width, height)) => {
self.frame.width = width;
self.frame.height = height;
}
None => {
let signature = self.bit_reader.read_bits::<u8>(8)?;
if signature != 0x2f {
return Err(DecodingError::LosslessSignatureInvalid(signature));
}

let mut color_index_index = 5;
let mut xsize = self.frame.width;
for (i, transform) in self.transforms.iter().enumerate() {
if let Some(TransformType::ColorIndexingTransform { table_size, .. }) = transform {
let bits = if *table_size <= 2 {
3
} else if *table_size <= 4 {
2
} else if *table_size <= 16 {
1
} else {
0
};
self.frame.width = self.bit_reader.read_bits::<u16>(14)? + 1;
self.frame.height = self.bit_reader.read_bits::<u16>(14)? + 1;

xsize = subsample_size(xsize, bits);
color_index_index = i;
let _alpha_used = self.bit_reader.read_bits::<u8>(1)?;
let version_num = self.bit_reader.read_bits::<u8>(3)?;
if version_num != 0 {
return Err(DecodingError::VersionNumberInvalid(version_num));
}
}
}

for &trans_index in self.transform_order.iter().rev() {
let trans = self.transforms[usize::from(trans_index)].as_ref().unwrap();
trans.apply_transform(&mut data, xsize, self.frame.height)?;
if trans_index == color_index_index as u8 {
xsize = self.frame.width;
}
}
let transformed_width = self.read_transforms()?;
let mut data = self.decode_image_stream(transformed_width, self.frame.height, true)?;

self.frame.buf = data;
Ok(&self.frame)
}

//used for alpha data in extended decoding
pub(crate) fn decode_frame_implicit_dims(
&mut self,
width: u16,
height: u16,
) -> Result<&LosslessFrame, DecodingError> {
let mut buf = Vec::new();
self.r.read_to_end(&mut buf)?;
self.bit_reader.init(buf);

self.frame.width = width;
self.frame.height = height;

let mut data = self.decode_image_stream(self.frame.width, self.frame.height, true)?;

//transform_order is vector of indices(0-3) into transforms in order decoded
let mut width = transformed_width;
for &trans_index in self.transform_order.iter().rev() {
let trans = self.transforms[usize::from(trans_index)].as_ref().unwrap();
trans.apply_transform(&mut data, self.frame.width, self.frame.height)?;
let transform = self.transforms[usize::from(trans_index)].as_ref().unwrap();
if let TransformType::ColorIndexingTransform { .. } = transform {
width = self.frame.width;
}
transform.apply_transform(&mut data, width, self.frame.height)?;
}

self.frame.buf = data;
Ok(&self.frame)
}

/// Reads Image data from the bitstream
/// Can be in any of the 5 roles described in the Specification
/// ARGB Image role has different behaviour to the other 4
/// xsize and ysize describe the size of the blocks where each block has its own entropy code
///
/// Can be in any of the 5 roles described in the Specification. ARGB Image role has different
/// behaviour to the other 4. xsize and ysize describe the size of the blocks where each block
/// has its own entropy code
fn decode_image_stream(
&mut self,
xsize: u16,
ysize: u16,
is_argb_img: bool,
) -> Result<Vec<u32>, DecodingError> {
let trans_xsize = if is_argb_img {
self.read_transforms()?
} else {
xsize
};

let color_cache_bits = self.read_color_cache()?;

let color_cache = color_cache_bits.map(|bits| {
let size = 1 << bits;
let cache = vec![0u32; size];
Expand All @@ -191,12 +155,8 @@ impl<R: Read> LosslessDecoder<R> {
}
});

let huffman_info = self.read_huffman_codes(is_argb_img, trans_xsize, ysize, color_cache)?;

//decode data
let data = self.decode_image_data(trans_xsize, ysize, huffman_info)?;

Ok(data)
let huffman_info = self.read_huffman_codes(is_argb_img, xsize, ysize, color_cache)?;
self.decode_image_data(xsize, ysize, huffman_info)
}

/// Reads transforms and their data from the bitstream
Expand Down

0 comments on commit 3471c62

Please sign in to comment.