-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move some stuff into lib instead
- Loading branch information
Showing
22 changed files
with
748 additions
and
746 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Oops, something went wrong.