Apply lightness to preset color profile
This commit is contained in:
parent
86e442b8a4
commit
b0737a33ba
11 changed files with 343 additions and 90 deletions
|
|
@ -13,18 +13,21 @@ default-run = "hyfetch"
|
|||
# ansi_colours = { workspace = true, features = ["rgb"] }
|
||||
anyhow = { workspace = true, features = ["std"] }
|
||||
bpaf = { workspace = true, features = [] }
|
||||
# bytemuck = { workspace = true, features = [] }
|
||||
chrono = { workspace = true, features = ["clock", "std"] }
|
||||
deranged = { workspace = true, features = ["serde", "std"] }
|
||||
# derive_more = { workspace = true, features = ["std"] }
|
||||
derive_more = { workspace = true, features = ["from", "from_str", "into", "std"] }
|
||||
directories = { workspace = true, features = [] }
|
||||
indexmap = { workspace = true, features = ["serde", "std"] }
|
||||
palette = { workspace = true, features = ["std"] }
|
||||
regex = { workspace = true, features = ["perf", "std", "unicode"] }
|
||||
rgb = { workspace = true, features = [] }
|
||||
# rgb = { workspace = true, features = [] }
|
||||
serde = { workspace = true, features = ["derive", "std"] }
|
||||
serde_json = { workspace = true, features = ["std"] }
|
||||
serde_path_to_error = { workspace = true, features = [] }
|
||||
shell-words = { workspace = true, features = ["std"] }
|
||||
strum = { workspace = true, features = ["derive", "std"] }
|
||||
thiserror = { workspace = true, features = [] }
|
||||
tracing = { workspace = true, features = ["attributes", "std"] }
|
||||
tracing-subscriber = { workspace = true, features = ["ansi", "fmt", "smallvec", "std", "tracing-log"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@ use std::path::Path;
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::Datelike;
|
||||
use directories::ProjectDirs;
|
||||
use hyfetch::cli_options::options;
|
||||
use hyfetch::models::Config;
|
||||
use hyfetch::neofetch_util::get_distro_ascii;
|
||||
use hyfetch::presets::AssignLightness;
|
||||
use hyfetch::utils::get_cache_path;
|
||||
use tracing::debug;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let options = options().run();
|
||||
|
||||
init_tracing_subsriber(options.debug).context("Failed to init tracing subscriber")?;
|
||||
init_tracing_subsriber(options.debug).context("failed to init tracing subscriber")?;
|
||||
|
||||
debug!(?options, "CLI options");
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ fn main() -> Result<()> {
|
|||
if options.test_print {
|
||||
println!(
|
||||
"{}",
|
||||
get_distro_ascii(options.distro.as_ref()).context("Failed to get distro ascii")?
|
||||
get_distro_ascii(options.distro.as_ref()).context("failed to get distro ascii")?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -31,20 +32,18 @@ fn main() -> Result<()> {
|
|||
// TODO
|
||||
|
||||
let config = if options.config {
|
||||
create_config(options.config_file).context("Failed to create config")?
|
||||
create_config(options.config_file).context("failed to create config")?
|
||||
} else if let Some(config) =
|
||||
read_config(&options.config_file).context("Failed to read config")?
|
||||
read_config(&options.config_file).context("failed to read config")?
|
||||
{
|
||||
config
|
||||
} else {
|
||||
create_config(options.config_file).context("Failed to create config")?
|
||||
create_config(options.config_file).context("failed to create config")?
|
||||
};
|
||||
|
||||
// Check if it's June (pride month)
|
||||
let now = chrono::Local::now();
|
||||
let cache_path = ProjectDirs::from("", "", "hyfetch")
|
||||
.context("Failed to get base dirs")?
|
||||
.cache_dir()
|
||||
.to_owned();
|
||||
let cache_path = get_cache_path().context("failed to get cache path")?;
|
||||
let june_path = cache_path.join(format!("animation-displayed-{}", now.year()));
|
||||
let show_pride_month =
|
||||
options.june || now.month() == 6 && !june_path.is_file() && io::stdout().is_terminal();
|
||||
|
|
@ -58,14 +57,31 @@ fn main() -> Result<()> {
|
|||
println!();
|
||||
|
||||
if !june_path.is_file() {
|
||||
fs::create_dir_all(cache_path).context("Failed to create cache dir")?;
|
||||
fs::create_dir_all(cache_path).context("failed to create cache dir")?;
|
||||
File::create(&june_path)
|
||||
.with_context(|| format!("Failed to create file {june_path:?}"))?;
|
||||
.with_context(|| format!("failed to create file {june_path:?}"))?;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
// Get preset
|
||||
let preset = options.preset.unwrap_or(config.preset);
|
||||
let color_profile = preset.color_profile();
|
||||
debug!(?color_profile, "color profile");
|
||||
|
||||
// Lighten
|
||||
let color_profile = if let Some(scale) = options.scale {
|
||||
color_profile.lighten(scale)
|
||||
} else if let Some(lightness) = options.lightness {
|
||||
color_profile.with_lightness(AssignLightness::Replace(lightness))
|
||||
} else {
|
||||
color_profile.with_lightness_dl(config.lightness(), config.light_dark)
|
||||
};
|
||||
debug!(?color_profile, "lightened color profile");
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -85,18 +101,18 @@ where
|
|||
return Ok(None);
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(err).with_context(|| format!("Failed to open {path:?}"));
|
||||
return Err(err).with_context(|| format!("failed to open {path:?}"));
|
||||
},
|
||||
};
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
file.read_to_string(&mut buf)
|
||||
.with_context(|| format!("Failed to read {path:?}"))?;
|
||||
.with_context(|| format!("failed to read {path:?}"))?;
|
||||
|
||||
let deserializer = &mut serde_json::Deserializer::from_str(&buf);
|
||||
let config: Config = serde_path_to_error::deserialize(deserializer)
|
||||
.with_context(|| format!("Failed to parse {path:?}"))?;
|
||||
.with_context(|| format!("failed to parse {path:?}"))?;
|
||||
|
||||
debug!(?config, "read config");
|
||||
|
||||
|
|
@ -158,5 +174,5 @@ fn init_tracing_subsriber(debug: bool) -> Result<()> {
|
|||
|
||||
subscriber
|
||||
.try_init()
|
||||
.context("Failed to set the global default subscriber")
|
||||
.context("failed to set the global default subscriber")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use bpaf::{construct, long, OptionParser, Parser};
|
|||
use directories::BaseDirs;
|
||||
use strum::VariantNames;
|
||||
|
||||
use crate::color_util::Lightness;
|
||||
use crate::presets::Preset;
|
||||
use crate::types::{AnsiMode, Backend};
|
||||
|
||||
|
|
@ -19,9 +20,9 @@ pub struct Options {
|
|||
pub mode: Option<AnsiMode>,
|
||||
pub backend: Option<Backend>,
|
||||
pub args: Vec<String>,
|
||||
pub colors_scale: Option<f32>,
|
||||
pub colors_set_lightness: Option<f32>,
|
||||
pub colors_use_overlay: bool,
|
||||
pub scale: Option<f32>,
|
||||
pub lightness: Option<Lightness>,
|
||||
pub overlay: bool,
|
||||
pub june: bool,
|
||||
pub debug: bool,
|
||||
pub distro: Option<String>,
|
||||
|
|
@ -42,7 +43,7 @@ pub fn options() -> OptionParser<Options> {
|
|||
.fallback_with(|| {
|
||||
Ok::<_, anyhow::Error>(
|
||||
BaseDirs::new()
|
||||
.context("Failed to get base dirs")?
|
||||
.context("failed to get base dirs")?
|
||||
.config_dir()
|
||||
.join("hyfetch.json"),
|
||||
)
|
||||
|
|
@ -107,15 +108,15 @@ BACKEND={{{}}}",
|
|||
.argument::<String>("ARGS")
|
||||
.parse(|s| shell_words::split(&s).context("ARGS should be valid command-line arguments"))
|
||||
.fallback(vec![]);
|
||||
let colors_scale = long("c-scale")
|
||||
let scale = long("c-scale")
|
||||
.help("Lighten colors by a multiplier")
|
||||
.argument("SCALE")
|
||||
.optional();
|
||||
let colors_set_lightness = long("c-set-l")
|
||||
let lightness = long("c-set-l")
|
||||
.help("Set lightness value of the colors")
|
||||
.argument("LIGHT")
|
||||
.argument("LIGHTNESS")
|
||||
.optional();
|
||||
let colors_use_overlay = long("c-overlay")
|
||||
let overlay = long("c-overlay")
|
||||
.help("Use experimental overlay color adjusting instead of HSL lightness")
|
||||
.switch();
|
||||
let june = long("june").help("Show pride month easter egg").switch();
|
||||
|
|
@ -151,9 +152,9 @@ BACKEND={{{}}}",
|
|||
mode,
|
||||
backend,
|
||||
args,
|
||||
colors_scale,
|
||||
colors_set_lightness,
|
||||
colors_use_overlay,
|
||||
scale,
|
||||
lightness,
|
||||
overlay,
|
||||
june,
|
||||
debug,
|
||||
distro,
|
||||
|
|
|
|||
|
|
@ -1,49 +1,113 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use std::num::ParseFloatError;
|
||||
|
||||
use anyhow::Result;
|
||||
use deranged::RangedU8;
|
||||
use rgb::RGB8;
|
||||
use derive_more::{From, FromStr, Into};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Represents the lightness component in HSL.
|
||||
///
|
||||
/// The range of valid values is
|
||||
/// `(`[`Lightness::MIN`]`..=`[`Lightness::MAX`]`)`.
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Deserialize, Into, Serialize)]
|
||||
pub struct Lightness(f32);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LightnessError {
|
||||
#[error(
|
||||
"invalid lightness {0}, expected value between {} and {}",
|
||||
Lightness::MIN,
|
||||
Lightness::MAX
|
||||
)]
|
||||
OutOfRange(f32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParseLightnessError {
|
||||
#[error("invalid float")]
|
||||
InvalidFloat(#[from] ParseFloatError),
|
||||
#[error("invalid lightness")]
|
||||
InvalidLightness(#[from] LightnessError),
|
||||
}
|
||||
|
||||
/// An indexed color where the color palette is the set of colors used in
|
||||
/// neofetch ascii art.
|
||||
///
|
||||
/// The range of valid values as supported in neofetch is `1`-`6`.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
|
||||
pub struct NeofetchAsciiIndexedColor(RangedU8<1, 6>);
|
||||
/// The range of valid values as supported in neofetch is
|
||||
/// `(`[`NeofetchAsciiIndexedColor::MIN`]`..
|
||||
/// =`[`NeofetchAsciiIndexedColor::MAX`]`)`.
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Hash,
|
||||
Debug,
|
||||
Deserialize,
|
||||
From,
|
||||
FromStr,
|
||||
Into,
|
||||
Serialize,
|
||||
)]
|
||||
pub struct NeofetchAsciiIndexedColor(
|
||||
RangedU8<{ NeofetchAsciiIndexedColor::MIN }, { NeofetchAsciiIndexedColor::MAX }>,
|
||||
);
|
||||
|
||||
/// An indexed color where the color palette is the set of unique colors in a
|
||||
/// preset.
|
||||
///
|
||||
/// The range of valid values depends on the number of unique colors in a
|
||||
/// certain preset.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Hash,
|
||||
Debug,
|
||||
Deserialize,
|
||||
From,
|
||||
FromStr,
|
||||
Into,
|
||||
Serialize,
|
||||
)]
|
||||
pub struct PresetIndexedColor(usize);
|
||||
|
||||
pub trait FromHex {
|
||||
/// Creates color from hex code.
|
||||
fn from_hex<S>(hex: S) -> Result<RGB8>
|
||||
where
|
||||
S: AsRef<str>;
|
||||
}
|
||||
impl Lightness {
|
||||
const MAX: f32 = 1.0f32;
|
||||
const MIN: f32 = 0.0f32;
|
||||
|
||||
impl FromHex for RGB8 {
|
||||
fn from_hex<S>(hex: S) -> Result<RGB8>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let hex = hex.as_ref();
|
||||
|
||||
let hex = hex.strip_prefix('#').unwrap_or(hex);
|
||||
if hex.len() != 6 {
|
||||
Err(anyhow!("invalid length for hex color"))?;
|
||||
pub fn new(value: f32) -> Result<Self, LightnessError> {
|
||||
if !(Self::MIN..=Self::MAX).contains(&value) {
|
||||
return Err(LightnessError::OutOfRange(value));
|
||||
}
|
||||
|
||||
let r =
|
||||
u8::from_str_radix(&hex[0..2], 16).context("Failed to parse hex color component")?;
|
||||
let g =
|
||||
u8::from_str_radix(&hex[2..4], 16).context("Failed to parse hex color component")?;
|
||||
let b =
|
||||
u8::from_str_radix(&hex[4..6], 16).context("Failed to parse hex color component")?;
|
||||
|
||||
Ok(RGB8::new(r, g, b))
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<f32> for Lightness {
|
||||
type Error = LightnessError;
|
||||
|
||||
fn try_from(value: f32) -> Result<Self, Self::Error> {
|
||||
Lightness::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Lightness {
|
||||
type Err = ParseLightnessError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Lightness::new(s.parse()?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl NeofetchAsciiIndexedColor {
|
||||
const MAX: u8 = 6;
|
||||
const MIN: u8 = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ pub mod models;
|
|||
pub mod neofetch_util;
|
||||
pub mod presets;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::color_util::Lightness;
|
||||
use crate::neofetch_util::ColorAlignment;
|
||||
use crate::presets::Preset;
|
||||
use crate::types::{AnsiMode, Backend, LightDark};
|
||||
|
|
@ -9,7 +10,7 @@ pub struct Config {
|
|||
pub preset: Preset,
|
||||
pub mode: AnsiMode,
|
||||
pub light_dark: LightDark,
|
||||
pub lightness: Option<f32>,
|
||||
lightness: Option<Lightness>,
|
||||
pub color_align: ColorAlignment,
|
||||
pub backend: Backend,
|
||||
#[serde(with = "self::args_serde_with")]
|
||||
|
|
@ -18,6 +19,20 @@ pub struct Config {
|
|||
pub pride_month_disable: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default_lightness(term: &LightDark) -> Lightness {
|
||||
match term {
|
||||
LightDark::Dark => Lightness::new(0.65).unwrap(),
|
||||
LightDark::Light => Lightness::new(0.4).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> Lightness {
|
||||
self.lightness
|
||||
.unwrap_or_else(|| Self::default_lightness(&self.light_dark))
|
||||
}
|
||||
}
|
||||
|
||||
mod args_serde_with {
|
||||
use std::fmt;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use tracing::debug;
|
|||
use crate::color_util::{NeofetchAsciiIndexedColor, PresetIndexedColor};
|
||||
use crate::distros::Distro;
|
||||
|
||||
const NEOFETCH_COLOR_PATTERN: &str = r"\$\{c[0-9]\}";
|
||||
static NEOFETCH_COLOR_RE: OnceLock<Regex> = OnceLock::new();
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||
|
|
@ -41,14 +42,14 @@ pub fn get_command_path() -> Result<PathBuf> {
|
|||
let path = path.join("neofetch");
|
||||
match path.try_exists() {
|
||||
Ok(true) => {
|
||||
return path.canonicalize().context("Failed to canonicalize path");
|
||||
return path.canonicalize().context("failed to canonicalize path");
|
||||
},
|
||||
Ok(false) => {
|
||||
Err(anyhow!("{path:?} does not exist or is not readable"))?;
|
||||
},
|
||||
Err(err) => {
|
||||
Err(err)
|
||||
.with_context(|| format!("Failed to check for existence of {path:?}"))?;
|
||||
.with_context(|| format!("failed to check for existence of {path:?}"))?;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +64,7 @@ pub fn get_command_path() -> Result<PathBuf> {
|
|||
if !path.is_file() {
|
||||
continue;
|
||||
}
|
||||
return path.canonicalize().context("Failed to canonicalize path");
|
||||
return path.canonicalize().context("failed to canonicalize path");
|
||||
}
|
||||
|
||||
Err(anyhow!("neofetch command not found"))
|
||||
|
|
@ -80,7 +81,7 @@ where
|
|||
distro.as_ref().into()
|
||||
} else {
|
||||
get_distro_name()
|
||||
.context("Failed to get distro name")?
|
||||
.context("failed to get distro name")?
|
||||
.into()
|
||||
};
|
||||
debug!(%distro, "distro name");
|
||||
|
|
@ -101,7 +102,7 @@ where
|
|||
|
||||
let Some(width) = NEOFETCH_COLOR_RE
|
||||
.get_or_init(|| {
|
||||
Regex::new(r"\$\{c[0-9]\}").expect("neofetch color regex should not be invalid")
|
||||
Regex::new(NEOFETCH_COLOR_PATTERN).expect("neofetch color regex should not be invalid")
|
||||
})
|
||||
.replace_all(asc, "")
|
||||
.split('\n')
|
||||
|
|
@ -140,11 +141,11 @@ fn run_neofetch_command_piped<S>(args: &[S]) -> Result<String>
|
|||
where
|
||||
S: AsRef<OsStr> + fmt::Debug,
|
||||
{
|
||||
let mut command = make_neofetch_command(args).context("Failed to make neofetch command")?;
|
||||
let mut command = make_neofetch_command(args).context("failed to make neofetch command")?;
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.context("Failed to execute neofetch as child process")?;
|
||||
.context("failed to execute neofetch as child process")?;
|
||||
debug!(?output, "neofetch output");
|
||||
|
||||
if !output.status.success() {
|
||||
|
|
@ -168,7 +169,7 @@ where
|
|||
}
|
||||
|
||||
let out = String::from_utf8(output.stdout)
|
||||
.context("Failed to process neofetch output as it contains invalid UTF-8")?
|
||||
.context("failed to process neofetch output as it contains invalid UTF-8")?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(out)
|
||||
|
|
@ -181,7 +182,7 @@ where
|
|||
#[cfg(not(windows))]
|
||||
{
|
||||
let mut command = Command::new("bash");
|
||||
command.arg(get_command_path().context("Failed to get neofetch command path")?);
|
||||
command.arg(get_command_path().context("failed to get neofetch command path")?);
|
||||
command.args(args);
|
||||
Ok(command)
|
||||
}
|
||||
|
|
@ -194,5 +195,5 @@ where
|
|||
#[tracing::instrument(level = "debug")]
|
||||
fn get_distro_name() -> Result<String> {
|
||||
run_neofetch_command_piped(&["ascii_distro_name"])
|
||||
.context("Failed to get distro name from neofetch")
|
||||
.context("failed to get distro name from neofetch")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ use std::iter;
|
|||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use indexmap::IndexSet;
|
||||
use rgb::RGB8;
|
||||
use palette::encoding::{self, Linear};
|
||||
use palette::num::ClampAssign;
|
||||
use palette::{Hsl, IntoColorMut, LinSrgb, Srgb};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{EnumString, VariantNames};
|
||||
|
||||
use crate::color_util::FromHex;
|
||||
use crate::color_util::Lightness;
|
||||
use crate::types::LightDark;
|
||||
|
||||
#[derive(Clone, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
|
||||
#[derive(Copy, Clone, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Preset {
|
||||
|
|
@ -82,9 +85,16 @@ pub enum Preset {
|
|||
Xenogender,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct ColorProfile {
|
||||
pub colors: Vec<RGB8>,
|
||||
pub colors: Vec<Srgb<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum AssignLightness {
|
||||
Replace(Lightness),
|
||||
ClampMax(Lightness),
|
||||
ClampMin(Lightness),
|
||||
}
|
||||
|
||||
impl Preset {
|
||||
|
|
@ -376,7 +386,7 @@ impl Preset {
|
|||
}
|
||||
|
||||
impl ColorProfile {
|
||||
pub fn new(colors: Vec<RGB8>) -> Self {
|
||||
pub fn new(colors: Vec<Srgb<u8>>) -> Self {
|
||||
Self { colors }
|
||||
}
|
||||
|
||||
|
|
@ -386,9 +396,9 @@ impl ColorProfile {
|
|||
{
|
||||
let colors = hex_colors
|
||||
.into_iter()
|
||||
.map(RGB8::from_hex)
|
||||
.collect::<Result<_>>()
|
||||
.context("Failed to parse hex colors")?;
|
||||
.map(|s| s.as_ref().parse())
|
||||
.collect::<Result<_, _>>()
|
||||
.context("failed to parse hex colors")?;
|
||||
Ok(Self::new(colors))
|
||||
}
|
||||
|
||||
|
|
@ -414,12 +424,79 @@ impl ColorProfile {
|
|||
Ok(Self::new(weighted_colors))
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors lightened by a multiplier.
|
||||
pub fn lighten(&self, multiplier: f32) -> Self {
|
||||
let mut rgb_f32_colors: Vec<LinSrgb> =
|
||||
self.colors.iter().map(|c| c.into_linear()).collect();
|
||||
|
||||
{
|
||||
let hsl_f32_colors: &mut [Hsl<Linear<encoding::Srgb>>] =
|
||||
&mut rgb_f32_colors.into_color_mut();
|
||||
|
||||
for hsl_f32_color in hsl_f32_colors {
|
||||
hsl_f32_color.lightness *= multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_u8_colors: Vec<_> = rgb_f32_colors
|
||||
.into_iter()
|
||||
.map(Srgb::<u8>::from_linear)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
colors: rgb_u8_colors,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors set to the specified HSL
|
||||
/// lightness value.
|
||||
pub fn with_lightness(&self, assign_lightness: AssignLightness) -> Self {
|
||||
let mut rgb_f32_colors: Vec<_> =
|
||||
self.colors.iter().map(|c| c.into_format::<f32>()).collect();
|
||||
|
||||
{
|
||||
let hsl_f32_colors: &mut [Hsl] = &mut rgb_f32_colors.into_color_mut();
|
||||
|
||||
for hsl_f32_color in hsl_f32_colors {
|
||||
match assign_lightness {
|
||||
AssignLightness::Replace(lightness) => {
|
||||
hsl_f32_color.lightness = lightness.into();
|
||||
},
|
||||
AssignLightness::ClampMax(lightness) => {
|
||||
hsl_f32_color.lightness.clamp_max_assign(lightness.into());
|
||||
},
|
||||
AssignLightness::ClampMin(lightness) => {
|
||||
hsl_f32_color.lightness.clamp_min_assign(lightness.into());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_u8_colors: Vec<_> = rgb_f32_colors
|
||||
.into_iter()
|
||||
.map(|c| c.into_format::<u8>())
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
colors: rgb_u8_colors,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors set to the specified HSL
|
||||
/// lightness value, with respect to dark/light terminals.
|
||||
pub fn with_lightness_dl(&self, lightness: Lightness, term: LightDark) -> Self {
|
||||
match term {
|
||||
LightDark::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
|
||||
LightDark::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates another color profile with only the unique colors.
|
||||
pub fn unique_colors(&self) -> Self {
|
||||
let unique_colors = self.colors.iter().collect::<IndexSet<_>>();
|
||||
let unique_colors: IndexSet<[u8; 3]> = self.colors.iter().map(|c| (*c).into()).collect();
|
||||
let unique_colors = {
|
||||
let mut v = Vec::with_capacity(unique_colors.len());
|
||||
v.extend(unique_colors);
|
||||
v.extend(unique_colors.into_iter().map(Srgb::<u8>::from));
|
||||
v
|
||||
};
|
||||
Self::new(unique_colors)
|
||||
|
|
|
|||
12
crates/hyfetch/src/utils.rs
Normal file
12
crates/hyfetch/src/utils.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use directories::ProjectDirs;
|
||||
|
||||
pub fn get_cache_path() -> Result<PathBuf> {
|
||||
let path = ProjectDirs::from("", "", "hyfetch")
|
||||
.context("failed to get base dirs")?
|
||||
.cache_dir()
|
||||
.to_owned();
|
||||
Ok(path)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue