phase 1 & 2 first
This commit is contained in:
5392
src-tauri/Cargo.lock
generated
Normal file
5392
src-tauri/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,10 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
tauri = { version = "2", features = ["protocol-asset"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
image = "0.25"
|
||||
rayon = "1.10"
|
||||
tauri-plugin-dialog = "2"
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default"
|
||||
"dialog:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,14 +1,179 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
use std::fs;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct ImageItem {
|
||||
path: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
#[tauri::command]
|
||||
fn scan_dir(path: String) -> Result<Vec<ImageItem>, String> {
|
||||
let mut images = Vec::new();
|
||||
let dir = fs::read_dir(&path).map_err(|e| e.to_string())?;
|
||||
|
||||
for entry in dir {
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
if let Some(ext) = path.extension() {
|
||||
let ext_str = ext.to_string_lossy().to_lowercase();
|
||||
if ["png", "jpg", "jpeg", "webp"].contains(&ext_str.as_str()) {
|
||||
images.push(ImageItem {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
name: path.file_name().unwrap_or_default().to_string_lossy().to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort by name
|
||||
images.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(images)
|
||||
}
|
||||
|
||||
use image::GenericImageView;
|
||||
|
||||
use image::Pixel;
|
||||
|
||||
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
|
||||
struct ZcaResult {
|
||||
|
||||
x: f64,
|
||||
|
||||
y: f64,
|
||||
|
||||
zone: String,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
fn get_zca_suggestion(path: String) -> Result<ZcaResult, String> {
|
||||
|
||||
let img = image::open(&path).map_err(|e| e.to_string())?;
|
||||
|
||||
let (width, height) = img.dimensions();
|
||||
|
||||
|
||||
|
||||
let bottom_start_y = (height as f64 * 0.8) as u32;
|
||||
|
||||
let zone_height = height - bottom_start_y;
|
||||
|
||||
let zone_width = width / 3;
|
||||
|
||||
|
||||
|
||||
let zones = [
|
||||
|
||||
("Left", 0, bottom_start_y),
|
||||
|
||||
("Center", zone_width, bottom_start_y),
|
||||
|
||||
("Right", zone_width * 2, bottom_start_y),
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
let mut min_std_dev = f64::MAX;
|
||||
|
||||
let mut best_zone = "Center";
|
||||
|
||||
let mut best_pos = (0.5, 0.9); // Default center
|
||||
|
||||
|
||||
|
||||
for (name, start_x, start_y) in zones.iter() {
|
||||
|
||||
let mut luma_values = Vec::with_capacity((zone_width * zone_height) as usize);
|
||||
|
||||
|
||||
|
||||
for y in *start_y..height {
|
||||
|
||||
for x in *start_x..(*start_x + zone_width) {
|
||||
|
||||
if x >= width { continue; }
|
||||
|
||||
let pixel = img.get_pixel(x, y);
|
||||
|
||||
let rgb = pixel.to_rgb();
|
||||
|
||||
let luma = 0.299 * rgb[0] as f64 + 0.587 * rgb[1] as f64 + 0.114 * rgb[2] as f64;
|
||||
|
||||
luma_values.push(luma);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
let count = luma_values.len() as f64;
|
||||
|
||||
if count == 0.0 { continue; }
|
||||
|
||||
|
||||
|
||||
let mean = luma_values.iter().sum::<f64>() / count;
|
||||
|
||||
let variance = luma_values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / count;
|
||||
|
||||
let std_dev = variance.sqrt();
|
||||
|
||||
|
||||
|
||||
if std_dev < min_std_dev {
|
||||
|
||||
min_std_dev = std_dev;
|
||||
|
||||
best_zone = name;
|
||||
|
||||
let center_x_px = *start_x as f64 + (zone_width as f64 / 2.0);
|
||||
|
||||
let center_y_px = *start_y as f64 + (zone_height as f64 / 2.0);
|
||||
|
||||
best_pos = (center_x_px / width as f64, center_y_px / height as f64);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Ok(ZcaResult {
|
||||
|
||||
x: best_pos.0,
|
||||
|
||||
y: best_pos.1,
|
||||
|
||||
zone: best_zone.to_string(),
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
|
||||
pub fn run() {
|
||||
|
||||
tauri::Builder::default()
|
||||
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
|
||||
.invoke_handler(tauri::generate_handler![scan_dir, get_zca_suggestion])
|
||||
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
.expect("error while running tauri application");
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost",
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": ["**"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
||||
Reference in New Issue
Block a user