You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
5.2 KiB
158 lines
5.2 KiB
/*
|
|
* Copyright 2021 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.ux.material.libmonet.hct;
|
|
|
|
import com.google.ux.material.libmonet.utils.ColorUtils;
|
|
|
|
/**
|
|
* A color system built using CAM16 hue and chroma, and L* from L*a*b*.
|
|
*
|
|
* <p>Using L* creates a link between the color system, contrast, and thus accessibility. Contrast
|
|
* ratio depends on relative luminance, or Y in the XYZ color space. L*, or perceptual luminance can
|
|
* be calculated from Y.
|
|
*
|
|
* <p>Unlike Y, L* is linear to human perception, allowing trivial creation of accurate color tones.
|
|
*
|
|
* <p>Unlike contrast ratio, measuring contrast in L* is linear, and simple to calculate. A
|
|
* difference of 40 in HCT tone guarantees a contrast ratio >= 3.0, and a difference of 50
|
|
* guarantees a contrast ratio >= 4.5.
|
|
*/
|
|
|
|
/**
|
|
* HCT, hue, chroma, and tone. A color system that provides a perceptually accurate color
|
|
* measurement system that can also accurately render what colors will appear as in different
|
|
* lighting environments.
|
|
*/
|
|
public final class Hct {
|
|
private double hue;
|
|
private double chroma;
|
|
private double tone;
|
|
private int argb;
|
|
|
|
/**
|
|
* Create an HCT color from hue, chroma, and tone.
|
|
*
|
|
* @param hue 0 <= hue < 360; invalid values are corrected.
|
|
* @param chroma 0 <= chroma < ?; Informally, colorfulness. The color returned may be lower than
|
|
* the requested chroma. Chroma has a different maximum for any given hue and tone.
|
|
* @param tone 0 <= tone <= 100; invalid values are corrected.
|
|
* @return HCT representation of a color in default viewing conditions.
|
|
*/
|
|
public static Hct from(double hue, double chroma, double tone) {
|
|
int argb = HctSolver.solveToInt(hue, chroma, tone);
|
|
return new Hct(argb);
|
|
}
|
|
|
|
/**
|
|
* Create an HCT color from a color.
|
|
*
|
|
* @param argb ARGB representation of a color.
|
|
* @return HCT representation of a color in default viewing conditions
|
|
*/
|
|
public static Hct fromInt(int argb) {
|
|
return new Hct(argb);
|
|
}
|
|
|
|
private Hct(int argb) {
|
|
setInternalState(argb);
|
|
}
|
|
|
|
public double getHue() {
|
|
return hue;
|
|
}
|
|
|
|
public double getChroma() {
|
|
return chroma;
|
|
}
|
|
|
|
public double getTone() {
|
|
return tone;
|
|
}
|
|
|
|
public int toInt() {
|
|
return argb;
|
|
}
|
|
|
|
/**
|
|
* Set the hue of this color. Chroma may decrease because chroma has a different maximum for any
|
|
* given hue and tone.
|
|
*
|
|
* @param newHue 0 <= newHue < 360; invalid values are corrected.
|
|
*/
|
|
public void setHue(double newHue) {
|
|
setInternalState(HctSolver.solveToInt(newHue, chroma, tone));
|
|
}
|
|
|
|
/**
|
|
* Set the chroma of this color. Chroma may decrease because chroma has a different maximum for
|
|
* any given hue and tone.
|
|
*
|
|
* @param newChroma 0 <= newChroma < ?
|
|
*/
|
|
public void setChroma(double newChroma) {
|
|
setInternalState(HctSolver.solveToInt(hue, newChroma, tone));
|
|
}
|
|
|
|
/**
|
|
* Set the tone of this color. Chroma may decrease because chroma has a different maximum for any
|
|
* given hue and tone.
|
|
*
|
|
* @param newTone 0 <= newTone <= 100; invalid valids are corrected.
|
|
*/
|
|
public void setTone(double newTone) {
|
|
setInternalState(HctSolver.solveToInt(hue, chroma, newTone));
|
|
}
|
|
|
|
/**
|
|
* Translate a color into different ViewingConditions.
|
|
*
|
|
* <p>Colors change appearance. They look different with lights on versus off, the same color, as
|
|
* in hex code, on white looks different when on black. This is called color relativity, most
|
|
* famously explicated by Josef Albers in Interaction of Color.
|
|
*
|
|
* <p>In color science, color appearance models can account for this and calculate the appearance
|
|
* of a color in different settings. HCT is based on CAM16, a color appearance model, and uses it
|
|
* to make these calculations.
|
|
*
|
|
* <p>See ViewingConditions.make for parameters affecting color appearance.
|
|
*/
|
|
public Hct inViewingConditions(ViewingConditions vc) {
|
|
// 1. Use CAM16 to find XYZ coordinates of color in specified VC.
|
|
Cam16 cam16 = Cam16.fromInt(toInt());
|
|
double[] viewedInVc = cam16.xyzInViewingConditions(vc, null);
|
|
|
|
// 2. Create CAM16 of those XYZ coordinates in default VC.
|
|
Cam16 recastInVc =
|
|
Cam16.fromXyzInViewingConditions(
|
|
viewedInVc[0], viewedInVc[1], viewedInVc[2], ViewingConditions.DEFAULT);
|
|
|
|
// 3. Create HCT from:
|
|
// - CAM16 using default VC with XYZ coordinates in specified VC.
|
|
// - L* converted from Y in XYZ coordinates in specified VC.
|
|
return Hct.from(
|
|
recastInVc.getHue(), recastInVc.getChroma(), ColorUtils.lstarFromY(viewedInVc[1]));
|
|
}
|
|
|
|
private void setInternalState(int argb) {
|
|
this.argb = argb;
|
|
Cam16 cam = Cam16.fromInt(argb);
|
|
hue = cam.getHue();
|
|
chroma = cam.getChroma();
|
|
this.tone = ColorUtils.lstarFromArgb(argb);
|
|
}
|
|
}
|