diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 9e4901cb9e..c2fcd234fd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -34,7 +34,6 @@ dependencies = [ "libc", "little_exif", "log", - "memmap2", "mimalloc", "mozjpeg-rs", "nalgebra", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 46f3737b47..82daa82641 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -51,7 +51,6 @@ tempfile = "3.27.0" mimalloc = "0.1.52" image_hasher = "3.1.1" regex = "1.12.4" -memmap2 = "0.9.11" half = { version = "2.7.1", features = ["bytemuck"] } glam = "0.33.2" quick-xml = { version = "0.41", features = ["serialize"] } diff --git a/src-tauri/src/export_processing.rs b/src-tauri/src/export_processing.rs index ddbf33ff8d..7ed3cfd288 100644 --- a/src-tauri/src/export_processing.rs +++ b/src-tauri/src/export_processing.rs @@ -17,7 +17,7 @@ use tauri::Manager; use crate::AppState; use crate::exif_processing; use crate::file_management::{ - generate_filename_from_template, parse_virtual_path, read_file_mapped, + generate_filename_from_template, parse_virtual_path, read_file_bytes, }; use crate::formats::is_raw_file; use crate::image_loader::{ @@ -915,30 +915,17 @@ pub async fn export_images( } } } else { - match read_file_mapped(Path::new(&source_path_str)) { - Ok(mmap) => load_and_composite( - &mmap, - &source_path_str, - &js_adjustments, - false, - &settings, - None, - ) - .map_err(|e| format!("Failed to load from mmap: {}", e))?, - Err(_) => { - let bytes = - fs::read(&source_path_str).map_err(|e| e.to_string())?; - load_and_composite( - &bytes, - &source_path_str, - &js_adjustments, - false, - &settings, - None, - ) - .map_err(|e| format!("Failed to load from bytes: {}", e))? - } - } + let bytes = read_file_bytes(Path::new(&source_path_str)) + .map_err(|e| e.to_string())?; + load_and_composite( + &bytes, + &source_path_str, + &js_adjustments, + false, + &settings, + None, + ) + .map_err(|e| format!("Failed to load image: {}", e))? }; let mut main_export_adjustments = js_adjustments.clone(); @@ -1212,18 +1199,9 @@ pub async fn estimate_export_sizes( const ESTIMATE_DIM: u32 = 1280; - let file_slice: Vec; - let mmap_guard; - let file_data: &[u8] = match read_file_mapped(Path::new(&source_path_str)) { - Ok(mmap) => { - mmap_guard = Some(mmap); - mmap_guard.as_ref().unwrap() - } - Err(_) => { - file_slice = fs::read(&source_path_str).map_err(|io_err| io_err.to_string())?; - &file_slice - } - }; + let file_bytes = + read_file_bytes(Path::new(&source_path_str)).map_err(|e| e.to_string())?; + let file_data: &[u8] = &file_bytes; let original_image = load_base_image_from_bytes(file_data, &source_path_str, true, &settings, None) diff --git a/src-tauri/src/file_management.rs b/src-tauri/src/file_management.rs index 56fc585659..50947b6901 100644 --- a/src-tauri/src/file_management.rs +++ b/src-tauri/src/file_management.rs @@ -1,4 +1,3 @@ -use memmap2::{Mmap, MmapOptions}; use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::collections::{HashMap, HashSet}; @@ -233,7 +232,6 @@ pub struct PresetFile { #[derive(Debug)] pub enum ReadFileError { Io(std::io::Error), - Locked, Empty, NotFound, Invalid, @@ -243,7 +241,6 @@ impl fmt::Display for ReadFileError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ReadFileError::Io(err) => write!(f, "IO error: {}", err), - ReadFileError::Locked => write!(f, "File is locked"), ReadFileError::Empty => write!(f, "File is empty"), ReadFileError::NotFound => write!(f, "File not found"), ReadFileError::Invalid => write!(f, "Invalid file"), @@ -321,9 +318,7 @@ pub async fn read_exif_for_paths( sidecar_exif } else if is_cloud_placeholder(&source_path) { HashMap::new() - } else if let Ok(mmap) = read_file_mapped(&source_path) { - crate::exif_processing::read_exif_data(&source_path_str, &mmap) - } else if let Ok(bytes) = fs::read(&source_path) { + } else if let Ok(bytes) = read_file_bytes(&source_path) { crate::exif_processing::read_exif_data(&source_path_str, &bytes) } else { HashMap::new() @@ -357,9 +352,7 @@ pub async fn update_exif_fields( let mut exif_data = temp_metadata.exif.unwrap_or_else(|| { if let Some(existing) = crate::exif_processing::read_rrexif_sidecar(original_path) { existing - } else if let Ok(mmap) = read_file_mapped(original_path) { - crate::exif_processing::read_exif_data_from_bytes(path, &mmap) - } else if let Ok(bytes) = fs::read(original_path) { + } else if let Ok(bytes) = read_file_bytes(original_path) { crate::exif_processing::read_exif_data_from_bytes(path, &bytes) } else { HashMap::new() @@ -1178,7 +1171,7 @@ pub fn is_cloud_placeholder(_path: &Path) -> bool { false } -pub fn read_file_mapped(path: &Path) -> Result { +pub fn read_file_bytes(path: &Path) -> Result, ReadFileError> { if !path.is_file() { return Err(ReadFileError::Invalid); } @@ -1188,17 +1181,7 @@ pub fn read_file_mapped(path: &Path) -> Result { if path.metadata().map_err(ReadFileError::Io)?.len() == 0 { return Err(ReadFileError::Empty); } - let file = fs::File::open(path).map_err(ReadFileError::Io)?; - if file.try_lock_shared().is_err() { - return Err(ReadFileError::Locked); - } - let mmap = unsafe { - MmapOptions::new() - .len(file.metadata().map_err(ReadFileError::Io)?.len() as usize) - .map(&file) - .map_err(ReadFileError::Io)? - }; - Ok(mmap) + fs::read(path).map_err(ReadFileError::Io) } pub fn generate_thumbnail_data( @@ -1274,29 +1257,10 @@ pub fn generate_thumbnail_data( let composite_image = if let Some(img) = preloaded_image { image_loader::composite_patches_on_image(img, &adjustments)? } else { - let mmap_guard; - let vec_guard; - - let file_slice: &[u8] = match read_file_mapped(&source_path) { - Ok(mmap) => { - mmap_guard = Some(mmap); - mmap_guard.as_ref().unwrap() - } - Err(e) => { - if preloaded_image.is_none() { - log::warn!("Fallback read for {}: {}", source_path_str, e); - } - let bytes = fs::read(&source_path).map_err(|io_err| { - anyhow::anyhow!( - "Fallback read failed for {}: {}", - source_path_str, - io_err - ) - })?; - vec_guard = Some(bytes); - vec_guard.as_ref().unwrap() - } - }; + let file_data = read_file_bytes(&source_path).map_err(|e| { + anyhow::anyhow!("Failed to read {}: {}", source_path_str, e) + })?; + let file_slice: &[u8] = &file_data; let img = image_loader::load_and_composite( file_slice, @@ -1463,28 +1427,16 @@ pub fn generate_thumbnail_data( let mut final_image = if let Some(img) = preloaded_image { image_loader::composite_patches_on_image(img, &adjustments)? } else { - match read_file_mapped(&source_path) { - Ok(mmap) => image_loader::load_and_composite( - &mmap, - &source_path_str, - &adjustments, - true, - &settings, - None, - )?, - Err(e) => { - log::warn!("Fallback read for {}: {}", source_path_str, e); - let bytes = fs::read(&source_path)?; - image_loader::load_and_composite( - &bytes, - &source_path_str, - &adjustments, - true, - &settings, - None, - )? - } - } + let bytes = read_file_bytes(&source_path) + .map_err(|e| anyhow::anyhow!("Failed to read {}: {}", source_path_str, e))?; + image_loader::load_and_composite( + &bytes, + &source_path_str, + &adjustments, + true, + &settings, + None, + )? }; if adjustments.is_null() { diff --git a/src-tauri/src/image_loader.rs b/src-tauri/src/image_loader.rs index 2e638a6f06..eca044b683 100644 --- a/src-tauri/src/image_loader.rs +++ b/src-tauri/src/image_loader.rs @@ -2,7 +2,7 @@ use crate::Cursor; use crate::app_settings::{AppSettings, load_settings}; use crate::app_state::{AppState, LoadedImage}; use crate::exif_processing; -use crate::file_management::{parse_virtual_path, read_file_mapped}; +use crate::file_management::{parse_virtual_path, read_file_bytes}; use crate::formats::is_raw_file; use crate::image_processing::ImageMetadata; use crate::image_processing::{ @@ -18,7 +18,6 @@ use rayon::prelude::*; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; -use std::fs; use std::panic; use std::path::Path; use std::sync::OnceLock; @@ -800,50 +799,25 @@ pub async fn load_image( return Err("Load cancelled".to_string()); } - let result: Result<(DynamicImage, HashMap), String> = - (|| match read_file_mapped(Path::new(&path_clone)) { - Ok(mmap) => { - if generation_tracker.load(Ordering::SeqCst) != my_generation { - return Err("Load cancelled".to_string()); - } + let result: Result<(DynamicImage, HashMap), String> = (|| { + let bytes = read_file_bytes(Path::new(&path_clone)) + .map_err(|e| format!("Failed to read {}: {}", path_clone, e))?; - let img = load_base_image_from_bytes( - &mmap, - &path_clone, - false, - &settings, - cancel_token.clone(), - ) - .map_err(|e| e.to_string())?; - let exif = exif_processing::read_exif_data(&path_clone, &mmap); - Ok((img, exif)) - } - Err(e) => { - log::warn!( - "Failed to memory-map file '{}': {}. Falling back to standard read.", - path_clone, - e - ); - let bytes = fs::read(&path_clone).map_err(|io_err| { - format!("Fallback read failed for {}: {}", path_clone, io_err) - })?; - - if generation_tracker.load(Ordering::SeqCst) != my_generation { - return Err("Load cancelled".to_string()); - } + if generation_tracker.load(Ordering::SeqCst) != my_generation { + return Err("Load cancelled".to_string()); + } - let img = load_base_image_from_bytes( - &bytes, - &path_clone, - false, - &settings, - cancel_token.clone(), - ) - .map_err(|e| e.to_string())?; - let exif = exif_processing::read_exif_data(&path_clone, &bytes); - Ok((img, exif)) - } - })(); + let img = load_base_image_from_bytes( + &bytes, + &path_clone, + false, + &settings, + cancel_token.clone(), + ) + .map_err(|e| e.to_string())?; + let exif = exif_processing::read_exif_data(&path_clone, &bytes); + Ok((img, exif)) + })(); result }) .await diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index dc5d11af05..55b4361727 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -73,7 +73,7 @@ use crate::cache_utils::{ DecodedImageCache, GEOMETRY_KEYS, calculate_full_job_hash, calculate_geometry_hash, calculate_transform_hash, calculate_visual_hash, }; -use crate::file_management::{parse_virtual_path, read_file_mapped}; +use crate::file_management::{parse_virtual_path, read_file_bytes}; use crate::formats::is_raw_file; use crate::hdr_deghosting::{align_hdr_frames, assert_uniform_dimensions, load_hdr_frames}; use crate::image_loader::{composite_patches_on_image, load_and_composite}; @@ -1522,34 +1522,16 @@ fn generate_preview_for_path( let is_raw = is_raw_file(&source_path_str); let settings = load_settings(app_handle.clone()).unwrap_or_default(); - let base_image = match read_file_mapped(&source_path) { - Ok(mmap) => load_and_composite( - &mmap, - &source_path_str, - &js_adjustments, - false, - &settings, - None, - ) - .map_err(|e| e.to_string())?, - Err(e) => { - log::warn!( - "Failed to memory-map file '{}': {}. Falling back to standard read.", - source_path_str, - e - ); - let bytes = fs::read(&source_path).map_err(|io_err| io_err.to_string())?; - load_and_composite( - &bytes, - &source_path_str, - &js_adjustments, - false, - &settings, - None, - ) - .map_err(|e| e.to_string())? - } - }; + let bytes = read_file_bytes(&source_path).map_err(|e| e.to_string())?; + let base_image = load_and_composite( + &bytes, + &source_path_str, + &js_adjustments, + false, + &settings, + None, + ) + .map_err(|e| e.to_string())?; let (transformed_image, unscaled_crop_offset) = apply_all_transformations(Cow::Borrowed(&base_image), &js_adjustments); diff --git a/src-tauri/src/negative_conversion.rs b/src-tauri/src/negative_conversion.rs index b31517d37f..181a0fa772 100644 --- a/src-tauri/src/negative_conversion.rs +++ b/src-tauri/src/negative_conversion.rs @@ -1,4 +1,4 @@ -use crate::file_management::{parse_virtual_path, read_file_mapped}; +use crate::file_management::{parse_virtual_path, read_file_bytes}; use crate::image_loader::load_base_image_from_bytes; use base64::{Engine as _, engine::general_purpose}; use image::codecs::jpeg::JpegEncoder; @@ -7,7 +7,6 @@ use rayon::prelude::*; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::hash_map::DefaultHasher; -use std::fs; use std::hash::{Hash, Hasher}; use std::io::Cursor; use std::path::Path; @@ -209,55 +208,25 @@ pub async fn preview_negative_conversion( drop(original_lock); let settings = load_settings(app_handle.clone()).unwrap_or_default(); - match read_file_mapped(Path::new(&source_path_str)) { - Ok(mmap) => load_base_image_from_bytes( - &mmap, - &source_path_str, - false, - &settings, - None, - ) - .map_err(|e| e.to_string())?, - Err(_e) => { - let bytes = fs::read(&source_path_str) - .map_err(|io_err| io_err.to_string())?; - load_base_image_from_bytes( - &bytes, - &source_path_str, - false, - &settings, - None, - ) - .map_err(|e| e.to_string())? - } - } - } - } else { - drop(original_lock); - let settings = load_settings(app_handle.clone()).unwrap_or_default(); - - match read_file_mapped(Path::new(&source_path_str)) { - Ok(mmap) => load_base_image_from_bytes( - &mmap, + let bytes = read_file_bytes(Path::new(&source_path_str)) + .map_err(|e| e.to_string())?; + load_base_image_from_bytes( + &bytes, &source_path_str, false, &settings, None, ) - .map_err(|e| e.to_string())?, - Err(_e) => { - let bytes = - fs::read(&source_path_str).map_err(|io_err| io_err.to_string())?; - load_base_image_from_bytes( - &bytes, - &source_path_str, - false, - &settings, - None, - ) - .map_err(|e| e.to_string())? - } + .map_err(|e| e.to_string())? } + } else { + drop(original_lock); + let settings = load_settings(app_handle.clone()).unwrap_or_default(); + + let bytes = read_file_bytes(Path::new(&source_path_str)) + .map_err(|e| e.to_string())?; + load_base_image_from_bytes(&bytes, &source_path_str, false, &settings, None) + .map_err(|e| e.to_string())? } }; @@ -304,14 +273,9 @@ pub async fn convert_negatives( let settings = load_settings(app_handle.clone()).unwrap_or_default(); - let img = match read_file_mapped(Path::new(&real_path)) { - Ok(mmap) => load_base_image_from_bytes(&mmap, &real_path, false, &settings, None), - Err(_) => { - let bytes = fs::read(&real_path).unwrap_or_default(); - load_base_image_from_bytes(&bytes, &real_path, false, &settings, None) - } - } - .map_err(|e| e.to_string())?; + let bytes = read_file_bytes(Path::new(&real_path)).map_err(|e| e.to_string())?; + let img = load_base_image_from_bytes(&bytes, &real_path, false, &settings, None) + .map_err(|e| e.to_string())?; let bounds_ref = downscale_f32_image(&img, 1080, 1080); let ref_rgb = bounds_ref.to_rgb32f();