Skip to content

Commit

Permalink
refactor: move some stuff into lib instead
Browse files Browse the repository at this point in the history
  • Loading branch information
InioX committed Aug 16, 2024
1 parent b3b3023 commit 2126147
Show file tree
Hide file tree
Showing 22 changed files with 748 additions and 746 deletions.
210 changes: 210 additions & 0 deletions src/color/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use material_colors::{
blend::harmonize, color::Argb, dynamic_color::{DynamicScheme, MaterialDynamicColors}, hct::Hct, image::{FilterType, ImageReader}, scheme::variant::{
SchemeContent, SchemeExpressive, SchemeFidelity, SchemeFruitSalad, SchemeMonochrome,
SchemeNeutral, SchemeRainbow, SchemeTonalSpot,
}, theme::{ColorGroup, CustomColor, CustomColorGroup}
};

use colorsys::{Hsl, Rgb};
use std::str::FromStr;

use crate::{color::math::get_color_distance_lab, scheme::scheme::SchemeTypes};

#[derive(clap::Parser, Debug)]
pub enum ColorFormat {
Hex { string: String },
Rgb { string: String },
Hsl { string: String },
}

#[derive(clap::Subcommand, Debug)]
pub enum Source {
/// The image to use for generating a color scheme
Image { path: String },
/// The image to fetch from web and use for generating a color scheme
WebImage { url: String },
/// The source color to use for generating a color scheme
#[clap(subcommand)]
Color(crate::color::color::ColorFormat),
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct ColorDefinition {
pub name: String,
pub color: String,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(untagged)]
pub enum OwnCustomColor {
Color(String),
Options { color: String, blend: bool },
}

impl OwnCustomColor {
pub fn to_custom_color(
&self,
name: String,
) -> Result<material_colors::theme::CustomColor, material_colors::error::Error> {
Ok(match self {
OwnCustomColor::Color(color) => material_colors::theme::CustomColor {
value: Argb::from_str(color)?,
blend: true,
name,
},
OwnCustomColor::Options { color, blend } => material_colors::theme::CustomColor {
value: Argb::from_str(color)?,
blend: *blend,
name,
},
})
}
}

pub fn get_source_color(source: &Source) -> Result<Argb, Box<dyn std::error::Error>> {
let source_color: Argb = match &source {
Source::Image { path } => {
// test
info!("Opening image in <d><u>{}</>", path);
crate::color::color::get_source_color_from_image(path).expect("Could not get source color from image")
}
Source::WebImage { url } => {
info!("Fetching image from <d><u>{}</>", url);
crate::color::color::get_source_color_from_web_image(url).expect("Could not get source color from web image")
}
Source::Color(color) => {
crate::color::color::get_source_color_from_color(color).expect("Could not get source color from color")
}
};
Ok(source_color)
}

pub fn get_source_color_from_image(path: &str) -> Result<Argb, Box<dyn std::error::Error>> {
Ok(ImageReader::extract_color(ImageReader::open(path)?.resize(
128,
128,
FilterType::Lanczos3,
)))
}

pub fn get_source_color_from_web_image(url: &str) -> Result<Argb, Box<dyn std::error::Error>> {
let bytes = reqwest::blocking::get(url)?.bytes()?;
Ok(ImageReader::extract_color(ImageReader::read(&bytes)?.resize(
128,
128,
FilterType::Lanczos3,
)))
}

pub fn get_source_color_from_color(color: &ColorFormat) -> Result<Argb, Box<dyn std::error::Error>> {
match color {
ColorFormat::Hex { string } => {
Ok(Argb::from_str(string).expect("Invalid hex color string provided"))
}
ColorFormat::Rgb { string } => {
Ok(string.parse().expect("Invalid rgb color string provided"))
}
ColorFormat::Hsl { string } => {
let rgb: Rgb = Hsl::from_str(string)
.expect("Invalid hsl color string provided")
.into();
Ok(Argb {
red: rgb.red() as u8,
green: rgb.green() as u8,
blue: rgb.blue() as u8,
alpha: 255,
})
}
}
}

pub fn generate_dynamic_scheme(
scheme_type: &Option<SchemeTypes>,
source_color: Argb,
is_dark: bool,
contrast_level: Option<f64>,
) -> DynamicScheme {
match scheme_type.unwrap() {
SchemeTypes::SchemeContent => {
SchemeContent::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeExpressive => {
SchemeExpressive::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeFidelity => {
SchemeFidelity::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeFruitSalad => {
SchemeFruitSalad::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeMonochrome => {
SchemeMonochrome::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeNeutral => {
SchemeNeutral::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeRainbow => {
SchemeRainbow::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
SchemeTypes::SchemeTonalSpot => {
SchemeTonalSpot::new(Hct::new(source_color), is_dark, contrast_level).scheme
}
}
}

pub fn make_custom_color(
color: CustomColor,
scheme_type: &Option<SchemeTypes>,
source_color: Argb,
contrast_level: Option<f64>,
) -> CustomColorGroup {
// debug!("make_custom_color: {:#?}", &color);

let value = if color.blend {
harmonize(color.value, source_color)
} else {
color.value
};

let light = generate_dynamic_scheme(scheme_type, value, false, contrast_level);
let dark = generate_dynamic_scheme(scheme_type, value, true, contrast_level);



// debug!("custom_color: {:#?}", &custom_color);
CustomColorGroup {
color,
value,
light: ColorGroup {
color: MaterialDynamicColors::primary().get_argb(&light),
on_color: MaterialDynamicColors::on_primary().get_argb(&light),
color_container: MaterialDynamicColors::primary_container().get_argb(&light),
on_color_container: MaterialDynamicColors::on_primary_container().get_argb(&light),
},
dark: ColorGroup {
color: MaterialDynamicColors::primary().get_argb(&dark),
on_color: MaterialDynamicColors::on_primary().get_argb(&dark),
color_container: MaterialDynamicColors::primary_container().get_argb(&dark),
on_color_container: MaterialDynamicColors::on_primary_container().get_argb(&dark),
},
}
}

pub fn color_to_string(colors_to_compare: &Vec<ColorDefinition>, compare_to: &str) -> String {
let mut closest_distance: Option<f64> = None;
let mut closest_color: &str = "";

for c in colors_to_compare {
let distance = get_color_distance_lab(&c.color, compare_to);
if closest_distance.is_none() || closest_distance.unwrap() > distance {
closest_distance = Some(distance);
closest_color = &c.name;
}
debug!("distance: {}, name: {}", distance, c.name)
}
debug!(
"closest distance: {:?}, closest color: {}",
closest_distance, closest_color
);
closest_color.to_string()
}
77 changes: 77 additions & 0 deletions src/color/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use material_colors::color::Argb;
use colorsys::{ColorAlpha, Hsl, Rgb};

pub fn rgb_from_argb(color: Argb) -> Rgb {
Rgb::from([
color.red as f64,
color.green as f64,
color.blue as f64,
color.alpha as f64,
])
}

pub fn format_hex(color: &Rgb) -> String {
color.to_hex_string()
}

pub fn format_hex_stripped(color: &Rgb) -> String {
color.to_hex_string()[1..].to_string()
}

pub fn format_rgb(color: &Rgb) -> String {
format!(
"rgb({:?}, {:?}, {:?})",
color.red() as u8,
color.green() as u8,
color.blue() as u8,
)
}

pub fn format_rgba(color: &Rgb, divide: bool) -> String {
if divide {
format!(
"rgba({:?}, {:?}, {:?}, {:.1})",
color.red() as u8,
color.green() as u8,
color.blue() as u8,
color.alpha() / 255.
)
} else {
format!(
"rgba({:?}, {:?}, {:?}, {:.1})",
color.red() as u8,
color.green() as u8,
color.blue() as u8,
color.alpha()
)
}
}

pub fn format_hsl(color: &Hsl) -> String {
format!(
"hsl({:?}, {:?}%, {:?}%)",
color.hue() as u8,
color.saturation() as u8,
color.lightness() as u8,
)
}

pub fn format_hsla(color: &Hsl, divide: bool) -> String {
if divide {
format!(
"hsla({:?}, {:?}%, {:?}%, {:.1})",
color.hue() as u8,
color.saturation() as u8,
color.lightness() as u8,
color.alpha() / 255.
)
} else {
format!(
"hsla({:?}, {:?}%, {:?}%, {:.1})",
color.hue() as u8,
color.saturation() as u8,
color.lightness() as u8,
color.alpha()
)
}
}
29 changes: 29 additions & 0 deletions src/color/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use material_colors::color::{Argb, Lab};
use std::str::FromStr;
use colorsys::Rgb;

pub fn get_color_distance_lab(c1: &str, c2: &str) -> f64 {
let c1 = Lab::from(Argb::from_str(c1).unwrap());
let c2 = Lab::from(Argb::from_str(c2).unwrap());

let l: f64 = c1.l - c2.l;
let a: f64 = c1.a - c2.a;
let b: f64 = c1.b - c2.b;

f64::sqrt((l * l) + (a * a) + (b * b))
}

// for rgb - useless but ill keep it here

#[allow(dead_code)]
pub fn get_color_distance(c1: &Rgb, c2: &Rgb) -> f64 {
let (r1, g1, b1) = (c1.red() as i64, c1.blue() as i64, c1.green() as i64);
let (r2, g2, b2) = (c2.red() as i64, c2.green() as i64, c2.blue() as i64);

let rmean: f64 = ((r1 + r2) / 2) as f64;
let weight_r: f64 = 2.0 + rmean / 256.0;
let weight_g: f64 = 4.0;
let weight_b: f64 = 2.0 + (255.0 - rmean) / 256.0;

return f64::sqrt(weight_r * i64::pow(r1-r2, 2) as f64 + weight_g * i64::pow(g1-g2, 2) as f64 + weight_b * i64::pow(b1-b2, 2) as f64)
}
4 changes: 4 additions & 0 deletions src/color/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod format;
pub mod math;
pub mod color;
pub mod parse;
24 changes: 24 additions & 0 deletions src/color/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use upon::Value;

pub fn parse_color(string: &str) -> Option<&str> {
if let Some(_s) = string.strip_prefix('#') {
return Some("hex");
}

if let (Some(i), Some(s)) = (string.find('('), string.strip_suffix(')')) {
let fname = s[..i].trim_end();
Some(fname)
} else if string.len() == 6 {
// Does not matter if it is actually a stripped hex or not, we handle it somewhere else.
return Some("hex_stripped");
} else {
None
}
}

pub fn check_string_value(value: &Value) -> Option<&String> {
match value {
Value::String(v) => Some(v),
_v => None,
}
}
Empty file added src/exec/command.rs
Empty file.
Loading

0 comments on commit 2126147

Please sign in to comment.