Working fast minifb
This commit is contained in:
parent
dd982a9f42
commit
1b75a04ab2
818
Cargo.lock
generated
818
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -4,17 +4,33 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
env_logger = "0.11.7"
|
||||||
iced = { version = "0.13.1", features = ["debug", "canvas", "tokio", "image"]}
|
iced = { version = "0.13.1", features = ["debug", "canvas", "tokio", "image"]}
|
||||||
image = "0.25.6"
|
image = "0.25.6"
|
||||||
|
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
memmap2 = "0.9.5"
|
memmap2 = "0.9.5"
|
||||||
|
minifb = "0.28.0"
|
||||||
|
pollster = "0.4.0"
|
||||||
# rustc-hash.workspace = true
|
# rustc-hash.workspace = true
|
||||||
tokio = { version = "1.44.1", features = ["sync"] }
|
tokio = { version = "1.44.1", features = ["sync"] }
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
wgpu = "24.0.3"
|
||||||
|
winit = "0.30.9"
|
||||||
zune-image = {version = "0.4.15", features = ["all"]}
|
zune-image = {version = "0.4.15", features = ["all"]}
|
||||||
|
|
||||||
[profile.dev.package.zune-jpeg]
|
[profile.dev.package.zune-jpeg]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = false
|
||||||
|
debug-assertions = false
|
||||||
|
overflow-checks = false
|
||||||
|
lto = false
|
||||||
|
panic = 'unwind'
|
||||||
|
incremental = false
|
||||||
|
codegen-units = 32
|
||||||
|
rpath = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
@ -1,17 +1,44 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use criterion::BenchmarkId;
|
use criterion::BenchmarkId;
|
||||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
use imflow::image::{Approach, load_thumbnail};
|
use imflow::image::{load_available_images, load_image_argb, load_image_argb_imagers, load_thumbnail, Approach};
|
||||||
const PATH: &str = "test_images/20240811-194516_DSC02274.JPG";
|
const PATH: &str = "test_images";
|
||||||
|
|
||||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("from_elem");
|
let mut group = c.benchmark_group("image_decode");
|
||||||
group.bench_function("mmap", |b| b.iter(|| load_thumbnail(PATH, Approach::Mmap)));
|
|
||||||
group.bench_function("path", |b| b.iter(|| load_thumbnail(PATH, Approach::Path)));
|
group
|
||||||
group.bench_function("iced", |b| b.iter(|| load_thumbnail(PATH, Approach::Iced)));
|
.sample_size(10) // Reduce number of samples (default is 100)
|
||||||
|
.measurement_time(Duration::from_millis(500)) // Reduce measurement time (default is 5 seconds)
|
||||||
|
.warm_up_time(Duration::from_millis(200)); // Reduce warm-up time (default is 3 seconds)
|
||||||
|
|
||||||
|
let images = load_available_images(PATH.into());
|
||||||
|
for image in images.iter() {
|
||||||
|
let image_name = image.to_str().unwrap();
|
||||||
|
|
||||||
|
// Benchmark zune for this image
|
||||||
|
group.bench_with_input(
|
||||||
|
format!("{}/zune", image_name),
|
||||||
|
image,
|
||||||
|
|b, image| {
|
||||||
|
b.iter(|| load_image_argb(image.clone().into()));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Benchmark image-rs for the same image
|
||||||
|
group.bench_with_input(
|
||||||
|
format!("{}/image-rs", image_name),
|
||||||
|
image,
|
||||||
|
|b, image| {
|
||||||
|
b.iter(|| load_image_argb_imagers(image.clone().into()));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
201
src/image.rs
201
src/image.rs
@ -1,13 +1,28 @@
|
|||||||
|
use iced::widget::image::Handle;
|
||||||
|
use iced::widget::image::Image as IcedImage;
|
||||||
|
use image::DynamicImage;
|
||||||
|
use image::ImageReader;
|
||||||
|
use memmap2::Mmap;
|
||||||
use zune_image::codecs::qoi::zune_core::options::DecoderOptions;
|
use zune_image::codecs::qoi::zune_core::options::DecoderOptions;
|
||||||
|
use zune_image::image::Image as ZuneImage;
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
pub enum Approach {
|
pub enum Approach {
|
||||||
Mmap,
|
Mmap,
|
||||||
Path,
|
Path,
|
||||||
|
ImageRs,
|
||||||
Iced,
|
Iced,
|
||||||
|
ImageRsPath,
|
||||||
}
|
}
|
||||||
fn convert_rgb_to_rgba(rgb_data: Vec<Vec<u8>>) -> Vec<u8> {
|
|
||||||
|
pub fn convert_zune_rgb_to_rgba(rgb_data: Vec<Vec<u8>>) -> Vec<u8> {
|
||||||
let r_channel = &rgb_data[0];
|
let r_channel = &rgb_data[0];
|
||||||
|
|
||||||
let num_pixels = r_channel.len() / 3;
|
let num_pixels = r_channel.len() / 3;
|
||||||
@ -22,56 +37,206 @@ fn convert_rgb_to_rgba(rgb_data: Vec<Vec<u8>>) -> Vec<u8> {
|
|||||||
|
|
||||||
rgba_data
|
rgba_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map_file(path: &str) -> io::Result<Mmap> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
unsafe { Mmap::map(&file) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_file_path(path: PathBuf) -> io::Result<Mmap> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
unsafe { Mmap::map(&file) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_zune_image(mmap: &[u8]) -> Result<ZuneImage, String> {
|
||||||
|
ZuneImage::read(mmap, DecoderOptions::new_fast()).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_zune_image_path(path: &str) -> ZuneImage {
|
||||||
|
zune_image::image::Image::open_with_options(path, DecoderOptions::new_fast()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flatten_zune_image(img: &ZuneImage) -> Vec<Vec<u8>> {
|
||||||
|
img.flatten_to_u8()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flatten_image_image(img: DynamicImage) -> Vec<u8> {
|
||||||
|
img.into_rgba8().into_raw()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_iced_handle(width: u32, height: u32, rgba: Vec<u8>) -> Handle {
|
||||||
|
Handle::from_rgba(width, height, rgba)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_thumbnail(
|
pub fn load_thumbnail(
|
||||||
path: &str,
|
path: &str,
|
||||||
approach: Approach,
|
approach: Approach,
|
||||||
) -> Result<iced::widget::image::Handle, String> {
|
) -> Result<iced::widget::image::Handle, String> {
|
||||||
match approach {
|
match approach {
|
||||||
Approach::Mmap => {
|
Approach::Mmap => {
|
||||||
let file = File::open(path).map_err(|e| e.to_string())?;
|
let mmap = map_file(path).unwrap();
|
||||||
let mmap = unsafe { memmap2::Mmap::map(&file) }.map_err(|e| e.to_string())?;
|
|
||||||
println!("mapped file");
|
println!("mapped file");
|
||||||
let img = zune_image::image::Image::read(&*mmap, DecoderOptions::default()).unwrap();
|
let img = read_zune_image(mmap.deref())?;
|
||||||
let width = img.dimensions().0 as u32;
|
let width = img.dimensions().0 as u32;
|
||||||
let height = img.dimensions().1 as u32;
|
let height = img.dimensions().1 as u32;
|
||||||
println!("loaded");
|
println!("loaded");
|
||||||
let flat = img.flatten_to_u8();
|
let flat = flatten_zune_image(&img);
|
||||||
println!("flattened");
|
println!("flattened");
|
||||||
let rgba = convert_rgb_to_rgba(flat);
|
let rgba = convert_zune_rgb_to_rgba(flat);
|
||||||
println!("rgbad");
|
println!("rgbad");
|
||||||
let conv = iced::widget::image::Handle::from_rgba(width, height, rgba);
|
let conv = create_iced_handle(width, height, rgba);
|
||||||
println!("iced");
|
println!("iced");
|
||||||
|
|
||||||
Ok(conv)
|
Ok(conv)
|
||||||
}
|
}
|
||||||
Approach::Path => {
|
Approach::Path => {
|
||||||
let img = zune_image::image::Image::open_with_options(path, DecoderOptions::default())
|
let img = read_zune_image_path(path);
|
||||||
.unwrap();
|
|
||||||
let width = img.dimensions().0 as u32;
|
let width = img.dimensions().0 as u32;
|
||||||
let height = img.dimensions().1 as u32;
|
let height = img.dimensions().1 as u32;
|
||||||
println!("loaded");
|
println!("loaded");
|
||||||
let flat = img.flatten_to_u8();
|
let flat = flatten_zune_image(&img);
|
||||||
println!("flattened");
|
println!("flattened");
|
||||||
let rgba = convert_rgb_to_rgba(flat);
|
let rgba = convert_zune_rgb_to_rgba(flat);
|
||||||
println!("rgbad");
|
println!("rgbad");
|
||||||
let conv = iced::widget::image::Handle::from_rgba(width, height, rgba);
|
let conv = create_iced_handle(width, height, rgba);
|
||||||
println!("iced");
|
println!("iced");
|
||||||
|
|
||||||
Ok(conv)
|
Ok(conv)
|
||||||
}
|
}
|
||||||
Approach::Iced => {
|
Approach::ImageRs => {
|
||||||
let file = File::open(path).map_err(|e| e.to_string())?;
|
let mmap = map_file(path).unwrap();
|
||||||
let mmap = unsafe { memmap2::Mmap::map(&file) }.map_err(|e| e.to_string())?;
|
let img = image::load_from_memory(mmap.deref()).map_err(|e| e.to_string())?;
|
||||||
let img = image::load_from_memory(&mmap).map_err(|e| e.to_string())?;
|
|
||||||
let width = img.width();
|
let width = img.width();
|
||||||
let height = img.height();
|
let height = img.height();
|
||||||
println!("loaded");
|
println!("loaded");
|
||||||
let rgba = img.into_rgba8().into_raw();
|
let rgba = flatten_image_image(img);
|
||||||
println!("rgbad");
|
println!("rgbad");
|
||||||
let conv = iced::widget::image::Handle::from_rgba(width, height, rgba);
|
let conv = create_iced_handle(width, height, rgba);
|
||||||
println!("iced");
|
println!("iced");
|
||||||
|
|
||||||
Ok(conv)
|
Ok(conv)
|
||||||
}
|
}
|
||||||
|
Approach::ImageRsPath => {
|
||||||
|
let img = ImageReader::open(path).unwrap().decode().unwrap();
|
||||||
|
let width = img.width();
|
||||||
|
let height = img.height();
|
||||||
|
println!("loaded");
|
||||||
|
let rgba = flatten_image_image(img);
|
||||||
|
println!("rgbad");
|
||||||
|
let conv = create_iced_handle(width, height, rgba);
|
||||||
|
println!("iced");
|
||||||
|
|
||||||
|
Ok(conv)
|
||||||
|
}
|
||||||
|
Approach::Iced => Ok(Handle::from_path(path)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_image_argb(path: PathBuf) -> ImflowImageBuffer {
|
||||||
|
let total_start = Instant::now();
|
||||||
|
|
||||||
|
// Stage 1: Memory map the file
|
||||||
|
let stage1_start = Instant::now();
|
||||||
|
let mmap = map_file_path(path).unwrap();
|
||||||
|
let stage1_time = stage1_start.elapsed();
|
||||||
|
println!("File mapping took: {:?}", stage1_time);
|
||||||
|
|
||||||
|
// Stage 2: Read the image
|
||||||
|
let stage2_start = Instant::now();
|
||||||
|
let img = read_zune_image(mmap.deref()).unwrap();
|
||||||
|
let width = img.dimensions().0;
|
||||||
|
let height = img.dimensions().1;
|
||||||
|
let stage2_time = stage2_start.elapsed();
|
||||||
|
println!("Image decoding took: {:?}", stage2_time);
|
||||||
|
|
||||||
|
// Stage 3: Flatten the image
|
||||||
|
let stage3_start = Instant::now();
|
||||||
|
let flat = &mut flatten_zune_image(&img)[0];
|
||||||
|
let stage3_time = stage3_start.elapsed();
|
||||||
|
println!("Image flattening took: {:?}", stage3_time);
|
||||||
|
|
||||||
|
// Stage 4: Convert to ARGB format
|
||||||
|
let stage4_start = Instant::now();
|
||||||
|
let mut buffer: Vec<u32> = vec![0; width * height];
|
||||||
|
|
||||||
|
for (rgba, argb) in flat.chunks_mut(3).zip(buffer.iter_mut()) {
|
||||||
|
let r = rgba[0] as u32;
|
||||||
|
let g = rgba[1] as u32;
|
||||||
|
let b = rgba[2] as u32;
|
||||||
|
*argb = r << 16 | g << 8 | b;
|
||||||
|
}
|
||||||
|
let stage4_time = stage4_start.elapsed();
|
||||||
|
println!("RGBA to ARGB conversion took: {:?}", stage4_time);
|
||||||
|
|
||||||
|
// Total time
|
||||||
|
let total_time = total_start.elapsed();
|
||||||
|
println!("Total loading time: {:?}", total_time);
|
||||||
|
|
||||||
|
ImflowImageBuffer {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
argb_buffer: buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImflowImageBuffer {
|
||||||
|
pub width: usize,
|
||||||
|
pub height: usize,
|
||||||
|
pub argb_buffer: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_image_argb_imagers(path: PathBuf) -> ImflowImageBuffer {
|
||||||
|
let total_start = Instant::now();
|
||||||
|
|
||||||
|
// Stage 1: Memory map the file
|
||||||
|
let stage1_start = Instant::now();
|
||||||
|
let mmap = map_file_path(path).unwrap();
|
||||||
|
let stage1_time = stage1_start.elapsed();
|
||||||
|
println!("File mapping took: {:?}", stage1_time);
|
||||||
|
|
||||||
|
// Stage 2: Read the image
|
||||||
|
let stage2_start = Instant::now();
|
||||||
|
let img = image::load_from_memory(mmap.deref()).map_err(|e| e.to_string()).unwrap();
|
||||||
|
let width = img.width() as usize;
|
||||||
|
let height = img.height() as usize;
|
||||||
|
let stage2_time = stage2_start.elapsed();
|
||||||
|
println!("Image decoding took: {:?}", stage2_time);
|
||||||
|
|
||||||
|
// Stage 3: Flatten the image
|
||||||
|
let stage3_start = Instant::now();
|
||||||
|
let mut flat = img.into_rgba8().into_raw();
|
||||||
|
let stage3_time = stage3_start.elapsed();
|
||||||
|
println!("Image flattening took: {:?}", stage3_time);
|
||||||
|
|
||||||
|
// Stage 4: Convert to ARGB format
|
||||||
|
let stage4_start = Instant::now();
|
||||||
|
let mut buffer: Vec<u32> = vec![0; width * height];
|
||||||
|
|
||||||
|
for (rgba, argb) in flat.chunks_mut(4).zip(buffer.iter_mut()) {
|
||||||
|
let r = rgba[0] as u32;
|
||||||
|
let g = rgba[1] as u32;
|
||||||
|
let b = rgba[2] as u32;
|
||||||
|
*argb = r << 16 | g << 8 | b;
|
||||||
|
}
|
||||||
|
let stage4_time = stage4_start.elapsed();
|
||||||
|
println!("RGBA to ARGB conversion took: {:?}", stage4_time);
|
||||||
|
|
||||||
|
// Total time
|
||||||
|
let total_time = total_start.elapsed();
|
||||||
|
println!("Total loading time: {:?}", total_time);
|
||||||
|
|
||||||
|
ImflowImageBuffer {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
argb_buffer: buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn load_available_images(dir: PathBuf) -> Vec<PathBuf> {
|
||||||
|
let mut files: Vec<PathBuf> = fs::read_dir(dir)
|
||||||
|
.unwrap()
|
||||||
|
.map(|f| f.unwrap().path())
|
||||||
|
.collect();
|
||||||
|
files.sort();
|
||||||
|
files
|
||||||
|
}
|
||||||
|
|
||||||
|
396
src/main.rs
396
src/main.rs
@ -5,8 +5,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{self};
|
use std::fs::{self};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{self, Duration};
|
use std::time::{self, Duration, Instant};
|
||||||
|
|
||||||
use iced::futures::AsyncReadExt;
|
use iced::futures::AsyncReadExt;
|
||||||
use iced::widget::shader::wgpu::core::command::LoadOp;
|
use iced::widget::shader::wgpu::core::command::LoadOp;
|
||||||
@ -16,22 +17,391 @@ use iced::widget::{
|
|||||||
Column, Container, button, center, checkbox, column, container, pick_list, row, slider, text,
|
Column, Container, button, center, checkbox, column, container, pick_list, row, slider, text,
|
||||||
};
|
};
|
||||||
use iced::{Center, Element, Fill, Length, Size, Subscription, Task, Theme, keyboard};
|
use iced::{Center, Element, Fill, Length, Size, Subscription, Task, Theme, keyboard};
|
||||||
use image::{self, DynamicImage, EncodableLayout, ImageBuffer, ImageReader};
|
use image::{self, DynamicImage, EncodableLayout, GenericImageView, ImageBuffer, ImageReader};
|
||||||
|
|
||||||
use imflow::image::{Approach, load_thumbnail};
|
use imflow::image::{
|
||||||
|
flatten_image_image, flatten_zune_image, load_available_images, load_image_argb, load_image_argb_imagers, load_thumbnail, map_file, map_file_path, read_zune_image, Approach, ImflowImageBuffer
|
||||||
|
};
|
||||||
|
use minifb::{Key, Window, WindowOptions};
|
||||||
use zune_image::codecs::qoi::zune_core::options::DecoderOptions; // for general image operations
|
use zune_image::codecs::qoi::zune_core::options::DecoderOptions; // for general image operations
|
||||||
// use image::io::Reader as ImageReader; // specifically for Reader
|
// use image::io::Reader as ImageReader; // specifically for Reader
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
use std::sync::Arc;
|
||||||
tracing_subscriber::fmt::init();
|
|
||||||
|
|
||||||
iced::application("Game of Life - Iced", GameOfLife::update, GameOfLife::view)
|
// use winit::{
|
||||||
.subscription(GameOfLife::subscription)
|
// application::ApplicationHandler,
|
||||||
.theme(|_| Theme::Dark)
|
// event::WindowEvent,
|
||||||
.antialiasing(true)
|
// event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
|
||||||
.centered()
|
// window::{Window, WindowId},
|
||||||
.window_size(Size::new(1500.0, 1000.0))
|
// };
|
||||||
.run()
|
// struct State {
|
||||||
|
// window: Arc<Window>,
|
||||||
|
// device: wgpu::Device,
|
||||||
|
// queue: wgpu::Queue,
|
||||||
|
// size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
// surface: wgpu::Surface<'static>,
|
||||||
|
// surface_format: wgpu::TextureFormat,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl State {
|
||||||
|
// async fn new(window: Arc<Window>) -> State {
|
||||||
|
// let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
|
||||||
|
// let adapter = instance
|
||||||
|
// .request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let (device, queue) = adapter
|
||||||
|
// .request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let size = window.inner_size();
|
||||||
|
|
||||||
|
// let surface = instance.create_surface(window.clone()).unwrap();
|
||||||
|
// let cap = surface.get_capabilities(&adapter);
|
||||||
|
// let surface_format = cap.formats[0];
|
||||||
|
|
||||||
|
// let state = State {
|
||||||
|
// window,
|
||||||
|
// device,
|
||||||
|
// queue,
|
||||||
|
// size,
|
||||||
|
// surface,
|
||||||
|
// surface_format,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Configure surface for the first time
|
||||||
|
// state.configure_surface();
|
||||||
|
|
||||||
|
// state
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn get_window(&self) -> &Window {
|
||||||
|
// &self.window
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn configure_surface(&self) {
|
||||||
|
// let surface_config = wgpu::SurfaceConfiguration {
|
||||||
|
// usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
// format: self.surface_format,
|
||||||
|
// // Request compatibility with the sRGB-format texture view we‘re going to create later.
|
||||||
|
// view_formats: vec![self.surface_format.add_srgb_suffix()],
|
||||||
|
// alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
|
// width: self.size.width,
|
||||||
|
// height: self.size.height,
|
||||||
|
// desired_maximum_frame_latency: 2,
|
||||||
|
// present_mode: wgpu::PresentMode::AutoVsync,
|
||||||
|
// };
|
||||||
|
// self.surface.configure(&self.device, &surface_config);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||||
|
// self.size = new_size;
|
||||||
|
|
||||||
|
// // reconfigure the surface
|
||||||
|
// self.configure_surface();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // fn render(&mut self) {
|
||||||
|
// // // Create texture view
|
||||||
|
// // let surface_texture = self
|
||||||
|
// // .surface
|
||||||
|
// // .get_current_texture()
|
||||||
|
// // .expect("failed to acquire next swapchain texture");
|
||||||
|
// // let texture_view = surface_texture
|
||||||
|
// // .texture
|
||||||
|
// // .create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
// // // Without add_srgb_suffix() the image we will be working with
|
||||||
|
// // // might not be "gamma correct".
|
||||||
|
// // format: Some(self.surface_format.add_srgb_suffix()),
|
||||||
|
// // ..Default::default()
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// // // Renders a GREEN screen
|
||||||
|
// // let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||||
|
// // // Create the renderpass which will clear the screen.
|
||||||
|
// // let renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
// // label: None,
|
||||||
|
// // color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
// // view: &texture_view,
|
||||||
|
// // resolve_target: None,
|
||||||
|
// // ops: wgpu::Operations {
|
||||||
|
// // load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
|
||||||
|
// // store: wgpu::StoreOp::Store,
|
||||||
|
// // },
|
||||||
|
// // })],
|
||||||
|
// // depth_stencil_attachment: None,
|
||||||
|
// // timestamp_writes: None,
|
||||||
|
// // occlusion_query_set: None,
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// // // If you wanted to call any drawing commands, they would go here.
|
||||||
|
|
||||||
|
// // // End the renderpass.
|
||||||
|
// // drop(renderpass);
|
||||||
|
|
||||||
|
// // // Submit the command in the queue to execute
|
||||||
|
// // self.queue.submit([encoder.finish()]);
|
||||||
|
// // self.window.pre_present_notify();
|
||||||
|
// // surface_texture.present();
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// fn render(&mut self) {
|
||||||
|
// let mmap = map_file("test_images/20240811-194516_DSC02274.JPG").unwrap();
|
||||||
|
// println!("mapped file");
|
||||||
|
// let img = read_zune_image(mmap.deref()).unwrap();
|
||||||
|
// let width = img.dimensions().0 as u32;
|
||||||
|
// let height = img.dimensions().1 as u32;
|
||||||
|
// println!("loaded");
|
||||||
|
// let flat = flatten_zune_image(&img);
|
||||||
|
// println!("flattened");
|
||||||
|
|
||||||
|
// let rgb_bytes = flat[0].as_slice();
|
||||||
|
// // Assuming `self.rgb_bytes` is your buffer containing RGB data.
|
||||||
|
// let texture_extent = wgpu::Extent3d {
|
||||||
|
// width: width,
|
||||||
|
// height: height,
|
||||||
|
// depth_or_array_layers: 1,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Create a wgpu texture
|
||||||
|
// let texture = self.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
// label: Some("RGB Texture"),
|
||||||
|
// size: texture_extent,
|
||||||
|
// mip_level_count: 1,
|
||||||
|
// sample_count: 1,
|
||||||
|
// dimension: wgpu::TextureDimension::D2,
|
||||||
|
// format: wgpu::TextureFormat::Rgba8Unorm, // It's better to use RGBA with proper padding
|
||||||
|
// usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
// view_formats: &[],
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Upload your RGB data into the texture
|
||||||
|
// self.queue.write_texture(
|
||||||
|
// wgpu::TexelCopyTextureInfo{
|
||||||
|
// texture: &texture,
|
||||||
|
// mip_level: 0,
|
||||||
|
// origin: wgpu::Origin3d::ZERO,
|
||||||
|
// aspect: wgpu::TextureAspect::All,
|
||||||
|
// },
|
||||||
|
// &rgb_bytes,
|
||||||
|
// wgpu::TexelCopyBufferLayout {
|
||||||
|
// offset: 0,
|
||||||
|
// bytes_per_row: Some(4 * width), // Assuming padded row length
|
||||||
|
// rows_per_image: Some(height),
|
||||||
|
// },
|
||||||
|
// texture_extent,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Create a texture view
|
||||||
|
// let surface_texture = self
|
||||||
|
// .surface
|
||||||
|
// .get_current_texture()
|
||||||
|
// .expect("failed to acquire next swapchain texture");
|
||||||
|
|
||||||
|
// let texture_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
// format: Some(self.surface_format.add_srgb_suffix()),
|
||||||
|
// ..Default::default()
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let rgb_texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
// let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||||
|
|
||||||
|
// // Create the renderpass
|
||||||
|
// let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
// label: None,
|
||||||
|
// color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
// view: &texture_view,
|
||||||
|
// resolve_target: None,
|
||||||
|
// ops: wgpu::Operations {
|
||||||
|
// load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||||
|
// store: wgpu::StoreOp::Store,
|
||||||
|
// },
|
||||||
|
// })],
|
||||||
|
// depth_stencil_attachment: None,
|
||||||
|
// timestamp_writes: None,
|
||||||
|
// occlusion_query_set: None,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Bind and draw
|
||||||
|
// // renderpass.set_pipeline(&self.pipeline); // Ensure self.pipeline is your render pipeline setup
|
||||||
|
// // renderpass.set_bind_group(0, &self.texture_bind_group, &[]); // Assuming you have a bind group which holds the texture
|
||||||
|
// renderpass.draw(0..3, 0..1); // Draws a triangle to cover the viewport, modify as needed for quads
|
||||||
|
|
||||||
|
// // End the renderpass
|
||||||
|
// drop(renderpass);
|
||||||
|
|
||||||
|
// // Submit the command buffer
|
||||||
|
// // self.queue.submit(iter::once(encoder.finish()));
|
||||||
|
// self.window.pre_present_notify();
|
||||||
|
// surface_texture.present();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[derive(Default)]
|
||||||
|
// struct App {
|
||||||
|
// state: Option<State>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ApplicationHandler for App {
|
||||||
|
// fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
// // Create window object
|
||||||
|
// let window = Arc::new(
|
||||||
|
// event_loop
|
||||||
|
// .create_window(Window::default_attributes())
|
||||||
|
// .unwrap(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let state = pollster::block_on(State::new(window.clone()));
|
||||||
|
// self.state = Some(state);
|
||||||
|
|
||||||
|
// window.request_redraw();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
||||||
|
// let state = self.state.as_mut().unwrap();
|
||||||
|
// match event {
|
||||||
|
// WindowEvent::CloseRequested => {
|
||||||
|
// println!("The close button was pressed; stopping");
|
||||||
|
// event_loop.exit();
|
||||||
|
// }
|
||||||
|
// WindowEvent::RedrawRequested => {
|
||||||
|
// state.render();
|
||||||
|
// // Emits a new redraw requested event.
|
||||||
|
// state.get_window().request_redraw();
|
||||||
|
// }
|
||||||
|
// WindowEvent::Resized(size) => {
|
||||||
|
// // Reconfigures the size of the surface. We do not re-render
|
||||||
|
// // here as this event is always followed up by redraw request.
|
||||||
|
// state.resize(size);
|
||||||
|
// }
|
||||||
|
// _ => (),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// pub fn main() -> iced::Result {
|
||||||
|
// tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
// iced::application("Game of Life - Iced", GameOfLife::update, GameOfLife::view)
|
||||||
|
// .subscription(GameOfLife::subscription)
|
||||||
|
// .theme(|_| Theme::Dark)
|
||||||
|
// .antialiasing(true)
|
||||||
|
// .centered()
|
||||||
|
// .window_size(Size::new(1500.0, 1000.0))
|
||||||
|
// .run()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn main() {
|
||||||
|
// let mut window = match Window::new("Test", 640, 400, WindowOptions::default()) {
|
||||||
|
// Ok(win) => win,
|
||||||
|
// Err(err) => {
|
||||||
|
// println!("Unable to create window {}", err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn main() {
|
||||||
|
// // wgpu uses `log` for all of our logging, so we initialize a logger with the `env_logger` crate.
|
||||||
|
// //
|
||||||
|
// // To change the log level, set the `RUST_LOG` environment variable. See the `env_logger`
|
||||||
|
// // documentation for more information.
|
||||||
|
// env_logger::init();
|
||||||
|
|
||||||
|
// let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
// // When the current loop iteration finishes, immediately begin a new
|
||||||
|
// // iteration regardless of whether or not new events are available to
|
||||||
|
// // process. Preferred for applications that want to render as fast as
|
||||||
|
// // possible, like games.
|
||||||
|
// event_loop.set_control_flow(ControlFlow::Poll);
|
||||||
|
|
||||||
|
// // When the current loop iteration finishes, suspend the thread until
|
||||||
|
// // another event arrives. Helps keeping CPU utilization low if nothing
|
||||||
|
// // is happening, which is preferred if the application might be idling in
|
||||||
|
// // the background.
|
||||||
|
// // event_loop.set_control_flow(ControlFlow::Wait);
|
||||||
|
|
||||||
|
// let mut app = App::default();
|
||||||
|
// event_loop.run_app(&mut app).unwrap();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
current_image_id: usize,
|
||||||
|
loaded_images: HashMap<PathBuf, ImflowImageBuffer>,
|
||||||
|
available_images: Vec<PathBuf>,
|
||||||
|
current_image_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new() -> Self {
|
||||||
|
let current_image_id: usize = 0;
|
||||||
|
let mut loaded_images: HashMap<PathBuf, ImflowImageBuffer> = HashMap::new();
|
||||||
|
let available_images = load_available_images("./test_images".into());
|
||||||
|
let new_path = available_images[0].clone();
|
||||||
|
let current_image = load_image_argb_imagers(new_path.clone());
|
||||||
|
loaded_images.insert(new_path.clone(), current_image);
|
||||||
|
Self {
|
||||||
|
current_image_id,
|
||||||
|
loaded_images,
|
||||||
|
available_images,
|
||||||
|
current_image_path: new_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_image(&mut self, change: i32) {
|
||||||
|
self.current_image_id = (self.current_image_id as i32 + change)
|
||||||
|
.clamp(0, self.available_images.len() as i32 - 1)
|
||||||
|
as usize;
|
||||||
|
let new_path = self.available_images[self.current_image_id].clone();
|
||||||
|
if !self.loaded_images.contains_key(&new_path) {
|
||||||
|
let new_image = load_image_argb(new_path.clone());
|
||||||
|
self.loaded_images.insert(new_path.clone(), new_image);
|
||||||
|
}
|
||||||
|
self.current_image_path = new_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_image(&self) -> &ImflowImageBuffer {
|
||||||
|
self.loaded_images.get(&self.current_image_path).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
const WIDTH: usize = 1920;
|
||||||
|
const HEIGHT: usize = 1080;
|
||||||
|
let mut window = Window::new(
|
||||||
|
"Test - ESC to exit",
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT,
|
||||||
|
WindowOptions::default(),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!("{}", e);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.set_target_fps(60);
|
||||||
|
|
||||||
|
let mut state = State::new();
|
||||||
|
show_image(&mut window, state.get_current_image());
|
||||||
|
|
||||||
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
|
window.update();
|
||||||
|
if window.is_key_down(Key::Right) {
|
||||||
|
state.next_image(1);
|
||||||
|
show_image(&mut window, state.get_current_image());
|
||||||
|
} else if window.is_key_down(Key::Left) {
|
||||||
|
state.next_image(-1);
|
||||||
|
show_image(&mut window, state.get_current_image());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_image(window: &mut Window, image: &ImflowImageBuffer) {
|
||||||
|
window
|
||||||
|
.update_with_buffer(&image.argb_buffer, image.width, image.height)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GameOfLife {
|
struct GameOfLife {
|
||||||
@ -145,7 +515,7 @@ impl GameOfLife {
|
|||||||
if !self.loaded_images.contains_key(&path.to_path_buf()) {
|
if !self.loaded_images.contains_key(&path.to_path_buf()) {
|
||||||
self.loaded_images.insert(
|
self.loaded_images.insert(
|
||||||
path.to_path_buf(),
|
path.to_path_buf(),
|
||||||
load_thumbnail(path.to_str().unwrap(), Approach::Iced).unwrap(),
|
load_thumbnail(path.to_str().unwrap(), Approach::ImageRs).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user