Working HEIF, working aspect ratio, resize

This commit is contained in:
Dawid Pietrykowski 2025-04-06 17:24:17 +02:00
parent ed66f17139
commit b2dc693d0e
6 changed files with 232 additions and 67 deletions

48
Cargo.lock generated
View File

@ -1553,6 +1553,17 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "enumn"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.3" version = "0.1.3"
@ -1788,6 +1799,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "four-cc"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "795cbfc56d419a7ce47ccbb7504dd9a5b7c484c083c356e797de08bd988d9629"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.31" version = "0.3.31"
@ -2666,6 +2683,7 @@ dependencies = [
"iced", "iced",
"image 0.25.6", "image 0.25.6",
"itertools 0.12.1", "itertools 0.12.1",
"libheif-rs",
"memmap2", "memmap2",
"minifb", "minifb",
"pollster", "pollster",
@ -3019,6 +3037,30 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "libheif-rs"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a26370abb4723a3ce73083e479b98017604206cadb0e35da5eac4813600d85"
dependencies = [
"enumn",
"four-cc",
"libc",
"libheif-sys",
]
[[package]]
name = "libheif-sys"
version = "3.1.0+1.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e663db80d4272b60c066c5a9d17370ffa0433a31d424152f95f1e1effb9b3860"
dependencies = [
"libc",
"pkg-config",
"vcpkg",
"walkdir",
]
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.7.4" version = "0.7.4"
@ -5363,6 +5405,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.1.1" version = "0.1.1"

View File

@ -31,6 +31,7 @@ wgpu = "24.0.3"
# winit = "0.30.9" # winit = "0.30.9"
zune-image = {version = "0.4.15", features = ["all"]} zune-image = {version = "0.4.15", features = ["all"]}
bytemuck = "1.22.0" bytemuck = "1.22.0"
libheif-rs = "1.1.0"
[profile.release] [profile.release]
opt-level = 3 opt-level = 3
debug = false debug = false

View File

@ -1,14 +1,11 @@
use crate::egui_tools::EguiRenderer; use crate::egui_tools::EguiRenderer;
use eframe::WindowAttributes;
use egui::{Event, Key}; use egui::{Event, Key};
use egui_wgpu::wgpu::SurfaceError; use egui_wgpu::wgpu::SurfaceError;
use egui_wgpu::{ScreenDescriptor, wgpu}; use egui_wgpu::{ScreenDescriptor, wgpu};
use imflow::store::ImageStore; use imflow::store::ImageStore;
use std::any::Any;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use std::sync::Arc; use std::sync::Arc;
use std::time;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use wgpu::{PipelineCompilationOptions, SurfaceConfiguration}; use wgpu::{PipelineCompilationOptions, SurfaceConfiguration};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
@ -23,20 +20,28 @@ use winit::window::{Window, WindowId};
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Transforms { struct Transforms {
transform: [f32; 16], // 4x4 matrix transform: [f32; 16], // 4x4 matrix
width: u32,
height: u32,
_padding1: u32,
_padding2: u32,
} }
struct TransformData { pub(crate) struct TransformData {
pan_x: f32, pan_x: f32,
pan_y: f32, pan_y: f32,
zoom: f32, zoom: f32,
width: u32,
height: u32,
} }
fn create_transform_matrix(data: &TransformData) -> [f32; 16] { fn create_transform_matrix(data: &TransformData, scale_x: f32, scale_y: f32) -> [f32; 16] {
const ZOOM_MULTIPLIER: f32 = 3.0; const ZOOM_MULTIPLIER: f32 = 3.0;
let zoom = data.zoom.powf(ZOOM_MULTIPLIER); let zoom = data.zoom.powf(ZOOM_MULTIPLIER);
[ [
zoom, 0.0, 0.0, 0.0, 0.0, zoom, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, data.pan_x, data.pan_y, 0.0, zoom * scale_x, 0.0, 0.0, 0.0,
1.0, 0.0, zoom * scale_y, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
data.pan_x, data.pan_y, 0.0, 1.0,
] ]
} }
@ -62,7 +67,7 @@ fn setup_texture(
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[], view_formats: &[],
}); });
@ -101,7 +106,7 @@ fn setup_texture(
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 2, binding: 2,
visibility: wgpu::ShaderStages::VERTEX, visibility: wgpu::ShaderStages::all(),
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -225,7 +230,7 @@ impl AppState {
window: &Window, window: &Window,
width: u32, width: u32,
height: u32, height: u32,
path: PathBuf path: PathBuf,
) -> Self { ) -> Self {
let power_pref = wgpu::PowerPreference::default(); let power_pref = wgpu::PowerPreference::default();
let adapter = instance let adapter = instance
@ -279,12 +284,15 @@ impl AppState {
let store = ImageStore::new(path); let store = ImageStore::new(path);
let (image_texture, bind_group, render_pipeline, transform_buffer) = let (image_texture, bind_group, render_pipeline, transform_buffer) =
setup_texture(&device, surface_config.clone(), 6000, 4000); // setup_texture(&device, surface_config.clone(), 6000, 4000);
setup_texture(&device, surface_config.clone(), 8192, 8192);
let transform_data = TransformData { let transform_data = TransformData {
pan_x: 0.0, pan_x: 0.0,
pan_y: 0.0, pan_y: 0.0,
zoom: 1.0, zoom: 1.0,
width: 10000,
height: 10000,
}; };
Self { Self {
@ -314,7 +322,7 @@ pub struct App {
instance: wgpu::Instance, instance: wgpu::Instance,
state: Option<AppState>, state: Option<AppState>,
window: Option<Arc<Window>>, window: Option<Arc<Window>>,
path: PathBuf path: PathBuf,
} }
impl App { impl App {
@ -324,7 +332,7 @@ impl App {
instance, instance,
state: None, state: None,
window: None, window: None,
path path,
} }
} }
@ -346,7 +354,7 @@ impl App {
&window, &window,
initial_width, initial_width,
initial_width, initial_width,
self.path.clone() self.path.clone(),
) )
.await; .await;
@ -362,6 +370,7 @@ impl App {
if width > 0 && height > 0 { if width > 0 && height > 0 {
self.state.as_mut().unwrap().resize_surface(width, height); self.state.as_mut().unwrap().resize_surface(width, height);
} }
self.pan_zoom(0.0, 0.0, 0.0);
} }
pub fn update_texture(&mut self) { pub fn update_texture(&mut self) {
@ -369,7 +378,7 @@ impl App {
state.store.check_loaded_images(); state.store.check_loaded_images();
let imbuf = if let Some(full) = state.store.get_current_image() { let imbuf = if let Some(full) = state.store.get_current_image() {
println!("full"); // println!("full");
full full
} else { } else {
state.store.get_thumbnail() state.store.get_thumbnail()
@ -378,11 +387,15 @@ impl App {
let height = imbuf.height as u32; let height = imbuf.height as u32;
let buffer_u8 = unsafe { let buffer_u8 = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
imbuf.argb_buffer.as_ptr() as *const u8, imbuf.rgba_buffer.as_ptr() as *const u8,
imbuf.argb_buffer.len() * 4, imbuf.rgba_buffer.len() * 4,
) )
}; };
state.transform_data.width = width;
state.transform_data.height = height;
state.queue.write_texture( state.queue.write_texture(
wgpu::TexelCopyTextureInfo { wgpu::TexelCopyTextureInfo {
texture: &state.image_texture, texture: &state.image_texture,
@ -402,18 +415,38 @@ impl App {
depth_or_array_layers: 1, depth_or_array_layers: 1,
}, },
); );
self.pan_zoom(0.0, 0.0, 0.0);
} }
pub fn pan_zoom(&mut self, zoom_delta: f32, pan_x: f32, pan_y: f32) { pub fn pan_zoom(&mut self, zoom_delta: f32, pan_x: f32, pan_y: f32) {
let state = self.state.as_mut().unwrap(); let state = self.state.as_mut().unwrap();
let image_aspect_ratio = (state.transform_data.width as f32) / (state.transform_data.height as f32);
let window_size = self.window.as_ref().unwrap().inner_size();
let window_aspect_ratio = window_size.width as f32 / window_size.height as f32;
let mut scale_x = 1.0;
let mut scale_y = 1.0;
if window_aspect_ratio > image_aspect_ratio {
scale_x = image_aspect_ratio / window_aspect_ratio;
} else {
scale_y = window_aspect_ratio / image_aspect_ratio;
}
state.transform_data.zoom = (state.transform_data.zoom + zoom_delta).clamp(1.0, 20.0); state.transform_data.zoom = (state.transform_data.zoom + zoom_delta).clamp(1.0, 20.0);
state.transform_data.pan_x += pan_x; state.transform_data.pan_x += pan_x;
state.transform_data.pan_y += pan_y; state.transform_data.pan_y += pan_y;
let transform = create_transform_matrix(&state.transform_data); let transform = create_transform_matrix(&state.transform_data, scale_x, scale_y);
state.queue.write_buffer( state.queue.write_buffer(
&state.transform_buffer, &state.transform_buffer,
0, 0,
bytemuck::cast_slice(&[Transforms { transform }]), bytemuck::cast_slice(&[Transforms {
transform,
width: state.transform_data.width,
height: state.transform_data.height,
_padding1: 0,
_padding2: 0,
}]),
); );
} }
@ -473,7 +506,7 @@ impl App {
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color { load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.0, r: 0.0,
g: 1.0, // Green g: 0.0, // Green
b: 0.0, b: 0.0,
a: 1.0, a: 1.0,
}), }),
@ -611,7 +644,7 @@ impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let attributes = Window::default_attributes() let attributes = Window::default_attributes()
.with_base_size(LogicalSize::new(2000, 4000)) .with_base_size(LogicalSize::new(2000, 4000))
.with_resizable(false); .with_resizable(true);
let window = event_loop.create_window(attributes).unwrap(); let window = event_loop.create_window(attributes).unwrap();
pollster::block_on(self.set_window(window)); pollster::block_on(self.set_window(window));
} }
@ -630,7 +663,7 @@ impl ApplicationHandler for App {
event_loop.exit(); event_loop.exit();
} }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let start = time::Instant::now(); // let start = time::Instant::now();
self.handle_redraw(); self.handle_redraw();
// println!("Updated in: {}ms", start.elapsed().as_millis()); // println!("Updated in: {}ms", start.elapsed().as_millis());
// Extract the events by cloning them from the input context // Extract the events by cloning them from the input context
@ -677,12 +710,7 @@ impl ApplicationHandler for App {
Key::Escape => exit(0), Key::Escape => exit(0),
_ => {} _ => {}
} }
} else if let Event::MouseWheel { } else if let Event::MouseWheel { delta, .. } = e {
unit,
delta,
modifiers,
} = e
{
self.pan_zoom(delta.y * 0.2, 0.0, 0.0); self.pan_zoom(delta.y * 0.2, 0.0, 0.0);
} else if let Event::PointerMoved(pos) = e { } else if let Event::PointerMoved(pos) = e {
if keys_down.contains(&Key::Tab) { if keys_down.contains(&Key::Tab) {

View File

@ -1,6 +1,7 @@
use iced::widget::image::Handle; use iced::widget::image::Handle;
use image::DynamicImage; use image::DynamicImage;
use image::imageops::FilterType; use image::imageops::FilterType;
use libheif_rs::{HeifContext, LibHeif, RgbChroma};
use rexiv2::Metadata; use rexiv2::Metadata;
use zune_image::codecs::jpeg::JpegDecoder; use zune_image::codecs::jpeg::JpegDecoder;
use zune_image::codecs::qoi::zune_core::colorspace::ColorSpace; use zune_image::codecs::qoi::zune_core::colorspace::ColorSpace;
@ -17,7 +18,7 @@ use std::time::Instant;
pub struct ImflowImageBuffer { pub struct ImflowImageBuffer {
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub argb_buffer: Vec<u32>, pub rgba_buffer: Vec<u32>,
pub rating: i32, pub rating: i32,
} }
@ -25,11 +26,7 @@ pub fn create_iced_handle(width: u32, height: u32, rgba: Vec<u8>) -> Handle {
Handle::from_rgba(width, height, rgba) Handle::from_rgba(width, height, rgba)
} }
fn get_rating(filename: PathBuf) -> i32 { fn get_rating(filename: &PathBuf) -> i32 {
// if !path_exists(filename.clone()) {
// anyhow::bail!("File doesn't exist");
// }
// // Use xmp-toolkit for video files // // Use xmp-toolkit for video files
// if is_video(&filename) { // if is_video(&filename) {
// return Ok(read_rating_xmp(filename.clone()).unwrap_or(0)); // return Ok(read_rating_xmp(filename.clone()).unwrap_or(0));
@ -46,12 +43,19 @@ fn get_rating(filename: PathBuf) -> i32 {
} }
} }
pub fn load_image(path: PathBuf) -> ImflowImageBuffer { pub fn load_image(path: &PathBuf) -> ImflowImageBuffer {
let total_start = Instant::now(); let total_start = Instant::now();
if is_heif(path) {
let img = load_heif(path, false);
let total_time = total_start.elapsed();
println!("Total HEIF loading time: {:?}", total_time);
return img;
}
let file = read(path.clone()).unwrap(); let file = read(path.clone()).unwrap();
let mut decoder = JpegDecoder::new(&file); let mut decoder = JpegDecoder::new(&file);
let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(ColorSpace::BGRA); let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(ColorSpace::RGBA);
decoder.set_options(options); decoder.set_options(options);
decoder.decode_headers().unwrap(); decoder.decode_headers().unwrap();
@ -80,30 +84,28 @@ pub fn load_image(path: PathBuf) -> ImflowImageBuffer {
ImflowImageBuffer { ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer_u32, rgba_buffer: buffer_u32,
rating, rating,
} }
} }
pub fn image_to_argb_buffer(img: DynamicImage) -> Vec<u32> { pub fn image_to_rgba_buffer(img: DynamicImage) -> Vec<u32> {
let flat = img.into_rgba8(); let flat = img.to_rgba8();
let buf = flat.as_raw(); let mut buffer = flat.to_vec();
unsafe {
buf.chunks_exact(4) Vec::from_raw_parts(
.map(|rgba| { buffer.as_mut_ptr() as *mut u32,
let r = rgba[0] as u32; buffer.len() / 4,
let g = rgba[1] as u32; buffer.len() / 4,
let b = rgba[2] as u32; )
r << 16 | g << 8 | b }
})
.collect()
} }
pub fn load_available_images(dir: PathBuf) -> Vec<PathBuf> { pub fn load_available_images(dir: PathBuf) -> Vec<PathBuf> {
let mut files: Vec<PathBuf> = fs::read_dir(dir) let mut files: Vec<PathBuf> = fs::read_dir(dir)
.unwrap() .unwrap()
.map(|f| f.unwrap().path()) .map(|f| f.unwrap().path())
.filter(|f| ["jpg", "heic"].contains(&f.extension().unwrap().to_ascii_lowercase().to_str().unwrap())) .filter(is_image)
.collect(); .collect();
files.sort(); files.sort();
files files
@ -124,7 +126,35 @@ pub fn get_embedded_thumbnail(path: PathBuf) -> Option<Vec<u8>> {
} }
} }
fn is_image(path: &PathBuf) -> bool {
if !path.is_file() {
return false;
}
["jpg", "heic", "heif"].contains(
&path
.extension()
.unwrap()
.to_ascii_lowercase()
.to_str()
.unwrap(),
)
}
fn is_heif(path: &PathBuf) -> bool {
["heif", "heic"].contains(
&path
.extension()
.unwrap()
.to_ascii_lowercase()
.to_str()
.unwrap(),
)
}
pub fn load_thumbnail(path: &PathBuf) -> ImflowImageBuffer { pub fn load_thumbnail(path: &PathBuf) -> ImflowImageBuffer {
if is_heif(path) {
return load_heif(path, true);
}
match load_thumbnail_exif(path) { match load_thumbnail_exif(path) {
Some(thumbnail) => return thumbnail, Some(thumbnail) => return thumbnail,
None => load_thumbnail_full(path), None => load_thumbnail_full(path),
@ -141,22 +171,22 @@ pub fn load_thumbnail_exif(path: &PathBuf) -> Option<ImflowImageBuffer> {
let width: usize = image.width() as usize; let width: usize = image.width() as usize;
let height: usize = image.height() as usize; let height: usize = image.height() as usize;
let mut flat = image.into_rgba8().into_raw(); let flat = image.into_rgba8().into_raw();
let mut buffer: Vec<u32> = vec![0; width * height]; let mut buffer = flat.to_vec();
let buffer_u32 = unsafe {
for (rgba, argb) in flat.chunks_mut(4).zip(buffer.iter_mut()) { Vec::from_raw_parts(
let r = rgba[0] as u32; buffer.as_mut_ptr() as *mut u32,
let g = rgba[1] as u32; buffer.len() / 4,
let b = rgba[2] as u32; buffer.len() / 4,
*argb = r << 16 | g << 8 | b; )
} };
let rating = get_rating(path.into()); let rating = get_rating(path.into());
Some(ImflowImageBuffer { Some(ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer, rgba_buffer: buffer_u32,
rating, rating,
}) })
} }
@ -175,13 +205,66 @@ pub fn load_thumbnail_full(path: &PathBuf) -> ImflowImageBuffer {
.resize(640, 480, FilterType::Nearest); .resize(640, 480, FilterType::Nearest);
let width = image.width() as usize; let width = image.width() as usize;
let height = image.height() as usize; let height = image.height() as usize;
let buffer = image_to_argb_buffer(image); let buffer = image_to_rgba_buffer(image);
let rating = get_rating(path.into()); let rating = get_rating(path.into());
ImflowImageBuffer { ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer, rgba_buffer: buffer,
rating,
}
}
pub fn load_heif(path: &PathBuf, resize: bool) -> ImflowImageBuffer {
let lib_heif = LibHeif::new();
let ctx = HeifContext::read_from_file(path.to_str().unwrap()).unwrap();
let handle = ctx.primary_image_handle().unwrap();
// assert_eq!(handle.width(), 1652);
// assert_eq!(handle.height(), 1791);
// Get Exif
// let mut meta_ids: Vec<ItemId> = vec![0; 1];
// let count = handle.metadata_block_ids(&mut meta_ids, b"Exif");
// assert_eq!(count, 1);
// let exif: Vec<u8> = handle.metadata(meta_ids[0]).unwrap();
// Decode the image
let mut image = lib_heif
.decode(&handle, libheif_rs::ColorSpace::Rgb(RgbChroma::Rgba), None)
.unwrap();
assert_eq!(
image.color_space(),
Some(libheif_rs::ColorSpace::Rgb(RgbChroma::Rgba)),
);
// Scale the image
if resize {
image = image.scale(640, 480, None).unwrap();
assert_eq!(image.width(), 640);
assert_eq!(image.height(), 480);
}
let width = image.width() as usize;
let height = image.height() as usize;
let rating = get_rating(path);
// Get "pixels"
let planes = image.planes();
let interleaved_plane = planes.interleaved.unwrap();
assert!(!interleaved_plane.data.is_empty());
assert!(interleaved_plane.stride > 0);
let rgba_buffer = interleaved_plane.data;
// Create a slice of u32 from the u8 slice
let u32_slice = unsafe {
std::slice::from_raw_parts(rgba_buffer.as_ptr() as *const u32, rgba_buffer.len() / 4)
};
ImflowImageBuffer {
width,
height,
rgba_buffer: u32_slice.to_vec(),
rating, rating,
} }
} }

