Skip to content

Add support for image and GIF rendering #85

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

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

Conversation

weberval
Copy link

@weberval weberval commented Jul 19, 2025

Summary by Sourcery

Implement image and animated GIF support by extending content variants, processing image/GIF frames into the payload buffer, rename payload generator function, and enforce display dimensions via constants

New Features:

  • Add support for rendering static images via an ImageFile content variant
  • Add support for rendering animated GIFs via a GifFile content variant

Enhancements:

  • Rename and correct typo in payload generation function from gnerate_payload to generate_payload
  • Introduce DISPLAY_HEIGHT and DISPLAY_WIDTH constants and apply them for bitstring and image dimension checks

Build:

  • Add image crate dependency

Copy link

sourcery-ai bot commented Jul 19, 2025

Reviewer's Guide

This PR expands payload generation to support embedding static images and animated GIFs by leveraging the image crate, refactors the payload generator (typo fix and dimension constants), and adds the image dependency.

Entity relationship diagram for Content enum variants

erDiagram
    CONTENT {
        STRING Bitstring
        STRING BitmapBase64
        PATH BitmapFile
        PATH ImageFile
        PATH GifFile
    }
    CONTENT ||--o{ IMAGEFILE : contains
    CONTENT ||--o{ GIFFILE : contains
Loading

Class diagram for updated Content enum and payload generation

classDiagram
    class Content {
        <<enum>>
        Bitstring
        BitmapBase64
        BitmapFile
        ImageFile
        GifFile
    }
    class Args
    class PayloadBuffer
    class ImageReader
    class GifDecoder
    class Frame
    class Pixel
    class Point
    class BinaryColor

    Content <|-- ImageFile
    Content <|-- GifFile
    Args --> Content
    Args --> PayloadBuffer
    ImageReader --> ImageFile
    GifDecoder --> GifFile
    GifDecoder --> Frame
    Frame --> PayloadBuffer
    Pixel --> PayloadBuffer
    Point --> Pixel
    BinaryColor --> Pixel
Loading

Class diagram for generate_payload function changes

classDiagram
    class generate_payload {
        +DISPLAY_HEIGHT: u32
        +DISPLAY_WIDTH: u32
        +args: &mut Args
        +returns: Result<PayloadBuffer>
    }
    generate_payload --> Content
    generate_payload --> PayloadBuffer
    generate_payload --> ImageReader
    generate_payload --> GifDecoder
Loading

File-Level Changes

Change Details Files
Support embedding static images
  • Added ImageFile variant to Content enum
  • Decoded and resized images via ImageReader
  • Thresholded and drew image pixels into payload buffer
src/main.rs
Support embedding animated GIFs
  • Added GifFile variant to Content enum
  • Decoded and collected GIF frames with GifDecoder
  • Validated frame dimensions and concatenated frames into the payload
src/main.rs
Refactor and typo fix in payload generation
  • Renamed gnerate_payload to generate_payload across call sites
  • Introduced DISPLAY_WIDTH and DISPLAY_HEIGHT constants
  • Updated bitstring line-count validation to use constants
src/main.rs
Add image crate dependency
  • Added image = "0.25.6" to Cargo.toml
Cargo.toml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@weberval weberval changed the title Upstream/feat/image support feat: add image and animated gif support Jul 19, 2025
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 @weberval - I've reviewed your changes - here's some feedback:

  • The new image and GIF handling blocks share a lot of pixel‐drawing logic—consider extracting that into a helper function to avoid duplication.
  • Avoid hard‐coded dimensions and magic numbers (e.g. 44, 11, 48, 31)—pull them into named constants or make them configurable for clarity.
  • Replace the expect on GIF decoding with proper error propagation (using ? or with_context) to avoid unintended panics.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new image and GIF handling blocks share a lot of pixel‐drawing logic—consider extracting that into a helper function to avoid duplication.
- Avoid hard‐coded dimensions and magic numbers (e.g. 44, 11, 48, 31)—pull them into named constants or make them configurable for clarity.
- Replace the `expect` on GIF decoding with proper error propagation (using `?` or `with_context`) to avoid unintended panics.

## Individual Comments

### Comment 1
<location> `src/main.rs:231` </location>
<code_context>
                 let image = Image::new(&image_raw, Point::zero());
                 payload.add_message_drawable(style, &image);
             }
+            Content::ImageFile { img_file } => {
+                let img_reader = ImageReader::open(img_file)?;
+                let img = img_reader
+                    .decode()?
+                    .resize(u32::MAX, 11, FilterType::Nearest)
+                    .into_luma8();
+                let (width, height) = img.dimensions();
+                let mut buffer = payload.add_message(style, (width as usize + 7) / 8);
+                for y in 0..height {
+                    for x in 0..width {
+                        if img.get_pixel(x, y).0 > [31] {
+                            Pixel(Point::new(x.try_into()?, y.try_into()?), BinaryColor::On)
+                                .draw(&mut buffer)?;
+                        }
+                    }
+                }
</code_context>

<issue_to_address>
Potential edge case: image resizing may not preserve aspect ratio.

Resizing with `.resize(u32::MAX, 11, FilterType::Nearest)` may produce images with extremely large widths. Consider limiting the maximum width or enforcing an aspect ratio to avoid display issues with very wide images.
</issue_to_address>

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 and I'll use the feedback to improve your reviews.

@weberval
Copy link
Author

@sourcery-ai review

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 @weberval - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/main.rs:271` </location>
<code_context>
+                    );
+                }
+
+                let mut buffer = payload.add_message(
+                    style,
+                    ((DISPLAY_WIDTH as usize + 4) * frame_count).div_ceil(8),
+                );
+
+                for (i, frame) in frames.iter().enumerate() {
</code_context>

<issue_to_address>
Calculation for GIF buffer size may not be correct for all cases.

This calculation assumes a fixed 4-pixel gap between frames, which may not always apply. Please make the calculation adaptable or clearly document the assumptions.

Suggested implementation:

```rust
                // Define the gap between frames in pixels. Adjust as needed.
                const FRAME_GAP: usize = 4; // Assumes a 4-pixel gap between frames. Change if needed.

                let mut buffer = payload.add_message(
                    style,
                    ((DISPLAY_WIDTH as usize + FRAME_GAP) * frame_count).div_ceil(8),
                );

```

```rust
                    for y in 0..DISPLAY_HEIGHT {
                        for x in 0..DISPLAY_WIDTH {
                            if buf.get_pixel(x, y).to_luma().0 > [31] {
                                Pixel(
                                    Point::new(
                                        (x as usize + i * (DISPLAY_WIDTH as usize + FRAME_GAP))
                                            .try_into()?,
                                        y.try_into()?,
                                    ),

```
</issue_to_address>

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 and I'll use the feedback to improve your reviews.

"expected 11 lines in bitstring, found {} lines",
lines.len() == DISPLAY_HEIGHT as usize,
"expected {} lines in bitstring, found {} lines",
DISPLAY_HEIGHT,
lines.len()
);
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): Calculation for GIF buffer size may not be correct for all cases.

This calculation assumes a fixed 4-pixel gap between frames, which may not always apply. Please make the calculation adaptable or clearly document the assumptions.

Suggested implementation:

                // Define the gap between frames in pixels. Adjust as needed.
                const FRAME_GAP: usize = 4; // Assumes a 4-pixel gap between frames. Change if needed.

                let mut buffer = payload.add_message(
                    style,
                    ((DISPLAY_WIDTH as usize + FRAME_GAP) * frame_count).div_ceil(8),
                );
                    for y in 0..DISPLAY_HEIGHT {
                        for x in 0..DISPLAY_WIDTH {
                            if buf.get_pixel(x, y).to_luma().0 > [31] {
                                Pixel(
                                    Point::new(
                                        (x as usize + i * (DISPLAY_WIDTH as usize + FRAME_GAP))
                                            .try_into()?,
                                        y.try_into()?,
                                    ),

@weberval weberval changed the title feat: add image and animated gif support feat: add image and animated gif support @sourcery-ai Jul 20, 2025
@sourcery-ai sourcery-ai bot changed the title feat: add image and animated gif support @sourcery-ai Add support for image and GIF rendering Jul 20, 2025
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