use crate::{
Version,
bit_enumerator::BitEnumerator,
color::{Color, lerp, modulo},
color_func::{ColorFunc, blend, blend2, reverse},
hsb_color::HSBColor,
};
fn grayscale() -> ColorFunc { blend2(Color::BLACK, Color::WHITE) }
fn select_grayscale(entropy: &mut BitEnumerator) -> ColorFunc {
if entropy.next() {
grayscale()
} else {
reverse(grayscale())
}
}
fn make_hue(t: f64) -> Color { HSBColor::from_hue(t).color() }
fn spectrum() -> ColorFunc {
blend(vec![
Color::from_uint8_values(0, 168, 222),
Color::from_uint8_values(51, 51, 145),
Color::from_uint8_values(233, 19, 136),
Color::from_uint8_values(235, 45, 46),
Color::from_uint8_values(253, 233, 43),
Color::from_uint8_values(0, 158, 84),
Color::from_uint8_values(0, 168, 222),
])
}
fn spectrum_cmyk_safe() -> ColorFunc {
blend(vec![
Color::from_uint8_values(0, 168, 222),
Color::from_uint8_values(41, 60, 130),
Color::from_uint8_values(210, 59, 130),
Color::from_uint8_values(217, 63, 53),
Color::from_uint8_values(244, 228, 81),
Color::from_uint8_values(0, 158, 84),
Color::from_uint8_values(0, 168, 222),
])
}
fn adjust_for_luminance(color: &Color, contrast_color: &Color) -> Color {
let lum = color.luminance();
let contrast_lum = contrast_color.luminance();
let threshold = 0.6;
let offset = (lum - contrast_lum).abs();
if offset > threshold {
return *color;
}
let boost = 0.7;
let t = lerp(0.0, threshold, boost, 0.0, offset);
if contrast_lum > lum {
color.darken(t).burn(t * 0.6)
} else {
color.lighten(t).burn(t * 0.6)
}
}
fn monochromatic(
entropy: &mut BitEnumerator,
hue_generator: &ColorFunc,
) -> ColorFunc {
let hue = entropy.next_frac();
let is_tint = entropy.next();
let is_reversed = entropy.next();
let key_advance = entropy.next_frac() * 0.3 + 0.05;
let neutral_advance = entropy.next_frac() * 0.3 + 0.05;
let mut key_color = hue_generator(hue);
let contrast_brightness;
if is_tint {
contrast_brightness = 1.0;
key_color = key_color.darken(0.5);
} else {
contrast_brightness = 0.0;
}
let gs = grayscale();
let neutral_color = gs(contrast_brightness);
let key_color_2 = key_color.lerp_to(&neutral_color, key_advance);
let neutral_color_2 = neutral_color.lerp_to(&key_color, neutral_advance);
let gradient = blend2(key_color_2, neutral_color_2);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn monochromatic_fiducial(entropy: &mut BitEnumerator) -> ColorFunc {
let hue = entropy.next_frac();
let is_reversed = entropy.next();
let is_tint = entropy.next();
let contrast_color = if is_tint { Color::WHITE } else { Color::BLACK };
let spec = spectrum_cmyk_safe();
let key_color = adjust_for_luminance(&spec(hue), &contrast_color);
let gradient = blend(vec![key_color, contrast_color, key_color]);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn complementary(
entropy: &mut BitEnumerator,
hue_generator: &ColorFunc,
) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 0.5, 1.0);
let lighter_advance = entropy.next_frac() * 0.3;
let darker_advance = entropy.next_frac() * 0.3;
let is_reversed = entropy.next();
let color1 = hue_generator(spectrum1);
let color2 = hue_generator(spectrum2);
let luma1 = color1.luminance();
let luma2 = color2.luminance();
let (darker_color, lighter_color) = if luma1 > luma2 {
(color2, color1)
} else {
(color1, color2)
};
let adjusted_lighter = lighter_color.lighten(lighter_advance);
let adjusted_darker = darker_color.darken(darker_advance);
let gradient = blend2(adjusted_darker, adjusted_lighter);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn complementary_fiducial(entropy: &mut BitEnumerator) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 0.5, 1.0);
let is_tint = entropy.next();
let is_reversed = entropy.next();
let neutral_color_bias = entropy.next();
let neutral_color = if is_tint { Color::WHITE } else { Color::BLACK };
let spec = spectrum_cmyk_safe();
let color1 = spec(spectrum1);
let color2 = spec(spectrum2);
let bias_color = if neutral_color_bias { color1 } else { color2 };
let biased_neutral_color =
neutral_color.lerp_to(&bias_color, 0.2).burn(0.1);
let gradient = blend(vec![
adjust_for_luminance(&color1, &biased_neutral_color),
biased_neutral_color,
adjust_for_luminance(&color2, &biased_neutral_color),
]);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn triadic(
entropy: &mut BitEnumerator,
hue_generator: &ColorFunc,
) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 1.0 / 3.0, 1.0);
let spectrum3 = modulo(spectrum1 + 2.0 / 3.0, 1.0);
let lighter_advance = entropy.next_frac() * 0.3;
let darker_advance = entropy.next_frac() * 0.3;
let is_reversed = entropy.next();
let color1 = hue_generator(spectrum1);
let color2 = hue_generator(spectrum2);
let color3 = hue_generator(spectrum3);
let mut colors = [color1, color2, color3];
colors.sort_by(|a, b| a.luminance().partial_cmp(&b.luminance()).unwrap());
let darker_color = colors[0];
let middle_color = colors[1];
let lighter_color = colors[2];
let adjusted_lighter = lighter_color.lighten(lighter_advance);
let adjusted_darker = darker_color.darken(darker_advance);
let gradient = blend(vec![adjusted_lighter, middle_color, adjusted_darker]);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn triadic_fiducial(entropy: &mut BitEnumerator) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 1.0 / 3.0, 1.0);
let spectrum3 = modulo(spectrum1 + 2.0 / 3.0, 1.0);
let is_tint = entropy.next();
let neutral_insert_index = (entropy.next_uint8() % 2 + 1) as usize;
let is_reversed = entropy.next();
let neutral_color = if is_tint { Color::WHITE } else { Color::BLACK };
let spec = spectrum_cmyk_safe();
let mut colors = vec![spec(spectrum1), spec(spectrum2), spec(spectrum3)];
match neutral_insert_index {
1 => {
colors[0] = adjust_for_luminance(&colors[0], &neutral_color);
colors[1] = adjust_for_luminance(&colors[1], &neutral_color);
colors[2] = adjust_for_luminance(&colors[2], &colors[1]);
}
2 => {
colors[1] = adjust_for_luminance(&colors[1], &neutral_color);
colors[2] = adjust_for_luminance(&colors[2], &neutral_color);
colors[0] = adjust_for_luminance(&colors[0], &colors[1]);
}
_ => panic!("Internal error"),
}
colors.insert(neutral_insert_index, neutral_color);
let gradient = blend(colors);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn analogous(
entropy: &mut BitEnumerator,
hue_generator: &ColorFunc,
) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 1.0 / 12.0, 1.0);
let spectrum3 = modulo(spectrum1 + 2.0 / 12.0, 1.0);
let spectrum4 = modulo(spectrum1 + 3.0 / 12.0, 1.0);
let advance = entropy.next_frac() * 0.5 + 0.2;
let is_reversed = entropy.next();
let color1 = hue_generator(spectrum1);
let color2 = hue_generator(spectrum2);
let color3 = hue_generator(spectrum3);
let color4 = hue_generator(spectrum4);
let (darkest_color, dark_color, light_color, lightest_color) =
if color1.luminance() < color4.luminance() {
(color1, color2, color3, color4)
} else {
(color4, color3, color2, color1)
};
let adjusted_darkest = darkest_color.darken(advance);
let adjusted_dark = dark_color.darken(advance / 2.0);
let adjusted_light = light_color.lighten(advance / 2.0);
let adjusted_lightest = lightest_color.lighten(advance);
let gradient = blend(vec![
adjusted_darkest,
adjusted_dark,
adjusted_light,
adjusted_lightest,
]);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
fn analogous_fiducial(entropy: &mut BitEnumerator) -> ColorFunc {
let spectrum1 = entropy.next_frac();
let spectrum2 = modulo(spectrum1 + 1.0 / 10.0, 1.0);
let spectrum3 = modulo(spectrum1 + 2.0 / 10.0, 1.0);
let is_tint = entropy.next();
let neutral_insert_index = (entropy.next_uint8() % 2 + 1) as usize;
let is_reversed = entropy.next();
let neutral_color = if is_tint { Color::WHITE } else { Color::BLACK };
let spec = spectrum_cmyk_safe();
let mut colors = vec![spec(spectrum1), spec(spectrum2), spec(spectrum3)];
match neutral_insert_index {
1 => {
colors[0] = adjust_for_luminance(&colors[0], &neutral_color);
colors[1] = adjust_for_luminance(&colors[1], &neutral_color);
colors[2] = adjust_for_luminance(&colors[2], &colors[1]);
}
2 => {
colors[1] = adjust_for_luminance(&colors[1], &neutral_color);
colors[2] = adjust_for_luminance(&colors[2], &neutral_color);
colors[0] = adjust_for_luminance(&colors[0], &colors[1]);
}
_ => panic!("Internal error"),
}
colors.insert(neutral_insert_index, neutral_color);
let gradient = blend(colors);
if is_reversed {
reverse(gradient)
} else {
gradient
}
}
pub fn select_gradient(
entropy: &mut BitEnumerator,
version: Version,
) -> ColorFunc {
if version == Version::GrayscaleFiducial {
return select_grayscale(entropy);
}
let value = entropy.next_uint2();
match value {
0 => match version {
Version::Version1 => {
let hue_gen: ColorFunc = Box::new(make_hue);
monochromatic(entropy, &hue_gen)
}
Version::Version2 | Version::Detailed => {
let spec = spectrum_cmyk_safe();
monochromatic(entropy, &spec)
}
Version::Fiducial => monochromatic_fiducial(entropy),
Version::GrayscaleFiducial => grayscale(),
},
1 => match version {
Version::Version1 => {
let spec = spectrum();
complementary(entropy, &spec)
}
Version::Version2 | Version::Detailed => {
let spec = spectrum_cmyk_safe();
complementary(entropy, &spec)
}
Version::Fiducial => complementary_fiducial(entropy),
Version::GrayscaleFiducial => grayscale(),
},
2 => match version {
Version::Version1 => {
let spec = spectrum();
triadic(entropy, &spec)
}
Version::Version2 | Version::Detailed => {
let spec = spectrum_cmyk_safe();
triadic(entropy, &spec)
}
Version::Fiducial => triadic_fiducial(entropy),
Version::GrayscaleFiducial => grayscale(),
},
3 => match version {
Version::Version1 => {
let spec = spectrum();
analogous(entropy, &spec)
}
Version::Version2 | Version::Detailed => {
let spec = spectrum_cmyk_safe();
analogous(entropy, &spec)
}
Version::Fiducial => analogous_fiducial(entropy),
Version::GrayscaleFiducial => grayscale(),
},
_ => grayscale(),
}
}