Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
SLiV9 committed Jan 11, 2025
2 parents 14d00e4 + f295220 commit 522858c
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 187 deletions.
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# reset to default
30 changes: 16 additions & 14 deletions src/alpha_blending.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Optimized alpha blending routines based on libwebp
//!
//! https://github.com/webmproject/libwebp/blob/e4f7a9f0c7c9fbfae1568bc7fa5c94b989b50872/src/demux/anim_decode.c#L215-L267
//! <https://github.com/webmproject/libwebp/blob/e4f7a9f0c7c9fbfae1568bc7fa5c94b989b50872/src/demux/anim_decode.c#L215-L267>
fn channel_shift(i: u32) -> u32 {
const fn channel_shift(i: u32) -> u32 {
i * 8
}

Expand All @@ -18,8 +18,9 @@ fn blend_channel_nonpremult(
) -> u8 {
let src_channel = ((src >> shift) & 0xff) as u8;
let dst_channel = ((dst >> shift) & 0xff) as u8;
let blend_unscaled = (src_channel as u32 * src_a as u32) + (dst_channel as u32 * dst_a as u32);
debug_assert!(u64::from(blend_unscaled) < (1u64 << 32) / scale as u64);
let blend_unscaled =
(u32::from(src_channel) * u32::from(src_a)) + (u32::from(dst_channel) * u32::from(dst_a));
debug_assert!(u64::from(blend_unscaled) < (1u64 << 32) / u64::from(scale));
((blend_unscaled * scale) >> channel_shift(3)) as u8
}

Expand All @@ -35,8 +36,8 @@ fn blend_pixel_nonpremult(src: u32, dst: u32) -> u32 {
// libwebp used the following formula here:
//let dst_factor_a = (dst_a as u32 * (256 - src_a as u32)) >> 8;
// however, we've found that we can use a more precise approximation without losing performance:
let dst_factor_a = div_by_255(dst_a as u32 * (255 - src_a as u32));
let blend_a = src_a as u32 + dst_factor_a;
let dst_factor_a = div_by_255(u32::from(dst_a) * (255 - u32::from(src_a)));
let blend_a = u32::from(src_a) + dst_factor_a;
let scale = (1u32 << 24) / blend_a;

let blend_r =
Expand All @@ -45,11 +46,11 @@ fn blend_pixel_nonpremult(src: u32, dst: u32) -> u32 {
blend_channel_nonpremult(src, src_a, dst, dst_factor_a as u8, scale, channel_shift(1));
let blend_b =
blend_channel_nonpremult(src, src_a, dst, dst_factor_a as u8, scale, channel_shift(2));
debug_assert!(src_a as u32 + dst_factor_a < 256);
debug_assert!(u32::from(src_a) + dst_factor_a < 256);

((blend_r as u32) << channel_shift(0))
| ((blend_g as u32) << channel_shift(1))
| ((blend_b as u32) << channel_shift(2))
(u32::from(blend_r) << channel_shift(0))
| (u32::from(blend_g) << channel_shift(1))
| (u32::from(blend_b) << channel_shift(2))
| (blend_a << channel_shift(3))
}
}
Expand All @@ -69,7 +70,7 @@ pub(crate) fn do_alpha_blending(buffer: [u8; 4], canvas: [u8; 4]) -> [u8; 4] {
// https://arxiv.org/pdf/2202.02864
// https://github.com/image-rs/image-webp/issues/119#issuecomment-2544007820
#[inline]
fn div_by_255(v: u32) -> u32 {
const fn div_by_255(v: u32) -> u32 {
(((v + 0x80) >> 8) + v + 0x80) >> 8
}

Expand Down Expand Up @@ -116,9 +117,10 @@ mod tests {
let slow = do_alpha_blending_reference([r1, 0, 0, a1], [r2, 0, 0, a2]);
// libwebp doesn't do exact blending and so we don't either
for (o, s) in opt.iter().zip(slow.iter()) {
if o.abs_diff(*s) > 3 {
panic!("Mismatch in results! opt: {opt:?}, slow: {slow:?}, blended values: [{r1}, 0, 0, {a1}], [{r2}, 0, 0, {a2}]");
}
assert!(
o.abs_diff(*s) <= 3,
"Mismatch in results! opt: {opt:?}, slow: {slow:?}, blended values: [{r1}, 0, 0, {a1}], [{r2}, 0, 0, {a2}]"
);
}
}
}
Expand Down
43 changes: 22 additions & 21 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pub(crate) enum WebPRiffChunk {
}

impl WebPRiffChunk {
pub(crate) fn from_fourcc(chunk_fourcc: [u8; 4]) -> Self {
pub(crate) const fn from_fourcc(chunk_fourcc: [u8; 4]) -> Self {
match &chunk_fourcc {
b"RIFF" => Self::RIFF,
b"WEBP" => Self::WEBP,
Expand All @@ -203,7 +203,7 @@ impl WebPRiffChunk {
}
}

pub(crate) fn to_fourcc(self) -> [u8; 4] {
pub(crate) const fn to_fourcc(self) -> [u8; 4] {
match self {
Self::RIFF => *b"RIFF",
Self::WEBP => *b"WEBP",
Expand All @@ -220,7 +220,7 @@ impl WebPRiffChunk {
}
}

pub(crate) fn is_unknown(&self) -> bool {
pub(crate) const fn is_unknown(self) -> bool {
matches!(self, Self::Unknown(_))
}
}
Expand Down Expand Up @@ -292,10 +292,10 @@ pub struct WebPDecoder<R> {
}

impl<R: BufRead + Seek> WebPDecoder<R> {
/// Create a new WebPDecoder from the reader `r`. The decoder performs many small reads, so the
/// Create a new `WebPDecoder` from the reader `r`. The decoder performs many small reads, so the
/// reader should be buffered.
pub fn new(r: R) -> Result<WebPDecoder<R>, DecodingError> {
let mut decoder = WebPDecoder {
pub fn new(r: R) -> Result<Self, DecodingError> {
let mut decoder = Self {
r,
width: 0,
height: 0,
Expand Down Expand Up @@ -346,8 +346,8 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
let w = self.r.read_u16::<LittleEndian>()?;
let h = self.r.read_u16::<LittleEndian>()?;

self.width = (w & 0x3FFF) as u32;
self.height = (h & 0x3FFF) as u32;
self.width = u32::from(w & 0x3FFF);
self.height = u32::from(h & 0x3FFF);
if self.width == 0 || self.height == 0 {
return Err(DecodingError::InconsistentImageSizes);
}
Expand Down Expand Up @@ -402,7 +402,7 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
self.chunks.entry(chunk).or_insert(range);
}

if let WebPRiffChunk::ANMF = chunk {
if chunk == WebPRiffChunk::ANMF {
self.num_frames += 1;
if chunk_size < 24 {
return Err(DecodingError::InvalidChunkSize);
Expand Down Expand Up @@ -603,7 +603,7 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
}

/// Returns the number of bytes required to store the image or a single frame, or None if that
/// would take more than usize::MAX bytes.
/// would take more than `usize::MAX` bytes.
pub fn output_buffer_size(&self) -> Option<usize> {
let bytes_per_pixel = if self.has_alpha() { 4 } else { 3 };
(self.width as usize)
Expand All @@ -612,8 +612,12 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
}

/// Returns the raw bytes of the image. For animated images, this is the first frame.
///
/// Fails with `ImageTooLarge` if `buf` has length different than `output_buffer_size()`
pub fn read_image(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> {
assert_eq!(Some(buf.len()), self.output_buffer_size());
if Some(buf.len()) != self.output_buffer_size() {
return Err(DecodingError::ImageTooLarge);
}

if self.is_animated() {
let saved = std::mem::take(&mut self.animation);
Expand All @@ -639,10 +643,8 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
.chunks
.get(&WebPRiffChunk::VP8)
.ok_or(DecodingError::ChunkMissing)?;
// TODO: avoid cloning frame
let frame = Vp8Decoder::new(range_reader(&mut self.r, range.start..range.end)?)
.decode_frame()?
.clone();
let reader = range_reader(&mut self.r, range.start..range.end)?;
let frame = Vp8Decoder::decode_frame(reader)?;
if u32::from(frame.width) != self.width || u32::from(frame.height) != self.height {
return Err(DecodingError::InconsistentImageSizes);
}
Expand All @@ -656,7 +658,7 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
.ok_or(DecodingError::ChunkMissing)?
.clone();
let alpha_chunk = read_alpha_chunk(
&mut range_reader(&mut self.r, range.start..range.end)?,
&mut range_reader(&mut self.r, range)?,
self.width as u16,
self.height as u16,
)?;
Expand Down Expand Up @@ -746,9 +748,9 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
let (frame, frame_has_alpha): (Vec<u8>, bool) = match chunk {
WebPRiffChunk::VP8 => {
let reader = (&mut self.r).take(chunk_size);
let mut vp8_decoder = Vp8Decoder::new(reader);
let raw_frame = vp8_decoder.decode_frame()?;
if raw_frame.width as u32 != frame_width || raw_frame.height as u32 != frame_height
let raw_frame = Vp8Decoder::decode_frame(reader)?;
if u32::from(raw_frame.width) != frame_width
|| u32::from(raw_frame.height) != frame_height
{
return Err(DecodingError::InconsistentImageSizes);
}
Expand Down Expand Up @@ -781,8 +783,7 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
return Err(DecodingError::ChunkHeaderInvalid(next_chunk.to_fourcc()));
}

let mut vp8_decoder = Vp8Decoder::new((&mut self.r).take(next_chunk_size));
let frame = vp8_decoder.decode_frame()?;
let frame = Vp8Decoder::decode_frame((&mut self.r).take(next_chunk_size))?;

let mut rgba_frame = vec![0; frame_width as usize * frame_height as usize * 4];
frame.fill_rgba(&mut rgba_frame);
Expand Down
Loading

0 comments on commit 522858c

Please sign in to comment.