mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
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 ecow::{eco_format, EcoString, EcoVec};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use palette::convert::FromColorUnclamped;
|
|
||||||
use palette::encoding::{self, Linear};
|
use palette::encoding::{self, Linear};
|
||||||
use palette::{
|
use palette::{
|
||||||
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
|
Darken, Desaturate, FromColor, Lighten, OklabHue, RgbHue, Saturate, ShiftHue,
|
||||||
};
|
};
|
||||||
use qcms::Profile;
|
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]
|
#[func]
|
||||||
pub fn negate(self) -> Color {
|
pub fn negate(
|
||||||
match self {
|
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::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(
|
Self::Oklch(c) => Self::Oklch(Oklch::new(
|
||||||
c.l,
|
1.0 - c.l,
|
||||||
-c.chroma,
|
c.chroma,
|
||||||
OklabHue::from_degrees(360.0 - c.hue.into_degrees()),
|
OklabHue::from_degrees(c.hue.into_degrees() + 180.0),
|
||||||
c.alpha,
|
c.alpha,
|
||||||
)),
|
)),
|
||||||
Self::LinearRgb(c) => Self::LinearRgb(LinearRgb::new(
|
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::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(
|
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.saturation,
|
||||||
c.lightness,
|
c.lightness,
|
||||||
c.alpha,
|
c.alpha,
|
||||||
)),
|
)),
|
||||||
Self::Hsv(c) => Self::Hsv(Hsv::new(
|
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.saturation,
|
||||||
c.value,
|
c.value,
|
||||||
c.alpha,
|
c.alpha,
|
||||||
)),
|
)),
|
||||||
}
|
};
|
||||||
|
result.to_space(self.space())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotates the hue of the color by a given angle.
|
/// Rotates the hue of the color by a given angle.
|
||||||
@ -1304,10 +1317,8 @@ impl Color {
|
|||||||
pub fn to_luma(self) -> Self {
|
pub fn to_luma(self) -> Self {
|
||||||
Self::Luma(match self {
|
Self::Luma(match self {
|
||||||
Self::Luma(c) => c,
|
Self::Luma(c) => c,
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => Luma::from_color(c),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => Luma::from_color(c),
|
||||||
Self::Oklab(c) => Luma::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Oklch(c) => Luma::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Rgb(c) => Luma::from_color(c),
|
Self::Rgb(c) => Luma::from_color(c),
|
||||||
Self::LinearRgb(c) => Luma::from_color(c),
|
Self::LinearRgb(c) => Luma::from_color(c),
|
||||||
Self::Cmyk(c) => Luma::from_color(c.to_rgba()),
|
Self::Cmyk(c) => Luma::from_color(c.to_rgba()),
|
||||||
@ -1320,9 +1331,7 @@ impl Color {
|
|||||||
Self::Oklab(match self {
|
Self::Oklab(match self {
|
||||||
Self::Luma(c) => Oklab::from_color(c),
|
Self::Luma(c) => Oklab::from_color(c),
|
||||||
Self::Oklab(c) => c,
|
Self::Oklab(c) => c,
|
||||||
// No clamping is necessary for this conversion because the
|
Self::Oklch(c) => Oklab::from_color(c),
|
||||||
// lightness property is the same for both Oklab and Oklch.
|
|
||||||
Self::Oklch(c) => Oklab::from_color_unclamped(c),
|
|
||||||
Self::Rgb(c) => Oklab::from_color(c),
|
Self::Rgb(c) => Oklab::from_color(c),
|
||||||
Self::LinearRgb(c) => Oklab::from_color(c),
|
Self::LinearRgb(c) => Oklab::from_color(c),
|
||||||
Self::Cmyk(c) => Oklab::from_color(c.to_rgba()),
|
Self::Cmyk(c) => Oklab::from_color(c.to_rgba()),
|
||||||
@ -1334,9 +1343,7 @@ impl Color {
|
|||||||
pub fn to_oklch(self) -> Self {
|
pub fn to_oklch(self) -> Self {
|
||||||
Self::Oklch(match self {
|
Self::Oklch(match self {
|
||||||
Self::Luma(c) => Oklch::from_color(c),
|
Self::Luma(c) => Oklch::from_color(c),
|
||||||
// No clamping is necessary for this conversion because the
|
Self::Oklab(c) => Oklch::from_color(c),
|
||||||
// lightness property is the same for both Oklab and Oklch.
|
|
||||||
Self::Oklab(c) => Oklch::from_color_unclamped(c),
|
|
||||||
Self::Oklch(c) => c,
|
Self::Oklch(c) => c,
|
||||||
Self::Rgb(c) => Oklch::from_color(c),
|
Self::Rgb(c) => Oklch::from_color(c),
|
||||||
Self::LinearRgb(c) => Oklch::from_color(c),
|
Self::LinearRgb(c) => Oklch::from_color(c),
|
||||||
@ -1349,10 +1356,8 @@ impl Color {
|
|||||||
pub fn to_rgb(self) -> Self {
|
pub fn to_rgb(self) -> Self {
|
||||||
Self::Rgb(match self {
|
Self::Rgb(match self {
|
||||||
Self::Luma(c) => Rgb::from_color(c),
|
Self::Luma(c) => Rgb::from_color(c),
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => Rgb::from_color(c),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => Rgb::from_color(c),
|
||||||
Self::Oklab(c) => Rgb::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Oklch(c) => Rgb::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Rgb(c) => c,
|
Self::Rgb(c) => c,
|
||||||
Self::LinearRgb(c) => Rgb::from_linear(c),
|
Self::LinearRgb(c) => Rgb::from_linear(c),
|
||||||
Self::Cmyk(c) => Rgb::from_color(c.to_rgba()),
|
Self::Cmyk(c) => Rgb::from_color(c.to_rgba()),
|
||||||
@ -1364,10 +1369,8 @@ impl Color {
|
|||||||
pub fn to_linear_rgb(self) -> Self {
|
pub fn to_linear_rgb(self) -> Self {
|
||||||
Self::LinearRgb(match self {
|
Self::LinearRgb(match self {
|
||||||
Self::Luma(c) => LinearRgb::from_color(c),
|
Self::Luma(c) => LinearRgb::from_color(c),
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => LinearRgb::from_color(c),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => LinearRgb::from_color(c),
|
||||||
Self::Oklab(c) => LinearRgb::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Oklch(c) => LinearRgb::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Rgb(c) => LinearRgb::from_color(c),
|
Self::Rgb(c) => LinearRgb::from_color(c),
|
||||||
Self::LinearRgb(c) => c,
|
Self::LinearRgb(c) => c,
|
||||||
Self::Cmyk(c) => LinearRgb::from_color(c.to_rgba()),
|
Self::Cmyk(c) => LinearRgb::from_color(c.to_rgba()),
|
||||||
@ -1379,10 +1382,8 @@ impl Color {
|
|||||||
pub fn to_cmyk(self) -> Self {
|
pub fn to_cmyk(self) -> Self {
|
||||||
Self::Cmyk(match self {
|
Self::Cmyk(match self {
|
||||||
Self::Luma(c) => Cmyk::from_luma(c),
|
Self::Luma(c) => Cmyk::from_luma(c),
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => Cmyk::from_rgba(Rgb::from_color(c)),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => Cmyk::from_rgba(Rgb::from_color(c)),
|
||||||
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::Rgb(c) => Cmyk::from_rgba(c),
|
Self::Rgb(c) => Cmyk::from_rgba(c),
|
||||||
Self::LinearRgb(c) => Cmyk::from_rgba(Rgb::from_linear(c)),
|
Self::LinearRgb(c) => Cmyk::from_rgba(Rgb::from_linear(c)),
|
||||||
Self::Cmyk(c) => c,
|
Self::Cmyk(c) => c,
|
||||||
@ -1394,10 +1395,8 @@ impl Color {
|
|||||||
pub fn to_hsl(self) -> Self {
|
pub fn to_hsl(self) -> Self {
|
||||||
Self::Hsl(match self {
|
Self::Hsl(match self {
|
||||||
Self::Luma(c) => Hsl::from_color(c),
|
Self::Luma(c) => Hsl::from_color(c),
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => Hsl::from_color(c),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => Hsl::from_color(c),
|
||||||
Self::Oklab(c) => Hsl::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Oklch(c) => Hsl::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Rgb(c) => Hsl::from_color(c),
|
Self::Rgb(c) => Hsl::from_color(c),
|
||||||
Self::LinearRgb(c) => Hsl::from_color(Rgb::from_linear(c)),
|
Self::LinearRgb(c) => Hsl::from_color(Rgb::from_linear(c)),
|
||||||
Self::Cmyk(c) => Hsl::from_color(c.to_rgba()),
|
Self::Cmyk(c) => Hsl::from_color(c.to_rgba()),
|
||||||
@ -1409,10 +1408,8 @@ impl Color {
|
|||||||
pub fn to_hsv(self) -> Self {
|
pub fn to_hsv(self) -> Self {
|
||||||
Self::Hsv(match self {
|
Self::Hsv(match self {
|
||||||
Self::Luma(c) => Hsv::from_color(c),
|
Self::Luma(c) => Hsv::from_color(c),
|
||||||
// Perform sRGB gamut mapping by converting to Okhsv first.
|
Self::Oklab(c) => Hsv::from_color(c),
|
||||||
// This yields better results than clamping.
|
Self::Oklch(c) => Hsv::from_color(c),
|
||||||
Self::Oklab(c) => Hsv::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Oklch(c) => Hsv::from_color(Okhsva::from_color(c)),
|
|
||||||
Self::Rgb(c) => Hsv::from_color(c),
|
Self::Rgb(c) => Hsv::from_color(c),
|
||||||
Self::LinearRgb(c) => Hsv::from_color(Rgb::from_linear(c)),
|
Self::LinearRgb(c) => Hsv::from_color(Rgb::from_linear(c)),
|
||||||
Self::Cmyk(c) => Hsv::from_color(c.to_rgba()),
|
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,
|
spacing: 1fr,
|
||||||
rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%)),
|
rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%)),
|
||||||
rect(width: 1cm, fill: c),
|
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) {
|
#for x in range(0, 11) {
|
||||||
@ -82,4 +82,4 @@
|
|||||||
// Ref: false
|
// Ref: false
|
||||||
#test-repr(luma(20%).lighten(50%), luma(60%))
|
#test-repr(luma(20%).lighten(50%), luma(60%))
|
||||||
#test-repr(luma(80%).darken(20%), luma(64%))
|
#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 color modification methods.
|
||||||
#test(rgb(25, 35, 45).lighten(10%), rgb(48, 57, 66))
|
#test(rgb(25, 35, 45).lighten(10%), rgb(48, 57, 66))
|
||||||
#test(rgb(40, 30, 20).darken(10%), rgb(36, 27, 18))
|
#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)
|
#test(white.lighten(100%), white)
|
||||||
|
|
||||||
// Color mixing, in Oklab space by default.
|
// Color mixing, in Oklab space by default.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user