use std::fmt::{Formatter, LowerHex, UpperHex}; use std::path::{Component, Path, PathBuf}; use deltae::LabValue; use glam::Vec3; use image::{GenericImageView, Rgb, Rgba, RgbaImage}; use lab::Lab; use serde::{Deserialize, Serialize}; use crate::OutputFormat; pub fn max_f32(a: f32, b: f32) -> f32 { if a > b { a } else { b } } pub fn min_f32(a: f32, b: f32) -> f32 { if a < b { a } else { b } } pub fn new_image(new_width: u32, new_height: u32) -> OutputFormat { let mut new_image = RgbaImage::new(new_width, new_height); let (new_image_x, new_image_y, new_image_width, new_image_height) = new_image.bounds(); for x in new_image_x..new_image_width { for y in new_image_y..new_image_height { new_image.put_pixel(x, y, Rgba::from([0, 0, 0, 0])); } } new_image } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct BasicRgba { pub r: u8, pub g: u8, pub b: u8, pub a: u8, } impl BasicRgba { pub fn hue(&self) -> f32 { let red = self.r as f32 / u8::MAX as f32; let green = self.g as f32 / u8::MAX as f32; let blue = self.b as f32 / u8::MAX as f32; let minimum = min_f32(red, min_f32(green, blue)); let maximum = max_f32(red, max_f32(green, blue)); let mut hue = if red >= green && red >= blue { (green - blue) / (maximum - minimum) } else if green >= red && green >= blue { 2.0 + (blue - red) / (maximum - minimum) } else { 4.0 + (red - green) / (maximum - minimum) }; hue *= 60.0; while hue < 0.0 { hue += 360.0; } hue } pub fn transparent() -> Self { Self { r: 0, g: 0, b: 0, a: 0, } } } impl From<Rgba<u8>> for BasicRgba { fn from(other: Rgba<u8>) -> Self { Self { r: other.0[0], g: other.0[1], b: other.0[2], a: other.0[3], } } } impl From<&Rgba<u8>> for BasicRgba { fn from(other: &Rgba<u8>) -> Self { Self { r: other.0[0], g: other.0[1], b: other.0[2], a: other.0[3], } } } impl From<Rgb<u8>> for BasicRgba { fn from(other: Rgb<u8>) -> Self { Self { r: other.0[0], g: other.0[1], b: other.0[2], a: u8::MAX, } } } impl From<&Rgb<u8>> for BasicRgba { fn from(other: &Rgb<u8>) -> Self { Self { r: other.0[0], g: other.0[1], b: other.0[2], a: u8::MAX, } } } impl From<BasicRgba> for Rgba<u8> { fn from(other: BasicRgba) -> Self { Self::from([other.r, other.g, other.b, other.a]) } } impl From<&BasicRgba> for Rgba<u8> { fn from(other: &BasicRgba) -> Self { Self::from([other.r, other.g, other.b, other.a]) } } impl From<BasicRgba> for Rgb<u8> { fn from(other: BasicRgba) -> Self { Self::from([other.r, other.g, other.b]) } } impl From<&BasicRgba> for Rgb<u8> { fn from(other: &BasicRgba) -> Self { Self::from([other.r, other.g, other.b]) } } impl From<BasicRgba> for LabValue { fn from(other: BasicRgba) -> Self { let converted = lab::Lab::from_rgb(&[other.r, other.g, other.b]); LabValue { l: converted.l, a: converted.a, b: converted.b, } } } impl From<&BasicRgba> for LabValue { fn from(other: &BasicRgba) -> Self { let converted = lab::Lab::from_rgb(&[other.r, other.g, other.b]); LabValue { l: converted.l, a: converted.a, b: converted.b, } } } #[derive(Clone, Copy, Debug)] pub struct BasicLab { pub l: f32, pub a: f32, pub b: f32, } impl From<LabValue> for BasicLab { fn from(other: LabValue) -> Self { Self { l: other.l, a: other.a, b: other.b, } } } impl From<BasicLab> for LabValue { fn from(other: BasicLab) -> Self { Self { l: other.l, a: other.a, b: other.b, } } } impl From<BasicLab> for Lab { fn from(other: BasicLab) -> Self { Self { l: other.l, a: other.a, b: other.b, } } } impl From<BasicLab> for BasicRgba { fn from(other: BasicLab) -> Self { let lab: Lab = other.into(); let vals = lab.to_rgb(); Self { r: vals[0], g: vals[1], b: vals[2], a: u8::MAX, } } } impl From<BasicRgba> for Vec3 { fn from(other: BasicRgba) -> Self { Vec3::new(other.r as f32, other.g as f32, other.b as f32) } } impl From<BasicLab> for Vec3 { fn from(other: BasicLab) -> Self { Vec3::new(other.l, other.a, other.b) } } impl UpperHex for BasicRgba { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "{:2X}{:2X}{:2X}{:2X}", self.r, self.g, self.b, self.a )) } } impl LowerHex for BasicRgba { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "{:2x}{:2x}{:2x}{:2x}", self.r, self.g, self.b, self.a )) } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Pipeline { pub input_path: String, pub output_path: String, pub actions: Vec<crate::cli_args::CrunchCommand>, } pub fn normalize_path<T: AsRef<Path>>(path: T) -> PathBuf { let path = path.as_ref(); let mut components = path.components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { components.next(); PathBuf::from(c.as_os_str()) } else { PathBuf::new() }; for component in components { match component { Component::Prefix(..) => unreachable!(), Component::RootDir => { ret.push(component.as_os_str()); } Component::CurDir => {} Component::ParentDir => { ret.pop(); } Component::Normal(c) => { ret.push(c); } } } ret }