Add wallpaper color scheme
This commit is contained in:
parent
408a23563e
commit
8bb3737460
@ -201,4 +201,12 @@ val OpenSourceLicenses = arrayOf(
|
|||||||
licenseText = R.raw.license_apache_2,
|
licenseText = R.raw.license_apache_2,
|
||||||
url = "https://github.com/promeG/TinyPinyin"
|
url = "https://github.com/promeG/TinyPinyin"
|
||||||
),
|
),
|
||||||
|
OpenSourceLibrary(
|
||||||
|
name = "material-color-utilities",
|
||||||
|
copyrightNote = "Copyright 2021 Google LLC",
|
||||||
|
description = "Algorithms and utilities that power the Material Design 3 (M3) color system, including choosing theme colors from images and creating tones of colors; all in a new color space.",
|
||||||
|
licenseName = R.string.apache_license_name,
|
||||||
|
licenseText = R.raw.license_apache_2,
|
||||||
|
url = "https://github.com/material-foundation/material-color-utilities"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
@ -8,7 +8,6 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:7.1.3")
|
classpath("com.android.tools.build:gradle:7.1.3")
|
||||||
classpath(libs.kotlin.gradle)
|
classpath(libs.kotlin.gradle)
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
|||||||
@ -387,6 +387,7 @@
|
|||||||
<string name="preference_screen_colors">Color scheme</string>
|
<string name="preference_screen_colors">Color scheme</string>
|
||||||
<string name="preference_colors_default">Default</string>
|
<string name="preference_colors_default">Default</string>
|
||||||
<string name="preference_colors_bw">Black and White</string>
|
<string name="preference_colors_bw">Black and White</string>
|
||||||
|
<string name="preference_colors_wallpaper">From wallpaper</string>
|
||||||
<string name="preference_screen_about">About</string>
|
<string name="preference_screen_about">About</string>
|
||||||
<string name="preference_version">Version</string>
|
<string name="preference_version">Version</string>
|
||||||
<string name="preference_category_links">Links</string>
|
<string name="preference_category_links">Links</string>
|
||||||
|
|||||||
1
material-color-utilities/.gitignore
vendored
Normal file
1
material-color-utilities/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
42
material-color-utilities/build.gradle.kts
Normal file
42
material-color-utilities/build.gradle.kts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
id("kotlin-android")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = sdk.versions.compileSdk.get().toInt()
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = sdk.versions.minSdk.get().toInt()
|
||||||
|
targetSdk = sdk.versions.targetSdk.get().toInt()
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.bundles.kotlin)
|
||||||
|
implementation(libs.androidx.core)
|
||||||
|
|
||||||
|
implementation(libs.koin.android)
|
||||||
|
|
||||||
|
}
|
||||||
0
material-color-utilities/consumer-rules.pro
Normal file
0
material-color-utilities/consumer-rules.pro
Normal file
21
material-color-utilities/proguard-rules.pro
vendored
Normal file
21
material-color-utilities/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
5
material-color-utilities/src/main/AndroidManifest.xml
Normal file
5
material-color-utilities/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="de.mm20.launcher2.materialcolorutilities">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
435
material-color-utilities/src/main/java/hct/Cam16.java
Normal file
435
material-color-utilities/src/main/java/hct/Cam16.java
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
/*
|
||||||
|
* 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 hct;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
|
import utils.ColorUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CAM16, a color appearance model. Colors are not just defined by their hex code, but rather, a hex
|
||||||
|
* code and viewing conditions.
|
||||||
|
*
|
||||||
|
* <p>CAM16 instances also have coordinates in the CAM16-UCS space, called J*, a*, b*, or jstar,
|
||||||
|
* astar, bstar in code. CAM16-UCS is included in the CAM16 specification, and should be used when
|
||||||
|
* measuring distances between colors.
|
||||||
|
*
|
||||||
|
* <p>In traditional color spaces, a color can be identified solely by the observer's measurement of
|
||||||
|
* the color. Color appearance models such as CAM16 also use information about the environment where
|
||||||
|
* the color was observed, known as the viewing conditions.
|
||||||
|
*
|
||||||
|
* <p>For example, white under the traditional assumption of a midday sun white point is accurately
|
||||||
|
* measured as a slightly chromatic blue by CAM16. (roughly, hue 203, chroma 3, lightness 100)
|
||||||
|
*/
|
||||||
|
public final class Cam16 {
|
||||||
|
// Transforms XYZ color space coordinates to 'cone'/'RGB' responses in CAM16.
|
||||||
|
static final float[][] XYZ_TO_CAM16RGB = {
|
||||||
|
{0.401288f, 0.650173f, -0.051461f},
|
||||||
|
{-0.250268f, 1.204414f, 0.045854f},
|
||||||
|
{-0.002079f, 0.048952f, 0.953127f}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transforms 'cone'/'RGB' responses in CAM16 to XYZ color space coordinates.
|
||||||
|
static final float[][] CAM16RGB_TO_XYZ = {
|
||||||
|
{1.8620678f, -1.0112547f, 0.14918678f},
|
||||||
|
{0.38752654f, 0.62144744f, -0.00897398f},
|
||||||
|
{-0.01584150f, -0.03412294f, 1.0499644f}
|
||||||
|
};
|
||||||
|
|
||||||
|
// CAM16 color dimensions, see getters for documentation.
|
||||||
|
private final float hue;
|
||||||
|
private final float chroma;
|
||||||
|
private final float j;
|
||||||
|
private final float q;
|
||||||
|
private final float m;
|
||||||
|
private final float s;
|
||||||
|
|
||||||
|
// Coordinates in UCS space. Used to determine color distance, like delta E equations in L*a*b*.
|
||||||
|
private final float jstar;
|
||||||
|
private final float astar;
|
||||||
|
private final float bstar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CAM16 instances also have coordinates in the CAM16-UCS space, called J*, a*, b*, or jstar,
|
||||||
|
* astar, bstar in code. CAM16-UCS is included in the CAM16 specification, and is used to measure
|
||||||
|
* distances between colors.
|
||||||
|
*/
|
||||||
|
float distance(Cam16 other) {
|
||||||
|
float dJ = getJStar() - other.getJStar();
|
||||||
|
float dA = getAStar() - other.getAStar();
|
||||||
|
float dB = getBStar() - other.getBStar();
|
||||||
|
double dEPrime = Math.sqrt(dJ * dJ + dA * dA + dB * dB);
|
||||||
|
double dE = 1.41 * Math.pow(dEPrime, 0.63);
|
||||||
|
return (float) dE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Hue in CAM16 */
|
||||||
|
public float getHue() {
|
||||||
|
return hue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Chroma in CAM16 */
|
||||||
|
public float getChroma() {
|
||||||
|
return chroma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lightness in CAM16 */
|
||||||
|
public float getJ() {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brightness in CAM16.
|
||||||
|
*
|
||||||
|
* <p>Prefer lightness, brightness is an absolute quantity. For example, a sheet of white paper is
|
||||||
|
* much brighter viewed in sunlight than in indoor light, but it is the lightest object under any
|
||||||
|
* lighting.
|
||||||
|
*/
|
||||||
|
public float getQ() {
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Colorfulness in CAM16.
|
||||||
|
*
|
||||||
|
* <p>Prefer chroma, colorfulness is an absolute quantity. For example, a yellow toy car is much
|
||||||
|
* more colorful outside than inside, but it has the same chroma in both environments.
|
||||||
|
*/
|
||||||
|
public float getM() {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saturation in CAM16.
|
||||||
|
*
|
||||||
|
* <p>Colorfulness in proportion to brightness. Prefer chroma, saturation measures colorfulness
|
||||||
|
* relative to the color's own brightness, where chroma is colorfulness relative to white.
|
||||||
|
*/
|
||||||
|
public float getS() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lightness coordinate in CAM16-UCS */
|
||||||
|
public float getJStar() {
|
||||||
|
return jstar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** a* coordinate in CAM16-UCS */
|
||||||
|
public float getAStar() {
|
||||||
|
return astar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** b* coordinate in CAM16-UCS */
|
||||||
|
public float getBStar() {
|
||||||
|
return bstar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All of the CAM16 dimensions can be calculated from 3 of the dimensions, in the following
|
||||||
|
* combinations: - {j or q} and {c, m, or s} and hue - jstar, astar, bstar Prefer using a static
|
||||||
|
* method that constructs from 3 of those dimensions. This constructor is intended for those
|
||||||
|
* methods to use to return all possible dimensions.
|
||||||
|
*
|
||||||
|
* @param hue for example, red, orange, yellow, green, etc.
|
||||||
|
* @param chroma informally, colorfulness / color intensity. like saturation in HSL, except
|
||||||
|
* perceptually accurate.
|
||||||
|
* @param j lightness
|
||||||
|
* @param q brightness; ratio of lightness to white point's lightness
|
||||||
|
* @param m colorfulness
|
||||||
|
* @param s saturation; ratio of chroma to white point's chroma
|
||||||
|
* @param jstar CAM16-UCS J coordinate
|
||||||
|
* @param astar CAM16-UCS a coordinate
|
||||||
|
* @param bstar CAM16-UCS b coordinate
|
||||||
|
*/
|
||||||
|
private Cam16(
|
||||||
|
float hue,
|
||||||
|
float chroma,
|
||||||
|
float j,
|
||||||
|
float q,
|
||||||
|
float m,
|
||||||
|
float s,
|
||||||
|
float jstar,
|
||||||
|
float astar,
|
||||||
|
float bstar) {
|
||||||
|
this.hue = hue;
|
||||||
|
this.chroma = chroma;
|
||||||
|
this.j = j;
|
||||||
|
this.q = q;
|
||||||
|
this.m = m;
|
||||||
|
this.s = s;
|
||||||
|
this.jstar = jstar;
|
||||||
|
this.astar = astar;
|
||||||
|
this.bstar = bstar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CAM16 color from a color, assuming the color was viewed in default viewing conditions.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color.
|
||||||
|
*/
|
||||||
|
public static Cam16 fromInt(int argb) {
|
||||||
|
return fromIntInViewingConditions(argb, ViewingConditions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CAM16 color from a color in defined viewing conditions.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color.
|
||||||
|
* @param viewingConditions Information about the environment where the color was observed.
|
||||||
|
*/
|
||||||
|
// The RGB => XYZ conversion matrix elements are derived scientific constants. While the values
|
||||||
|
// may differ at runtime due to floating point imprecision, keeping the values the same, and
|
||||||
|
// accurate, across implementations takes precedence.
|
||||||
|
@SuppressWarnings("FloatingPointLiteralPrecision")
|
||||||
|
static Cam16 fromIntInViewingConditions(int argb, ViewingConditions viewingConditions) {
|
||||||
|
// Transform ARGB int to XYZ
|
||||||
|
int red = (argb & 0x00ff0000) >> 16;
|
||||||
|
int green = (argb & 0x0000ff00) >> 8;
|
||||||
|
int blue = (argb & 0x000000ff);
|
||||||
|
float redL = (float) ColorUtils.linearized(red);
|
||||||
|
float greenL = (float) ColorUtils.linearized(green);
|
||||||
|
float blueL = (float) ColorUtils.linearized(blue);
|
||||||
|
float x = 0.41233895f * redL + 0.35762064f * greenL + 0.18051042f * blueL;
|
||||||
|
float y = 0.2126f * redL + 0.7152f * greenL + 0.0722f * blueL;
|
||||||
|
float z = 0.01932141f * redL + 0.11916382f * greenL + 0.95034478f * blueL;
|
||||||
|
|
||||||
|
// Transform XYZ to 'cone'/'rgb' responses
|
||||||
|
float[][] matrix = XYZ_TO_CAM16RGB;
|
||||||
|
float rT = (x * matrix[0][0]) + (y * matrix[0][1]) + (z * matrix[0][2]);
|
||||||
|
float gT = (x * matrix[1][0]) + (y * matrix[1][1]) + (z * matrix[1][2]);
|
||||||
|
float bT = (x * matrix[2][0]) + (y * matrix[2][1]) + (z * matrix[2][2]);
|
||||||
|
|
||||||
|
// Discount illuminant
|
||||||
|
float rD = viewingConditions.getRgbD()[0] * rT;
|
||||||
|
float gD = viewingConditions.getRgbD()[1] * gT;
|
||||||
|
float bD = viewingConditions.getRgbD()[2] * bT;
|
||||||
|
|
||||||
|
// Chromatic adaptation
|
||||||
|
float rAF = (float) Math.pow(viewingConditions.getFl() * Math.abs(rD) / 100.0, 0.42);
|
||||||
|
float gAF = (float) Math.pow(viewingConditions.getFl() * Math.abs(gD) / 100.0, 0.42);
|
||||||
|
float bAF = (float) Math.pow(viewingConditions.getFl() * Math.abs(bD) / 100.0, 0.42);
|
||||||
|
float rA = Math.signum(rD) * 400.0f * rAF / (rAF + 27.13f);
|
||||||
|
float gA = Math.signum(gD) * 400.0f * gAF / (gAF + 27.13f);
|
||||||
|
float bA = Math.signum(bD) * 400.0f * bAF / (bAF + 27.13f);
|
||||||
|
|
||||||
|
// redness-greenness
|
||||||
|
float a = (float) (11.0 * rA + -12.0 * gA + bA) / 11.0f;
|
||||||
|
// yellowness-blueness
|
||||||
|
float b = (float) (rA + gA - 2.0 * bA) / 9.0f;
|
||||||
|
|
||||||
|
// auxiliary components
|
||||||
|
float u = (20.0f * rA + 20.0f * gA + 21.0f * bA) / 20.0f;
|
||||||
|
float p2 = (40.0f * rA + 20.0f * gA + bA) / 20.0f;
|
||||||
|
|
||||||
|
// hue
|
||||||
|
float atan2 = (float) Math.atan2(b, a);
|
||||||
|
float atanDegrees = atan2 * 180.0f / (float) Math.PI;
|
||||||
|
float hue =
|
||||||
|
atanDegrees < 0
|
||||||
|
? atanDegrees + 360.0f
|
||||||
|
: atanDegrees >= 360 ? atanDegrees - 360.0f : atanDegrees;
|
||||||
|
float hueRadians = hue * (float) Math.PI / 180.0f;
|
||||||
|
|
||||||
|
// achromatic response to color
|
||||||
|
float ac = p2 * viewingConditions.getNbb();
|
||||||
|
|
||||||
|
// CAM16 lightness and brightness
|
||||||
|
float j =
|
||||||
|
100.0f
|
||||||
|
* (float)
|
||||||
|
Math.pow(
|
||||||
|
ac / viewingConditions.getAw(),
|
||||||
|
viewingConditions.getC() * viewingConditions.getZ());
|
||||||
|
float q =
|
||||||
|
4.0f
|
||||||
|
/ viewingConditions.getC()
|
||||||
|
* (float) Math.sqrt(j / 100.0f)
|
||||||
|
* (viewingConditions.getAw() + 4.0f)
|
||||||
|
* viewingConditions.getFlRoot();
|
||||||
|
|
||||||
|
// CAM16 chroma, colorfulness, and saturation.
|
||||||
|
float huePrime = (hue < 20.14) ? hue + 360 : hue;
|
||||||
|
float eHue = 0.25f * (float) (Math.cos(Math.toRadians(huePrime) + 2.0) + 3.8);
|
||||||
|
float p1 = 50000.0f / 13.0f * eHue * viewingConditions.getNc() * viewingConditions.getNcb();
|
||||||
|
float t = p1 * (float) Math.hypot(a, b) / (u + 0.305f);
|
||||||
|
float alpha =
|
||||||
|
(float) Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73)
|
||||||
|
* (float) Math.pow(t, 0.9);
|
||||||
|
// CAM16 chroma, colorfulness, saturation
|
||||||
|
float c = alpha * (float) Math.sqrt(j / 100.0);
|
||||||
|
float m = c * viewingConditions.getFlRoot();
|
||||||
|
float s =
|
||||||
|
50.0f
|
||||||
|
* (float)
|
||||||
|
Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0f));
|
||||||
|
|
||||||
|
// CAM16-UCS components
|
||||||
|
float jstar = (1.0f + 100.0f * 0.007f) * j / (1.0f + 0.007f * j);
|
||||||
|
float mstar = 1.0f / 0.0228f * (float) Math.log1p(0.0228f * m);
|
||||||
|
float astar = mstar * (float) Math.cos(hueRadians);
|
||||||
|
float bstar = mstar * (float) Math.sin(hueRadians);
|
||||||
|
|
||||||
|
return new Cam16(hue, c, j, q, m, s, jstar, astar, bstar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param j CAM16 lightness
|
||||||
|
* @param c CAM16 chroma
|
||||||
|
* @param h CAM16 hue
|
||||||
|
*/
|
||||||
|
static Cam16 fromJch(float j, float c, float h) {
|
||||||
|
return fromJchInViewingConditions(j, c, h, ViewingConditions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param j CAM16 lightness
|
||||||
|
* @param c CAM16 chroma
|
||||||
|
* @param h CAM16 hue
|
||||||
|
* @param viewingConditions Information about the environment where the color was observed.
|
||||||
|
*/
|
||||||
|
private static Cam16 fromJchInViewingConditions(
|
||||||
|
float j, float c, float h, ViewingConditions viewingConditions) {
|
||||||
|
float q =
|
||||||
|
4.0f
|
||||||
|
/ viewingConditions.getC()
|
||||||
|
* (float) Math.sqrt(j / 100.0)
|
||||||
|
* (viewingConditions.getAw() + 4.0f)
|
||||||
|
* viewingConditions.getFlRoot();
|
||||||
|
float m = c * viewingConditions.getFlRoot();
|
||||||
|
float alpha = c / (float) Math.sqrt(j / 100.0);
|
||||||
|
float s =
|
||||||
|
50.0f
|
||||||
|
* (float)
|
||||||
|
Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0f));
|
||||||
|
|
||||||
|
float hueRadians = h * (float) Math.PI / 180.0f;
|
||||||
|
float jstar = (1.0f + 100.0f * 0.007f) * j / (1.0f + 0.007f * j);
|
||||||
|
float mstar = 1.0f / 0.0228f * (float) Math.log1p(0.0228 * m);
|
||||||
|
float astar = mstar * (float) Math.cos(hueRadians);
|
||||||
|
float bstar = mstar * (float) Math.sin(hueRadians);
|
||||||
|
return new Cam16(h, c, j, q, m, s, jstar, astar, bstar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CAM16 color from CAM16-UCS coordinates.
|
||||||
|
*
|
||||||
|
* @param jstar CAM16-UCS lightness.
|
||||||
|
* @param astar CAM16-UCS a dimension. Like a* in L*a*b*, it is a Cartesian coordinate on the Y
|
||||||
|
* axis.
|
||||||
|
* @param bstar CAM16-UCS b dimension. Like a* in L*a*b*, it is a Cartesian coordinate on the X
|
||||||
|
* axis.
|
||||||
|
*/
|
||||||
|
public static Cam16 fromUcs(float jstar, float astar, float bstar) {
|
||||||
|
|
||||||
|
return fromUcsInViewingConditions(jstar, astar, bstar, ViewingConditions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CAM16 color from CAM16-UCS coordinates in defined viewing conditions.
|
||||||
|
*
|
||||||
|
* @param jstar CAM16-UCS lightness.
|
||||||
|
* @param astar CAM16-UCS a dimension. Like a* in L*a*b*, it is a Cartesian coordinate on the Y
|
||||||
|
* axis.
|
||||||
|
* @param bstar CAM16-UCS b dimension. Like a* in L*a*b*, it is a Cartesian coordinate on the X
|
||||||
|
* axis.
|
||||||
|
* @param viewingConditions Information about the environment where the color was observed.
|
||||||
|
*/
|
||||||
|
public static Cam16 fromUcsInViewingConditions(
|
||||||
|
float jstar, float astar, float bstar, ViewingConditions viewingConditions) {
|
||||||
|
|
||||||
|
double m = Math.hypot(astar, bstar);
|
||||||
|
double m2 = Math.expm1(m * 0.0228f) / 0.0228f;
|
||||||
|
double c = m2 / viewingConditions.getFlRoot();
|
||||||
|
double h = Math.atan2(bstar, astar) * (180.0f / Math.PI);
|
||||||
|
if (h < 0.0) {
|
||||||
|
h += 360.0f;
|
||||||
|
}
|
||||||
|
float j = jstar / (1f - (jstar - 100f) * 0.007f);
|
||||||
|
return fromJchInViewingConditions(j, (float) c, (float) h, viewingConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ARGB representation of the color. Assumes the color was viewed in default viewing conditions,
|
||||||
|
* which are near-identical to the default viewing conditions for sRGB.
|
||||||
|
*/
|
||||||
|
public int getInt() {
|
||||||
|
return viewed(ViewingConditions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ARGB representation of the color, in defined viewing conditions.
|
||||||
|
*
|
||||||
|
* @param viewingConditions Information about the environment where the color will be viewed.
|
||||||
|
* @return ARGB representation of color
|
||||||
|
*/
|
||||||
|
int viewed(ViewingConditions viewingConditions) {
|
||||||
|
float alpha =
|
||||||
|
(getChroma() == 0.0 || getJ() == 0.0)
|
||||||
|
? 0.0f
|
||||||
|
: getChroma() / (float) Math.sqrt(getJ() / 100.0);
|
||||||
|
|
||||||
|
float t =
|
||||||
|
(float)
|
||||||
|
Math.pow(
|
||||||
|
alpha / Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73), 1.0 / 0.9);
|
||||||
|
float hRad = getHue() * (float) Math.PI / 180.0f;
|
||||||
|
|
||||||
|
float eHue = 0.25f * (float) (Math.cos(hRad + 2.0) + 3.8);
|
||||||
|
float ac =
|
||||||
|
viewingConditions.getAw()
|
||||||
|
* (float)
|
||||||
|
Math.pow(getJ() / 100.0, 1.0 / viewingConditions.getC() / viewingConditions.getZ());
|
||||||
|
float p1 = eHue * (50000.0f / 13.0f) * viewingConditions.getNc() * viewingConditions.getNcb();
|
||||||
|
float p2 = (ac / viewingConditions.getNbb());
|
||||||
|
|
||||||
|
float hSin = (float) Math.sin(hRad);
|
||||||
|
float hCos = (float) Math.cos(hRad);
|
||||||
|
|
||||||
|
float gamma = 23.0f * (p2 + 0.305f) * t / (23.0f * p1 + 11.0f * t * hCos + 108.0f * t * hSin);
|
||||||
|
float a = gamma * hCos;
|
||||||
|
float b = gamma * hSin;
|
||||||
|
float rA = (460.0f * p2 + 451.0f * a + 288.0f * b) / 1403.0f;
|
||||||
|
float gA = (460.0f * p2 - 891.0f * a - 261.0f * b) / 1403.0f;
|
||||||
|
float bA = (460.0f * p2 - 220.0f * a - 6300.0f * b) / 1403.0f;
|
||||||
|
|
||||||
|
float rCBase = (float) max(0, (27.13 * Math.abs(rA)) / (400.0 - Math.abs(rA)));
|
||||||
|
float rC =
|
||||||
|
Math.signum(rA)
|
||||||
|
* (100.0f / viewingConditions.getFl())
|
||||||
|
* (float) Math.pow(rCBase, 1.0 / 0.42);
|
||||||
|
float gCBase = (float) max(0, (27.13 * Math.abs(gA)) / (400.0 - Math.abs(gA)));
|
||||||
|
float gC =
|
||||||
|
Math.signum(gA)
|
||||||
|
* (100.0f / viewingConditions.getFl())
|
||||||
|
* (float) Math.pow(gCBase, 1.0 / 0.42);
|
||||||
|
float bCBase = (float) max(0, (27.13 * Math.abs(bA)) / (400.0 - Math.abs(bA)));
|
||||||
|
float bC =
|
||||||
|
Math.signum(bA)
|
||||||
|
* (100.0f / viewingConditions.getFl())
|
||||||
|
* (float) Math.pow(bCBase, 1.0 / 0.42);
|
||||||
|
float rF = rC / viewingConditions.getRgbD()[0];
|
||||||
|
float gF = gC / viewingConditions.getRgbD()[1];
|
||||||
|
float bF = bC / viewingConditions.getRgbD()[2];
|
||||||
|
|
||||||
|
float[][] matrix = CAM16RGB_TO_XYZ;
|
||||||
|
float x = (rF * matrix[0][0]) + (gF * matrix[0][1]) + (bF * matrix[0][2]);
|
||||||
|
float y = (rF * matrix[1][0]) + (gF * matrix[1][1]) + (bF * matrix[1][2]);
|
||||||
|
float z = (rF * matrix[2][0]) + (gF * matrix[2][1]) + (bF * matrix[2][2]);
|
||||||
|
|
||||||
|
return ColorUtils.argbFromXyz(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
262
material-color-utilities/src/main/java/hct/Hct.java
Normal file
262
material-color-utilities/src/main/java/hct/Hct.java
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* 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 hct;
|
||||||
|
|
||||||
|
import utils.ColorUtils;
|
||||||
|
import utils.MathUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 float hue;
|
||||||
|
private float chroma;
|
||||||
|
private float tone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(float hue, float chroma, float tone) {
|
||||||
|
return new Hct(hue, chroma, tone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
Cam16 cam = Cam16.fromInt(argb);
|
||||||
|
return new Hct(cam.getHue(), cam.getChroma(), (float) ColorUtils.lstarFromArgb(argb));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hct(float hue, float chroma, float tone) {
|
||||||
|
setInternalState(gamutMap(hue, chroma, tone));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getHue() {
|
||||||
|
return hue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getChroma() {
|
||||||
|
return chroma;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getTone() {
|
||||||
|
return tone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int toInt() {
|
||||||
|
return gamutMap(hue, chroma, tone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(float newHue) {
|
||||||
|
setInternalState(gamutMap((float) MathUtils.sanitizeDegreesDouble(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(float newChroma) {
|
||||||
|
setInternalState(gamutMap(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(float newTone) {
|
||||||
|
setInternalState(gamutMap(hue, chroma, newTone));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInternalState(int argb) {
|
||||||
|
Cam16 cam = Cam16.fromInt(argb);
|
||||||
|
float tone = (float) ColorUtils.lstarFromArgb(argb);
|
||||||
|
hue = cam.getHue();
|
||||||
|
chroma = cam.getChroma();
|
||||||
|
this.tone = tone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the delta between the floor & ceiling of a binary search for maximum chroma at a hue and
|
||||||
|
* tone is less than this, the binary search terminates.
|
||||||
|
*/
|
||||||
|
private static final float CHROMA_SEARCH_ENDPOINT = 0.4f;
|
||||||
|
|
||||||
|
/** The maximum color distance, in CAM16-UCS, between a requested color and the color returned. */
|
||||||
|
private static final float DE_MAX = 1.0f;
|
||||||
|
|
||||||
|
/** The maximum difference between the requested L* and the L* returned. */
|
||||||
|
private static final float DL_MAX = 0.2f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum color distance, in CAM16-UCS, between a requested color and an 'exact' match. This
|
||||||
|
* allows the binary search during gamut mapping to terminate much earlier when the error is
|
||||||
|
* infinitesimal.
|
||||||
|
*/
|
||||||
|
private static final float DE_MAX_ERROR = 0.000000001f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the delta between the floor & ceiling of a binary search for J, lightness in CAM16, is
|
||||||
|
* less than this, the binary search terminates.
|
||||||
|
*/
|
||||||
|
private static final float LIGHTNESS_SEARCH_ENDPOINT = 0.01f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hue a number, in degrees, representing ex. red, orange, yellow, etc. Ranges from 0 <=
|
||||||
|
* hue < 360.
|
||||||
|
* @param chroma Informally, colorfulness. Ranges from 0 to roughly 150. Like all perceptually
|
||||||
|
* accurate color systems, chroma has a different maximum for any given hue and tone, so the
|
||||||
|
* color returned may be lower than the requested chroma.
|
||||||
|
* @param tone Lightness. Ranges from 0 to 100.
|
||||||
|
* @return ARGB representation of a color in default viewing conditions
|
||||||
|
*/
|
||||||
|
private static int gamutMap(float hue, float chroma, float tone) {
|
||||||
|
return gamutMapInViewingConditions(hue, chroma, tone, ViewingConditions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hue CAM16 hue.
|
||||||
|
* @param chroma CAM16 chroma.
|
||||||
|
* @param tone L*a*b* lightness.
|
||||||
|
* @param viewingConditions Information about the environment where the color was observed.
|
||||||
|
*/
|
||||||
|
static int gamutMapInViewingConditions(
|
||||||
|
float hue, float chroma, float tone, ViewingConditions viewingConditions) {
|
||||||
|
|
||||||
|
if (chroma < 1.0 || Math.round(tone) <= 0.0 || Math.round(tone) >= 100.0) {
|
||||||
|
return ColorUtils.argbFromLstar(tone);
|
||||||
|
}
|
||||||
|
|
||||||
|
hue = (float) MathUtils.sanitizeDegreesDouble(hue);
|
||||||
|
|
||||||
|
float high = chroma;
|
||||||
|
float mid = chroma;
|
||||||
|
float low = 0.0f;
|
||||||
|
boolean isFirstLoop = true;
|
||||||
|
|
||||||
|
Cam16 answer = null;
|
||||||
|
while (Math.abs(low - high) >= CHROMA_SEARCH_ENDPOINT) {
|
||||||
|
Cam16 possibleAnswer = findCamByJ(hue, mid, tone);
|
||||||
|
|
||||||
|
if (isFirstLoop) {
|
||||||
|
if (possibleAnswer != null) {
|
||||||
|
return possibleAnswer.viewed(viewingConditions);
|
||||||
|
} else {
|
||||||
|
isFirstLoop = false;
|
||||||
|
mid = low + (high - low) / 2.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (possibleAnswer == null) {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
answer = possibleAnswer;
|
||||||
|
low = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
mid = low + (high - low) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (answer == null) {
|
||||||
|
return ColorUtils.argbFromLstar(tone);
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer.viewed(viewingConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hue CAM16 hue
|
||||||
|
* @param chroma CAM16 chroma
|
||||||
|
* @param tone L*a*b* lightness
|
||||||
|
* @return CAM16 instance within error tolerance of the provided dimensions, or null.
|
||||||
|
*/
|
||||||
|
private static Cam16 findCamByJ(float hue, float chroma, float tone) {
|
||||||
|
float low = 0.0f;
|
||||||
|
float high = 100.0f;
|
||||||
|
float mid = 0.0f;
|
||||||
|
float bestdL = 1000.0f;
|
||||||
|
float bestdE = 1000.0f;
|
||||||
|
|
||||||
|
Cam16 bestCam = null;
|
||||||
|
while (Math.abs(low - high) > LIGHTNESS_SEARCH_ENDPOINT) {
|
||||||
|
mid = low + (high - low) / 2;
|
||||||
|
Cam16 camBeforeClip = Cam16.fromJch(mid, chroma, hue);
|
||||||
|
int clipped = camBeforeClip.getInt();
|
||||||
|
float clippedLstar = (float) ColorUtils.lstarFromArgb(clipped);
|
||||||
|
float dL = Math.abs(tone - clippedLstar);
|
||||||
|
|
||||||
|
if (dL < DL_MAX) {
|
||||||
|
Cam16 camClipped = Cam16.fromInt(clipped);
|
||||||
|
float dE =
|
||||||
|
camClipped.distance(Cam16.fromJch(camClipped.getJ(), camClipped.getChroma(), hue));
|
||||||
|
if (dE <= DE_MAX && dE <= bestdE) {
|
||||||
|
bestdL = dL;
|
||||||
|
bestdE = dE;
|
||||||
|
bestCam = camClipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestdL == 0 && bestdE < DE_MAX_ERROR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clippedLstar < tone) {
|
||||||
|
low = mid;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestCam;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* 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 hct;
|
||||||
|
|
||||||
|
import utils.ColorUtils;
|
||||||
|
import utils.MathUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In traditional color spaces, a color can be identified solely by the observer's measurement of
|
||||||
|
* the color. Color appearance models such as CAM16 also use information about the environment where
|
||||||
|
* the color was observed, known as the viewing conditions.
|
||||||
|
*
|
||||||
|
* <p>For example, white under the traditional assumption of a midday sun white point is accurately
|
||||||
|
* measured as a slightly chromatic blue by CAM16. (roughly, hue 203, chroma 3, lightness 100)
|
||||||
|
*
|
||||||
|
* <p>This class caches intermediate values of the CAM16 conversion process that depend only on
|
||||||
|
* viewing conditions, enabling speed ups.
|
||||||
|
*/
|
||||||
|
public final class ViewingConditions {
|
||||||
|
/** sRGB-like viewing conditions. */
|
||||||
|
public static final ViewingConditions DEFAULT =
|
||||||
|
ViewingConditions.make(
|
||||||
|
new float[] {
|
||||||
|
(float) ColorUtils.whitePointD65()[0],
|
||||||
|
(float) ColorUtils.whitePointD65()[1],
|
||||||
|
(float) ColorUtils.whitePointD65()[2]
|
||||||
|
},
|
||||||
|
(float) (200.0f / Math.PI * ColorUtils.yFromLstar(50.0f) / 100.f),
|
||||||
|
50.0f,
|
||||||
|
2.0f,
|
||||||
|
false);
|
||||||
|
|
||||||
|
private final float aw;
|
||||||
|
private final float nbb;
|
||||||
|
private final float ncb;
|
||||||
|
private final float c;
|
||||||
|
private final float nc;
|
||||||
|
private final float n;
|
||||||
|
private final float[] rgbD;
|
||||||
|
private final float fl;
|
||||||
|
private final float flRoot;
|
||||||
|
private final float z;
|
||||||
|
|
||||||
|
public float getAw() {
|
||||||
|
return aw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getN() {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getNbb() {
|
||||||
|
return nbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getNcb() {
|
||||||
|
return ncb;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getC() {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getNc() {
|
||||||
|
return nc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRgbD() {
|
||||||
|
return rgbD;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getFl() {
|
||||||
|
return fl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFlRoot() {
|
||||||
|
return flRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getZ() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ViewingConditions from a simple, physically relevant, set of parameters.
|
||||||
|
*
|
||||||
|
* @param whitePoint White point, measured in the XYZ color space. default = D65, or sunny day
|
||||||
|
* afternoon
|
||||||
|
* @param adaptingLuminance The luminance of the adapting field. Informally, how bright it is in
|
||||||
|
* the room where the color is viewed. Can be calculated from lux by multiplying lux by
|
||||||
|
* 0.0586. default = 11.72, or 200 lux.
|
||||||
|
* @param backgroundLstar The lightness of the area surrounding the color. measured by L* in
|
||||||
|
* L*a*b*. default = 50.0
|
||||||
|
* @param surround A general description of the lighting surrounding the color. 0 is pitch dark,
|
||||||
|
* like watching a movie in a theater. 1.0 is a dimly light room, like watching TV at home at
|
||||||
|
* night. 2.0 means there is no difference between the lighting on the color and around it.
|
||||||
|
* default = 2.0
|
||||||
|
* @param discountingIlluminant Whether the eye accounts for the tint of the ambient lighting,
|
||||||
|
* such as knowing an apple is still red in green light. default = false, the eye does not
|
||||||
|
* perform this process on self-luminous objects like displays.
|
||||||
|
*/
|
||||||
|
static ViewingConditions make(
|
||||||
|
float[] whitePoint,
|
||||||
|
float adaptingLuminance,
|
||||||
|
float backgroundLstar,
|
||||||
|
float surround,
|
||||||
|
boolean discountingIlluminant) {
|
||||||
|
// Transform white point XYZ to 'cone'/'rgb' responses
|
||||||
|
float[][] matrix = Cam16.XYZ_TO_CAM16RGB;
|
||||||
|
float[] xyz = whitePoint;
|
||||||
|
float rW = (xyz[0] * matrix[0][0]) + (xyz[1] * matrix[0][1]) + (xyz[2] * matrix[0][2]);
|
||||||
|
float gW = (xyz[0] * matrix[1][0]) + (xyz[1] * matrix[1][1]) + (xyz[2] * matrix[1][2]);
|
||||||
|
float bW = (xyz[0] * matrix[2][0]) + (xyz[1] * matrix[2][1]) + (xyz[2] * matrix[2][2]);
|
||||||
|
float f = 0.8f + (surround / 10.0f);
|
||||||
|
float c =
|
||||||
|
(f >= 0.9)
|
||||||
|
? (float) MathUtils.lerp(0.59f, 0.69f, ((f - 0.9f) * 10.0f))
|
||||||
|
: (float) MathUtils.lerp(0.525f, 0.59f, ((f - 0.8f) * 10.0f));
|
||||||
|
float d =
|
||||||
|
discountingIlluminant
|
||||||
|
? 1.0f
|
||||||
|
: f * (1.0f - ((1.0f / 3.6f) * (float) Math.exp((-adaptingLuminance - 42.0f) / 92.0f)));
|
||||||
|
d = (d > 1.0) ? 1.0f : (d < 0.0) ? 0.0f : d;
|
||||||
|
float nc = f;
|
||||||
|
float[] rgbD =
|
||||||
|
new float[] {
|
||||||
|
d * (100.0f / rW) + 1.0f - d, d * (100.0f / gW) + 1.0f - d, d * (100.0f / bW) + 1.0f - d
|
||||||
|
};
|
||||||
|
float k = 1.0f / (5.0f * adaptingLuminance + 1.0f);
|
||||||
|
float k4 = k * k * k * k;
|
||||||
|
float k4F = 1.0f - k4;
|
||||||
|
float fl =
|
||||||
|
(k4 * adaptingLuminance) + (0.1f * k4F * k4F * (float) Math.cbrt(5.0 * adaptingLuminance));
|
||||||
|
float n = (float) (ColorUtils.yFromLstar(backgroundLstar) / whitePoint[1]);
|
||||||
|
float z = 1.48f + (float) Math.sqrt(n);
|
||||||
|
float nbb = 0.725f / (float) Math.pow(n, 0.2);
|
||||||
|
float ncb = nbb;
|
||||||
|
float[] rgbAFactors =
|
||||||
|
new float[] {
|
||||||
|
(float) Math.pow(fl * rgbD[0] * rW / 100.0, 0.42),
|
||||||
|
(float) Math.pow(fl * rgbD[1] * gW / 100.0, 0.42),
|
||||||
|
(float) Math.pow(fl * rgbD[2] * bW / 100.0, 0.42)
|
||||||
|
};
|
||||||
|
|
||||||
|
float[] rgbA =
|
||||||
|
new float[] {
|
||||||
|
(400.0f * rgbAFactors[0]) / (rgbAFactors[0] + 27.13f),
|
||||||
|
(400.0f * rgbAFactors[1]) / (rgbAFactors[1] + 27.13f),
|
||||||
|
(400.0f * rgbAFactors[2]) / (rgbAFactors[2] + 27.13f)
|
||||||
|
};
|
||||||
|
|
||||||
|
float aw = ((2.0f * rgbA[0]) + rgbA[1] + (0.05f * rgbA[2])) * nbb;
|
||||||
|
return new ViewingConditions(n, aw, nbb, ncb, c, nc, rgbD, fl, (float) Math.pow(fl, 0.25), z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters are intermediate values of the CAM16 conversion process. Their names are shorthand
|
||||||
|
* for technical color science terminology, this class would not benefit from documenting them
|
||||||
|
* individually. A brief overview is available in the CAM16 specification, and a complete overview
|
||||||
|
* requires a color science textbook, such as Fairchild's Color Appearance Models.
|
||||||
|
*/
|
||||||
|
private ViewingConditions(
|
||||||
|
float n,
|
||||||
|
float aw,
|
||||||
|
float nbb,
|
||||||
|
float ncb,
|
||||||
|
float c,
|
||||||
|
float nc,
|
||||||
|
float[] rgbD,
|
||||||
|
float fl,
|
||||||
|
float flRoot,
|
||||||
|
float z) {
|
||||||
|
this.n = n;
|
||||||
|
this.aw = aw;
|
||||||
|
this.nbb = nbb;
|
||||||
|
this.ncb = ncb;
|
||||||
|
this.c = c;
|
||||||
|
this.nc = nc;
|
||||||
|
this.rgbD = rgbD;
|
||||||
|
this.fl = fl;
|
||||||
|
this.flRoot = flRoot;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 palettes;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
|
import hct.Hct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An intermediate concept between the key color for a UI theme, and a full color scheme. 5 sets of
|
||||||
|
* tones are generated, all except one use the same hue as the key color, and all vary in chroma.
|
||||||
|
*/
|
||||||
|
public final class CorePalette {
|
||||||
|
public TonalPalette a1;
|
||||||
|
public TonalPalette a2;
|
||||||
|
public TonalPalette a3;
|
||||||
|
public TonalPalette n1;
|
||||||
|
public TonalPalette n2;
|
||||||
|
public TonalPalette error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create key tones from a color.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color
|
||||||
|
*/
|
||||||
|
public static CorePalette of(int argb) {
|
||||||
|
return new CorePalette(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CorePalette(int argb) {
|
||||||
|
Hct hct = Hct.fromInt(argb);
|
||||||
|
float hue = hct.getHue();
|
||||||
|
this.a1 = TonalPalette.fromHueAndChroma(hue, max(48f, hct.getChroma()));
|
||||||
|
this.a2 = TonalPalette.fromHueAndChroma(hue, 16f);
|
||||||
|
this.a3 = TonalPalette.fromHueAndChroma(hue + 60f, 24f);
|
||||||
|
this.n1 = TonalPalette.fromHueAndChroma(hue, 4f);
|
||||||
|
this.n2 = TonalPalette.fromHueAndChroma(hue, 8f);
|
||||||
|
this.error = TonalPalette.fromHueAndChroma(25, 84f);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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 palettes;
|
||||||
|
|
||||||
|
import hct.Hct;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience class for retrieving colors that are constant in hue and chroma, but vary in tone.
|
||||||
|
*/
|
||||||
|
public final class TonalPalette {
|
||||||
|
Map<Integer, Integer> cache;
|
||||||
|
float hue;
|
||||||
|
float chroma;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create tones using the HCT hue and chroma from a color.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color
|
||||||
|
* @return Tones matching that color's hue and chroma.
|
||||||
|
*/
|
||||||
|
public static final TonalPalette fromInt(int argb) {
|
||||||
|
Hct hct = Hct.fromInt(argb);
|
||||||
|
return TonalPalette.fromHueAndChroma(hct.getHue(), hct.getChroma());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create tones from a defined HCT hue and chroma.
|
||||||
|
*
|
||||||
|
* @param hue HCT hue
|
||||||
|
* @param chroma HCT chroma
|
||||||
|
* @return Tones matching hue and chroma.
|
||||||
|
*/
|
||||||
|
public static final TonalPalette fromHueAndChroma(float hue, float chroma) {
|
||||||
|
return new TonalPalette(hue, chroma);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TonalPalette(float hue, float chroma) {
|
||||||
|
cache = new HashMap<>();
|
||||||
|
this.hue = hue;
|
||||||
|
this.chroma = chroma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an ARGB color with HCT hue and chroma of this Tones instance, and the provided HCT tone.
|
||||||
|
*
|
||||||
|
* @param tone HCT tone, measured from 0 to 100.
|
||||||
|
* @return ARGB representation of a color with that tone.
|
||||||
|
*/
|
||||||
|
// AndroidJdkLibsChecker is higher priority than ComputeIfAbsentUseValue (b/119581923)
|
||||||
|
@SuppressWarnings("ComputeIfAbsentUseValue")
|
||||||
|
public int tone(int tone) {
|
||||||
|
Integer color = cache.get(tone);
|
||||||
|
if (color == null) {
|
||||||
|
color = Hct.from(this.hue, this.chroma, tone).toInt();
|
||||||
|
cache.put(tone, color);
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
717
material-color-utilities/src/main/java/scheme/Scheme.java
Normal file
717
material-color-utilities/src/main/java/scheme/Scheme.java
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
/*
|
||||||
|
* 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 scheme;
|
||||||
|
|
||||||
|
import palettes.CorePalette;
|
||||||
|
|
||||||
|
/** Represents a Material color scheme, a mapping of color roles to colors. */
|
||||||
|
public class Scheme {
|
||||||
|
private int primary;
|
||||||
|
private int onPrimary;
|
||||||
|
private int primaryContainer;
|
||||||
|
private int onPrimaryContainer;
|
||||||
|
private int secondary;
|
||||||
|
private int onSecondary;
|
||||||
|
private int secondaryContainer;
|
||||||
|
private int onSecondaryContainer;
|
||||||
|
private int tertiary;
|
||||||
|
private int onTertiary;
|
||||||
|
private int tertiaryContainer;
|
||||||
|
private int onTertiaryContainer;
|
||||||
|
private int error;
|
||||||
|
private int onError;
|
||||||
|
private int errorContainer;
|
||||||
|
private int onErrorContainer;
|
||||||
|
private int background;
|
||||||
|
private int onBackground;
|
||||||
|
private int surface;
|
||||||
|
private int onSurface;
|
||||||
|
private int surfaceVariant;
|
||||||
|
private int onSurfaceVariant;
|
||||||
|
private int outline;
|
||||||
|
private int shadow;
|
||||||
|
private int inverseSurface;
|
||||||
|
private int inverseOnSurface;
|
||||||
|
private int inversePrimary;
|
||||||
|
|
||||||
|
public Scheme() {}
|
||||||
|
|
||||||
|
public Scheme(
|
||||||
|
int primary,
|
||||||
|
int onPrimary,
|
||||||
|
int primaryContainer,
|
||||||
|
int onPrimaryContainer,
|
||||||
|
int secondary,
|
||||||
|
int onSecondary,
|
||||||
|
int secondaryContainer,
|
||||||
|
int onSecondaryContainer,
|
||||||
|
int tertiary,
|
||||||
|
int onTertiary,
|
||||||
|
int tertiaryContainer,
|
||||||
|
int onTertiaryContainer,
|
||||||
|
int error,
|
||||||
|
int onError,
|
||||||
|
int errorContainer,
|
||||||
|
int onErrorContainer,
|
||||||
|
int background,
|
||||||
|
int onBackground,
|
||||||
|
int surface,
|
||||||
|
int onSurface,
|
||||||
|
int surfaceVariant,
|
||||||
|
int onSurfaceVariant,
|
||||||
|
int outline,
|
||||||
|
int shadow,
|
||||||
|
int inverseSurface,
|
||||||
|
int inverseOnSurface,
|
||||||
|
int inversePrimary) {
|
||||||
|
super();
|
||||||
|
this.primary = primary;
|
||||||
|
this.onPrimary = onPrimary;
|
||||||
|
this.primaryContainer = primaryContainer;
|
||||||
|
this.onPrimaryContainer = onPrimaryContainer;
|
||||||
|
this.secondary = secondary;
|
||||||
|
this.onSecondary = onSecondary;
|
||||||
|
this.secondaryContainer = secondaryContainer;
|
||||||
|
this.onSecondaryContainer = onSecondaryContainer;
|
||||||
|
this.tertiary = tertiary;
|
||||||
|
this.onTertiary = onTertiary;
|
||||||
|
this.tertiaryContainer = tertiaryContainer;
|
||||||
|
this.onTertiaryContainer = onTertiaryContainer;
|
||||||
|
this.error = error;
|
||||||
|
this.onError = onError;
|
||||||
|
this.errorContainer = errorContainer;
|
||||||
|
this.onErrorContainer = onErrorContainer;
|
||||||
|
this.background = background;
|
||||||
|
this.onBackground = onBackground;
|
||||||
|
this.surface = surface;
|
||||||
|
this.onSurface = onSurface;
|
||||||
|
this.surfaceVariant = surfaceVariant;
|
||||||
|
this.onSurfaceVariant = onSurfaceVariant;
|
||||||
|
this.outline = outline;
|
||||||
|
this.shadow = shadow;
|
||||||
|
this.inverseSurface = inverseSurface;
|
||||||
|
this.inverseOnSurface = inverseOnSurface;
|
||||||
|
this.inversePrimary = inversePrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Scheme light(int argb) {
|
||||||
|
CorePalette core = CorePalette.of(argb);
|
||||||
|
return new Scheme()
|
||||||
|
.withPrimary(core.a1.tone(40))
|
||||||
|
.withOnPrimary(core.a1.tone(100))
|
||||||
|
.withPrimaryContainer(core.a1.tone(90))
|
||||||
|
.withOnPrimaryContainer(core.a1.tone(10))
|
||||||
|
.withSecondary(core.a2.tone(40))
|
||||||
|
.withOnSecondary(core.a2.tone(100))
|
||||||
|
.withSecondaryContainer(core.a2.tone(90))
|
||||||
|
.withOnSecondaryContainer(core.a2.tone(10))
|
||||||
|
.withTertiary(core.a3.tone(40))
|
||||||
|
.withOnTertiary(core.a3.tone(100))
|
||||||
|
.withTertiaryContainer(core.a3.tone(90))
|
||||||
|
.withOnTertiaryContainer(core.a3.tone(10))
|
||||||
|
.withError(core.error.tone(40))
|
||||||
|
.withOnError(core.error.tone(100))
|
||||||
|
.withErrorContainer(core.error.tone(90))
|
||||||
|
.withOnErrorContainer(core.error.tone(10))
|
||||||
|
.withBackground(core.n1.tone(99))
|
||||||
|
.withOnBackground(core.n1.tone(10))
|
||||||
|
.withSurface(core.n1.tone(99))
|
||||||
|
.withOnSurface(core.n1.tone(10))
|
||||||
|
.withSurfaceVariant(core.n2.tone(90))
|
||||||
|
.withOnSurfaceVariant(core.n2.tone(30))
|
||||||
|
.withOutline(core.n2.tone(50))
|
||||||
|
.withShadow(core.n1.tone(0))
|
||||||
|
.withInverseSurface(core.n1.tone(20))
|
||||||
|
.withInverseOnSurface(core.n1.tone(95))
|
||||||
|
.withInversePrimary(core.a1.tone(80));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Scheme dark(int argb) {
|
||||||
|
CorePalette core = CorePalette.of(argb);
|
||||||
|
return new Scheme()
|
||||||
|
.withPrimary(core.a1.tone(80))
|
||||||
|
.withOnPrimary(core.a1.tone(20))
|
||||||
|
.withPrimaryContainer(core.a1.tone(30))
|
||||||
|
.withOnPrimaryContainer(core.a1.tone(90))
|
||||||
|
.withSecondary(core.a2.tone(80))
|
||||||
|
.withOnSecondary(core.a2.tone(20))
|
||||||
|
.withSecondaryContainer(core.a2.tone(30))
|
||||||
|
.withOnSecondaryContainer(core.a2.tone(90))
|
||||||
|
.withTertiary(core.a3.tone(80))
|
||||||
|
.withOnTertiary(core.a3.tone(20))
|
||||||
|
.withTertiaryContainer(core.a3.tone(30))
|
||||||
|
.withOnTertiaryContainer(core.a3.tone(90))
|
||||||
|
.withError(core.error.tone(80))
|
||||||
|
.withOnError(core.error.tone(20))
|
||||||
|
.withErrorContainer(core.error.tone(30))
|
||||||
|
.withOnErrorContainer(core.error.tone(80))
|
||||||
|
.withBackground(core.n1.tone(10))
|
||||||
|
.withOnBackground(core.n1.tone(90))
|
||||||
|
.withSurface(core.n1.tone(10))
|
||||||
|
.withOnSurface(core.n1.tone(90))
|
||||||
|
.withSurfaceVariant(core.n2.tone(30))
|
||||||
|
.withOnSurfaceVariant(core.n2.tone(80))
|
||||||
|
.withOutline(core.n2.tone(60))
|
||||||
|
.withShadow(core.n1.tone(0))
|
||||||
|
.withInverseSurface(core.n1.tone(90))
|
||||||
|
.withInverseOnSurface(core.n1.tone(20))
|
||||||
|
.withInversePrimary(core.a1.tone(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrimary() {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimary(int primary) {
|
||||||
|
this.primary = primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withPrimary(int primary) {
|
||||||
|
this.primary = primary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnPrimary() {
|
||||||
|
return onPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnPrimary(int onPrimary) {
|
||||||
|
this.onPrimary = onPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnPrimary(int onPrimary) {
|
||||||
|
this.onPrimary = onPrimary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrimaryContainer() {
|
||||||
|
return primaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryContainer(int primaryContainer) {
|
||||||
|
this.primaryContainer = primaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withPrimaryContainer(int primaryContainer) {
|
||||||
|
this.primaryContainer = primaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnPrimaryContainer() {
|
||||||
|
return onPrimaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnPrimaryContainer(int onPrimaryContainer) {
|
||||||
|
this.onPrimaryContainer = onPrimaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnPrimaryContainer(int onPrimaryContainer) {
|
||||||
|
this.onPrimaryContainer = onPrimaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSecondary() {
|
||||||
|
return secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecondary(int secondary) {
|
||||||
|
this.secondary = secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withSecondary(int secondary) {
|
||||||
|
this.secondary = secondary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnSecondary() {
|
||||||
|
return onSecondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnSecondary(int onSecondary) {
|
||||||
|
this.onSecondary = onSecondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnSecondary(int onSecondary) {
|
||||||
|
this.onSecondary = onSecondary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSecondaryContainer() {
|
||||||
|
return secondaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecondaryContainer(int secondaryContainer) {
|
||||||
|
this.secondaryContainer = secondaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withSecondaryContainer(int secondaryContainer) {
|
||||||
|
this.secondaryContainer = secondaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnSecondaryContainer() {
|
||||||
|
return onSecondaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnSecondaryContainer(int onSecondaryContainer) {
|
||||||
|
this.onSecondaryContainer = onSecondaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnSecondaryContainer(int onSecondaryContainer) {
|
||||||
|
this.onSecondaryContainer = onSecondaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTertiary() {
|
||||||
|
return tertiary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTertiary(int tertiary) {
|
||||||
|
this.tertiary = tertiary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withTertiary(int tertiary) {
|
||||||
|
this.tertiary = tertiary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnTertiary() {
|
||||||
|
return onTertiary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTertiary(int onTertiary) {
|
||||||
|
this.onTertiary = onTertiary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnTertiary(int onTertiary) {
|
||||||
|
this.onTertiary = onTertiary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTertiaryContainer() {
|
||||||
|
return tertiaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTertiaryContainer(int tertiaryContainer) {
|
||||||
|
this.tertiaryContainer = tertiaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withTertiaryContainer(int tertiaryContainer) {
|
||||||
|
this.tertiaryContainer = tertiaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnTertiaryContainer() {
|
||||||
|
return onTertiaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTertiaryContainer(int onTertiaryContainer) {
|
||||||
|
this.onTertiaryContainer = onTertiaryContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnTertiaryContainer(int onTertiaryContainer) {
|
||||||
|
this.onTertiaryContainer = onTertiaryContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(int error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withError(int error) {
|
||||||
|
this.error = error;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnError() {
|
||||||
|
return onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnError(int onError) {
|
||||||
|
this.onError = onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnError(int onError) {
|
||||||
|
this.onError = onError;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorContainer() {
|
||||||
|
return errorContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorContainer(int errorContainer) {
|
||||||
|
this.errorContainer = errorContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withErrorContainer(int errorContainer) {
|
||||||
|
this.errorContainer = errorContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnErrorContainer() {
|
||||||
|
return onErrorContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnErrorContainer(int onErrorContainer) {
|
||||||
|
this.onErrorContainer = onErrorContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnErrorContainer(int onErrorContainer) {
|
||||||
|
this.onErrorContainer = onErrorContainer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBackground() {
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackground(int background) {
|
||||||
|
this.background = background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withBackground(int background) {
|
||||||
|
this.background = background;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnBackground() {
|
||||||
|
return onBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnBackground(int onBackground) {
|
||||||
|
this.onBackground = onBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnBackground(int onBackground) {
|
||||||
|
this.onBackground = onBackground;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSurface() {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurface(int surface) {
|
||||||
|
this.surface = surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withSurface(int surface) {
|
||||||
|
this.surface = surface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnSurface() {
|
||||||
|
return onSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnSurface(int onSurface) {
|
||||||
|
this.onSurface = onSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnSurface(int onSurface) {
|
||||||
|
this.onSurface = onSurface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSurfaceVariant() {
|
||||||
|
return surfaceVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurfaceVariant(int surfaceVariant) {
|
||||||
|
this.surfaceVariant = surfaceVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withSurfaceVariant(int surfaceVariant) {
|
||||||
|
this.surfaceVariant = surfaceVariant;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOnSurfaceVariant() {
|
||||||
|
return onSurfaceVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnSurfaceVariant(int onSurfaceVariant) {
|
||||||
|
this.onSurfaceVariant = onSurfaceVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOnSurfaceVariant(int onSurfaceVariant) {
|
||||||
|
this.onSurfaceVariant = onSurfaceVariant;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutline() {
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutline(int outline) {
|
||||||
|
this.outline = outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withOutline(int outline) {
|
||||||
|
this.outline = outline;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShadow() {
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShadow(int shadow) {
|
||||||
|
this.shadow = shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withShadow(int shadow) {
|
||||||
|
this.shadow = shadow;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInverseSurface() {
|
||||||
|
return inverseSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInverseSurface(int inverseSurface) {
|
||||||
|
this.inverseSurface = inverseSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withInverseSurface(int inverseSurface) {
|
||||||
|
this.inverseSurface = inverseSurface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInverseOnSurface() {
|
||||||
|
return inverseOnSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInverseOnSurface(int inverseOnSurface) {
|
||||||
|
this.inverseOnSurface = inverseOnSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withInverseOnSurface(int inverseOnSurface) {
|
||||||
|
this.inverseOnSurface = inverseOnSurface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInversePrimary() {
|
||||||
|
return inversePrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInversePrimary(int inversePrimary) {
|
||||||
|
this.inversePrimary = inversePrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scheme withInversePrimary(int inversePrimary) {
|
||||||
|
this.inversePrimary = inversePrimary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Scheme{"
|
||||||
|
+ "primary="
|
||||||
|
+ primary
|
||||||
|
+ ", onPrimary="
|
||||||
|
+ onPrimary
|
||||||
|
+ ", primaryContainer="
|
||||||
|
+ primaryContainer
|
||||||
|
+ ", onPrimaryContainer="
|
||||||
|
+ onPrimaryContainer
|
||||||
|
+ ", secondary="
|
||||||
|
+ secondary
|
||||||
|
+ ", onSecondary="
|
||||||
|
+ onSecondary
|
||||||
|
+ ", secondaryContainer="
|
||||||
|
+ secondaryContainer
|
||||||
|
+ ", onSecondaryContainer="
|
||||||
|
+ onSecondaryContainer
|
||||||
|
+ ", tertiary="
|
||||||
|
+ tertiary
|
||||||
|
+ ", onTertiary="
|
||||||
|
+ onTertiary
|
||||||
|
+ ", tertiaryContainer="
|
||||||
|
+ tertiaryContainer
|
||||||
|
+ ", onTertiaryContainer="
|
||||||
|
+ onTertiaryContainer
|
||||||
|
+ ", error="
|
||||||
|
+ error
|
||||||
|
+ ", onError="
|
||||||
|
+ onError
|
||||||
|
+ ", errorContainer="
|
||||||
|
+ errorContainer
|
||||||
|
+ ", onErrorContainer="
|
||||||
|
+ onErrorContainer
|
||||||
|
+ ", background="
|
||||||
|
+ background
|
||||||
|
+ ", onBackground="
|
||||||
|
+ onBackground
|
||||||
|
+ ", surface="
|
||||||
|
+ surface
|
||||||
|
+ ", onSurface="
|
||||||
|
+ onSurface
|
||||||
|
+ ", surfaceVariant="
|
||||||
|
+ surfaceVariant
|
||||||
|
+ ", onSurfaceVariant="
|
||||||
|
+ onSurfaceVariant
|
||||||
|
+ ", outline="
|
||||||
|
+ outline
|
||||||
|
+ ", shadow="
|
||||||
|
+ shadow
|
||||||
|
+ ", inverseSurface="
|
||||||
|
+ inverseSurface
|
||||||
|
+ ", inverseOnSurface="
|
||||||
|
+ inverseOnSurface
|
||||||
|
+ ", inversePrimary="
|
||||||
|
+ inversePrimary
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (this == object) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(object instanceof Scheme)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!super.equals(object)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme scheme = (Scheme) object;
|
||||||
|
|
||||||
|
if (primary != scheme.primary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onPrimary != scheme.onPrimary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (primaryContainer != scheme.primaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onPrimaryContainer != scheme.onPrimaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (secondary != scheme.secondary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onSecondary != scheme.onSecondary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (secondaryContainer != scheme.secondaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onSecondaryContainer != scheme.onSecondaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tertiary != scheme.tertiary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onTertiary != scheme.onTertiary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tertiaryContainer != scheme.tertiaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onTertiaryContainer != scheme.onTertiaryContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (error != scheme.error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onError != scheme.onError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (errorContainer != scheme.errorContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onErrorContainer != scheme.onErrorContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (background != scheme.background) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onBackground != scheme.onBackground) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (surface != scheme.surface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onSurface != scheme.onSurface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (surfaceVariant != scheme.surfaceVariant) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (onSurfaceVariant != scheme.onSurfaceVariant) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (outline != scheme.outline) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (shadow != scheme.shadow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inverseSurface != scheme.inverseSurface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inverseOnSurface != scheme.inverseOnSurface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inversePrimary != scheme.inversePrimary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = 31 * result + primary;
|
||||||
|
result = 31 * result + onPrimary;
|
||||||
|
result = 31 * result + primaryContainer;
|
||||||
|
result = 31 * result + onPrimaryContainer;
|
||||||
|
result = 31 * result + secondary;
|
||||||
|
result = 31 * result + onSecondary;
|
||||||
|
result = 31 * result + secondaryContainer;
|
||||||
|
result = 31 * result + onSecondaryContainer;
|
||||||
|
result = 31 * result + tertiary;
|
||||||
|
result = 31 * result + onTertiary;
|
||||||
|
result = 31 * result + tertiaryContainer;
|
||||||
|
result = 31 * result + onTertiaryContainer;
|
||||||
|
result = 31 * result + error;
|
||||||
|
result = 31 * result + onError;
|
||||||
|
result = 31 * result + errorContainer;
|
||||||
|
result = 31 * result + onErrorContainer;
|
||||||
|
result = 31 * result + background;
|
||||||
|
result = 31 * result + onBackground;
|
||||||
|
result = 31 * result + surface;
|
||||||
|
result = 31 * result + onSurface;
|
||||||
|
result = 31 * result + surfaceVariant;
|
||||||
|
result = 31 * result + onSurfaceVariant;
|
||||||
|
result = 31 * result + outline;
|
||||||
|
result = 31 * result + shadow;
|
||||||
|
result = 31 * result + inverseSurface;
|
||||||
|
result = 31 * result + inverseOnSurface;
|
||||||
|
result = 31 * result + inversePrimary;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
265
material-color-utilities/src/main/java/utils/ColorUtils.java
Normal file
265
material-color-utilities/src/main/java/utils/ColorUtils.java
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file is automatically generated. Do not modify it.
|
||||||
|
|
||||||
|
package utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color science utilities.
|
||||||
|
*
|
||||||
|
* <p>Utility methods for color science constants and color space conversions that aren't HCT or
|
||||||
|
* CAM16.
|
||||||
|
*/
|
||||||
|
public class ColorUtils {
|
||||||
|
private ColorUtils() {}
|
||||||
|
|
||||||
|
static final double[][] SRGB_TO_XYZ =
|
||||||
|
new double[][] {
|
||||||
|
new double[] {0.41233895, 0.35762064, 0.18051042},
|
||||||
|
new double[] {0.2126, 0.7152, 0.0722},
|
||||||
|
new double[] {0.01932141, 0.11916382, 0.95034478},
|
||||||
|
};
|
||||||
|
|
||||||
|
static final double[][] XYZ_TO_SRGB =
|
||||||
|
new double[][] {
|
||||||
|
new double[] {
|
||||||
|
3.2413774792388685, -1.5376652402851851, -0.49885366846268053,
|
||||||
|
},
|
||||||
|
new double[] {
|
||||||
|
-0.9691452513005321, 1.8758853451067872, 0.04156585616912061,
|
||||||
|
},
|
||||||
|
new double[] {
|
||||||
|
0.05562093689691305, -0.20395524564742123, 1.0571799111220335,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static final double[] WHITE_POINT_D65 = new double[] {95.047, 100.0, 108.883};
|
||||||
|
|
||||||
|
/** Converts a color from RGB components to ARGB format. */
|
||||||
|
public static int argbFromRgb(int red, int green, int blue) {
|
||||||
|
return (255 << 24) | ((red & 255) << 16) | ((green & 255) << 8) | (blue & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the alpha component of a color in ARGB format. */
|
||||||
|
public static int alphaFromArgb(int argb) {
|
||||||
|
return (argb >> 24) & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the red component of a color in ARGB format. */
|
||||||
|
public static int redFromArgb(int argb) {
|
||||||
|
return (argb >> 16) & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the green component of a color in ARGB format. */
|
||||||
|
public static int greenFromArgb(int argb) {
|
||||||
|
return (argb >> 8) & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the blue component of a color in ARGB format. */
|
||||||
|
public static int blueFromArgb(int argb) {
|
||||||
|
return argb & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether a color in ARGB format is opaque. */
|
||||||
|
public static boolean isOpaque(int argb) {
|
||||||
|
return alphaFromArgb(argb) >= 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a color from ARGB to XYZ. */
|
||||||
|
public static int argbFromXyz(double x, double y, double z) {
|
||||||
|
double[][] matrix = XYZ_TO_SRGB;
|
||||||
|
double linearR = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z;
|
||||||
|
double linearG = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z;
|
||||||
|
double linearB = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z;
|
||||||
|
int r = delinearized(linearR);
|
||||||
|
int g = delinearized(linearG);
|
||||||
|
int b = delinearized(linearB);
|
||||||
|
return argbFromRgb(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a color from XYZ to ARGB. */
|
||||||
|
public static double[] xyzFromArgb(int argb) {
|
||||||
|
double r = linearized(redFromArgb(argb));
|
||||||
|
double g = linearized(greenFromArgb(argb));
|
||||||
|
double b = linearized(blueFromArgb(argb));
|
||||||
|
return MathUtils.matrixMultiply(new double[] {r, g, b}, SRGB_TO_XYZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a color represented in Lab color space into an ARGB integer. */
|
||||||
|
public static int argbFromLab(double l, double a, double b) {
|
||||||
|
double[] whitePoint = WHITE_POINT_D65;
|
||||||
|
double fy = (l + 16.0) / 116.0;
|
||||||
|
double fx = a / 500.0 + fy;
|
||||||
|
double fz = fy - b / 200.0;
|
||||||
|
double xNormalized = labInvf(fx);
|
||||||
|
double yNormalized = labInvf(fy);
|
||||||
|
double zNormalized = labInvf(fz);
|
||||||
|
double x = xNormalized * whitePoint[0];
|
||||||
|
double y = yNormalized * whitePoint[1];
|
||||||
|
double z = zNormalized * whitePoint[2];
|
||||||
|
return argbFromXyz(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a color from ARGB representation to L*a*b* representation.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB representation of a color
|
||||||
|
* @return a Lab object representing the color
|
||||||
|
*/
|
||||||
|
public static double[] labFromArgb(int argb) {
|
||||||
|
double linearR = linearized(redFromArgb(argb));
|
||||||
|
double linearG = linearized(greenFromArgb(argb));
|
||||||
|
double linearB = linearized(blueFromArgb(argb));
|
||||||
|
double[][] matrix = SRGB_TO_XYZ;
|
||||||
|
double x = matrix[0][0] * linearR + matrix[0][1] * linearG + matrix[0][2] * linearB;
|
||||||
|
double y = matrix[1][0] * linearR + matrix[1][1] * linearG + matrix[1][2] * linearB;
|
||||||
|
double z = matrix[2][0] * linearR + matrix[2][1] * linearG + matrix[2][2] * linearB;
|
||||||
|
double[] whitePoint = WHITE_POINT_D65;
|
||||||
|
double xNormalized = x / whitePoint[0];
|
||||||
|
double yNormalized = y / whitePoint[1];
|
||||||
|
double zNormalized = z / whitePoint[2];
|
||||||
|
double fx = labF(xNormalized);
|
||||||
|
double fy = labF(yNormalized);
|
||||||
|
double fz = labF(zNormalized);
|
||||||
|
double l = 116.0 * fy - 16;
|
||||||
|
double a = 500.0 * (fx - fy);
|
||||||
|
double b = 200.0 * (fy - fz);
|
||||||
|
return new double[] {l, a, b};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an L* value to an ARGB representation.
|
||||||
|
*
|
||||||
|
* @param lstar L* in L*a*b*
|
||||||
|
* @return ARGB representation of grayscale color with lightness matching L*
|
||||||
|
*/
|
||||||
|
public static int argbFromLstar(double lstar) {
|
||||||
|
double fy = (lstar + 16.0) / 116.0;
|
||||||
|
double fz = fy;
|
||||||
|
double fx = fy;
|
||||||
|
double kappa = 24389.0 / 27.0;
|
||||||
|
double epsilon = 216.0 / 24389.0;
|
||||||
|
boolean lExceedsEpsilonKappa = lstar > 8.0;
|
||||||
|
double y = lExceedsEpsilonKappa ? fy * fy * fy : lstar / kappa;
|
||||||
|
boolean cubeExceedEpsilon = fy * fy * fy > epsilon;
|
||||||
|
double x = cubeExceedEpsilon ? fx * fx * fx : lstar / kappa;
|
||||||
|
double z = cubeExceedEpsilon ? fz * fz * fz : lstar / kappa;
|
||||||
|
double[] whitePoint = WHITE_POINT_D65;
|
||||||
|
return argbFromXyz(x * whitePoint[0], y * whitePoint[1], z * whitePoint[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the L* value of a color in ARGB representation.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color
|
||||||
|
* @return L*, from L*a*b*, coordinate of the color
|
||||||
|
*/
|
||||||
|
public static double lstarFromArgb(int argb) {
|
||||||
|
double y = xyzFromArgb(argb)[1] / 100.0;
|
||||||
|
double e = 216.0 / 24389.0;
|
||||||
|
if (y <= e) {
|
||||||
|
return 24389.0 / 27.0 * y;
|
||||||
|
} else {
|
||||||
|
double yIntermediate = Math.pow(y, 1.0 / 3.0);
|
||||||
|
return 116.0 * yIntermediate - 16.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an L* value to a Y value.
|
||||||
|
*
|
||||||
|
* <p>L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
|
||||||
|
*
|
||||||
|
* <p>L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
|
||||||
|
* logarithmic scale.
|
||||||
|
*
|
||||||
|
* @param lstar L* in L*a*b*
|
||||||
|
* @return Y in XYZ
|
||||||
|
*/
|
||||||
|
public static double yFromLstar(double lstar) {
|
||||||
|
double ke = 8.0;
|
||||||
|
if (lstar > ke) {
|
||||||
|
return Math.pow((lstar + 16.0) / 116.0, 3.0) * 100.0;
|
||||||
|
} else {
|
||||||
|
return lstar / 24389.0 / 27.0 * 100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linearizes an RGB component.
|
||||||
|
*
|
||||||
|
* @param rgbComponent 0 <= rgb_component <= 255, represents R/G/B channel
|
||||||
|
* @return 0.0 <= output <= 100.0, color channel converted to linear RGB space
|
||||||
|
*/
|
||||||
|
public static double linearized(int rgbComponent) {
|
||||||
|
double normalized = rgbComponent / 255.0;
|
||||||
|
if (normalized <= 0.040449936) {
|
||||||
|
return normalized / 12.92 * 100.0;
|
||||||
|
} else {
|
||||||
|
return Math.pow((normalized + 0.055) / 1.055, 2.4) * 100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delinearizes an RGB component.
|
||||||
|
*
|
||||||
|
* @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
|
||||||
|
* @return 0 <= output <= 255, color channel converted to regular RGB space
|
||||||
|
*/
|
||||||
|
public static int delinearized(double rgbComponent) {
|
||||||
|
double normalized = rgbComponent / 100.0;
|
||||||
|
double delinearized = 0.0;
|
||||||
|
if (normalized <= 0.0031308) {
|
||||||
|
delinearized = normalized * 12.92;
|
||||||
|
} else {
|
||||||
|
delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055;
|
||||||
|
}
|
||||||
|
return MathUtils.clampInt(0, 255, (int) Math.round(delinearized * 255.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the standard white point; white on a sunny day.
|
||||||
|
*
|
||||||
|
* @return The white point
|
||||||
|
*/
|
||||||
|
public static double[] whitePointD65() {
|
||||||
|
return WHITE_POINT_D65;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double labF(double t) {
|
||||||
|
double e = 216.0 / 24389.0;
|
||||||
|
double kappa = 24389.0 / 27.0;
|
||||||
|
if (t > e) {
|
||||||
|
return Math.pow(t, 1.0 / 3.0);
|
||||||
|
} else {
|
||||||
|
return (kappa * t + 16) / 116;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double labInvf(double ft) {
|
||||||
|
double e = 216.0 / 24389.0;
|
||||||
|
double kappa = 24389.0 / 27.0;
|
||||||
|
double ft3 = ft * ft * ft;
|
||||||
|
if (ft3 > e) {
|
||||||
|
return ft3;
|
||||||
|
} else {
|
||||||
|
return (116 * ft - 16) / kappa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
119
material-color-utilities/src/main/java/utils/MathUtils.java
Normal file
119
material-color-utilities/src/main/java/utils/MathUtils.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file is automatically generated. Do not modify it.
|
||||||
|
|
||||||
|
package utils;
|
||||||
|
|
||||||
|
/** Utility methods for mathematical operations. */
|
||||||
|
public class MathUtils {
|
||||||
|
private MathUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The signum function.
|
||||||
|
*
|
||||||
|
* @return 1 if num > 0, -1 if num < 0, and 0 if num = 0
|
||||||
|
*/
|
||||||
|
public static int signum(double num) {
|
||||||
|
if (num < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (num == 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The linear interpolation function.
|
||||||
|
*
|
||||||
|
* @return start if amount = 0 and stop if amount = 1
|
||||||
|
*/
|
||||||
|
public static double lerp(double start, double stop, double amount) {
|
||||||
|
return (1.0 - amount) * start + amount * stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamps an integer between two integers.
|
||||||
|
*
|
||||||
|
* @return input when min <= input <= max, and either min or max otherwise.
|
||||||
|
*/
|
||||||
|
public static int clampInt(int min, int max, int input) {
|
||||||
|
if (input < min) {
|
||||||
|
return min;
|
||||||
|
} else if (input > max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamps an integer between two floating-point numbers.
|
||||||
|
*
|
||||||
|
* @return input when min <= input <= max, and either min or max otherwise.
|
||||||
|
*/
|
||||||
|
public static double clampDouble(double min, double max, double input) {
|
||||||
|
if (input < min) {
|
||||||
|
return min;
|
||||||
|
} else if (input > max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a degree measure as an integer.
|
||||||
|
*
|
||||||
|
* @return a degree measure between 0 (inclusive) and 360 (exclusive).
|
||||||
|
*/
|
||||||
|
public static int sanitizeDegreesInt(int degrees) {
|
||||||
|
degrees = degrees % 360;
|
||||||
|
if (degrees < 0) {
|
||||||
|
degrees = degrees + 360;
|
||||||
|
}
|
||||||
|
return degrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a degree measure as a floating-point number.
|
||||||
|
*
|
||||||
|
* @return a degree measure between 0.0 (inclusive) and 360.0 (exclusive).
|
||||||
|
*/
|
||||||
|
public static double sanitizeDegreesDouble(double degrees) {
|
||||||
|
degrees = degrees % 360.0;
|
||||||
|
if (degrees < 0) {
|
||||||
|
degrees = degrees + 360.0;
|
||||||
|
}
|
||||||
|
return degrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Distance of two points on a circle, represented using degrees. */
|
||||||
|
public static double differenceDegrees(double a, double b) {
|
||||||
|
return 180.0 - Math.abs(Math.abs(a - b) - 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Multiplies a 1x3 row vector with a 3x3 matrix. */
|
||||||
|
public static double[] matrixMultiply(double[] row, double[][] matrix) {
|
||||||
|
double a = row[0] * matrix[0][0] + row[1] * matrix[0][1] + row[2] * matrix[0][2];
|
||||||
|
double b = row[0] * matrix[1][0] + row[1] * matrix[1][1] + row[2] * matrix[1][2];
|
||||||
|
double c = row[0] * matrix[2][0] + row[1] * matrix[2][1] + row[2] * matrix[2][2];
|
||||||
|
return new double[] {a, b, c};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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 utils;
|
||||||
|
|
||||||
|
/** Utility methods for string representations of colors. */
|
||||||
|
final class StringUtils {
|
||||||
|
private StringUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex string representing color, ex. #ff0000 for red.
|
||||||
|
*
|
||||||
|
* @param argb ARGB representation of a color.
|
||||||
|
*/
|
||||||
|
public static String hexFromArgb(int argb) {
|
||||||
|
int red = ColorUtils.redFromArgb(argb);
|
||||||
|
int blue = ColorUtils.blueFromArgb(argb);
|
||||||
|
int green = ColorUtils.greenFromArgb(argb);
|
||||||
|
return String.format("#%02x%02x%02x", red, green, blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,7 @@ message Settings {
|
|||||||
enum ColorScheme {
|
enum ColorScheme {
|
||||||
Default = 0;
|
Default = 0;
|
||||||
BlackAndWhite = 1;
|
BlackAndWhite = 1;
|
||||||
|
Wallpaper = 2;
|
||||||
}
|
}
|
||||||
ColorScheme color_scheme = 6;
|
ColorScheme color_scheme = 6;
|
||||||
bool dim_wallpaper = 7;
|
bool dim_wallpaper = 7;
|
||||||
|
|||||||
@ -385,3 +385,4 @@ dependencyResolutionManagement {
|
|||||||
include(":notifications")
|
include(":notifications")
|
||||||
include(":accounts")
|
include(":accounts")
|
||||||
include(":appshortcuts")
|
include(":appshortcuts")
|
||||||
|
include(":material-color-utilities")
|
||||||
|
|||||||
@ -104,6 +104,8 @@ dependencies {
|
|||||||
implementation(libs.coil.core)
|
implementation(libs.coil.core)
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
|
|
||||||
|
implementation(project(":material-color-utilities"))
|
||||||
|
|
||||||
implementation(project(":base"))
|
implementation(project(":base"))
|
||||||
implementation(project(":i18n"))
|
implementation(project(":i18n"))
|
||||||
implementation(project(":compat"))
|
implementation(project(":compat"))
|
||||||
|
|||||||
@ -71,6 +71,7 @@ fun AppearanceSettingsScreen() {
|
|||||||
summary = when (colorScheme) {
|
summary = when (colorScheme) {
|
||||||
ColorScheme.Default -> stringResource(R.string.preference_colors_default)
|
ColorScheme.Default -> stringResource(R.string.preference_colors_default)
|
||||||
ColorScheme.BlackAndWhite -> stringResource(R.string.preference_colors_bw)
|
ColorScheme.BlackAndWhite -> stringResource(R.string.preference_colors_bw)
|
||||||
|
ColorScheme.Wallpaper -> stringResource(R.string.preference_colors_wallpaper)
|
||||||
else -> null
|
else -> null
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.mm20.launcher2.ui.settings.colorscheme
|
package de.mm20.launcher2.ui.settings.colorscheme
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@ -7,6 +8,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
||||||
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -17,44 +19,42 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||||
import de.mm20.launcher2.ui.theme.getColorScheme
|
import de.mm20.launcher2.ui.theme.colorSchemeAsState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ColorSchemeSettingsScreen() {
|
fun ColorSchemeSettingsScreen() {
|
||||||
val viewModel: ColorSchemeSettingsScreenVM = viewModel()
|
val viewModel: ColorSchemeSettingsScreenVM = viewModel()
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
PreferenceScreen(title = stringResource(R.string.preference_screen_colors)) {
|
PreferenceScreen(title = stringResource(R.string.preference_screen_colors)) {
|
||||||
item {
|
item {
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
val theme by viewModel.theme.observeAsState()
|
|
||||||
val darkTheme =
|
|
||||||
theme == AppearanceSettings.Theme.Dark || theme == AppearanceSettings.Theme.System && isSystemInDarkTheme()
|
|
||||||
val colorScheme by viewModel.colorScheme.observeAsState()
|
val colorScheme by viewModel.colorScheme.observeAsState()
|
||||||
|
|
||||||
val items = listOf(
|
val items = mutableListOf(
|
||||||
AppearanceSettings.ColorScheme.Default to R.string.preference_colors_default,
|
AppearanceSettings.ColorScheme.Default to R.string.preference_colors_default,
|
||||||
AppearanceSettings.ColorScheme.BlackAndWhite to R.string.preference_colors_bw
|
AppearanceSettings.ColorScheme.BlackAndWhite to R.string.preference_colors_bw,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (isAtLeastApiLevel(Build.VERSION_CODES.O_MR1)) {
|
||||||
|
items.add(
|
||||||
|
AppearanceSettings.ColorScheme.Wallpaper to R.string.preference_colors_wallpaper
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
for (cs in items) {
|
for (cs in items) {
|
||||||
|
val scheme by colorSchemeAsState(cs.first)
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(cs.second),
|
title = stringResource(cs.second),
|
||||||
icon = if (colorScheme == cs.first) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
icon = if (colorScheme == cs.first) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||||
onClick = { viewModel.setColorScheme(cs.first) },
|
onClick = { viewModel.setColorScheme(cs.first) },
|
||||||
controls = {
|
controls = {
|
||||||
ColorSchemePreview(
|
ColorSchemePreview(scheme)
|
||||||
getColorScheme(
|
|
||||||
LocalContext.current,
|
|
||||||
cs.first,
|
|
||||||
darkTheme,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -65,51 +65,54 @@ fun ColorSchemeSettingsScreen() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ColorSchemePreview(colorScheme: ColorScheme) {
|
fun ColorSchemePreview(colorScheme: ColorScheme) {
|
||||||
Box(
|
MaterialTheme(colorScheme = colorScheme) {
|
||||||
modifier = Modifier
|
Box(
|
||||||
.padding(vertical = 12.dp)
|
modifier = Modifier
|
||||||
.width(72.dp)
|
.padding(vertical = 12.dp)
|
||||||
.height(36.dp),
|
.width(72.dp)
|
||||||
contentAlignment = Alignment.Center
|
.height(36.dp),
|
||||||
) {
|
contentAlignment = Alignment.Center
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
) {
|
||||||
Surface(
|
Row(
|
||||||
tonalElevation = 1.dp,
|
verticalAlignment = Alignment.CenterVertically
|
||||||
color = colorScheme.surface,
|
) {
|
||||||
modifier = Modifier
|
Surface(
|
||||||
.size(36.dp)
|
tonalElevation = 1.dp,
|
||||||
) {}
|
color = MaterialTheme.colorScheme.surface,
|
||||||
Surface(
|
modifier = Modifier
|
||||||
tonalElevation = 1.dp,
|
.size(36.dp)
|
||||||
color = colorScheme.surfaceVariant,
|
) {}
|
||||||
modifier = Modifier
|
Surface(
|
||||||
.size(36.dp)
|
tonalElevation = 1.dp,
|
||||||
) {}
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
}
|
modifier = Modifier
|
||||||
Row(
|
.size(36.dp)
|
||||||
verticalAlignment = Alignment.CenterVertically
|
) {}
|
||||||
) {
|
}
|
||||||
Surface(
|
Row(
|
||||||
tonalElevation = 1.dp,
|
verticalAlignment = Alignment.CenterVertically
|
||||||
color = colorScheme.primary,
|
) {
|
||||||
modifier = Modifier
|
Surface(
|
||||||
.size(16.dp)
|
tonalElevation = 1.dp,
|
||||||
) {}
|
color = MaterialTheme.colorScheme.primary,
|
||||||
Surface(
|
modifier = Modifier
|
||||||
tonalElevation = 1.dp,
|
.size(16.dp)
|
||||||
color = colorScheme.secondary,
|
) {}
|
||||||
modifier = Modifier
|
Surface(
|
||||||
.padding(horizontal = 8.dp)
|
tonalElevation = 1.dp,
|
||||||
.size(16.dp)
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
) {}
|
modifier = Modifier
|
||||||
Surface(
|
.padding(horizontal = 8.dp)
|
||||||
tonalElevation = 1.dp,
|
.size(16.dp)
|
||||||
color = colorScheme.tertiary,
|
) {}
|
||||||
modifier = Modifier
|
Surface(
|
||||||
.size(16.dp)
|
tonalElevation = 1.dp,
|
||||||
) {}
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(16.dp)
|
||||||
|
) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,24 +1,18 @@
|
|||||||
package de.mm20.launcher2.ui.theme
|
package de.mm20.launcher2.ui.theme
|
||||||
|
|
||||||
import android.content.Context
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
||||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
|
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
|
||||||
import de.mm20.launcher2.ui.theme.colorscheme.DarkBlackAndWhiteColorScheme
|
import de.mm20.launcher2.ui.theme.colorscheme.*
|
||||||
import de.mm20.launcher2.ui.theme.colorscheme.DarkPre31DefaultColorScheme
|
|
||||||
import de.mm20.launcher2.ui.theme.colorscheme.LightBlackAndWhiteColorScheme
|
|
||||||
import de.mm20.launcher2.ui.theme.colorscheme.LightPre31DefaultColorScheme
|
|
||||||
import de.mm20.launcher2.ui.theme.typography.DefaultTypography
|
import de.mm20.launcher2.ui.theme.typography.DefaultTypography
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import org.koin.androidx.compose.inject
|
import org.koin.androidx.compose.inject
|
||||||
@ -34,34 +28,52 @@ fun LauncherTheme(
|
|||||||
val colorSchemePreference by remember { dataStore.data.map { it.appearance.colorScheme } }.collectAsState(
|
val colorSchemePreference by remember { dataStore.data.map { it.appearance.colorScheme } }.collectAsState(
|
||||||
AppearanceSettings.ColorScheme.Default
|
AppearanceSettings.ColorScheme.Default
|
||||||
)
|
)
|
||||||
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
|
|
||||||
Theme.System
|
|
||||||
)
|
|
||||||
|
|
||||||
val darkTheme =
|
val colorScheme by colorSchemeAsState(colorSchemePreference)
|
||||||
themePreference == Theme.Dark || themePreference == Theme.System && isSystemInDarkTheme()
|
|
||||||
|
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colorScheme = getColorScheme(LocalContext.current, colorSchemePreference, darkTheme),
|
colorScheme = colorScheme,
|
||||||
typography = DefaultTypography,
|
typography = DefaultTypography,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getColorScheme(context: Context, colorScheme: AppearanceSettings.ColorScheme, darkTheme: Boolean): ColorScheme {
|
@Composable
|
||||||
return when (colorScheme) {
|
fun colorSchemeAsState(colorScheme: AppearanceSettings.ColorScheme): MutableState<ColorScheme> {
|
||||||
AppearanceSettings.ColorScheme.BlackAndWhite -> {
|
val context = LocalContext.current
|
||||||
if (darkTheme) DarkBlackAndWhiteColorScheme
|
val dataStore: LauncherDataStore by inject()
|
||||||
else LightBlackAndWhiteColorScheme
|
|
||||||
}
|
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
|
||||||
else -> {
|
Theme.System
|
||||||
if (darkTheme) {
|
)
|
||||||
if (isAtLeastApiLevel(31)) dynamicDarkColorScheme(context)
|
val darkTheme =
|
||||||
else DarkPre31DefaultColorScheme
|
themePreference == Theme.Dark || themePreference == Theme.System && isSystemInDarkTheme()
|
||||||
} else {
|
|
||||||
if (isAtLeastApiLevel(31)) dynamicLightColorScheme(context)
|
val state = remember(colorScheme, darkTheme) {
|
||||||
else LightPre31DefaultColorScheme
|
mutableStateOf(
|
||||||
|
when (colorScheme) {
|
||||||
|
AppearanceSettings.ColorScheme.BlackAndWhite -> {
|
||||||
|
if (darkTheme) DarkBlackAndWhiteColorScheme else LightBlackAndWhiteColorScheme
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (darkTheme) {
|
||||||
|
if (isAtLeastApiLevel(31)) dynamicDarkColorScheme(context)
|
||||||
|
else DarkPre31DefaultColorScheme
|
||||||
|
} else {
|
||||||
|
if (isAtLeastApiLevel(31)) dynamicLightColorScheme(context)
|
||||||
|
else LightPre31DefaultColorScheme
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorScheme == AppearanceSettings.ColorScheme.Wallpaper && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
|
val wallpaperColors by wallpaperColorsAsState()
|
||||||
|
LaunchedEffect(wallpaperColors, darkTheme) {
|
||||||
|
state.value = WallpaperColorScheme(wallpaperColors, darkTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import android.app.WallpaperManager
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
package de.mm20.launcher2.ui.theme.colorscheme
|
||||||
|
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import de.mm20.launcher2.ui.theme.WallpaperColors
|
||||||
|
import palettes.TonalPalette
|
||||||
|
|
||||||
|
fun WallpaperColorScheme(wallpaperColors: WallpaperColors, darkTheme: Boolean): ColorScheme {
|
||||||
|
val primary = TonalPalette.fromInt(wallpaperColors.primary.toArgb())
|
||||||
|
val secondary = TonalPalette.fromInt((wallpaperColors.secondary ?: wallpaperColors.primary).toArgb())
|
||||||
|
val tertiary = TonalPalette.fromInt((wallpaperColors.tertiary ?: wallpaperColors.primary).toArgb())
|
||||||
|
|
||||||
|
val neutral1 = TonalPalette.fromInt(Color.Black.toArgb())
|
||||||
|
val neutral2 = TonalPalette.fromInt(Color.Black.toArgb())
|
||||||
|
|
||||||
|
return if(darkTheme) {
|
||||||
|
darkColorScheme(
|
||||||
|
primary = Color(primary.tone(80)),
|
||||||
|
onPrimary = Color(primary.tone(20)),
|
||||||
|
primaryContainer = Color(primary.tone(30)),
|
||||||
|
onPrimaryContainer = Color(primary.tone(90)),
|
||||||
|
secondary = Color(secondary.tone(80)),
|
||||||
|
onSecondary = Color(secondary.tone(20)),
|
||||||
|
secondaryContainer = Color(secondary.tone(30)),
|
||||||
|
onSecondaryContainer = Color(secondary.tone(90)),
|
||||||
|
tertiary = Color(tertiary.tone(80)),
|
||||||
|
onTertiary = Color(tertiary.tone(20)),
|
||||||
|
tertiaryContainer = Color(tertiary.tone(30)),
|
||||||
|
onTertiaryContainer = Color(tertiary.tone(90)),
|
||||||
|
background = Color(neutral1.tone(10)),
|
||||||
|
onBackground = Color(neutral1.tone(90)),
|
||||||
|
surface = Color(neutral1.tone(10)),
|
||||||
|
onSurface = Color(neutral1.tone(90)),
|
||||||
|
surfaceVariant = Color(neutral2.tone(30)),
|
||||||
|
onSurfaceVariant = Color(neutral2.tone(80)),
|
||||||
|
outline = Color(neutral2.tone(60)),
|
||||||
|
inverseSurface = Color(neutral1.tone(90)),
|
||||||
|
inverseOnSurface = Color(neutral1.tone(20)),
|
||||||
|
inversePrimary = Color(primary.tone(40)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
lightColorScheme(
|
||||||
|
primary = Color(primary.tone(40)),
|
||||||
|
onPrimary = Color(primary.tone(100)),
|
||||||
|
primaryContainer = Color(primary.tone(90)),
|
||||||
|
onPrimaryContainer = Color(primary.tone(10)),
|
||||||
|
secondary = Color(secondary.tone(40)),
|
||||||
|
onSecondary = Color(secondary.tone(100)),
|
||||||
|
secondaryContainer = Color(secondary.tone(90)),
|
||||||
|
onSecondaryContainer = Color(secondary.tone(10)),
|
||||||
|
tertiary = Color(tertiary.tone(40)),
|
||||||
|
onTertiary = Color(tertiary.tone(100)),
|
||||||
|
tertiaryContainer = Color(tertiary.tone(90)),
|
||||||
|
onTertiaryContainer = Color(tertiary.tone(10)),
|
||||||
|
background = Color(neutral1.tone(99)),
|
||||||
|
onBackground = Color(neutral1.tone(10)),
|
||||||
|
surface = Color(neutral1.tone(99)),
|
||||||
|
onSurface = Color(neutral1.tone(10)),
|
||||||
|
surfaceVariant = Color(neutral2.tone(90)),
|
||||||
|
onSurfaceVariant = Color(neutral2.tone(30)),
|
||||||
|
outline = Color(neutral2.tone(50)),
|
||||||
|
inverseSurface = Color(neutral1.tone(20)),
|
||||||
|
inverseOnSurface = Color(neutral1.tone(95)),
|
||||||
|
inversePrimary = Color(primary.tone(80)),)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user