Skip to content
Snippets Groups Projects
utils.rs 5.17 KiB
Newer Older
Louis's avatar
Louis committed
use std::fmt::{Formatter, LowerHex, UpperHex};
use std::path::{Component, Path, PathBuf};
Louis's avatar
Louis committed

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
}