Attempt to implement nearest colors in different color spaces.
This commit is contained in:
parent
e7ac178f7f
commit
34f1924c74
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -157,13 +157,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gif"
|
name = "float-ord"
|
||||||
version = "0.10.3"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af"
|
checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color_quant",
|
"color_quant",
|
||||||
"lzw",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -186,7 +192,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.23.6"
|
version = "0.23.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -317,6 +323,7 @@ name = "perler"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"float-ord",
|
||||||
"image",
|
"image",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@ -547,6 +554,12 @@ version = "0.9.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e2bb9fc8309084dd7cd651336673844c1d47f8ef6d2091ec160b27f5c4aa277"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
|||||||
@ -16,6 +16,7 @@ thiserror = "1.0.20"
|
|||||||
anyhow = "1.0.31"
|
anyhow = "1.0.31"
|
||||||
#x11colors = {git="https://git.z.xinu.tv/wathiede/x11colors"}
|
#x11colors = {git="https://git.z.xinu.tv/wathiede/x11colors"}
|
||||||
x11colors = {path="../../xinu.tv/x11colors"}
|
x11colors = {path="../../xinu.tv/x11colors"}
|
||||||
|
float-ord = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|||||||
@ -4,16 +4,29 @@ use std::io::Write;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use float_ord::FloatOrd;
|
||||||
use image::imageops::colorops::{index_colors, ColorMap};
|
use image::imageops::colorops::{index_colors, ColorMap};
|
||||||
use image::{DynamicImage, GenericImage, GenericImageView, ImageBuffer, Rgba};
|
use image::{DynamicImage, GenericImage, GenericImageView, ImageBuffer, Rgba};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use x11colors::COLORS;
|
use x11colors::COLORS;
|
||||||
|
|
||||||
use perler::NearestColor;
|
use perler::{rgba_to_hsv, NearestColorCIELAB, NearestColorHSV, NearestColorRGB};
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Nearest {
|
NearestRGB {
|
||||||
|
/// Input image file.
|
||||||
|
input: PathBuf,
|
||||||
|
/// Output of processed image.
|
||||||
|
output: PathBuf,
|
||||||
|
},
|
||||||
|
NearestHSV {
|
||||||
|
/// Input image file.
|
||||||
|
input: PathBuf,
|
||||||
|
/// Output of processed image.
|
||||||
|
output: PathBuf,
|
||||||
|
},
|
||||||
|
NearestCIELAB {
|
||||||
/// Input image file.
|
/// Input image file.
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
/// Output of processed image.
|
/// Output of processed image.
|
||||||
@ -23,6 +36,10 @@ enum Command {
|
|||||||
/// Output of processed image.
|
/// Output of processed image.
|
||||||
output: PathBuf,
|
output: PathBuf,
|
||||||
},
|
},
|
||||||
|
GeneratePalette {
|
||||||
|
/// Output of processed image.
|
||||||
|
output: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert image to SVG in the style of perler instructions.
|
/// Convert image to SVG in the style of perler instructions.
|
||||||
@ -76,6 +93,27 @@ fn print_stats(img: &DynamicImage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_palette(output: PathBuf) -> Result<()> {
|
||||||
|
let w = 256;
|
||||||
|
let stripe_h = 8;
|
||||||
|
let h = COLORS.len() as u32 * stripe_h;
|
||||||
|
let mut img = DynamicImage::new_rgba8(w, h);
|
||||||
|
let mut colors: Vec<_> = COLORS.iter().collect();
|
||||||
|
colors.sort_by_key(|[r, g, b]| {
|
||||||
|
let [h, s, v] = rgba_to_hsv(&[*r, *g, *b, 255].into());
|
||||||
|
[FloatOrd(s), FloatOrd(h), FloatOrd(v)]
|
||||||
|
});
|
||||||
|
for (y, c) in colors.iter().enumerate() {
|
||||||
|
for x in 0..w {
|
||||||
|
for s in 0..stripe_h {
|
||||||
|
img.put_pixel(x, stripe_h * y as u32 + s, [c[0], c[1], c[2], 255].into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.save(output)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn generate(output: PathBuf) -> Result<()> {
|
fn generate(output: PathBuf) -> Result<()> {
|
||||||
let w = 256;
|
let w = 256;
|
||||||
let h = 300;
|
let h = 300;
|
||||||
@ -94,7 +132,13 @@ fn generate(output: PathBuf) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nearest(input: PathBuf, output: PathBuf) -> Result<()> {
|
enum NearestType {
|
||||||
|
RGB,
|
||||||
|
HSV,
|
||||||
|
CIELAB,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(input: PathBuf, output: PathBuf, n_type: NearestType) -> Result<()> {
|
||||||
let img = image::open(input)?;
|
let img = image::open(input)?;
|
||||||
let (w, h) = &img.dimensions();
|
let (w, h) = &img.dimensions();
|
||||||
println!("Before:");
|
println!("Before:");
|
||||||
@ -106,9 +150,16 @@ fn nearest(input: PathBuf, output: PathBuf) -> Result<()> {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.map(|[r, g, b]| [r, g, b, 255].into())
|
.map(|[r, g, b]| [r, g, b, 255].into())
|
||||||
.collect();
|
.collect();
|
||||||
palette.sort_by_key(|Rgba([r, g, b, a])| r << 24 | g << 16 | b << 8 | a);
|
palette.sort_by_key(|Rgba([r, g, b, a])| {
|
||||||
let cmap = NearestColor::new(palette);
|
(*r as u32) << 24 | (*g as u32) << 16 | (*b as u32) << 8 | *a as u32
|
||||||
let pal = index_colors(&mut img2, &cmap);
|
});
|
||||||
|
let cmap: Box<dyn ColorMap<Color = Rgba<u8>>> = match n_type {
|
||||||
|
NearestType::RGB => Box::new(NearestColorRGB::new(palette)),
|
||||||
|
NearestType::HSV => Box::new(NearestColorHSV::new(palette)),
|
||||||
|
NearestType::CIELAB => Box::new(NearestColorCIELAB::new(palette)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pal = index_colors(&mut img2, cmap.as_ref());
|
||||||
let out_buf = ImageBuffer::from_fn(*w, *h, |x, y| -> Rgba<u8> {
|
let out_buf = ImageBuffer::from_fn(*w, *h, |x, y| -> Rgba<u8> {
|
||||||
let p = pal.get_pixel(x, y);
|
let p = pal.get_pixel(x, y);
|
||||||
cmap.lookup(p.0[0] as usize)
|
cmap.lookup(p.0[0] as usize)
|
||||||
@ -128,7 +179,10 @@ fn main() -> Result<()> {
|
|||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
match opt.cmd {
|
match opt.cmd {
|
||||||
Command::Nearest { input, output } => nearest(input, output),
|
Command::NearestRGB { input, output } => nearest(input, output, NearestType::RGB),
|
||||||
|
Command::NearestHSV { input, output } => nearest(input, output, NearestType::HSV),
|
||||||
|
Command::NearestCIELAB { input, output } => nearest(input, output, NearestType::CIELAB),
|
||||||
Command::Generate { output } => generate(output),
|
Command::Generate { output } => generate(output),
|
||||||
|
Command::GeneratePalette { output } => generate_palette(output),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
231
src/lib.rs
231
src/lib.rs
@ -1,8 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use float_ord::FloatOrd;
|
||||||
|
|
||||||
use image::imageops::colorops::ColorMap;
|
use image::imageops::colorops::ColorMap;
|
||||||
use image::Rgba;
|
|
||||||
use image::{DynamicImage, GenericImage, GenericImageView};
|
use image::{DynamicImage, GenericImage, GenericImageView};
|
||||||
|
use image::{Rgb, Rgba};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use svg::node::element::Group;
|
use svg::node::element::Group;
|
||||||
use svg::node::element::{Circle, Rectangle};
|
use svg::node::element::{Circle, Rectangle};
|
||||||
@ -21,22 +20,187 @@ pub enum PerlerError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert tristimulus values in the XYZ color space (as defined by CIE) matching the human eye's
|
||||||
|
/// response to RGB values in the sRGB color space.
|
||||||
|
/// Values are nominally in the range [0..1].
|
||||||
|
pub fn xyz_to_rgb(xyz: [f32; 3]) -> [f32; 3] {
|
||||||
|
[
|
||||||
|
3.240479 * xyz[0] - 1.537150 * xyz[1] - 0.498535 * xyz[2],
|
||||||
|
-0.969256 * xyz[0] + 1.875991 * xyz[1] + 0.041556 * xyz[2],
|
||||||
|
0.055648 * xyz[0] - 0.204043 * xyz[1] + 1.057311 * xyz[2],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From https://en.wikipedia.org/wiki/CIELAB_color_space#CIELAB%E2%80%93CIEXYZ_conversions
|
||||||
|
pub fn xyz_to_cielab(xyz: [f32; 3]) -> [f32; 3] {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From https://en.wikipedia.org/wiki/CIELAB_color_space#CIELAB%E2%80%93CIEXYZ_conversions
|
||||||
|
pub fn cielab_to_xyz(cielab: [f32; 3]) -> [f32; 3] {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert tristimulus values in the sRGB color space values to the XYZ color space (as defined by
|
||||||
|
/// CIE) matching the human eye's response.
|
||||||
|
/// Values are nominally in the range [0..1].
|
||||||
|
pub fn rgb_to_xyz(rgb: [f32; 3]) -> [f32; 3] {
|
||||||
|
[
|
||||||
|
0.412453 * rgb[0] + 0.357580 * rgb[1] + 0.180423 * rgb[2],
|
||||||
|
0.212671 * rgb[0] + 0.715160 * rgb[1] + 0.072169 * rgb[2],
|
||||||
|
0.019334 * rgb[0] + 0.119193 * rgb[1] + 0.950227 * rgb[2],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// From https://www.rapidtables.com/convert/color/rgb-to-hsv.html
|
||||||
|
pub fn rgba_to_hsv(rgb: &Rgba<u8>) -> [f32; 3] {
|
||||||
|
let [r, g, b, _] = rgb.0;
|
||||||
|
let [f_r, f_g, f_b] = [r as f32 / 255., g as f32 / 255., b as f32 / 255.];
|
||||||
|
|
||||||
|
let c_max = *[r, g, b].iter().max().unwrap();
|
||||||
|
let f_c_max = c_max as f32 / 255.;
|
||||||
|
let c_min = *[r, g, b].iter().min().unwrap();
|
||||||
|
let f_c_min = c_min as f32 / 255.;
|
||||||
|
let delta = c_max - c_min;
|
||||||
|
let f_delta = f_c_max - f_c_min;
|
||||||
|
//dbg!((r, g, b, c_max, c_min, delta));
|
||||||
|
//dbg!((f_r, f_g, f_b, f_c_max, f_c_min, f_delta));
|
||||||
|
let mut h = if delta == 0 {
|
||||||
|
0.
|
||||||
|
} else if c_max == r {
|
||||||
|
60. * (((f_g - f_b) / f_delta) % 6.)
|
||||||
|
} else if c_max == g {
|
||||||
|
60. * ((f_b - f_r) / f_delta + 2.)
|
||||||
|
} else if c_max == b {
|
||||||
|
60. * ((f_r - f_g) / f_delta + 4.)
|
||||||
|
} else {
|
||||||
|
unreachable!("c_max value not equal to r, g, or b");
|
||||||
|
};
|
||||||
|
if h < 0. {
|
||||||
|
h += 360.;
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = match c_max {
|
||||||
|
0 => 0.,
|
||||||
|
_ => f_delta / f_c_max,
|
||||||
|
};
|
||||||
|
let v = f_c_max;
|
||||||
|
//dbg!(h, s, v);
|
||||||
|
[h / 360., s, v]
|
||||||
|
}
|
||||||
|
|
||||||
|
// HSV values in [0..1]
|
||||||
|
// returns [r, g, b] values from 0 to 255
|
||||||
|
//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
||||||
|
pub fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Rgb<u8> {
|
||||||
|
let h_i = (h * 6.) as i32;
|
||||||
|
let f = h * 6. - h_i as f32;
|
||||||
|
let p = v * (1. - s);
|
||||||
|
let q = v * (1. - f * s);
|
||||||
|
let t = v * (1. - (1. - f) * s);
|
||||||
|
let [r, g, b] = match h_i {
|
||||||
|
0 => [v, t, p],
|
||||||
|
1 => [q, v, p],
|
||||||
|
2 => [p, v, t],
|
||||||
|
3 => [p, q, v],
|
||||||
|
4 => [t, p, v],
|
||||||
|
5 => [v, p, q],
|
||||||
|
_ => panic!(format!("Unknown H value {}", h_i)),
|
||||||
|
};
|
||||||
|
[(r * 255.) as u8, (g * 255.) as u8, (b * 255.) as u8].into()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct NearestColor {
|
pub struct NearestColorCIELAB {
|
||||||
palette: Vec<Rgba<u8>>,
|
palette: Vec<Rgba<u8>>,
|
||||||
color_cache: HashMap<Rgba<u8>, usize>,
|
cielab: Vec<[f32; 3]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NearestColor {
|
impl NearestColorCIELAB {
|
||||||
pub fn new(palette: Vec<Rgba<u8>>) -> NearestColor {
|
pub fn new(palette: Vec<Rgba<u8>>) -> NearestColorCIELAB {
|
||||||
NearestColor {
|
let cielab = palette
|
||||||
palette,
|
.iter()
|
||||||
color_cache: HashMap::new(),
|
.map(|rgba| {
|
||||||
}
|
let [r, g, b, _] = rgba.0;
|
||||||
|
xyz_to_cielab(rgb_to_xyz([
|
||||||
|
r as f32 / 255.,
|
||||||
|
g as f32 / 255.,
|
||||||
|
b as f32 / 255.,
|
||||||
|
]))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
NearestColorCIELAB { palette, cielab }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorMap for NearestColor {
|
impl ColorMap for NearestColorCIELAB {
|
||||||
|
type Color = Rgba<u8>;
|
||||||
|
fn index_of(&self, color: &Self::Color) -> usize {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn map_color(&self, color: &mut Self::Color) {
|
||||||
|
*color = self.palette[self.index_of(color)];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, idx: usize) -> Option<Self::Color> {
|
||||||
|
self.palette.get(idx).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct NearestColorHSV {
|
||||||
|
palette: Vec<Rgba<u8>>,
|
||||||
|
hsv: Vec<[f32; 3]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NearestColorHSV {
|
||||||
|
pub fn new(palette: Vec<Rgba<u8>>) -> NearestColorHSV {
|
||||||
|
let hsv = palette.iter().map(|rgba| rgba_to_hsv(rgba)).collect();
|
||||||
|
NearestColorHSV { palette, hsv }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorMap for NearestColorHSV {
|
||||||
|
type Color = Rgba<u8>;
|
||||||
|
fn index_of(&self, color: &Self::Color) -> usize {
|
||||||
|
let c = rgba_to_hsv(color);
|
||||||
|
let mut min_idx = usize::MAX;
|
||||||
|
let mut min_dist = f32::MAX;
|
||||||
|
for (idx, hsv) in self.hsv.iter().enumerate() {
|
||||||
|
let [h1, s1, v1] = hsv;
|
||||||
|
let [h2, s2, v2] = c;
|
||||||
|
//dbg!((h1, s1, v1), (h2, s2, v2));
|
||||||
|
let dist = (h1 - h2).abs().min(1. - (h1 - h2).abs());
|
||||||
|
|
||||||
|
//dbg!((dist, min_dist, idx, min_idx));
|
||||||
|
if dist < min_dist {
|
||||||
|
min_dist = dist;
|
||||||
|
min_idx = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min_idx
|
||||||
|
}
|
||||||
|
fn map_color(&self, color: &mut Self::Color) {
|
||||||
|
*color = self.palette[self.index_of(color)];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, idx: usize) -> Option<Self::Color> {
|
||||||
|
self.palette.get(idx).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct NearestColorRGB {
|
||||||
|
palette: Vec<Rgba<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NearestColorRGB {
|
||||||
|
pub fn new(palette: Vec<Rgba<u8>>) -> NearestColorRGB {
|
||||||
|
NearestColorRGB { palette }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorMap for NearestColorRGB {
|
||||||
type Color = Rgba<u8>;
|
type Color = Rgba<u8>;
|
||||||
|
|
||||||
fn index_of(&self, color: &Self::Color) -> usize {
|
fn index_of(&self, color: &Self::Color) -> usize {
|
||||||
@ -334,11 +498,52 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|rgb| rgb.into())
|
.map(|rgb| rgb.into())
|
||||||
.collect();
|
.collect();
|
||||||
let cmap = NearestColor::new(palette);
|
let cmap = NearestColorRGB::new(palette);
|
||||||
let got = cmap.index_of(&[255, 10, 10, 255].into());
|
let got = cmap.index_of(&[255, 10, 10, 255].into());
|
||||||
assert_eq!(got, 2);
|
assert_eq!(got, 2);
|
||||||
|
|
||||||
let got = cmap.lookup(2);
|
let got = cmap.lookup(2);
|
||||||
assert_eq!(got, Some([255, 0, 0, 255].into()));
|
assert_eq!(got, Some([255, 0, 0, 255].into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COLOR_DATA: [(&str, [u8; 3], [f32; 3]); 16] = [
|
||||||
|
// Name, [R,G,B], [H,S,V]
|
||||||
|
("Black", [0, 0, 0], [0., 0., 0.]),
|
||||||
|
("White", [255, 255, 255], [0., 0., 1.]),
|
||||||
|
("Red", [255, 0, 0], [0., 1., 1.]),
|
||||||
|
("Lime", [0, 255, 0], [120., 1., 1.]),
|
||||||
|
("Blue", [0, 0, 255], [240., 1., 1.]),
|
||||||
|
("Yellow", [255, 255, 0], [60., 1., 1.]),
|
||||||
|
("Cyan", [0, 255, 255], [180., 1., 1.]),
|
||||||
|
("Magenta", [255, 0, 255], [300., 1., 1.]),
|
||||||
|
("Silver", [191, 191, 191], [0., 0., 0.7490196]),
|
||||||
|
("Gray", [128, 128, 128], [0., 0., 0.5019608]),
|
||||||
|
("Maroon", [128, 0, 0], [0., 1., 0.5019608]),
|
||||||
|
("Olive", [128, 128, 0], [60., 1., 0.5019608]),
|
||||||
|
("Green", [0, 128, 0], [120., 1., 0.5019608]),
|
||||||
|
("Purple", [128, 0, 128], [300., 1., 0.5019608]),
|
||||||
|
("Teal", [0, 128, 128], [180., 1., 0.5019608]),
|
||||||
|
("Navy", [0, 0, 128], [240., 1., 0.5019608]),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn color_roundtrip() {
|
||||||
|
for (name, rgb, hsv) in COLOR_DATA.iter() {
|
||||||
|
let rgba: Rgba<u8> = [rgb[0], rgb[1], rgb[2], 255].into();
|
||||||
|
let got = rgba_to_hsv(&rgba);
|
||||||
|
let [h, s, v] = *hsv;
|
||||||
|
assert_eq!(
|
||||||
|
got,
|
||||||
|
[h / 360., s, v],
|
||||||
|
"rgba_to_hsv failed for {} ({:?})",
|
||||||
|
name,
|
||||||
|
rgb
|
||||||
|
);
|
||||||
|
|
||||||
|
let rgb: Rgb<u8> = Rgb(*rgb);
|
||||||
|
let [h, s, v] = *hsv;
|
||||||
|
let got = hsv_to_rgb(h / 360., s, v);
|
||||||
|
assert_eq!(got, rgb, "hsv_to_rgb failed for {} ({:?})", name, hsv);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user