Repository: iced-rs/iced (language: rust)

Issue:
Image escaping parent bounds when ContentFIt is Cover
### Is your issue REALLY a bug?

- [X] My issue is indeed a bug!
- [X] I am not crazy! I will not fill out this form just to ask a question or request a feature. Pinky promise.

### Is there an existing issue for this?

- [X] I have searched the existing issues.

### Is this issue related to iced?

- [X] My hardware is compatible and my graphics drivers are up-to-date.

### What happened?

When wrapping a series of images in a scrollable(container(image)), if the image has ContentFit Cover and both Width and Height set to Fill it escapes the scrollable bounds, rendering it outside of it.


https://github.com/user-attachments/assets/bc2dd853-0dea-4dfc-a542-0ff2d14addc6

This example is running under Linux (Wayland, AMD) but the same happens on Windows.

[SSCCE](https://github.com/Stay1444/iced-image-overflow-sscce/)


### What is the expected behavior?

The images should not overflow the scrollable area and render on top of the TOP BAR

### Version

master

### Operating System

Linux

### Do you have any log output?

```shell
2024-11-10T21:20:15.879368Z  INFO wgpu_hal::vulkan::instance: Debug utils not enabled: debug_utils_user_data not passed to Instance::from_raw    
2024-11-10T21:20:15.885882Z  INFO wgpu_hal::gles::egl: Using Wayland platform    
2024-11-10T21:20:15.921017Z  INFO iced_wgpu::window::compositor: Settings {
    present_mode: AutoVsync,
    backends: Backends(
        VULKAN | GL | METAL | DX12 | BROWSER_WEBGPU,
    ),
    default_font: Font {
        family: SansSerif,
        weight: Normal,
        stretch: Normal,
        style: Normal,
    },
    default_text_size: Pixels(
        16.0,
    ),
    antialiasing: None,
}    
2024-11-10T21:20:15.931879Z  INFO wgpu_core::instance: Adapter AdapterInfo { name: "AMD Radeon RX 6600 XT (RADV NAVI23)", vendor: 4098, device: 29695, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 24.2.6-arch1.1", backend: Vulkan }    
2024-11-10T21:20:15.933769Z  INFO wgpu_core::instance: Adapter AdapterInfo { name: "AMD Radeon RX 6600 XT (radeonsi, navi23, LLVM 18.1.8, DRM 3.59, 6.11.6-zen1-1-zen)", vendor: 4098, device: 0, device_type: Other, driver: "", driver_info: "4.6 (Core Pr
ofile) Mesa 24.2.6-arch1.1", backend: Gl }    
2024-11-10T21:20:15.933885Z  INFO iced_wgpu::window::compositor: Available adapters: [
    AdapterInfo {
        name: "AMD Radeon RX 6600 XT (RADV NAVI23)",
        vendor: 4098,
        device: 29695,
        device_type: DiscreteGpu,
        driver: "radv",
        driver_info: "Mesa 24.2.6-arch1.1",
        backend: Vulkan,
    },
    AdapterInfo {
        name: "AMD Radeon RX 6600 XT (radeonsi, navi23, LLVM 18.1.8, DRM 3.59, 6.11.6-zen1-1-zen)",
        vendor: 4098,
        device: 0,
        device_type: Other,
        driver: "",
        driver_info: "4.6 (Core Profile) Mesa 24.2.6-arch1.1",
        backend: Gl,
    },
]    
2024-11-10T21:20:15.934015Z  WARN wgpu_hal::gles::egl: Re-initializing Gles context due to Wayland window    
2024-11-10T21:20:15.964327Z  INFO wgpu_core::instance: Adapter AdapterInfo { name: "AMD Radeon RX 6600 XT (RADV NAVI23)", vendor: 4098, device: 29695, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 24.2.6-arch1.1", backend: Vulkan }    
2024-11-10T21:20:15.964445Z  INFO iced_wgpu::window::compositor: Selected: AdapterInfo {
    name: "AMD Radeon RX 6600 XT (RADV NAVI23)",
    vendor: 4098,
    device: 29695,
    device_type: DiscreteGpu,
    driver: "radv",
    driver_info: "Mesa 24.2.6-arch1.1",
    backend: Vulkan,
}    
2024-11-10T21:20:15.965690Z  INFO iced_wgpu::window::compositor: Available formats: Copied {
    it: Iter(
        [
            Bgra8UnormSrgb,
            Rgba8UnormSrgb,
            Bgra8Unorm,
            Rgba8Unorm,
        ],
    ),
}    
2024-11-10T21:20:15.966018Z  INFO iced_wgpu::window::compositor: Available alpha modes: [
    Opaque,
    PreMultiplied,
]    
2024-11-10T21:20:15.966081Z  INFO iced_wgpu::window::compositor: Selected format: Bgra8UnormSrgb with alpha mode: PreMultiplied    
2024-11-10T21:20:16.035760Z  INFO iced_winit::program: Window attributes for id `Id(
    1,
)`: WindowAttributes {
    inner_size: Some(
        Logical(
            LogicalSize {
                width: 1024.0,
                height: 768.0,
            },
        ),
    ),
    min_inner_size: None,
    max_inner_size: None,
    position: None,
    resizable: true,
    enabled_buttons: WindowButtons(
        CLOSE | MINIMIZE | MAXIMIZE,
    ),
    title: "Iced Image Overflow",
    maximized: false,
    visible: false,
    transparent: false,
    blur: false,
    decorations: true,
    window_icon: None,
    preferred_theme: None,
    resize_increments: None,
    content_protected: false,
    window_level: Normal,
    active: true,
    cursor: Icon(
        Default,
    ),
    parent_window: None,
    fullscreen: None,
    platform_specific: PlatformSpecificWindowAttributes {
        name: Some(
            ApplicationName {
                general: "",
                instance: "",
            },
        ),
        activation_token: None,
        x11: X11WindowAttributes {
            visual_id: None,
            screen_id: None,
            base_size: None,
            override_redirect: false,
            x11_window_types: [
                Normal,
            ],
            embed_window: None,
        },
    },
}
```



Discussion / hints:
Have same problem with scrollable list of images. Image bleeds over into the above rows and does not disappear until the bottom of image reaches the top of the scroll. Circumvention? or is it being addressed?
Problem seems to be caused by an incorrect `scissor_rect` that is passed into `image_pipeline::render`. Scissor rect is transformed by scrolling. At least that's how far I was able to dig as of now.

Fix patch:
diff --git a/widget/src/image.rs b/widget/src/image.rs
index c8f2a620f6..f5a9c7f385 100644
--- a/widget/src/image.rs
+++ b/widget/src/image.rs
@@ -167,6 +167,7 @@ where
 pub fn draw<Renderer, Handle>(
     renderer: &mut Renderer,
     layout: Layout<'_>,
+    viewport: &Rectangle,
     handle: &Handle,
     content_fit: ContentFit,
     filter_method: FilterMethod,
@@ -218,7 +219,9 @@ pub fn draw<Renderer, Handle>(
 
     if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height
     {
-        renderer.with_layer(bounds, render);
+        if let Some(bounds) = bounds.intersection(viewport) {
+            renderer.with_layer(bounds, render);
+        }
     } else {
         render(renderer);
     }
@@ -262,11 +265,12 @@ where
         _style: &renderer::Style,
         layout: Layout<'_>,
         _cursor: mouse::Cursor,
-        _viewport: &Rectangle,
+        viewport: &Rectangle,
     ) {
         draw(
             renderer,
             layout,
+            viewport,
             &self.handle,
             self.content_fit,
             self.filter_method,


Test patch: