Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HCT gamut mapping and HCT color distance #420

Merged
merged 6 commits into from
Feb 11, 2024

Conversation

facelessuser
Copy link
Collaborator

  • Add an HCT color distancing to keep from converting out of HCT which can be slower. Since HCT is based on CAM16, convert the C and h components to CAM16 UCS a and b. Perform euclidean distance on the resulting Lab lightness (tone) and CAM16 a and b.
  • Add support for HCT gamut mapping. Add two keywords to configure HCT gamut mapping. "hct" giving a normal gamut mapping assuming a JND of 2 and "hct-tonal" which will perform a tighter gamut mapping with a JND of close to zero and will force clamping of black and white based on tone value. This will give results that quite close to Material's tonal palettes.
  • toGamut has been extended to allow configuring the delta E method used, the JND, and enabling white and black SDR clamp.
  • Epsilon is now generically calculated to be relative to the JND.
  • The epsilon of min/max bisect threshold is still based on the JND epsilon, but now it is arbitrarily clamped to 1e-6 to prevent calculating a very small epsilon that could cause infinite loops.
  • Gamut tests have been extended to allow for converting to target space after gamut mapping (if needed). This is used to demonstrate tonal maps which require us (for best results) to stay in HCT.

- Add an HCT color distancing to keep from converting out of HCT which
  can be slower. Since HCT is based on CAM16, convert the C and h
  components to CAM16 UCS a and b. Perform euclidean distance on the
  resulting Lab lightness (tone) and CAM16 a and b.
- Add support for HCT gamut mapping. Add two keywords to configure
  HCT gamut mapping. "hct" giving a normal gamut mapping assuming a JND
  of 2 and "hct-tonal" which will perform a tighter gamut mapping with a
  JND of close to zero and will force clamping of black and white based
  on tone value. This will give results that quite close to Material's
  tonal palettes.
- toGamut has been extended to allow configuring the delta E method
  used, the JND, and enabling white and black SDR clamp.
- Epsilon is now generically calculated to be relative to the JND.
- The epsilon of min/max bisect threshold is still based on the JND
  epsilon, but now it is arbitrarily clamped to 1e-6 to prevent
  calculating a very small epsilon that could cause infinite loops.
- Gamut tests have been extended to allow for converting to target space
  after gamut mapping (if needed). This is used to demonstrate tonal
  maps which require us (for best results) to stay in HCT.
Copy link

netlify bot commented Feb 10, 2024

Deploy Preview for colorjs ready!

Name Link
🔨 Latest commit 722253e
🔍 Latest deploy log https://app.netlify.com/sites/colorjs/deploys/65c90a302430830008701aaf
😎 Deploy Preview https://deploy-preview-420--colorjs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@facelessuser
Copy link
Collaborator Author

This completes my plan for enabling HCT tonal maps.

@facelessuser
Copy link
Collaborator Author

Forgot to mention that we now source mappedColor from color not spaceColor now. If we want good tonal palettes, we need to keep the original HCT color (when provided). Converting to the gamut space and back to the mapped color introduces error and can actually break tonal palettes if the color is too far out of gamut, beyond what HCT (and the underlying CAM16 algorithm) can handle.

Copy link
Member

@jgerigmeyer jgerigmeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deltaEHCT needs a new type definition, and the type def for toGamut should be adjusted to include the new options.

src/toGamut.js Show resolved Hide resolved
src/deltaE/index.js Outdated Show resolved Hide resolved
src/toGamut.js Outdated Show resolved Hide resolved
src/deltaE/deltaEHCT.js Outdated Show resolved Hide resolved
src/toGamut.js Outdated Show resolved Hide resolved
Copy link
Member

@jgerigmeyer jgerigmeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add tests for blackWhiteClamp? I don't see any.

types/src/deltaE/deltaEHCT.d.ts Show resolved Hide resolved
types/src/toGamut.d.ts Outdated Show resolved Hide resolved
@facelessuser

This comment was marked as resolved.

@jgerigmeyer

This comment was marked as resolved.

Copy link
Member

@svgeesus svgeesus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all looking good to me

@svgeesus
Copy link
Member

svgeesus commented Feb 11, 2024

I wish it was possible to have the API documentation be a bit less mysterious about the available options for the options objects on color.deltaE and color.toGamut.

I don't know if it is possible to automatically collect together the various options that have been implemented.

@svgeesus
Copy link
Member

I wish it was possible

To clarify, that occurred to me while reading through this PR; but not as a blocker, just would like it to be possible.

@svgeesus svgeesus linked an issue Feb 11, 2024 that may be closed by this pull request
@facelessuser
Copy link
Collaborator Author

I've made one last change to ensure that if the color is already in the mapping color space, that undefined values are normalized to zero. The conversion to the target gamut space will resolve undefined values, but then you are gamut mapping with undefined values. The recent change fixes this oddity.

I think I am done unless other issues are discovered.

@svgeesus
Copy link
Member

@facelessuser I see some commits a few minutes ago, is this ready to merge? Won't merge until you say you are ready. But it would be nice to get this in the 0.5 release!

@facelessuser
Copy link
Collaborator Author

@svgeesus I am good, my last comment states what I did, why I did it, and that I am done unless other issues are brought to my attention. Thanks for checking.

@svgeesus
Copy link
Member

I've made one last change to ensure that if the color is already in the mapping color space, that undefined values are normalized to zero. The conversion to the target gamut space will resolve undefined values, but then you are gamut mapping with undefined values. The recent change fixes this oddity.

Perfect, I made a similar change to CSS Color 4 recently just to deal with powerless values. Converting "if needed" was ambiguous if the color was already in the destination color space.

@svgeesus svgeesus merged commit decf024 into color-js:main Feb 11, 2024
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Approach to HCT Tonal Palettes
4 participants