- 
                Notifications
    You must be signed in to change notification settings 
- Fork 10
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
base: main
Are you sure you want to change the base?
Add support for image and GIF rendering #85
Conversation
| Reviewer's GuideThis 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 variantserDiagram
    CONTENT {
        STRING Bitstring
        STRING BitmapBase64
        PATH BitmapFile
        PATH ImageFile
        PATH GifFile
    }
    CONTENT ||--o{ IMAGEFILE : contains
    CONTENT ||--o{ GIFFILE : contains
Class diagram for updated Content enum and payload generationclassDiagram
    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
Class diagram for generate_payload function changesclassDiagram
    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
File-Level Changes
 Tips and commandsInteracting with Sourcery
 Customizing Your ExperienceAccess your dashboard to: 
 Getting Help
 | 
There was a problem hiding this 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 expecton GIF decoding with proper error propagation (using?orwith_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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @sourcery-ai review | 
There was a problem hiding this 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| "expected {} lines in bitstring, found {} lines", | ||
| DISPLAY_HEIGHT, | ||
| lines.len() | ||
| ); | 
There was a problem hiding this comment.
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()?,
                                    ),
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:
Enhancements:
Build: