Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use rust native webp en/decoding #31

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

nekowinston
Copy link

@nekowinston nekowinston commented Apr 3, 2024

Still a WIP, based on the recently released (and even more recently fixed) image-webp crate.

Couple of notes:

  • I opted for adding webp-pure as a mutually exclusive feature rather than a full replacement, as image-webp currently only supports lossless encoding.
  • This bumps the MSRV up to v1.67.1 because of image-webp dependencies.
  • I moved the COLORS static in ./tests/test_png.rs to a common module, since three files are now using the same contant, and using mod png; would also always run png tests when just testing with cargo test --test test_webp-pure --features all-pure for me.

We should probably wait for the release of image-webp v0.1.2/v0.2.0; I mainly wrote this PR for catppuccin/toolbox#147, to compile that project to WASM with pure Rust webp support, without having to rely on ImageMagick.

Summary by Sourcery

Add support for Rust-native WebP encoding and decoding with the image-webp crate, introducing a webp-pure feature for lossless encoding. Update the MSRV to v1.67.1 and refactor tests to use a common module for shared constants. Update documentation and add new tests for WebP functionality.

New Features:

  • Introduce support for WebP encoding and decoding using the Rust-native image-webp crate, with a new webp-pure feature for lossless encoding.

Enhancements:

  • Refactor the test suite to use a common module for shared constants, improving code reuse and maintainability.

Build:

  • Update the Minimum Supported Rust Version (MSRV) to v1.67.1 to accommodate dependencies of the image-webp crate.

Documentation:

  • Update the README to reflect the new MSRV and document the new webp-pure feature alongside existing image encoding features.

Tests:

  • Add new tests for WebP encoding and decoding, including both static and animated images, to ensure the correctness of the new webp-pure feature.

Chores:

  • Add a rust-toolchain.toml file to specify the Rust toolchain version and components required for the project.

@jay3332 jay3332 marked this pull request as ready for review September 23, 2024 02:30
Copy link

sourcery-ai bot commented Sep 23, 2024

Reviewer's Guide by Sourcery

This pull request introduces native Rust WebP encoding and decoding support using the image-webp crate, as an alternative to the existing libwebp-sys2 implementation. The changes include new pure Rust WebP encoder and decoder implementations, updated tests, and modifications to the feature system to support both WebP implementations.

File-Level Changes

Change Details Files
Implement pure Rust WebP encoding and decoding
  • Add new webp_pure module with WebP encoder and decoder implementations
  • Update feature system to support both webp and webp-pure features
  • Implement WebPStaticEncoder, WebPDecoder, and WebPSequenceDecoder structs
  • Add error handling for mutually exclusive webp and webp-pure features
src/encodings/mod.rs
src/encodings/webp_pure.rs
src/format.rs
Update tests for new WebP implementation
  • Add new test file for pure Rust WebP implementation
  • Update existing WebP tests to use new reference images
  • Implement tests for both lossless and lossy WebP encoding/decoding
tests/test_webp.rs
tests/test_webp-pure.rs
Refactor test structure and common code
  • Move COLORS constant to a new common module
  • Update test files to use the new common module
tests/common/mod.rs
tests/test_png.rs
tests/test_gif.rs
Update project metadata and documentation
  • Bump MSRV to 1.67.1
  • Update README with new WebP feature information
  • Add rust-toolchain.toml file
README.md
rust-toolchain.toml

Tips
  • Trigger a new Sourcery review by commenting @sourcery-ai review on the pull request.
  • Continue your discussion with Sourcery by replying directly to review comments.
  • You can change your review settings at any time by accessing your dashboard:
    • Enable or disable the Sourcery-generated pull request summary or reviewer's guide;
    • Change the review language;
  • You can always contact us if you have any questions or feedback.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @nekowinston - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

}
}

impl<P: Pixel> Iterator for WebPSequenceDecoder<P> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Consider optimizing the Iterator implementation for WebPSequenceDecoder

The current implementation allocates a new buffer and converts pixels for each frame. Consider pre-allocating the buffer and reusing it across frames, and look into ways to optimize the pixel conversion process. This could significantly improve performance for animated WebP images.

impl<P: Pixel> Iterator for WebPSequenceDecoder<P> {
    type Item = crate::Result<crate::Frame<P>>;

    fn next(&mut self) -> Option<Self::Item> {
        // Pre-allocate buffer here
        let mut buffer = Vec::with_capacity(self.width * self.height * 4);
        // Implement frame decoding and pixel conversion logic here
        // Reuse buffer for subsequent frames
    }
}

let mut image_buf: Vec<u8> = create_image_buffer(&decoder);
decoder
.read_image(&mut image_buf)
.map_err(|e| crate::Error::DecodingError(e.to_string()))?;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider implementing a more structured error handling approach

Instead of converting errors to strings, consider defining custom error types that wrap the underlying errors from the image_webp library. This would preserve more information about the errors and make it easier for users of your library to handle them appropriately.

            .map_err(|e| crate::Error::DecodingError(WebpDecodingError::from(e)))?;

// In the error module:
#[derive(Debug)]
pub enum WebpDecodingError {
    ImageError(image_webp::Error),
    // Add other specific error variants as needed
}

impl From<image_webp::Error> for WebpDecodingError {
    fn from(err: image_webp::Error) -> Self {
        WebpDecodingError::ImageError(err)
    }
}

Comment on lines -38 to -39
fn test_animated_webp_decode() -> ril::Result<()> {
for (frame, ref color) in ImageSequence::<Rgb>::open("tests/animated_sample.webp")?.zip(COLORS)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Improved test specificity for lossless WebP

The new test is more specific and checks against reference images, which is a good improvement. However, consider adding assertions for frame count and dimensions to ensure the overall structure of the animated WebP is correct.

Suggested change
fn test_animated_webp_decode() -> ril::Result<()> {
for (frame, ref color) in ImageSequence::<Rgb>::open("tests/animated_sample.webp")?.zip(COLORS)
fn test_animated_webp_lossless() -> ril::Result<()> {
let sequence = ImageSequence::<Rgb>::open("tests/animated_lossless.webp")?;
let frame_count = sequence.len();
assert_eq!(frame_count, 3, "Expected 3 frames in the animated WebP");
let first_frame = sequence.get(0)?.into_image();
assert_eq!(first_frame.width(), 400, "Unexpected frame width");
assert_eq!(first_frame.height(), 400, "Unexpected frame height");

Comment on lines +8 to +11
fn test_static_webp_encode() -> ril::Result<()> {
let image = Image::from_fn(256, 256, |x, _| L(x as u8));

image.save_inferred("tests/out/webp_pure_encode_output.webp")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): Static WebP encoding test lacks assertions

The test encodes an image but doesn't verify the result. Consider adding assertions to check the encoded file's properties or contents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant