Improve color conversions (#2659)

This commit is contained in:
frozolotl 2023-11-13 16:23:52 +01:00 committed by GitHub
parent c427ca9e4e
commit 7d5f6a8b73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 48 deletions

View File

@ -2,9 +2,10 @@ use std::str::FromStr;
use ecow::EcoVec;
use once_cell::sync::Lazy;
use palette::convert::FromColorUnclamped;
use palette::encoding::{self, Linear};
use palette::{
Darken, Desaturate, FromColor, Lighten, OklabHue, RgbHue, Saturate, ShiftHue,
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
};
use super::*;
@ -1251,13 +1252,16 @@ impl Color {
pub fn to_luma(self) -> Self {
Self::Luma(match self {
Self::Luma(c) => c,
Self::Oklab(c) => Luma::from_color(c),
Self::Oklch(c) => Luma::from_color(c),
Self::Rgba(c) => Luma::from_color(c),
Self::LinearRgb(c) => Luma::from_color(c),
Self::Cmyk(c) => Luma::from_color(c.to_rgba()),
Self::Hsl(c) => Luma::from_color(c),
Self::Hsv(c) => Luma::from_color(c),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => Luma::from_color_unclamped(Okhsva::from_color(c)),
Self::Oklch(c) => Luma::from_color_unclamped(Okhsva::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as [`Luma`].
Self::Rgba(c) => Luma::from_color_unclamped(c),
Self::LinearRgb(c) => Luma::from_color_unclamped(c),
Self::Cmyk(c) => Luma::from_color_unclamped(c.to_rgba()),
Self::Hsl(c) => Luma::from_color_unclamped(c),
Self::Hsv(c) => Luma::from_color_unclamped(c),
})
}
@ -1265,7 +1269,9 @@ impl Color {
Self::Oklab(match self {
Self::Luma(c) => Oklab::from_color(c),
Self::Oklab(c) => c,
Self::Oklch(c) => Oklab::from_color(c),
// No clamping is necessary for this conversion because the
// lightness property is the same for both Oklab and Oklch.
Self::Oklch(c) => Oklab::from_color_unclamped(c),
Self::Rgba(c) => Oklab::from_color(c),
Self::LinearRgb(c) => Oklab::from_color(c),
Self::Cmyk(c) => Oklab::from_color(c.to_rgba()),
@ -1277,7 +1283,9 @@ impl Color {
pub fn to_oklch(self) -> Self {
Self::Oklch(match self {
Self::Luma(c) => Oklch::from_color(c),
Self::Oklab(c) => Oklch::from_color(c),
// No clamping is necessary for this conversion because the
// lightness property is the same for both Oklab and Oklch.
Self::Oklab(c) => Oklch::from_color_unclamped(c),
Self::Oklch(c) => c,
Self::Rgba(c) => Oklch::from_color(c),
Self::LinearRgb(c) => Oklch::from_color(c),
@ -1287,67 +1295,90 @@ impl Color {
})
}
pub fn to_linear_rgb(self) -> Self {
Self::LinearRgb(match self {
Self::Luma(c) => LinearRgba::from_color(c),
Self::Oklab(c) => LinearRgba::from_color(c),
Self::Oklch(c) => LinearRgba::from_color(c),
Self::Rgba(c) => LinearRgba::from_color(c),
Self::LinearRgb(c) => c,
Self::Cmyk(c) => LinearRgba::from_color(c.to_rgba()),
Self::Hsl(c) => LinearRgba::from_color(Rgba::from_color(c)),
Self::Hsv(c) => LinearRgba::from_color(Rgba::from_color(c)),
pub fn to_rgba(self) -> Self {
Self::Rgba(match self {
// No clamping necessary because Luma is within sRGB, same as [`Rgba`].
Self::Luma(c) => Rgba::from_color_unclamped(c),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => Rgba::from_color_unclamped(Okhsva::from_color(c)),
Self::Oklch(c) => Rgba::from_color_unclamped(Okhsva::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as [`Rgba`].
Self::Rgba(c) => c,
Self::LinearRgb(c) => Rgba::from_linear(c),
Self::Cmyk(c) => Rgba::from_color_unclamped(c.to_rgba()),
Self::Hsl(c) => Rgba::from_color_unclamped(c),
Self::Hsv(c) => Rgba::from_color_unclamped(c),
})
}
pub fn to_rgba(self) -> Self {
Self::Rgba(match self {
Self::Luma(c) => Rgba::from_color(c),
Self::Oklab(c) => Rgba::from_color(c),
Self::Oklch(c) => Rgba::from_color(c),
Self::Rgba(c) => c,
Self::LinearRgb(c) => Rgba::from_linear(c),
Self::Cmyk(c) => c.to_rgba(),
Self::Hsl(c) => Rgba::from_color(c),
Self::Hsv(c) => Rgba::from_color(c),
pub fn to_linear_rgb(self) -> Self {
Self::LinearRgb(match self {
// No clamping necessary because Luma is within sRGB, same as $to.
Self::Luma(c) => LinearRgba::from_color_unclamped(c),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => LinearRgba::from_color_unclamped(Okhsva::from_color(c)),
Self::Oklch(c) => LinearRgba::from_color_unclamped(Okhsva::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as $to.
Self::Rgba(c) => LinearRgba::from_color_unclamped(c),
Self::LinearRgb(c) => c,
Self::Cmyk(c) => LinearRgba::from_color_unclamped(c.to_rgba()),
Self::Hsl(c) => Rgba::from_color_unclamped(c).into_linear(),
Self::Hsv(c) => Rgba::from_color_unclamped(c).into_linear(),
})
}
pub fn to_cmyk(self) -> Self {
Self::Cmyk(match self {
Self::Luma(c) => Cmyk::from_luma(c),
Self::Oklab(c) => Cmyk::from_rgba(Rgba::from_color(c)),
Self::Oklch(c) => Cmyk::from_rgba(Rgba::from_color(c)),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => {
Cmyk::from_rgba(Rgba::from_color_unclamped(Okhsva::from_color(c)))
}
Self::Oklch(c) => {
Cmyk::from_rgba(Rgba::from_color_unclamped(Okhsva::from_color(c)))
}
Self::Rgba(c) => Cmyk::from_rgba(c),
Self::LinearRgb(c) => Cmyk::from_rgba(Rgba::from_linear(c)),
Self::Cmyk(c) => c,
Self::Hsl(c) => Cmyk::from_rgba(Rgba::from_color(c)),
Self::Hsv(c) => Cmyk::from_rgba(Rgba::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as [`Rgba`].
Self::Hsl(c) => Cmyk::from_rgba(Rgba::from_color_unclamped(c)),
Self::Hsv(c) => Cmyk::from_rgba(Rgba::from_color_unclamped(c)),
})
}
pub fn to_hsl(self) -> Self {
Self::Hsl(match self {
Self::Luma(c) => Hsl::from_color(c),
Self::Oklab(c) => Hsl::from_color(c),
Self::Oklch(c) => Hsl::from_color(c),
Self::Rgba(c) => Hsl::from_color(c),
Self::LinearRgb(c) => Hsl::from_color(Rgba::from_linear(c)),
Self::Cmyk(c) => Hsl::from_color(c.to_rgba()),
// No clamping necessary because Luma is within sRGB, same as [`Hsl`].
Self::Luma(c) => Hsl::from_color_unclamped(c),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => Hsl::from_color_unclamped(Okhsva::from_color(c)),
Self::Oklch(c) => Hsl::from_color_unclamped(Okhsva::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as [`Hsl`].
Self::Rgba(c) => Hsl::from_color_unclamped(c),
Self::LinearRgb(c) => Hsl::from_color_unclamped(Rgba::from_linear(c)),
Self::Cmyk(c) => Hsl::from_color_unclamped(c.to_rgba()),
Self::Hsl(c) => c,
Self::Hsv(c) => Hsl::from_color(c),
Self::Hsv(c) => Hsl::from_color_unclamped(c),
})
}
pub fn to_hsv(self) -> Self {
Self::Hsv(match self {
Self::Luma(c) => Hsv::from_color(c),
Self::Oklab(c) => Hsv::from_color(c),
Self::Oklch(c) => Hsv::from_color(c),
Self::Rgba(c) => Hsv::from_color(c),
Self::LinearRgb(c) => Hsv::from_color(Rgba::from_linear(c)),
Self::Cmyk(c) => Hsv::from_color(c.to_rgba()),
Self::Hsl(c) => Hsv::from_color(c),
// No clamping necessary because Luma is within sRGB, same as [`Hsv`].
Self::Luma(c) => Hsv::from_color_unclamped(c),
// Perform sRGB gamut mapping by converting to Okhsv first.
// This yields better results than clamping.
Self::Oklab(c) => Hsv::from_color_unclamped(Okhsva::from_color(c)),
Self::Oklch(c) => Hsv::from_color_unclamped(Okhsva::from_color(c)),
// No clamping necessary because these color spaces are all within sRGB, same as [`Hsv`].
Self::Rgba(c) => Hsv::from_color_unclamped(c),
Self::LinearRgb(c) => Hsv::from_color_unclamped(Rgba::from_linear(c)),
Self::Cmyk(c) => Hsv::from_color_unclamped(c.to_rgba()),
Self::Hsl(c) => Hsv::from_color_unclamped(c),
Self::Hsv(c) => c,
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB