Skip to content

Commit

Permalink
[0a56fc7] Implement New Contrast Surfaces Spec.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyant0 committed Nov 28, 2023
1 parent 49c9352 commit 5a5c56c
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 117 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/modules/m3color.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions .idea/modules/m3color.main.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions .idea/modules/m3color.test.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ allprojects {
}
}

implementation("com.github.Kyant0:m3color:2023.10.1")
implementation("com.github.Kyant0:m3color:2023.11.1")
```

## Use with Compose M3
Expand Down
6 changes: 3 additions & 3 deletions m3color/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = 'com.kyant'
version = '2023.10.1'
version = '2023.11.1'

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand All @@ -16,7 +16,7 @@ repositories {

dependencies {
implementation('androidx.annotation:annotation:1.7.0')
implementation('com.google.errorprone:error_prone_annotations:2.22.0')
implementation('com.google.errorprone:error_prone_annotations:2.23.0')
}

afterEvaluate {
Expand All @@ -25,7 +25,7 @@ afterEvaluate {
maven(MavenPublication) {
groupId = 'com.kyant'
artifactId = 'm3color'
version = '2023.10.1'
version = '2023.11.1'
from components.java
}
}
Expand Down
2 changes: 1 addition & 1 deletion m3color/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri May 05 20:12:15 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,31 @@
import com.kyant.m3color.utils.MathUtils;

/**
* A class containing the contrast curve for a dynamic color on its background.
* A class containing a value that changes with the contrast level.
*
* <p>The four values correspond to contrast requirements for contrast levels -1.0, 0.0, 0.5, and
* 1.0, respectively.
* <p>Usually represents the contrast requirements for a dynamic color on its background. The four
* values correspond to values for contrast levels -1.0, 0.0, 0.5, and 1.0, respectively.
*/
public final class ContrastCurve {
/** Contrast requirement for contrast level -1.0 */
/** Value for contrast level -1.0 */
private final double low;

/** Contrast requirement for contrast level 0.0 */
/** Value for contrast level 0.0 */
private final double normal;

/** Contrast requirement for contrast level 0.5 */
/** Value for contrast level 0.5 */
private final double medium;

/** Contrast requirement for contrast level 1.0 */
/** Value for contrast level 1.0 */
private final double high;

/**
* Creates a `ContrastCurve` object.
*
* @param low Contrast requirement for contrast level -1.0
* @param normal Contrast requirement for contrast level 0.0
* @param medium Contrast requirement for contrast level 0.5
* @param high Contrast requirement for contrast level 1.0
* @param low Value for contrast level -1.0
* @param normal Value for contrast level 0.0
* @param medium Value for contrast level 0.5
* @param high Value for contrast level 1.0
*/
public ContrastCurve(double low, double normal, double medium, double high) {
this.low = low;
Expand All @@ -54,14 +54,14 @@ public ContrastCurve(double low, double normal, double medium, double high) {
}

/**
* Returns the contrast ratio at a given contrast level.
* Returns the value at a given contrast level.
*
* @param contrastLevel The contrast level. 0.0 is the default (normal); -1.0 is the lowest; 1.0
* is the highest.
* @return The contrast ratio, a number between 1.0 and 21.0.
* @return The value. For contrast ratios, a number between 1.0 and 21.0.
*/
@NonNull
public double getContrast(double contrastLevel) {
public double get(double contrastLevel) {
if (contrastLevel <= -1.0) {
return this.low;
} else if (contrastLevel < 0.0) {
Expand Down
115 changes: 57 additions & 58 deletions m3color/src/main/java/com/kyant/m3color/dynamiccolor/DynamicColor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
import com.google.errorprone.annotations.Var;
import com.kyant.m3color.contrast.Contrast;
import com.kyant.m3color.hct.Hct;
import com.kyant.m3color.utils.MathUtils;
import com.kyant.m3color.palettes.TonalPalette;
import com.kyant.m3color.scheme.DynamicScheme;

import com.kyant.m3color.utils.MathUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.function.Function;
Expand Down Expand Up @@ -101,14 +100,14 @@ public final class DynamicColor {
* colors. One of them must be the color being constructed.
*/
public DynamicColor(
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground,
@Nullable Function<DynamicScheme, DynamicColor> background,
@Nullable Function<DynamicScheme, DynamicColor> secondBackground,
@Nullable ContrastCurve contrastCurve,
@Nullable Function<DynamicScheme, ToneDeltaPair> toneDeltaPair) {
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground,
@Nullable Function<DynamicScheme, DynamicColor> background,
@Nullable Function<DynamicScheme, DynamicColor> secondBackground,
@Nullable ContrastCurve contrastCurve,
@Nullable Function<DynamicScheme, ToneDeltaPair> toneDeltaPair) {

this.name = name;
this.palette = palette;
Expand Down Expand Up @@ -152,15 +151,15 @@ public DynamicColor(
* @param opacity A function returning the opacity of a color, as a number between 0 and 1.
*/
public DynamicColor(
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground,
@Nullable Function<DynamicScheme, DynamicColor> background,
@Nullable Function<DynamicScheme, DynamicColor> secondBackground,
@Nullable ContrastCurve contrastCurve,
@Nullable Function<DynamicScheme, ToneDeltaPair> toneDeltaPair,
@Nullable Function<DynamicScheme, Double> opacity) {
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground,
@Nullable Function<DynamicScheme, DynamicColor> background,
@Nullable Function<DynamicScheme, DynamicColor> secondBackground,
@Nullable ContrastCurve contrastCurve,
@Nullable Function<DynamicScheme, ToneDeltaPair> toneDeltaPair,
@Nullable Function<DynamicScheme, Double> opacity) {
this.name = name;
this.palette = palette;
this.tone = tone;
Expand Down Expand Up @@ -195,18 +194,18 @@ public DynamicColor(
*/
@NonNull
public static DynamicColor fromPalette(
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone) {
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone) {
return new DynamicColor(
name,
palette,
tone,
/* isBackground= */ false,
/* background= */ null,
/* secondBackground= */ null,
/* contrastCurve= */ null,
/* toneDeltaPair= */ null);
name,
palette,
tone,
/* isBackground= */ false,
/* background= */ null,
/* secondBackground= */ null,
/* contrastCurve= */ null,
/* toneDeltaPair= */ null);
}

/**
Expand Down Expand Up @@ -234,19 +233,19 @@ public static DynamicColor fromPalette(
*/
@NonNull
public static DynamicColor fromPalette(
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground) {
@NonNull String name,
@NonNull Function<DynamicScheme, TonalPalette> palette,
@NonNull Function<DynamicScheme, Double> tone,
boolean isBackground) {
return new DynamicColor(
name,
palette,
tone,
isBackground,
/* background= */ null,
/* secondBackground= */ null,
/* contrastCurve= */ null,
/* toneDeltaPair= */ null);
name,
palette,
tone,
isBackground,
/* background= */ null,
/* secondBackground= */ null,
/* contrastCurve= */ null,
/* toneDeltaPair= */ null);
}

/**
Expand Down Expand Up @@ -326,33 +325,33 @@ public double getTone(@NonNull DynamicScheme scheme) {
double bgTone = bg.getTone(scheme);

boolean aIsNearer =
(polarity == TonePolarity.NEARER
|| (polarity == TonePolarity.LIGHTER && !scheme.isDark)
|| (polarity == TonePolarity.DARKER && scheme.isDark));
(polarity == TonePolarity.NEARER
|| (polarity == TonePolarity.LIGHTER && !scheme.isDark)
|| (polarity == TonePolarity.DARKER && scheme.isDark));
DynamicColor nearer = aIsNearer ? roleA : roleB;
DynamicColor farther = aIsNearer ? roleB : roleA;
boolean amNearer = name.equals(nearer.name);
double expansionDir = scheme.isDark ? 1 : -1;

// 1st round: solve to min, each
double nContrast = nearer.contrastCurve.getContrast(scheme.contrastLevel);
double fContrast = farther.contrastCurve.getContrast(scheme.contrastLevel);
double nContrast = nearer.contrastCurve.get(scheme.contrastLevel);
double fContrast = farther.contrastCurve.get(scheme.contrastLevel);

// If a color is good enough, it is not adjusted.
// Initial and adjusted tones for `nearer`
double nInitialTone = nearer.tone.apply(scheme);
@Var
double nTone =
Contrast.ratioOfTones(bgTone, nInitialTone) >= nContrast
? nInitialTone
: DynamicColor.foregroundTone(bgTone, nContrast);
Contrast.ratioOfTones(bgTone, nInitialTone) >= nContrast
? nInitialTone
: DynamicColor.foregroundTone(bgTone, nContrast);
// Initial and adjusted tones for `farther`
double fInitialTone = farther.tone.apply(scheme);
@Var
double fTone =
Contrast.ratioOfTones(bgTone, fInitialTone) >= fContrast
? fInitialTone
: DynamicColor.foregroundTone(bgTone, fContrast);
Contrast.ratioOfTones(bgTone, fInitialTone) >= fContrast
? fInitialTone
: DynamicColor.foregroundTone(bgTone, fContrast);

if (decreasingContrast) {
// If decreasing contrast, adjust color to the "bare minimum"
Expand Down Expand Up @@ -416,7 +415,7 @@ public double getTone(@NonNull DynamicScheme scheme) {

double bgTone = background.apply(scheme).getTone(scheme);

double desiredRatio = contrastCurve.getContrast(scheme.contrastLevel);
double desiredRatio = contrastCurve.get(scheme.contrastLevel);

if (Contrast.ratioOfTones(bgTone, answer) >= desiredRatio) {
// Don't "improve" what's good enough.
Expand Down Expand Up @@ -448,7 +447,7 @@ public double getTone(@NonNull DynamicScheme scheme) {
double lower = min(bgTone1, bgTone2);

if (Contrast.ratioOfTones(upper, answer) >= desiredRatio
&& Contrast.ratioOfTones(lower, answer) >= desiredRatio) {
&& Contrast.ratioOfTones(lower, answer) >= desiredRatio) {
return answer;
}

Expand All @@ -470,8 +469,8 @@ public double getTone(@NonNull DynamicScheme scheme) {
}

boolean prefersLight =
DynamicColor.tonePrefersLightForeground(bgTone1)
|| DynamicColor.tonePrefersLightForeground(bgTone2);
DynamicColor.tonePrefersLightForeground(bgTone1)
|| DynamicColor.tonePrefersLightForeground(bgTone2);
if (prefersLight) {
return (lightOption == -1) ? 100 : lightOption;
}
Expand Down Expand Up @@ -505,7 +504,7 @@ public static double foregroundTone(double bgTone, double ratio) {
// high and max contrast in light mode. PC's standard tone was T90, OPC's was T10, it was
// light mode, and the contrast level was 0.6568521221032331.
boolean negligibleDifference =
Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio && darkerRatio < ratio;
Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio && darkerRatio < ratio;
if (lighterRatio >= ratio || lighterRatio >= darkerRatio || negligibleDifference) {
return lighterTone;
} else {
Expand Down
Loading

0 comments on commit 5a5c56c

Please sign in to comment.