Files
Cagire/crates/ratatui/src/theme/transform.rs

100 lines
3.0 KiB
Rust

//! Hue rotation for palette-wide color transforms.
use super::palette::{Palette, Rgb};
use super::build::build;
use super::ThemeColors;
fn rgb_to_hsv(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
let r = r as f32 / 255.0;
let g = g as f32 / 255.0;
let b = b as f32 / 255.0;
let max = r.max(g).max(b);
let min = r.min(g).min(b);
let delta = max - min;
let h = if delta == 0.0 {
0.0
} else if max == r {
60.0 * (((g - b) / delta) % 6.0)
} else if max == g {
60.0 * (((b - r) / delta) + 2.0)
} else {
60.0 * (((r - g) / delta) + 4.0)
};
let h = if h < 0.0 { h + 360.0 } else { h };
let s = if max == 0.0 { 0.0 } else { delta / max };
(h, s, max)
}
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
let c = v * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = v - c;
let (r, g, b) = if h < 60.0 {
(c, x, 0.0)
} else if h < 120.0 {
(x, c, 0.0)
} else if h < 180.0 {
(0.0, c, x)
} else if h < 240.0 {
(0.0, x, c)
} else if h < 300.0 {
(x, 0.0, c)
} else {
(c, 0.0, x)
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
fn rotate(c: Rgb, degrees: f32) -> Rgb {
let (h, s, v) = rgb_to_hsv(c.0, c.1, c.2);
let new_h = (h + degrees) % 360.0;
let new_h = if new_h < 0.0 { new_h + 360.0 } else { new_h };
hsv_to_rgb(new_h, s, v)
}
fn rotate5(arr: [Rgb; 5], d: f32) -> [Rgb; 5] {
[rotate(arr[0], d), rotate(arr[1], d), rotate(arr[2], d), rotate(arr[3], d), rotate(arr[4], d)]
}
fn rotate3(arr: [Rgb; 3], d: f32) -> [Rgb; 3] {
[rotate(arr[0], d), rotate(arr[1], d), rotate(arr[2], d)]
}
/// Build a [`ThemeColors`] with all palette hues rotated by `degrees`.
pub fn rotate_palette(palette: &Palette, degrees: f32) -> ThemeColors {
if degrees == 0.0 {
return build(palette);
}
let d = degrees;
build(&Palette {
bg: rotate(palette.bg, d),
surface: rotate(palette.surface, d),
surface2: rotate(palette.surface2, d),
fg: rotate(palette.fg, d),
fg_dim: rotate(palette.fg_dim, d),
fg_muted: rotate(palette.fg_muted, d),
accent: rotate(palette.accent, d),
red: rotate(palette.red, d),
green: rotate(palette.green, d),
yellow: rotate(palette.yellow, d),
blue: rotate(palette.blue, d),
purple: rotate(palette.purple, d),
cyan: rotate(palette.cyan, d),
orange: rotate(palette.orange, d),
tempo_color: rotate(palette.tempo_color, d),
bank_color: rotate(palette.bank_color, d),
pattern_color: rotate(palette.pattern_color, d),
title_accent: rotate(palette.title_accent, d),
title_author: rotate(palette.title_author, d),
secondary: rotate(palette.secondary, d),
link_bright: rotate5(palette.link_bright, d),
link_dim: rotate5(palette.link_dim, d),
sparkle: rotate5(palette.sparkle, d),
meter: rotate3(palette.meter, d),
})
}