get rid of palette dependency
This commit is contained in:
parent
2b7d8eaaca
commit
44e13db1ef
7 changed files with 119 additions and 99 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2527,7 +2527,6 @@ dependencies = [
|
||||||
"lilt",
|
"lilt",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"palette",
|
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,6 @@ lyon = "1.0"
|
||||||
lyon_path = "1.0"
|
lyon_path = "1.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
ouroboros = "0.18"
|
ouroboros = "0.18"
|
||||||
palette = "0.7"
|
|
||||||
png = "0.17"
|
png = "0.17"
|
||||||
pulldown-cmark = "0.12"
|
pulldown-cmark = "0.12"
|
||||||
qrcode = { version = "0.13", default-features = false }
|
qrcode = { version = "0.13", default-features = false }
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ glam.workspace = true
|
||||||
lilt.workspace = true
|
lilt.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
palette.workspace = true
|
|
||||||
rustc-hash.workspace = true
|
rustc-hash.workspace = true
|
||||||
smol_str.workspace = true
|
smol_str.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use palette::rgb::{Srgb, Srgba};
|
|
||||||
|
|
||||||
/// A color in the `sRGB` color space.
|
/// A color in the `sRGB` color space.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
|
|
@ -242,70 +240,9 @@ macro_rules! color {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts from palette's `Rgba` type to a [`Color`].
|
|
||||||
impl From<Srgba> for Color {
|
|
||||||
fn from(rgba: Srgba) -> Self {
|
|
||||||
Color::new(rgba.red, rgba.green, rgba.blue, rgba.alpha)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts from [`Color`] to palette's `Rgba` type.
|
|
||||||
impl From<Color> for Srgba {
|
|
||||||
fn from(c: Color) -> Self {
|
|
||||||
Srgba::new(c.r, c.g, c.b, c.a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts from palette's `Rgb` type to a [`Color`].
|
|
||||||
impl From<Srgb> for Color {
|
|
||||||
fn from(rgb: Srgb) -> Self {
|
|
||||||
Color::new(rgb.red, rgb.green, rgb.blue, 1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts from [`Color`] to palette's `Rgb` type.
|
|
||||||
impl From<Color> for Srgb {
|
|
||||||
fn from(c: Color) -> Self {
|
|
||||||
Srgb::new(c.r, c.g, c.b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use palette::blend::Blend;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn srgba_traits() {
|
|
||||||
let c = Color::from_rgb(0.5, 0.4, 0.3);
|
|
||||||
// Round-trip conversion to the palette::Srgba type
|
|
||||||
let s: Srgba = c.into();
|
|
||||||
let r: Color = s.into();
|
|
||||||
assert_eq!(c, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn color_manipulation() {
|
|
||||||
use approx::assert_relative_eq;
|
|
||||||
|
|
||||||
let c1 = Color::from_rgb(0.5, 0.4, 0.3);
|
|
||||||
let c2 = Color::from_rgb(0.2, 0.5, 0.3);
|
|
||||||
|
|
||||||
// Convert to linear color for manipulation
|
|
||||||
let l1 = Srgba::from(c1).into_linear();
|
|
||||||
let l2 = Srgba::from(c2).into_linear();
|
|
||||||
|
|
||||||
// Take the lighter of each of the sRGB components
|
|
||||||
let lighter = l1.lighten(l2);
|
|
||||||
|
|
||||||
// Convert back to our Color
|
|
||||||
let result: Color = Srgba::from_linear(lighter).into();
|
|
||||||
|
|
||||||
assert_relative_eq!(result.r, 0.5);
|
|
||||||
assert_relative_eq!(result.g, 0.5);
|
|
||||||
assert_relative_eq!(result.b, 0.3);
|
|
||||||
assert_relative_eq!(result.a, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
//! Define the colors of a theme.
|
//! Define the colors of a theme.
|
||||||
use crate::{Color, color};
|
use crate::{Color, color};
|
||||||
|
|
||||||
use palette::color_difference::Wcag21RelativeContrast;
|
|
||||||
use palette::rgb::Rgb;
|
|
||||||
use palette::{FromColor, Hsl, Mix};
|
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
/// A color palette.
|
/// A color palette.
|
||||||
|
|
@ -607,13 +603,20 @@ impl Danger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Hsl {
|
||||||
|
h: f32,
|
||||||
|
s: f32,
|
||||||
|
l: f32,
|
||||||
|
a: f32,
|
||||||
|
}
|
||||||
|
|
||||||
fn darken(color: Color, amount: f32) -> Color {
|
fn darken(color: Color, amount: f32) -> Color {
|
||||||
let mut hsl = to_hsl(color);
|
let mut hsl = to_hsl(color);
|
||||||
|
|
||||||
hsl.lightness = if hsl.lightness - amount < 0.0 {
|
hsl.l = if hsl.l - amount < 0.0 {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
hsl.lightness - amount
|
hsl.l - amount
|
||||||
};
|
};
|
||||||
|
|
||||||
from_hsl(hsl)
|
from_hsl(hsl)
|
||||||
|
|
@ -622,10 +625,10 @@ fn darken(color: Color, amount: f32) -> Color {
|
||||||
fn lighten(color: Color, amount: f32) -> Color {
|
fn lighten(color: Color, amount: f32) -> Color {
|
||||||
let mut hsl = to_hsl(color);
|
let mut hsl = to_hsl(color);
|
||||||
|
|
||||||
hsl.lightness = if hsl.lightness + amount > 1.0 {
|
hsl.l = if hsl.l + amount > 1.0 {
|
||||||
1.0
|
1.0
|
||||||
} else {
|
} else {
|
||||||
hsl.lightness + amount
|
hsl.l + amount
|
||||||
};
|
};
|
||||||
|
|
||||||
from_hsl(hsl)
|
from_hsl(hsl)
|
||||||
|
|
@ -642,17 +645,24 @@ fn deviate(color: Color, amount: f32) -> Color {
|
||||||
fn muted(color: Color) -> Color {
|
fn muted(color: Color) -> Color {
|
||||||
let mut hsl = to_hsl(color);
|
let mut hsl = to_hsl(color);
|
||||||
|
|
||||||
hsl.saturation = hsl.saturation.min(0.5);
|
hsl.s = hsl.s.min(0.5);
|
||||||
|
|
||||||
from_hsl(hsl)
|
from_hsl(hsl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix(a: Color, b: Color, factor: f32) -> Color {
|
fn mix(a: Color, b: Color, factor: f32) -> Color {
|
||||||
let a_lin = Rgb::from(a).into_linear();
|
let b_amount = factor.clamp(0.0, 1.0);
|
||||||
let b_lin = Rgb::from(b).into_linear();
|
let a_amount = 1.0 - b_amount;
|
||||||
|
|
||||||
let mixed = a_lin.mix(b_lin, factor);
|
let a_linear = a.into_linear().map(|c| c * a_amount);
|
||||||
Rgb::from_linear(mixed).into()
|
let b_linear = b.into_linear().map(|c| c * b_amount);
|
||||||
|
|
||||||
|
Color::from_linear_rgba(
|
||||||
|
a_linear[0] + b_linear[0],
|
||||||
|
a_linear[1] + b_linear[1],
|
||||||
|
a_linear[2] + b_linear[2],
|
||||||
|
a_linear[3] + b_linear[3],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readable(background: Color, text: Color) -> Color {
|
fn readable(background: Color, text: Color) -> Color {
|
||||||
|
|
@ -680,27 +690,85 @@ fn readable(background: Color, text: Color) -> Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dark(color: Color) -> bool {
|
fn is_dark(color: Color) -> bool {
|
||||||
to_hsl(color).lightness < 0.6
|
to_hsl(color).l < 0.6
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_readable(a: Color, b: Color) -> bool {
|
fn is_readable(a: Color, b: Color) -> bool {
|
||||||
let a_srgb = Rgb::from(a);
|
relative_contrast(a, b) >= 7.0
|
||||||
let b_srgb = Rgb::from(b);
|
|
||||||
|
|
||||||
a_srgb.has_enhanced_contrast_text(b_srgb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
|
||||||
fn relative_contrast(a: Color, b: Color) -> f32 {
|
fn relative_contrast(a: Color, b: Color) -> f32 {
|
||||||
let a_srgb = Rgb::from(a);
|
let lum_a = relative_luminance(a);
|
||||||
let b_srgb = Rgb::from(b);
|
let lum_b = relative_luminance(b);
|
||||||
|
(lum_a.max(lum_b) + 0.05) / (lum_a.min(lum_b) + 0.05)
|
||||||
a_srgb.relative_contrast(b_srgb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
|
||||||
|
fn relative_luminance(color: Color) -> f32 {
|
||||||
|
let linear = color.into_linear();
|
||||||
|
0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
||||||
fn to_hsl(color: Color) -> Hsl {
|
fn to_hsl(color: Color) -> Hsl {
|
||||||
Hsl::from_color(Rgb::from(color))
|
let x_max = color.r.max(color.g).max(color.b);
|
||||||
|
let x_min = color.r.min(color.g).min(color.b);
|
||||||
|
let c = x_max - x_min;
|
||||||
|
let l = x_max.midpoint(x_min);
|
||||||
|
|
||||||
|
let h = if c == 0.0 {
|
||||||
|
0.0
|
||||||
|
} else if x_max == color.r {
|
||||||
|
60.0 * ((color.g - color.b) / c).rem_euclid(6.0)
|
||||||
|
} else if x_max == color.g {
|
||||||
|
60.0 * (((color.b - color.r) / c) + 2.0)
|
||||||
|
} else {
|
||||||
|
// x_max == color.b
|
||||||
|
60.0 * (((color.r - color.g) / c) + 4.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = if l == 0.0 || l == 1.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
(x_max - l) / l.min(1.0 - l)
|
||||||
|
};
|
||||||
|
|
||||||
|
Hsl {
|
||||||
|
h,
|
||||||
|
s,
|
||||||
|
l,
|
||||||
|
a: color.a,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB
|
||||||
fn from_hsl(hsl: Hsl) -> Color {
|
fn from_hsl(hsl: Hsl) -> Color {
|
||||||
Rgb::from_color(hsl).into()
|
let c = (1.0 - (2.0 * hsl.l - 1.0).abs()) * hsl.s;
|
||||||
|
let h = hsl.h / 60.0;
|
||||||
|
let x = c * (1.0 - (h.rem_euclid(2.0) - 1.0).abs());
|
||||||
|
|
||||||
|
let (r1, g1, b1) = if h < 1.0 {
|
||||||
|
(c, x, 0.0)
|
||||||
|
} else if h < 2.0 {
|
||||||
|
(x, c, 0.0)
|
||||||
|
} else if h < 3.0 {
|
||||||
|
(0.0, c, x)
|
||||||
|
} else if h < 4.0 {
|
||||||
|
(0.0, x, c)
|
||||||
|
} else if h < 5.0 {
|
||||||
|
(x, 0.0, c)
|
||||||
|
} else {
|
||||||
|
// h < 6.0
|
||||||
|
(c, 0.0, x)
|
||||||
|
};
|
||||||
|
|
||||||
|
let m = hsl.l - (c / 2.0);
|
||||||
|
|
||||||
|
Color {
|
||||||
|
r: r1 + m,
|
||||||
|
g: g1 + m,
|
||||||
|
b: b1 + m,
|
||||||
|
a: hsl.a,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,4 @@ publish = false
|
||||||
iced.workspace = true
|
iced.workspace = true
|
||||||
iced.features = ["canvas"]
|
iced.features = ["canvas"]
|
||||||
|
|
||||||
palette.workspace = true
|
palette = "0.7"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ pub enum Message {
|
||||||
impl ColorPalette {
|
impl ColorPalette {
|
||||||
fn update(&mut self, message: Message) {
|
fn update(&mut self, message: Message) {
|
||||||
let srgb = match message {
|
let srgb = match message {
|
||||||
Message::RgbColorChanged(rgb) => Rgb::from(rgb),
|
Message::RgbColorChanged(rgb) => to_rgb(rgb),
|
||||||
Message::HslColorChanged(hsl) => Rgb::from_color(hsl),
|
Message::HslColorChanged(hsl) => Rgb::from_color(hsl),
|
||||||
Message::HsvColorChanged(hsv) => Rgb::from_color(hsv),
|
Message::HsvColorChanged(hsv) => Rgb::from_color(hsv),
|
||||||
Message::HwbColorChanged(hwb) => Rgb::from_color(hwb),
|
Message::HwbColorChanged(hwb) => Rgb::from_color(hwb),
|
||||||
|
|
@ -54,13 +54,13 @@ impl ColorPalette {
|
||||||
Message::LchColorChanged(lch) => Rgb::from_color(lch),
|
Message::LchColorChanged(lch) => Rgb::from_color(lch),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.theme = Theme::new(srgb);
|
self.theme = Theme::new(to_color(srgb));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
let base = self.theme.base;
|
let base = self.theme.base;
|
||||||
|
|
||||||
let srgb = Rgb::from(base);
|
let srgb = to_rgb(base);
|
||||||
let hsl = palette::Hsl::from_color(srgb);
|
let hsl = palette::Hsl::from_color(srgb);
|
||||||
let hsv = palette::Hsv::from_color(srgb);
|
let hsv = palette::Hsv::from_color(srgb);
|
||||||
let hwb = palette::Hwb::from_color(srgb);
|
let hwb = palette::Hwb::from_color(srgb);
|
||||||
|
|
@ -109,7 +109,7 @@ impl Theme {
|
||||||
let base = base.into();
|
let base = base.into();
|
||||||
|
|
||||||
// Convert to HSL color for manipulation
|
// Convert to HSL color for manipulation
|
||||||
let hsl = Hsl::from_color(Rgb::from(base));
|
let hsl = Hsl::from_color(to_rgb(base));
|
||||||
|
|
||||||
let lower = [
|
let lower = [
|
||||||
hsl.shift_hue(-135.0).lighten(0.075),
|
hsl.shift_hue(-135.0).lighten(0.075),
|
||||||
|
|
@ -128,12 +128,12 @@ impl Theme {
|
||||||
Theme {
|
Theme {
|
||||||
lower: lower
|
lower: lower
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&color| Rgb::from_color(color).into())
|
.map(|&color| to_color(Rgb::from_color(color)))
|
||||||
.collect(),
|
.collect(),
|
||||||
base,
|
base,
|
||||||
higher: higher
|
higher: higher
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&color| Rgb::from_color(color).into())
|
.map(|&color| to_color(Rgb::from_color(color)))
|
||||||
.collect(),
|
.collect(),
|
||||||
canvas_cache: canvas::Cache::default(),
|
canvas_cache: canvas::Cache::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -216,14 +216,14 @@ impl Theme {
|
||||||
|
|
||||||
text.align_y = alignment::Vertical::Bottom;
|
text.align_y = alignment::Vertical::Bottom;
|
||||||
|
|
||||||
let hsl = Hsl::from_color(Rgb::from(self.base));
|
let hsl = Hsl::from_color(to_rgb(self.base));
|
||||||
for i in 0..self.len() {
|
for i in 0..self.len() {
|
||||||
let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0);
|
let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0);
|
||||||
let graded = Hsl {
|
let graded = Hsl {
|
||||||
lightness: 1.0 - pct,
|
lightness: 1.0 - pct,
|
||||||
..hsl
|
..hsl
|
||||||
};
|
};
|
||||||
let color: Color = Rgb::from_color(graded).into();
|
let color: Color = to_color(Rgb::from_color(graded));
|
||||||
|
|
||||||
let anchor = Point {
|
let anchor = Point {
|
||||||
x: (i as f32) * box_size.width,
|
x: (i as f32) * box_size.width,
|
||||||
|
|
@ -475,3 +475,21 @@ impl ColorSpace for palette::Lch {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_rgb(color: Color) -> Rgb {
|
||||||
|
Rgb {
|
||||||
|
red: color.r,
|
||||||
|
green: color.g,
|
||||||
|
blue: color.b,
|
||||||
|
..Rgb::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_color(rgb: Rgb) -> Color {
|
||||||
|
Color {
|
||||||
|
r: rgb.red,
|
||||||
|
g: rgb.green,
|
||||||
|
b: rgb.blue,
|
||||||
|
a: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue