Rust中显示动态生成ImageBuffer的最优方案及FLTK适配问题
在Rust中显示动态生成图像的方案
你手里的ImageBuffer<Rgb<u8>, Vec<u8>>是image库的标准动态图像类型,要在GUI里显示它,核心是让UI库能识别这个格式的像素数据。下面分两种场景给出具体方案:
方案一:继续使用fltk库
fltk的set_image要求实现ImageExt trait,而image库的ImageBuffer并没有默认实现这个接口,你可以通过两种方式转换:
方法1:将ImageBuffer编码为PNG后加载
把图像编码成通用的PNG格式,再用fltk的PngImage读取显示,兼容性最好:
use fltk::{app::App, app::AppScheme, image::PngImage, window::Window}; use image::{ImageBuffer, Rgb}; fn main() { // 模拟动态生成的ImageBuffer let image: ImageBuffer<Rgb<u8>, Vec<u8>> = ImageBuffer::from_fn(400, 300, |x, y| { if x < y { Rgb([255, 0, 0]) } else { Rgb([0, 255, 0]) } }); // 编码为PNG字节 let mut png_bytes = Vec::new(); image.write_to(&mut png_bytes, image::ImageOutputFormat::Png) .expect("编码PNG失败"); let app = App::default().with_scheme(AppScheme::Gleam); let mut wind = Window::new(100, 100, 400, 300, "Rust动态图像"); // 从PNG数据创建fltk图像 let fltk_image = PngImage::from_data(&png_bytes) .expect("创建PNG图像失败"); wind.set_image(Some(fltk_image)); wind.end(); wind.show(); app.run().unwrap(); }
方法2:直接转换为fltk的RgbImage
跳过编码步骤,直接把ImageBuffer的原始像素数据传给fltk的RgbImage,性能更好:
use fltk::{app::App, app::AppScheme, image::RgbImage, window::Window}; use image::{ImageBuffer, Rgb}; fn main() { let image: ImageBuffer<Rgb<u8>, Vec<u8>> = ImageBuffer::from_fn(400, 300, |x, y| { if x < y { Rgb([255, 0, 0]) } else { Rgb([0, 255, 0]) } }); let width = image.width() as i32; let height = image.height() as i32; // 获取原始RGB像素切片 let pixels = image.as_raw(); let app = App::default().with_scheme(AppScheme::Gleam); let mut wind = Window::new(100, 100, width, height, "Rust动态图像"); // 创建RgbImage,参数依次是像素数据、宽、高、每个像素的字节数(RGB是3) let fltk_image = RgbImage::new(pixels, width, height, 3) .expect("创建RGB图像失败"); wind.set_image(Some(fltk_image)); wind.end(); wind.show(); app.run().unwrap(); }
方案二:更换更兼容的UI库
如果觉得fltk的转换麻烦,可以试试对image库支持更好的UI库:
egui
egui和image库无缝兼容,直接调用from_image_buffer就能显示:
use eframe::egui; use image::{ImageBuffer, Rgb}; fn main() { let options = eframe::NativeOptions::default(); eframe::run_native("动态图像演示", options, Box::new(|_cc| Box::new(MyApp { image: ImageBuffer::from_fn(400, 300, |x, y| { if x < y { Rgb([255, 0, 0]) } else { Rgb([0, 255, 0]) } }) }))).unwrap(); } struct MyApp { image: ImageBuffer<Rgb<u8>, Vec<u8>>, } impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { let egui_image = egui::Image::from_image_buffer(&self.image) .expect("转换图像失败"); ui.image(egui_image); }); } }
gtk-rs
通过GdkPixbuf转换后显示,适合GNOME生态的应用:
use gtk::prelude::*; use image::{ImageBuffer, Rgb}; use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { gtk::init()?; let image: ImageBuffer<Rgb<u8>, Vec<u8>> = ImageBuffer::from_fn(400, 300, |x, y| { if x < y { Rgb([255, 0, 0]) } else { Rgb([0, 255, 0]) } }); let width = image.width() as i32; let height = image.height() as i32; let pixels = image.as_raw(); // 创建Pixbuf对象 let pixbuf = gdk_pixbuf::Pixbuf::new_from_bytes( &glib::Bytes::from(pixels), gdk_pixbuf::Colorspace::Rgb, false, 8, width, height, width * 3, // 每行字节数 )?; let window = gtk::Window::new(gtk::WindowType::Toplevel); window.set_title("动态图像"); window.set_default_size(width, height); let image_widget = gtk::Image::from_pixbuf(Some(&pixbuf)); window.add(&image_widget); window.connect_destroy(|_| gtk::main_quit()); window.show_all(); gtk::main(); Ok(()) }
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

