Is your issue REALLY a bug?
Is there an existing issue for this?
Is this issue related to iced?
What happened?
I draw an image from a source of arbitrary size and pixel source into a Canvas which may have a different size. The Canvas uses a Cache so the actual drawing code is in the Cache's closure parameter.
On updates, the image is flickering and for prolonged times replaced with white content. This is the more severe the larger the original image is - the size of the Canvas does not really matter.
The attached demo program shows the problem on my platform (Linux, X11, Ubuntu 24.04, Rust 1.92). Start it with the side length of a blue image rectangle and resize the screen. With a size length of 100 ("cargo run 100") rescaling is smooth. From size ~700 onward, the redrawing becomes more and more flickerish.
// Minimal demo program for the Iced 0.14 flicker behaviour
// Situation is: An image is generated from a pixel source and then drawn in a Canvas
// The Canvas uses a Cache internally so the actual drawing code resides in the closure parameter of the Cache.
// The drawn image is emulated by a blue square, in practice this could be a way more complex image.
// Use: "cargo run <pixel size>" and then rescale the frame
// "cargo run 100" → resizing is smooth, blue rectangle is shown all time
// "cargo run 900" → resizing becomes sluggish, white images appear during resizing
// "cargo run 2000" → slugginess increases vastly
// Cargo.toml:
/*
# Minimum Cargo file
[package]
name = "iced-canvas-flicker-repro"
version = "0.1.0"
edition = "2021"
[dependencies]
iced = { version = "0.14.0", features = ["image", "canvas"] } # GUI library
*/
use iced::widget::canvas::{self, Cache, Geometry};
use iced::widget::{canvas::Canvas, container};
use iced::{Element, Length, Rectangle, Size, Task, Theme};
use std::env;
fn main() -> iced::Result {
// Get image size from command line argument
let args: Vec<String> = env::args().collect();
let size = if args.len() > 1 {
args[1].parse::<u32>().unwrap_or(900)
} else {
900
};
println!("Creating {}x{} pixel image", size, size);
println!("Try resizing the window to see the issue");
iced::application(
move || AppState::new(size),
AppState::update,
AppState::view,
)
.title("Canvas Flicker Reproduction")
.run()
}
struct AppState {
image_size: u32,
}
impl AppState {
fn new(size: u32) -> (Self, Task<Message>) {
(Self { image_size: size }, Task::none())
}
fn update(&mut self, _message: Message) -> Task<Message> {
Task::none()
}
fn view(&self) -> Element<'_, Message> {
let canvas = Canvas::new(BlueCanvas {
image_size: self.image_size,
})
.width(Length::Fill)
.height(Length::Fill);
container(canvas)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
#[derive(Debug, Clone)]
enum Message {}
struct BlueCanvas {
image_size: u32,
}
impl canvas::Program<Message> for BlueCanvas {
type State = CanvasState;
fn draw(
&self,
state: &Self::State,
renderer: &iced::Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: iced::mouse::Cursor,
) -> Vec<Geometry> {
let start = std::time::Instant::now();
let geometry = state.cache.draw(renderer, bounds.size(), |frame| {
println!(
"[DRAW] Cache rebuild starting for {}x{}",
self.image_size, self.image_size
);
// Create blue pixels
let pixel_count = (self.image_size * self.image_size) as usize;
let mut pixels = vec![0u8; pixel_count * 4]; // RGBA
for i in 0..pixel_count {
pixels[i * 4] = 0; // R
pixels[i * 4 + 1] = 0; // G
pixels[i * 4 + 2] = 255; // B (blue)
pixels[i * 4 + 3] = 255; // A (opaque)
}
// Create image handle
let image = canvas::Image::new(iced::widget::image::Handle::from_rgba(
self.image_size,
self.image_size,
pixels,
))
.filter_method(iced::widget::image::FilterMethod::Linear);
// Draw image to fill the frame
let image_size = Size::new(self.image_size as f32, self.image_size as f32);
let frame_size = frame.size();
// Scale to fit
let scale =
(frame_size.width / image_size.width).min(frame_size.height / image_size.height);
let scaled_size = Size::new(image_size.width * scale, image_size.height * scale);
let position = iced::Point::new(
(frame_size.width - scaled_size.width) / 2.0,
(frame_size.height - scaled_size.height) / 2.0,
);
let bounds = Rectangle::new(position, scaled_size);
frame.draw_image(bounds, image);
println!("[DRAW] Cache rebuild finished in {:?}", start.elapsed());
});
println!("[DRAW] Total draw call took {:?}", start.elapsed());
vec![geometry]
}
}
struct CanvasState {
cache: Cache,
}
impl Default for CanvasState {
fn default() -> Self {
Self {
cache: Cache::new(),
}
}
}
What is the expected behavior?
No flickering during redraw for any image size as it was in 0.13.1.
Version
crates.io release
Operating System
Linux
Do you have any log output?
Is your issue REALLY a bug?
Is there an existing issue for this?
Is this issue related to iced?
What happened?
I draw an image from a source of arbitrary size and pixel source into a Canvas which may have a different size. The Canvas uses a Cache so the actual drawing code is in the Cache's closure parameter.
On updates, the image is flickering and for prolonged times replaced with white content. This is the more severe the larger the original image is - the size of the Canvas does not really matter.
The attached demo program shows the problem on my platform (Linux, X11, Ubuntu 24.04, Rust 1.92). Start it with the side length of a blue image rectangle and resize the screen. With a size length of 100 ("cargo run 100") rescaling is smooth. From size ~700 onward, the redrawing becomes more and more flickerish.
What is the expected behavior?
No flickering during redraw for any image size as it was in 0.13.1.
Version
crates.io release
Operating System
Linux
Do you have any log output?