Skip to content

Commit

Permalink
Document how to create a CSS compatible color object
Browse files Browse the repository at this point in the history
  • Loading branch information
facelessuser committed Oct 30, 2023
1 parent 34d4a0e commit d6a2aaa
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 6 deletions.
60 changes: 59 additions & 1 deletion docs/src/markdown/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,64 @@ Colors are complicated, and sometimes it may not be understood why colors or col
that they do. Here we'd like to cover more advanced or specific topics that don't fit well in existing topics or are too
verbose to be included elsewhere.

## CSS Compatibility

CSS is a convenient color syntax that people are familiar with, so it makes a great text representation of colors.
ColorAide supports the input and output of CSS syntax, but that doesn't mean it is attempting to be a CSS color library.
CSS goals and ColorAide goals are at times different, and some of the decisions they make are at odds with how we feel
colors should be treated in general. While we may not have all of CSS's behaviors enabled by default, we do provide a
way to simulate CSS logic.

There are four features that allow ColorAide to mimic CSS behavior. All four features can be used on demand via special
parameters when using the appropriate, related functions, but if desired, they can be forced to be enabled for a `Color`
class. The for features are as follows:

1. Gamut mapping with `oklch-chroma` is the current CSS recommended approach. It provides a color space with better
hue preservation, but the space does become a bit more distorted at very wide gamuts and can cause sane gamut
mapping to break down. Gamut mapping colors that fall within the Rec. 2020 and Display P3 range should work
reasonably well.

2. The `css-linear` interpolator follows CSS interpolation logic which differs from ColorAide's default interpolation
logic. CSS specifically treats interpolation between achromatic hues and non-achromatic hues as if there is a hue
arc. This means that when using `longer` hue fix-ups when interpolating between a color with a undefined hue and a
color with a defined hue, you will interpolate a full 360 degrees. We do not agree with this approach and feel in
both `shorter` and `longer` hue fix-ups that there should be no arc to interpolate along.

3. Auto powerless handling in CSS will force hues to be interpolated as powerless if under certain circumstances. This
is usually happens when color space chroma/saturation components are zero. While this behavior does make general
sense, and ensures that a user is always treating achromatic colors as achromatic, it cripples the user's control of
how a color is interpolated.

ColorAide, by default, respects what the user has explicitly specified. If a user has a component set as undefined,
it is treated as defined, it is explicitly set to a numerical value, it is treated defined. This makes interpolation
very transparent. Only through natural conversions do hues become achromatic. If a user has explicitly defined a
hue, they need to use `normalize()` to force ColorAide to update powerless hues.

With all of this said, there are times when a user wants to force powerless hues, even when not explicitly defined.
In these cases ColorAide can enforce this behavior.

4. CSS also implements the idea of carrying forward undefined values during interpolation. Essentially, if a user
specifies an undefined components, but interpolation is performed in a different color space, after conversion, if
the two color spaces have compatible components, the undefined values will be carried forward to the like
components. This means that an undefined hue in HSL would be carried forward to LCh. A red component in sRGB would
be carried over to Display P3.

The concept is interesting, but it can sometimes be a bit surprising in some cases. Currently, ColorAide does not
enable this by default.

If a CSS compatible color object is required, one can be derived from the base `Color` class. All four features can be
forced as enabled by default as shown below.

```py
from coloraide import Color as Base

class Color(base):
FIT = 'oklch-chroma'
INTERPOLATOR = 'css-linear'
POWERLESS = True
CARRYFORWARD = True
```

## Round Trip Accuracy

In general, ColorAide is careful to provide good round trip conversions where practical. What this means is that we
Expand Down Expand Up @@ -129,7 +187,7 @@ white.convert('cam16-jmh', norm=False).set('m', 0.0).convert('srgb').to_string(h
```

For these types of color spaces, ColorAide will map the achromatic response with a spline and use it as a reference
to give detect achromatic values for undefined chroma and hue.
to detect achromatic values for undefined chroma and hue.

```py play
Color('cam16-jmh', [100, NaN, NaN]).convert('srgb').to_string(hex=True)
Expand Down
13 changes: 8 additions & 5 deletions docs/src/markdown/gamut.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,15 @@ and then the chroma reduced color is clipped. Using ∆E~2000~, the distance bet
clipped chroma reduced color is measured. If the resultant distance falls within the specified threshold, the clipped
color is returned.

Computationally, LCh Chroma is slower to compute than clipping, but generally provides better results. LCh, is not
necessarily the best perceptual color space available, but it is generally well understood color space that has been
Computationally, LCh Chroma is slower to compute than clipping, but generally provides better results. CIELCh, is not
necessarily the best perceptual color space available, but it is a generally well understood color space that has been
available a long time. It does suffer from a purple shift when dealing with blue colors, but can generally handle colors
far out of gamut in a reasonable manner.
in very wide gamuts reasonably.

While CSS has currently proposed LCh Chroma reduction to be done with OkLCh, and we do offer an [OkLCh variant](#oklch-chroma),
we currently still use CIELCh as the default until OkLCh can be evaluated more fully.
we currently still use CIELCh as the default until OkLCh can be evaluated more fully as it suffers from its own set of
issues even if generally has better hue preservation. In the future, the default gamut mapping approach could change if
a definitively better option is determined.

LCh Chroma is the default gamut mapping algorithm by default, unless otherwise changed, and can be performed by simply
calling `fit()` or by calling `fit(method='lch-chroma')`.
Expand Down Expand Up @@ -262,7 +264,8 @@ c.fit(method='oklch-chroma')
OkLCh is a very new color space to be used in the field of gamut mapping. While CIELCh is not perfect, its weakness are
known. OkLCh does seem to have certain quirks of its own, and may have more that have yet to be discovered. OkLCh gamut
mapping can exhibit some issues with some colors with extremely large chroma, near the edge of the visible spectrum.
While we have not made `oklch-chroma` our default yet, we have exposed the algorithm so users can begin exploring it.
While we have not made `oklch-chroma` our default, we have exposed the algorithm so users can begin exploring it mimic
the CSS approach.

### HCT Chroma

Expand Down

0 comments on commit d6a2aaa

Please sign in to comment.