use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::ops::Deref;
use std::path::PathBuf;
use std::str::FromStr;
use egui::TextureHandle;
use log::{debug, error};
use uuid::Uuid;
use crate::palette::DynamicPalette;
use crate::worker::{BackendEvent, LutAlgorithmArgs, WorkerHandle};
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct UiState {
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,
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 common_rbf: CommonRbf,
pub common: Common,
pub show_about: bool,
#[serde(skip)]
pub last_event: String,
}
impl Default for UiState {
fn default() -> Self {
Self {
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: LutAlgorithm::GaussianRbf,
guassian_rbf: Default::default(),
shepards_method: Default::default(),
guassian_sampling: Default::default(),
common_rbf: Default::default(),
common: Default::default(),
show_about: true,
last_event: "Started.".to_string(),
}
}
}
impl UiState {
pub fn handle_event(&mut self, ctx: &egui::Context, event: BackendEvent) {
self.last_event = event.to_string();
if !self.last_event.is_empty() {
debug!("{}", self.last_event);
}
match event {
BackendEvent::Error(e) => {
error!("{e}");
},
BackendEvent::PickFile(path_buf, _) => {
self.current_image = Some(path_buf);
},
BackendEvent::SetImage {
path,
image,
dim: (width, height),
} => {
let texture = ctx.load_texture(
format!(
"bytes://{}",
path.as_ref()
.map(|p| p.display().to_string())
.unwrap_or(Uuid::new_v4().to_string())
),
egui::ColorImage::from_rgba_unmultiplied(
[height as usize, width as usize],
&image,
),
egui::TextureOptions::default()
.with_mipmap_mode(Some(egui::TextureFilter::Nearest)),
);
if let Some(path) = path {
self.current_image = Some(path);
self.image_texture = Some(texture);
self.show_original = true;
} else {
self.edited_texture = Some(texture);
self.show_original = false;
}
},
_ => {},
}
}
pub fn apply(&self, worker: &mut WorkerHandle) {
let args = match self.current_alg {
LutAlgorithm::GaussianRbf => LutAlgorithmArgs::GaussianRbf {
rbf: self.common_rbf,
args: self.guassian_rbf,
},
LutAlgorithm::ShepardsMethod => LutAlgorithmArgs::ShepardsMethod {
rbf: self.common_rbf,
args: self.shepards_method,
},
LutAlgorithm::GaussianSampling => LutAlgorithmArgs::GaussianSampling {
args: self.guassian_sampling,
},
LutAlgorithm::NearestNeighbor => LutAlgorithmArgs::NearestNeighbor,
};
worker.apply_palette(self.palette.clone(), self.common, args);
}
}
#[derive(
Clone,
Copy,
Debug,
Hash,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::VariantArray,
)]
pub enum LutAlgorithm {
GaussianRbf,
ShepardsMethod,
GaussianSampling,
NearestNeighbor,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Hashed<T: Clone + Debug>(pub T);
impl<T: Clone + Copy + Debug> Copy for Hashed<T> {}
impl<T: Clone + Debug + ToString> Hash for Hashed<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.to_string().hash(state);
}
}
impl<T: Clone + Debug + FromStr> FromStr for Hashed<T> {
type Err = T::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
T::from_str(s).map(Hashed)
}
}
impl<T: Clone + Debug> AsRef<T> for Hashed<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: Clone + Debug> Display for Hashed<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<T: Clone + Copy + Debug> Deref for Hashed<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct Common {
pub lum_factor: Hashed<f64>,
pub level: u8,
}
impl Default for Common {
fn default() -> Self {
Self {
lum_factor: Hashed(0.7),
level: 2,
}
}
}
#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize)]
pub struct CommonRbf {
pub nearest: usize,
pub preserve: bool,
}
impl Default for CommonRbf {
fn default() -> Self {
Self {
nearest: 16,
preserve: false,
}
}
}
#[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(64.) }
}
}
#[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,
}
}
}