use std::fmt::Debug;
use std::hash::Hash;
use std::path::PathBuf;
use egui::TextureHandle;
use log::{error, info};
use crate::palette::DynamicPalette;
use crate::updates::UpdateInfo;
use crate::utils::Hashed;
use crate::worker::{BackendEvent, ImageSource};
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct UiState {
pub show_about: bool,
#[serde(skip)]
pub processing: bool,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
pub current_image: Option<PathBuf>,
#[serde(skip)]
pub image_texture: Option<TextureHandle>,
#[serde(skip)]
pub edited_texture: Option<TextureHandle>,
#[serde(skip)]
pub show_original: bool,
#[serde(skip)]
pub update: Option<UpdateInfo>,
#[serde(skip)]
pub last_event: String,
pub palette_selection: DynamicPalette,
pub palette: Vec<[u8; 3]>,
pub current_alg: LutAlgorithm,
pub guassian_rbf: GaussianRbfArgs,
pub shepards_method: ShepardsMethodArgs,
pub guassian_sampling: GaussianSamplingArgs,
pub gaussian_blur: BlurArgs,
pub common_rbf: CommonRbf,
pub common: Common,
}
impl Default for UiState {
fn default() -> Self {
Self {
show_about: true,
processing: false,
update: None,
last_event: "Started.".to_string(),
current_image: None,
image_texture: None,
edited_texture: None,
show_original: false,
palette_selection: DynamicPalette::Builtin(lutgen_palettes::Palette::Carburetor),
palette: lutgen_palettes::Palette::Carburetor.get().to_vec(),
current_alg: Default::default(),
guassian_rbf: Default::default(),
shepards_method: Default::default(),
guassian_sampling: Default::default(),
gaussian_blur: Default::default(),
common_rbf: Default::default(),
common: Default::default(),
}
}
}
impl UiState {
pub fn handle_event(&mut self, ctx: &egui::Context, event: BackendEvent) {
self.last_event = event.to_string();
info!("Received event: {}", self.last_event);
match event {
BackendEvent::Error(e) => {
self.processing = false;
error!("{e}");
},
BackendEvent::SetImage {
source,
image,
dim: (width, height),
..
} => {
let texture = ctx.load_texture(
format!("bytes://{source}",),
egui::ColorImage::from_rgba_unmultiplied(
[height as usize, width as usize],
&image,
),
egui::TextureOptions::NEAREST
.with_mipmap_mode(Some(egui::TextureFilter::Nearest)),
);
self.processing = false;
match source {
ImageSource::Image(path) => {
self.current_image = Some(path);
self.image_texture = Some(texture);
self.edited_texture = None;
self.show_original = true;
},
ImageSource::Edited(_) => {
self.edited_texture = Some(texture);
self.show_original = false;
},
}
},
BackendEvent::Update(update) => {
self.update = Some(update);
},
#[cfg(target_arch = "wasm32")]
BackendEvent::SaveData(_, data, format) => {
use web_sys::wasm_bindgen::JsCast;
self.processing = false;
let mut filename = self.current_image.clone().unwrap_or("lutgen.png".into());
filename.set_extension(format.extensions_str().first().unwrap());
let win = web_sys::window().expect("failed to get window");
let doc = win.document().expect("failed to get document");
let link = doc.create_element("a").expect("failed to create link");
link.set_attribute("href", &data)
.expect("failed to set data");
link.set_attribute("download", &filename.display().to_string())
.expect("failed to set download name");
web_sys::HtmlAnchorElement::unchecked_from_js(link.clone().into()).click();
link.remove();
},
}
}
pub fn cli_args(&self) -> Vec<String> {
macro_rules! arg {
($args:expr, $flag:expr, $arg:expr, $default:expr) => {
if $arg != $default {
$args.push($flag.to_string());
$args.push($arg.to_string());
}
};
($args:expr, $flag:expr, bool $arg:expr, $default:expr) => {
if $arg != $default {
$args.push($flag.to_string());
}
};
}
let mut args = Vec::new();
if let DynamicPalette::Builtin(palette) = self.palette_selection {
arg!(args, "-p", palette.to_string(), "");
}
match self.current_alg {
LutAlgorithm::GaussianRbf | LutAlgorithm::ShepardsMethod => {
arg!(args, "-n", self.common_rbf.nearest, 16);
},
_ => {},
}
arg!(args, "-l", self.common.level, 12);
arg!(args, "-L", self.common.lum_factor.0, 0.7);
arg!(args, "-P", bool self.common.preserve, false);
match self.current_alg {
LutAlgorithm::GaussianRbf => {
arg!(args, "-s", self.guassian_rbf.shape.0, 128.);
},
LutAlgorithm::ShepardsMethod => {
arg!(args, "-p", self.shepards_method.power.0, 4.);
},
LutAlgorithm::GaussianSampling => {
arg!(args, "-m", self.guassian_sampling.mean.0, 0.);
arg!(args, "-s", self.guassian_sampling.std_dev.0, 20.);
arg!(args, "-i", self.guassian_sampling.iterations, 512);
arg!(args, "-S", self.guassian_sampling.seed, 42080085);
},
LutAlgorithm::GaussianBlur => {
arg!(args, "-r", self.gaussian_blur.radius.0, 8.0);
},
LutAlgorithm::NearestNeighbor => {},
}
if let Some(path) = &self.current_image {
args.push(path.display().to_string());
}
if matches!(self.palette_selection, DynamicPalette::Custom(_)) {
args.push("--".to_string());
for [r, g, b] in &self.palette {
args.push(format!("{r:0x}{g:0x}{b:0x}"));
}
}
args
}
pub fn reset_current_args(&mut self) {
let default = Self::default();
self.common = default.common;
match self.current_alg {
LutAlgorithm::GaussianRbf | LutAlgorithm::ShepardsMethod => {
self.common_rbf = default.common_rbf;
},
_ => {},
}
match self.current_alg {
LutAlgorithm::GaussianRbf => {
self.guassian_rbf = default.guassian_rbf;
},
LutAlgorithm::ShepardsMethod => {
self.shepards_method = default.shepards_method;
},
LutAlgorithm::GaussianSampling => {
self.guassian_sampling = default.guassian_sampling;
},
LutAlgorithm::GaussianBlur => {
self.gaussian_blur = default.gaussian_blur;
},
LutAlgorithm::NearestNeighbor => {},
}
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
Hash,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::VariantArray,
)]
pub enum LutAlgorithm {
#[default]
GaussianBlur,
GaussianRbf,
GaussianSampling,
ShepardsMethod,
NearestNeighbor,
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct Common {
pub preserve: bool,
pub lum_factor: Hashed<f64>,
pub level: u8,
}
impl Default for Common {
fn default() -> Self {
Self {
preserve: true,
lum_factor: Hashed(0.7),
#[cfg(not(target_arch = "wasm32"))]
level: 12,
#[cfg(target_arch = "wasm32")]
level: 8,
}
}
}
#[derive(Clone, Copy, Debug, Default, Hash, serde::Deserialize, serde::Serialize)]
pub struct CommonRbf {
pub nearest: usize,
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct GaussianRbfArgs {
pub shape: Hashed<f64>,
}
impl Default for GaussianRbfArgs {
fn default() -> Self {
Self {
shape: Hashed(128.),
}
}
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct ShepardsMethodArgs {
pub power: Hashed<f64>,
}
impl Default for ShepardsMethodArgs {
fn default() -> Self {
Self { power: Hashed(4.) }
}
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct GaussianSamplingArgs {
pub mean: Hashed<f64>,
pub std_dev: Hashed<f64>,
pub iterations: usize,
pub seed: u64,
}
impl Default for GaussianSamplingArgs {
fn default() -> Self {
Self {
mean: Hashed(4.),
std_dev: Hashed(20.),
iterations: 128,
seed: 42080085,
}
}
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct BlurArgs {
pub radius: Hashed<f64>,
}
impl Default for BlurArgs {
fn default() -> Self {
Self {
radius: Hashed(8.0),
}
}
}