use crate::color::{
brightness::{Channel, ChannelVector},
ApproxBrightness,
BadCmyColor,
BadGrayColor,
BasicColor,
Brightness,
};
use crossterm::style::Color as CrosstermColor;
use std::{convert::TryFrom, fmt, ops::Not};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CmyColor {
code: u8,
}
impl CmyColor {
pub const BASE: u8 = 6;
pub fn try_new(
cyan: u8,
magenta: u8,
yellow: u8,
) -> Result<Self, BadCmyColor> {
if cyan >= Self::BASE || magenta >= Self::BASE || yellow >= Self::BASE {
Err(BadCmyColor { cyan, magenta, yellow })
} else {
Ok(Self {
code: cyan * Self::BASE.pow(2) + magenta * Self::BASE + yellow,
})
}
}
pub fn new(cyan: u8, magenta: u8, yellow: u8) -> Self {
Self::try_new(cyan, magenta, yellow).expect("Bad Cmy Color")
}
pub const fn cyan(self) -> u8 {
self.code / Self::BASE / Self::BASE % Self::BASE
}
pub const fn magenta(self) -> u8 {
self.code / Self::BASE % Self::BASE
}
pub const fn yellow(self) -> u8 {
self.code % Self::BASE
}
pub const fn code(self) -> u8 {
self.code
}
pub fn set_cyan(self, cyan: u8) -> Self {
Self::new(cyan, self.magenta(), self.yellow())
}
pub fn set_magenta(self, magenta: u8) -> Self {
Self::new(self.cyan(), magenta, self.yellow())
}
pub fn set_yellow(self, yellow: u8) -> Self {
Self::new(self.cyan(), self.magenta(), yellow)
}
fn from_channels(channels: [Channel; 3]) -> Self {
Self::new(channels[0].value(), channels[1].value(), channels[2].value())
}
fn channels(self) -> [Channel; 3] {
[
Channel::new(self.cyan(), 30),
Channel::new(self.magenta(), 59),
Channel::new(self.yellow(), 11),
]
}
}
impl fmt::Debug for CmyColor {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
fmtr.debug_struct("CmyColor")
.field("cyan", &self.cyan())
.field("magenta", &self.magenta())
.field("yellow", &self.yellow())
.finish()
}
}
impl Not for CmyColor {
type Output = Self;
fn not(self) -> Self::Output {
Self::new(
Self::BASE - self.cyan(),
Self::BASE - self.magenta(),
Self::BASE - self.yellow(),
)
}
}
impl ApproxBrightness for CmyColor {
fn approx_brightness(&self) -> Brightness {
let mut channels = self.channels();
let vector = ChannelVector::new(&mut channels, Self::BASE - 1);
vector.approx_brightness()
}
fn set_approx_brightness(&mut self, brightness: Brightness) {
let mut channels = self.channels();
let mut vector = ChannelVector::new(&mut channels, Self::BASE - 1);
vector.set_approx_brightness(brightness);
*self = Self::from_channels(channels);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GrayColor {
brightness: u8,
}
impl GrayColor {
pub const MIN: Self = Self { brightness: 0 };
pub const HALF: Self = Self { brightness: 12 };
pub const MAX: Self = Self { brightness: 23 };
pub fn try_new(brightness: u8) -> Result<Self, BadGrayColor> {
if brightness > Self::MAX.brightness() {
Err(BadGrayColor { brightness })
} else {
Ok(Self { brightness })
}
}
pub fn new(brightness: u8) -> Self {
Self::try_new(brightness).expect("Bad gray color")
}
pub const fn brightness(self) -> u8 {
self.brightness
}
}
impl Not for GrayColor {
type Output = Self;
fn not(self) -> Self::Output {
Self::new(Self::MAX.brightness() + 1 - self.brightness)
}
}
impl ApproxBrightness for GrayColor {
fn approx_brightness(&self) -> Brightness {
let brightness = Brightness { level: u16::from(self.brightness) };
brightness.spread(u16::from(Self::MAX.brightness))
}
fn set_approx_brightness(&mut self, brightness: Brightness) {
let compressed = brightness.compress(u16::from(Self::MAX.brightness));
let res = u8::try_from(compressed.level);
self.brightness = res.expect("Color brightness bug");
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Color8BitKind {
Basic(BasicColor),
Cmy(CmyColor),
Gray(GrayColor),
}
impl Not for Color8BitKind {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Color8BitKind::Basic(color) => Color8BitKind::Basic(!color),
Color8BitKind::Cmy(color) => Color8BitKind::Cmy(!color),
Color8BitKind::Gray(color) => Color8BitKind::Gray(!color),
}
}
}
impl From<BasicColor> for Color8BitKind {
fn from(color: BasicColor) -> Self {
Color8BitKind::Basic(color)
}
}
impl From<CmyColor> for Color8BitKind {
fn from(color: CmyColor) -> Self {
Color8BitKind::Cmy(color)
}
}
impl From<GrayColor> for Color8BitKind {
fn from(color: GrayColor) -> Self {
Color8BitKind::Gray(color)
}
}
impl From<Color8Bit> for Color8BitKind {
fn from(color: Color8Bit) -> Self {
color.kind()
}
}
impl ApproxBrightness for Color8BitKind {
fn approx_brightness(&self) -> Brightness {
match self {
Color8BitKind::Basic(color) => color.approx_brightness(),
Color8BitKind::Cmy(color) => color.approx_brightness(),
Color8BitKind::Gray(color) => color.approx_brightness(),
}
}
fn set_approx_brightness(&mut self, brightness: Brightness) {
match self {
Color8BitKind::Basic(color) => color.set_approx_brightness(brightness),
Color8BitKind::Cmy(color) => color.set_approx_brightness(brightness),
Color8BitKind::Gray(color) => color.set_approx_brightness(brightness),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Color8Bit {
code: u8,
}
impl Color8Bit {
const BASIC_SIZE: u8 = 16;
const BASIC_CMY_SIZE: u8 =
Self::BASIC_SIZE + CmyColor::BASE * CmyColor::BASE * CmyColor::BASE;
pub const fn basic(color: BasicColor) -> Self {
Self { code: color as u8 }
}
pub const fn cmy(color: CmyColor) -> Self {
Self { code: color.code() + Self::BASIC_SIZE }
}
pub const fn gray(color: GrayColor) -> Self {
Self { code: color.brightness() + Self::BASIC_CMY_SIZE }
}
pub const fn code(self) -> u8 {
self.code
}
pub fn kind(self) -> Color8BitKind {
if self.code < 16 {
Color8BitKind::Basic(BasicColor::try_from(self.code).unwrap())
} else if self.code < Self::BASIC_CMY_SIZE {
Color8BitKind::Cmy(CmyColor { code: self.code - Self::BASIC_SIZE })
} else {
Color8BitKind::Gray(GrayColor {
brightness: self.code - Self::BASIC_CMY_SIZE,
})
}
}
pub(crate) fn to_crossterm(self) -> CrosstermColor {
CrosstermColor::AnsiValue(self.code())
}
}
impl fmt::Debug for Color8Bit {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
fmtr.debug_struct("Color8Bit").field("kind", &self.kind()).finish()
}
}
impl Not for Color8Bit {
type Output = Self;
fn not(self) -> Self::Output {
Self::from(!self.kind())
}
}
impl From<BasicColor> for Color8Bit {
fn from(color: BasicColor) -> Self {
Self::basic(color)
}
}
impl From<CmyColor> for Color8Bit {
fn from(color: CmyColor) -> Self {
Self::cmy(color)
}
}
impl From<GrayColor> for Color8Bit {
fn from(color: GrayColor) -> Self {
Self::gray(color)
}
}
impl From<Color8BitKind> for Color8Bit {
fn from(kind: Color8BitKind) -> Self {
match kind {
Color8BitKind::Basic(color) => Self::from(color),
Color8BitKind::Cmy(color) => Self::from(color),
Color8BitKind::Gray(color) => Self::from(color),
}
}
}
impl ApproxBrightness for Color8Bit {
fn approx_brightness(&self) -> Brightness {
self.kind().approx_brightness()
}
fn set_approx_brightness(&mut self, brightness: Brightness) {
*self = Self::from(self.kind().with_approx_brightness(brightness));
}
}