Improve color negation (#3500)
This commit is contained in:
parent
0aa9254356
commit
79615a01bd
@ -4,10 +4,9 @@ use std::str::FromStr;
|
||||
|
||||
use ecow::{eco_format, EcoString, EcoVec};
|
||||
use once_cell::sync::Lazy;
|
||||
use palette::convert::FromColorUnclamped;
|
||||
use palette::encoding::{self, Linear};
|
||||
use palette::{
|
||||
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
|
||||
Darken, Desaturate, FromColor, Lighten, OklabHue, RgbHue, Saturate, ShiftHue,
|
||||
};
|
||||
use qcms::Profile;
|
||||
|
||||
@ -1010,16 +1009,29 @@ impl Color {
|
||||
})
|
||||
}
|
||||
|
||||
/// Produces the negative of the color.
|
||||
/// Produces the complementary color using a provided color space.
|
||||
/// You can think of it as the opposite side on a color wheel.
|
||||
///
|
||||
/// ```example
|
||||
/// #square(fill: yellow)
|
||||
/// #square(fill: yellow.negate())
|
||||
/// #square(fill: yellow.negate(space: rgb))
|
||||
/// ```
|
||||
#[func]
|
||||
pub fn negate(self) -> Color {
|
||||
match self {
|
||||
pub fn negate(
|
||||
self,
|
||||
/// The color space used for the transformation. By default, a perceptual color space is used.
|
||||
#[named]
|
||||
#[default(ColorSpace::Oklab)]
|
||||
space: ColorSpace,
|
||||
) -> Color {
|
||||
let result = match self.to_space(space) {
|
||||
Self::Luma(c) => Self::Luma(Luma::new(1.0 - c.luma, c.alpha)),
|
||||
Self::Oklab(c) => Self::Oklab(Oklab::new(c.l, -c.a, -c.b, c.alpha)),
|
||||
Self::Oklab(c) => Self::Oklab(Oklab::new(1.0 - c.l, -c.a, -c.b, c.alpha)),
|
||||
Self::Oklch(c) => Self::Oklch(Oklch::new(
|
||||
c.l,
|
||||
-c.chroma,
|
||||
OklabHue::from_degrees(360.0 - c.hue.into_degrees()),
|
||||
1.0 - c.l,
|
||||
c.chroma,
|
||||
OklabHue::from_degrees(c.hue.into_degrees() + 180.0),
|
||||
c.alpha,
|
||||
)),
|
||||
Self::LinearRgb(c) => Self::LinearRgb(LinearRgb::new(
|
||||
@ -1033,18 +1045,19 @@ impl Color {
|
||||
}
|
||||
Self::Cmyk(c) => Self::Cmyk(Cmyk::new(1.0 - c.c, 1.0 - c.m, 1.0 - c.y, c.k)),
|
||||
Self::Hsl(c) => Self::Hsl(Hsl::new(
|
||||
RgbHue::from_degrees(360.0 - c.hue.into_degrees()),
|
||||
RgbHue::from_degrees(c.hue.into_degrees() + 180.0),
|
||||
c.saturation,
|
||||
c.lightness,
|
||||
c.alpha,
|
||||
)),
|
||||
Self::Hsv(c) => Self::Hsv(Hsv::new(
|
||||
RgbHue::from_degrees(360.0 - c.hue.into_degrees()),
|
||||
RgbHue::from_degrees(c.hue.into_degrees() + 180.0),
|
||||
c.saturation,
|
||||
c.value,
|
||||
c.alpha,
|
||||
)),
|
||||
}
|
||||
};
|
||||
result.to_space(self.space())
|
||||
}
|
||||
|
||||
/// Rotates the hue of the color by a given angle.
|
||||
@ -1304,10 +1317,8 @@ impl Color {
|
||||
pub fn to_luma(self) -> Self {
|
||||
Self::Luma(match self {
|
||||
Self::Luma(c) => c,
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => Luma::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklch(c) => Luma::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklab(c) => Luma::from_color(c),
|
||||
Self::Oklch(c) => Luma::from_color(c),
|
||||
Self::Rgb(c) => Luma::from_color(c),
|
||||
Self::LinearRgb(c) => Luma::from_color(c),
|
||||
Self::Cmyk(c) => Luma::from_color(c.to_rgba()),
|
||||
@ -1320,9 +1331,7 @@ impl Color {
|
||||
Self::Oklab(match self {
|
||||
Self::Luma(c) => Oklab::from_color(c),
|
||||
Self::Oklab(c) => 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::Oklch(c) => Oklab::from_color(c),
|
||||
Self::Rgb(c) => Oklab::from_color(c),
|
||||
Self::LinearRgb(c) => Oklab::from_color(c),
|
||||
Self::Cmyk(c) => Oklab::from_color(c.to_rgba()),
|
||||
@ -1334,9 +1343,7 @@ impl Color {
|
||||
pub fn to_oklch(self) -> Self {
|
||||
Self::Oklch(match self {
|
||||
Self::Luma(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::Oklab(c) => Oklch::from_color(c),
|
||||
Self::Oklch(c) => c,
|
||||
Self::Rgb(c) => Oklch::from_color(c),
|
||||
Self::LinearRgb(c) => Oklch::from_color(c),
|
||||
@ -1349,10 +1356,8 @@ impl Color {
|
||||
pub fn to_rgb(self) -> Self {
|
||||
Self::Rgb(match self {
|
||||
Self::Luma(c) => Rgb::from_color(c),
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => Rgb::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklch(c) => Rgb::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklab(c) => Rgb::from_color(c),
|
||||
Self::Oklch(c) => Rgb::from_color(c),
|
||||
Self::Rgb(c) => c,
|
||||
Self::LinearRgb(c) => Rgb::from_linear(c),
|
||||
Self::Cmyk(c) => Rgb::from_color(c.to_rgba()),
|
||||
@ -1364,10 +1369,8 @@ impl Color {
|
||||
pub fn to_linear_rgb(self) -> Self {
|
||||
Self::LinearRgb(match self {
|
||||
Self::Luma(c) => LinearRgb::from_color(c),
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => LinearRgb::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklch(c) => LinearRgb::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklab(c) => LinearRgb::from_color(c),
|
||||
Self::Oklch(c) => LinearRgb::from_color(c),
|
||||
Self::Rgb(c) => LinearRgb::from_color(c),
|
||||
Self::LinearRgb(c) => c,
|
||||
Self::Cmyk(c) => LinearRgb::from_color(c.to_rgba()),
|
||||
@ -1379,10 +1382,8 @@ impl Color {
|
||||
pub fn to_cmyk(self) -> Self {
|
||||
Self::Cmyk(match self {
|
||||
Self::Luma(c) => Cmyk::from_luma(c),
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => Cmyk::from_rgba(Rgb::from_color(Okhsva::from_color(c))),
|
||||
Self::Oklch(c) => Cmyk::from_rgba(Rgb::from_color(Okhsva::from_color(c))),
|
||||
Self::Oklab(c) => Cmyk::from_rgba(Rgb::from_color(c)),
|
||||
Self::Oklch(c) => Cmyk::from_rgba(Rgb::from_color(c)),
|
||||
Self::Rgb(c) => Cmyk::from_rgba(c),
|
||||
Self::LinearRgb(c) => Cmyk::from_rgba(Rgb::from_linear(c)),
|
||||
Self::Cmyk(c) => c,
|
||||
@ -1394,10 +1395,8 @@ impl Color {
|
||||
pub fn to_hsl(self) -> Self {
|
||||
Self::Hsl(match self {
|
||||
Self::Luma(c) => Hsl::from_color(c),
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => Hsl::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklch(c) => Hsl::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklab(c) => Hsl::from_color(c),
|
||||
Self::Oklch(c) => Hsl::from_color(c),
|
||||
Self::Rgb(c) => Hsl::from_color(c),
|
||||
Self::LinearRgb(c) => Hsl::from_color(Rgb::from_linear(c)),
|
||||
Self::Cmyk(c) => Hsl::from_color(c.to_rgba()),
|
||||
@ -1409,10 +1408,8 @@ impl Color {
|
||||
pub fn to_hsv(self) -> Self {
|
||||
Self::Hsv(match self {
|
||||
Self::Luma(c) => Hsv::from_color(c),
|
||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
||||
// This yields better results than clamping.
|
||||
Self::Oklab(c) => Hsv::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklch(c) => Hsv::from_color(Okhsva::from_color(c)),
|
||||
Self::Oklab(c) => Hsv::from_color(c),
|
||||
Self::Oklch(c) => Hsv::from_color(c),
|
||||
Self::Rgb(c) => Hsv::from_color(c),
|
||||
Self::LinearRgb(c) => Hsv::from_color(Rgb::from_linear(c)),
|
||||
Self::Cmyk(c) => Hsv::from_color(c.to_rgba()),
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
@ -8,7 +8,7 @@
|
||||
spacing: 1fr,
|
||||
rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%)),
|
||||
rect(width: 1cm, fill: c),
|
||||
rect(width: 1cm, fill: c.negate()),
|
||||
rect(width: 1cm, fill: c.negate(space: cmyk)),
|
||||
)
|
||||
|
||||
#for x in range(0, 11) {
|
||||
@ -82,4 +82,4 @@
|
||||
// Ref: false
|
||||
#test-repr(luma(20%).lighten(50%), luma(60%))
|
||||
#test-repr(luma(80%).darken(20%), luma(64%))
|
||||
#test-repr(luma(80%).negate(), luma(20%))
|
||||
#test-repr(luma(80%).negate(space: luma), luma(20%))
|
||||
|
@ -11,7 +11,7 @@
|
||||
// Test color modification methods.
|
||||
#test(rgb(25, 35, 45).lighten(10%), rgb(48, 57, 66))
|
||||
#test(rgb(40, 30, 20).darken(10%), rgb(36, 27, 18))
|
||||
#test(rgb("#133337").negate(), rgb(236, 204, 200))
|
||||
#test(rgb("#133337").negate(space: rgb), rgb(236, 204, 200))
|
||||
#test(white.lighten(100%), white)
|
||||
|
||||
// Color mixing, in Oklab space by default.
|
||||
|
Loading…
x
Reference in New Issue
Block a user