View File

@ -1,5 +1,7 @@
struct Transforms { struct Transforms {
transform: mat4x4<f32>, transform: mat4x4<f32>,
width: u32,
height: u32
}; };
@group(0) @binding(2) var<uniform> transforms: Transforms; @group(0) @binding(2) var<uniform> transforms: Transforms;
@ -16,7 +18,6 @@ struct VertexOutput {
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
// Apply zoom/transformation matrix
out.position = transforms.transform * vec4<f32>(in.position, 1.0); out.position = transforms.transform * vec4<f32>(in.position, 1.0);
out.uv = in.uv; out.uv = in.uv;
return out; return out;
@ -27,5 +28,9 @@ fn vs_main(in: VertexInput) -> VertexOutput {
@fragment @fragment
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
return textureSample(texture, texture_sampler, uv); let texture_size = vec2<f32>(f32(transforms.width), f32(transforms.height));
let out_dim = vec2<f32>(textureDimensions(texture));
let scale = texture_size / out_dim;
let pixel = uv * scale;
return textureSample(texture, texture_sampler, pixel);
} }

View File

@ -53,7 +53,7 @@ impl ImageStore {
); );
let path = available_images[0].clone(); let path = available_images[0].clone();
let image = load_image(path.clone()); let image = load_image(&path.clone());
loaded_images.insert(path, image); loaded_images.insert(path, image);
let mut state = Self { let mut state = Self {
current_image_id, current_image_id,
@ -91,7 +91,7 @@ impl ImageStore {
pub fn get_current_rating(&self) -> i32 { pub fn get_current_rating(&self) -> i32 {
let imbuf = if let Some(full) = self.get_current_image() { let imbuf = if let Some(full) = self.get_current_image() {
println!("full"); // println!("full");
full full
} else { } else {
// TODO: this assumes loaded thumbnail // TODO: this assumes loaded thumbnail
@ -122,7 +122,7 @@ impl ImageStore {
self.currently_loading.insert(path.clone()); self.currently_loading.insert(path.clone());
self.pool.execute(move || { self.pool.execute(move || {
let image = load_image(path.clone()); let image = load_image(&path.clone());
let _ = tx.send((path, image)); let _ = tx.send((path, image));
}); });
} }