use exoquant::Color;
use serde::Serialize;
use std::cmp::Ordering;
#[derive(Debug, Clone, Serialize)]
pub struct HSVA {
pub h: f32,
pub s: f32,
pub v: f32,
pub a: f32,
}
#[derive(Debug, Clone, Serialize, Eq)]
pub enum NHPaletteItem {
Color{
h: u8,
s: u8,
v: u8,
},
Transparent,
}
impl Ord for NHPaletteItem {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(NHPaletteItem::Transparent, NHPaletteItem::Transparent) => Ordering::Equal,
(NHPaletteItem::Color{..}, NHPaletteItem::Transparent) => Ordering::Less,
(NHPaletteItem::Transparent, NHPaletteItem::Color{..}) => Ordering::Greater,
(NHPaletteItem::Color{h: lh, s: ls, v:lv, ..}, NHPaletteItem::Color{h: rh, s: rs, v:rv, ..}) => {
match lh.cmp(rh) {
Ordering::Equal => {
match ls.cmp(rs) {
Ordering::Equal => lv.cmp(rv),
o => o,
}
},
o => o,
}
},
}
}
}
impl PartialOrd for NHPaletteItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for NHPaletteItem {
fn eq(&self, other: &Self) -> bool {
if let Ordering::Equal = self.cmp(other) {
true
} else {
false
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct HSVRGBA {
pub h: u8,
pub s: u8,
pub v: u8,
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Default for HSVA {
fn default() -> Self {
HSVA {
h: 0.0,
s: 0.0,
v: 0.0,
a: 1.0,
}
}
}
impl Default for NHPaletteItem {
fn default() -> Self {
NHPaletteItem::Color{
h: 0,
s: 0,
v: 0,
}
}
}
impl From<&Color> for HSVA {
fn from(color: &Color) -> Self {
let a = color.a as f32 / 255.0;
let r_prime = color.r as f32 / 255.0;
if color.r == color.g && color.g == color.b {
return HSVA {
h: 0.0,
s: 0.0,
v: r_prime,
a,
};
}
let g_prime = color.g as f32 / 255.0;
let b_prime = color.b as f32 / 255.0;
let c_max = r_prime.max(g_prime).max(b_prime);
let c_min = r_prime.min(g_prime).min(b_prime);
let delta = c_max - c_min;
let mut hue = 60.0
* if c_max == r_prime {
(g_prime - b_prime) / delta
} else if c_max == g_prime {
(b_prime - r_prime) / delta + 2.0
} else {
(r_prime - g_prime) / delta + 4.0
};
while hue < 0.0 {
hue += 360.0;
}
while hue >= 360.0 {
hue -= 360.0;
}
let saturation = if c_max == 0.0 { 0.0 } else { delta / c_max };
HSVA {
h: hue,
s: saturation,
v: c_max,
a,
}
}
}
impl From<&HSVA> for Color {
fn from(hsv: &HSVA) -> Self {
let a = (hsv.a * 255.0).round() as u8;
if hsv.v == 0.0 {
return Color::new(0, 0, 0, a);
} else if hsv.s == 0.0 {
let v = (hsv.v * 255.0).round() as u8;
return Color::new(v, v, v, a);
}
let chroma = hsv.v * hsv.s;
let h_prime = hsv.h / 60.0;
let x = chroma * (1.0 - (h_prime % 2.0 - 1.0).abs());
let (r_prime, g_prime, b_prime) = if h_prime >= 0.0 && h_prime <= 1.0 {
(chroma, x, 0.0)
} else if h_prime <= 2.0 {
(x, chroma, 0.0)
} else if h_prime <= 3.0 {
(0.0, chroma, x)
} else if h_prime <= 4.0 {
(0.0, x, chroma)
} else if h_prime <= 5.0 {
(x, 0.0, chroma)
} else if h_prime <= 6.0 {
(chroma, 0.0, x)
} else {
panic!("this should never ever happen");
};
let m = hsv.v - chroma;
Color {
r: ((r_prime + m) * 255.0).round() as u8,
g: ((g_prime + m) * 255.0).round() as u8,
b: ((b_prime + m) * 255.0).round() as u8,
a,
}
}
}
impl From<&HSVA> for NHPaletteItem {
fn from(hsv: &HSVA) -> Self {
if hsv.a == 0.0 {
NHPaletteItem::Transparent
} else {
let v = ((hsv.v * 15.0).round() as u8).min(14).max(0);
let s = if v == 0 {
0
} else {
((hsv.s * 15.0).round() as u8).min(14).max(0)
};
let h = if s == 0 {
0
} else {
((hsv.h / 360.0 * 30.0).round() as u8) % 30
};
NHPaletteItem::Color{
h,
s,
v,
}
}
}
}
impl From<&NHPaletteItem> for HSVA {
fn from(palette_item: &NHPaletteItem) -> Self {
match palette_item {
NHPaletteItem::Transparent => HSVA {
h: 0.0,
s: 0.0,
v: 0.0,
a: 0.0,
},
NHPaletteItem::Color{h, s, v, ..} => HSVA {
h: *h as f32 * (360.0 / 30.0),
s: *s as f32 / 15.0,
v: *v as f32 / 15.0,
a: 1.0,
},
}
}
}
impl From<&Color> for NHPaletteItem {
fn from(color: &Color) -> Self {
let hsv: HSVA = color.into();
(&hsv).into()
}
}
impl From<&NHPaletteItem> for Color {
fn from(palette_item: &NHPaletteItem) -> Self {
let hsv: HSVA = palette_item.into();
(&hsv).into()
}
}
impl From<&HSVRGBA> for NHPaletteItem {
fn from(color: &HSVRGBA) -> Self {
if color.a == 0 {
NHPaletteItem::Transparent
} else {
NHPaletteItem::Color{
h: color.h,
s: color.s,
v: color.v,
}
}
}
}
impl From<&NHPaletteItem> for HSVRGBA {
fn from(palette_item: &NHPaletteItem) -> Self {
let color: Color = palette_item.into();
let (h, s, v) = match palette_item {
NHPaletteItem::Transparent => (0, 0, 0),
NHPaletteItem::Color{h, s, v, ..} => (*h, *s, *v),
};
HSVRGBA {
h,
s,
v,
r: color.r,
g: color.g,
b: color.b,
a: color.a,
}
}
}
impl From<Color> for HSVA {
fn from(color: Color) -> Self {
(&color).into()
}
}
impl From<HSVA> for Color {
fn from(hsv: HSVA) -> Self {
(&hsv).into()
}
}
impl From<HSVA> for NHPaletteItem {
fn from(hsv: HSVA) -> Self {
(&hsv).into()
}
}
impl From<NHPaletteItem> for HSVA {
fn from(palette_item: NHPaletteItem) -> Self {
(&palette_item).into()
}
}
impl From<Color> for NHPaletteItem {
fn from(color: Color) -> Self {
(&color).into()
}
}
impl From<NHPaletteItem> for Color {
fn from(palette_item: NHPaletteItem) -> Self {
(&palette_item).into()
}
}
impl From<HSVRGBA> for NHPaletteItem {
fn from(color: HSVRGBA) -> Self {
(&color).into()
}
}
impl From<NHPaletteItem> for HSVRGBA {
fn from(palette_item: NHPaletteItem) -> Self {
(&palette_item).into()
}
}