use crate::color::*;
fn map_range(x: f32, in_min: f32, in_max: f32, out_min: f32, out_max: f32) -> f32 {
(x - in_min) / (in_max - in_min) * (out_max - out_min) + out_min
}
use color_thief::{get_palette, ColorFormat};
use image::GenericImageView;
use palette::{Lcha, Okhsla};
use rand::{rngs::SmallRng, Rng, SeedableRng};
use std::{
f32::consts::TAU,
ops::{Index, IndexMut},
path::Path,
};
use tiny_skia::Color;
#[derive(Clone, Debug)]
pub struct Palette {
pub colors: Vec<Color>,
rng: SmallRng,
pub current: usize,
}
impl Default for Palette {
fn default() -> Self {
Palette::new(vec![])
}
}
impl Palette {
pub fn new(colors: Vec<Color>) -> Self {
let rng = SmallRng::seed_from_u64(0);
Palette {
colors,
rng,
current: 0,
}
}
pub fn set_seed(&mut self, seed: u64) {
self.rng = SmallRng::seed_from_u64(seed);
}
pub fn set_index(&mut self, i: usize) {
self.current = i % self.colors.len();
}
pub fn with_img<T: AsRef<Path>>(path: T, n: Option<usize>) -> Self {
let img = image::open(path).expect("Could not find image file");
let mut cs: Vec<Color> = vec![];
let w = img.width();
let h = img.height();
if let Some(n) = n {
let delta = (w as f32 * h as f32 / n as f32).sqrt();
let mut x = 0.0;
let mut y = 0.0;
while x < w as f32 {
while y < h as f32 {
let p = img.get_pixel(x as u32, y as u32);
cs.push(p.to_color());
y += delta;
}
x += delta;
y = 0.0;
}
cs.truncate(n)
} else {
for (_, _, p) in img.pixels() {
cs.push(p.to_color());
}
cs.sort_by_cached_key(|c| c.as_u8s());
cs.dedup_by_key(|c| c.as_u8s());
}
Self::new(cs)
}
pub fn steal<T: AsRef<Path>>(path: T, max_colors: u8) -> Self {
fn find_color(t: image::ColorType) -> ColorFormat {
match t {
image::ColorType::Rgb8 => ColorFormat::Rgb,
image::ColorType::Rgba8 => ColorFormat::Rgba,
_ => unreachable!(),
}
}
let img = image::open(path).expect("Could not find image file");
let color_type = find_color(img.color());
let palette = get_palette(img.as_bytes(), color_type, 10, max_colors).unwrap();
let palette = palette
.into_iter()
.map(|c| Color::from_rgba8(c.r, c.g, c.b, 255));
Self::new(palette.collect())
}
pub fn tighten(&mut self) {
self.colors = self.colors.iter().map(|c| c.tighten()).collect();
}
pub fn spread(&mut self) {
self.colors = self.colors.iter().map(|c| c.spread()).collect();
}
pub fn rotate_hue(&mut self, degrees: f32) {
self.colors = self.colors.iter().map(|c| c.rotate_hue(degrees)).collect();
}
pub fn saturate(&mut self, factor: f32) {
self.colors = self.colors.iter().map(|c| c.saturate(factor)).collect();
}
pub fn saturate_fixed(&mut self, amount: f32) {
self.colors = self
.colors
.iter()
.map(|c| c.saturate_fixed(amount))
.collect();
}
pub fn desaturate(&mut self, factor: f32) {
self.colors = self.colors.iter().map(|c| c.desaturate(factor)).collect();
}
pub fn desaturate_fixed(&mut self, amount: f32) {
self.colors = self
.colors
.iter()
.map(|c| c.desaturate_fixed(amount))
.collect();
}
pub fn lighten(&mut self, factor: f32) {
self.colors = self.colors.iter().map(|c| c.lighten(factor)).collect();
}
pub fn lighten_fixed(&mut self, amount: f32) {
self.colors = self
.colors
.iter()
.map(|c| c.lighten_fixed(amount))
.collect();
}
pub fn darken(&mut self, factor: f32) {
self.colors = self.colors.iter().map(|c| c.darken(factor)).collect();
}
pub fn darken_fixed(&mut self, amount: f32) {
self.colors = self.colors.iter().map(|c| c.darken_fixed(amount)).collect();
}
pub fn sort_by_hue(&mut self) {
self.colors.sort_by_cached_key(|c| {
let okhsla: Okhsla = <Okhsla as ConvertColor>::from_color(c);
(1000.0 * okhsla.hue.into_radians()) as u32
})
}
pub fn sort_by_saturation(&mut self) {
self.colors.sort_by_cached_key(|c| {
let okhsla: Okhsla = <Okhsla as ConvertColor>::from_color(c);
(1000.0 * okhsla.saturation) as u32
})
}
pub fn sort_by_lightness(&mut self) {
self.colors.sort_by_cached_key(|c| {
let okhsla: Okhsla = <Okhsla as ConvertColor>::from_color(c);
(1000.0 * okhsla.lightness) as u32
})
}
pub fn sort_by_chroma(&mut self) {
self.colors.sort_by_cached_key(|c| {
let lcha: Lcha = <Lcha as ConvertColor>::from_color(c);
(1000.0 * lcha.chroma) as u32
})
}
pub fn sort_by_alpha(&mut self) {
self.colors.sort_by_cached_key(|c| {
let okhsla: Okhsla = <Okhsla as ConvertColor>::from_color(c);
(1000.0 * okhsla.alpha) as u32
})
}
pub fn rand_color(&mut self) -> Color {
self.colors[self.rng.random_range(0..self.colors.len())]
}
pub fn jiggle(&mut self, std_dev: f32) {
let cs: Vec<Color> = self
.colors
.iter()
.map(|c| jiggle(&mut self.rng, std_dev, *c))
.collect();
self.colors = cs;
}
pub fn len(&self) -> usize {
self.colors.len()
}
pub fn is_empty(&self) -> bool {
self.colors.is_empty()
}
}
impl Index<usize> for Palette {
type Output = Color;
fn index(&self, index: usize) -> &Self::Output {
let index = index % self.colors.len();
&self.colors[index]
}
}
impl IndexMut<usize> for Palette {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let index = index % self.colors.len();
&mut self.colors[index]
}
}
impl IntoIterator for Palette {
type Item = Color;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.colors.into_iter()
}
}
#[derive(Clone, Debug)]
pub struct ColorScale {
pub a0: (f32, f32, f32),
pub a1: (f32, f32, f32),
pub a2: (f32, f32, f32),
pub b1: (f32, f32, f32),
pub b2: (f32, f32, f32),
}
fn f(x: f32) -> f32 {
2.0 * x - 1.0
}
fn coefficients(
channel0: f32,
channel1: f32,
channel2: f32,
channel3: f32,
channel4: f32,
) -> (f32, f32, f32, f32, f32) {
let a0 = 0.3333 * f(channel0) + 0.3333 * f(channel2) + 0.3333 * f(channel4);
let a1 = 0.5 * f(channel0) - 0.5 * f(channel1) - 0.5 * f(channel3) + 0.5 * f(channel4);
let a2 = 0.1667 * f(channel0) - 0.5 * f(channel1) + 0.6667 * f(channel2) - 0.5 * f(channel3)
+ 0.1667 * f(channel4);
let b1 =
0.2887 * f(channel0) + 0.2887 * f(channel1) - 0.2887 * f(channel3) - 0.2887 * f(channel4);
let b2 =
0.2887 * f(channel0) - 0.2887 * f(channel1) + 0.2887 * f(channel3) - 0.2887 * f(channel4);
(a0, a1, a2, b1, b2)
}
impl ColorScale {
pub fn new(color0: Color, color1: Color, color2: Color, color3: Color, color4: Color) -> Self {
let (ra0, ra1, ra2, rb1, rb2) = coefficients(
color0.red(),
color1.red(),
color2.red(),
color3.red(),
color4.red(),
);
let (ga0, ga1, ga2, gb1, gb2) = coefficients(
color0.green(),
color1.green(),
color2.green(),
color3.green(),
color4.green(),
);
let (ba0, ba1, ba2, bb1, bb2) = coefficients(
color0.blue(),
color1.blue(),
color2.blue(),
color3.blue(),
color4.blue(),
);
Self {
a0: (ra0, ga0, ba0),
a1: (ra1, ga1, ba1),
a2: (ra2, ga2, ba2),
b1: (rb1, gb1, bb1),
b2: (rb2, gb2, bb2),
}
}
pub fn from_2(color0: Color, color4: Color) -> Self {
let color1 = color0.lerp(&color4, 0.25);
let color2 = color0.lerp(&color4, 0.5);
let color3 = color0.lerp(&color4, 0.75);
Self::new(color0, color1, color2, color3, color4)
}
pub fn from_3_colors(color0: Color, color2: Color, color4: Color) -> Self {
let color1 = color0.lerp(&color2, 0.5);
let color3 = color2.lerp(&color4, 0.5);
Self::new(color0, color1, color2, color3, color4)
}
pub fn from_3_bw(color0: Color, color2: Color, color4: Color) -> Self {
let color1 = Color::from_rgba8(0, 0, 0, 255);
let color3 = Color::from_rgba8(255, 255, 255, 255);
Self::new(color0, color1, color2, color3, color4)
}
pub fn get_color(&self, t: f32) -> Color {
let red = 0.5
+ 0.5
* (self.a0.0
+ self.a1.0 * (TAU * t).cos()
+ self.a2.0 * (2.0 * TAU * t).cos()
+ self.b1.0 * (TAU * t).sin()
+ self.b2.0 * (2.0 * TAU * t).sin());
let green = 0.5
+ 0.5
* (self.a0.1
+ self.a1.1 * (TAU * t).cos()
+ self.a2.1 * (2.0 * TAU * t).cos()
+ self.b1.1 * (TAU * t).sin()
+ self.b2.1 * (2.0 * TAU * t).sin());
let blue = 0.5
+ 0.5
* (self.a0.2
+ self.a1.2 * (TAU * t).cos()
+ self.a2.2 * (2.0 * TAU * t).cos()
+ self.b1.2 * (TAU * t).sin()
+ self.b2.2 * (2.0 * TAU * t).sin());
Color::from_rgba(
red.clamp(0.0, 1.0),
green.clamp(0.0, 1.0),
blue.clamp(0.0, 1.0),
1.0,
)
.unwrap()
}
pub fn get_color_fractal(&self, t: f32, phase: f32) -> Color {
let a = self.get_color(t);
let b = self.get_color(2.0 * t).rotate_hue(phase);
let c = self.get_color(3.0 * t).rotate_hue(2.0 * phase);
let d = b.lerp(&c, 0.5);
a.lerp(&d, 0.5)
}
pub fn get_color_clip(&self, t: f32, clip: f32) -> Color {
let t = map_range(t, 0.0, 1.0, clip, 1.0 - clip);
self.get_color(t)
}
}