From 20532d7c21f4a0dff2e32cd5979c9c41d0ad1f81 Mon Sep 17 00:00:00 2001 From: James O'Leary Date: Wed, 5 Jan 2022 10:20:21 -0500 Subject: [PATCH] ColorScheme provides options for styling - Add 5(!) lightweight classes for storing a design spec for a style. - Add parameter to ColorScheme constructor for Style - Add constructor replicating previous constructor for backwards compatibility with existing code, uses Tonal Spot as tyle - Reconfigure ColorScheme to use style to generate palettes Test: atest ColorSchemeTest passes. Change default style to style #1, make sure it works as intended, and matches what we've seen in mocks. Continue through all 4 styles. Bug: 213314628 Bug: 195969565 Change-Id: Iae2b4fc4ffd630bb4275f827798ae4643b230ae1 --- .../com/android/systemui/monet/ColorScheme.kt | 118 +++++++++++++++--- .../systemui/monet/ColorSchemeTest.java | 30 +++++ 2 files changed, 131 insertions(+), 17 deletions(-) diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 0b3eccfd3a913..37df597a28b31 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -28,18 +28,88 @@ import kotlin.math.roundToInt const val TAG = "ColorScheme" const val ACCENT1_CHROMA = 48.0f -const val ACCENT2_CHROMA = 16.0f -const val ACCENT3_CHROMA = 32.0f -const val ACCENT3_HUE_SHIFT = 60.0f - -const val NEUTRAL1_CHROMA = 4.0f -const val NEUTRAL2_CHROMA = 8.0f - const val GOOGLE_BLUE = 0xFF1b6ef3.toInt() - const val MIN_CHROMA = 5 -public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) { +enum class ChromaStrategy { + EQ, GTE +} + +enum class HueStrategy { + SOURCE, ADD, SUBTRACT +} + +class Chroma(val strategy: ChromaStrategy, val value: Double) { + fun get(sourceChroma: Double): Double { + return when (strategy) { + ChromaStrategy.EQ -> value + ChromaStrategy.GTE -> sourceChroma.coerceAtLeast(value) + } + } +} + +class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) { + fun get(sourceHue: Double): Double { + return when (strategy) { + HueStrategy.SOURCE -> sourceHue + HueStrategy.ADD -> ColorScheme.wrapDegreesDouble(sourceHue + value) + HueStrategy.SUBTRACT -> ColorScheme.wrapDegreesDouble(sourceHue - value) + } + } +} + +class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) { + fun shades(sourceColor: Cam): List { + val hue = hue.get(sourceColor.hue.toDouble()) + val chroma = chroma.get(sourceColor.chroma.toDouble()) + return Shades.of(hue.toFloat(), chroma.toFloat()).toList() + } +} + +class CoreSpec( + val a1: TonalSpec, + val a2: TonalSpec, + val a3: TonalSpec, + val n1: TonalSpec, + val n2: TonalSpec +) + +enum class Style(val coreSpec: CoreSpec) { + SPRITZ(CoreSpec( + a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), + a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), + a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)) + )), + TONAL_SPOT(CoreSpec( + a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), + a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)) + )), + VIBRANT(CoreSpec( + a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(Hue(HueStrategy.ADD, 10.0), Chroma(ChromaStrategy.EQ, 24.0)), + a3 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.GTE, 32.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)) + )), + EXPRESSIVE(CoreSpec( + a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 40.0), Chroma(ChromaStrategy.GTE, 64.0)), + a2 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.EQ, 24.0)), + a3 = TonalSpec(Hue(HueStrategy.SUBTRACT, 80.0), Chroma(ChromaStrategy.GTE, 64.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0)) + )), +} + +public class ColorScheme( + @ColorInt seed: Int, + val darkTheme: Boolean, + val style: Style = Style.TONAL_SPOT +) { val accent1: List val accent2: List @@ -47,6 +117,9 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) { val neutral1: List val neutral2: List + constructor(@ColorInt seed: Int, darkTheme: Boolean): + this(seed, darkTheme, Style.TONAL_SPOT) + constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean): this(getSeedColor(wallpaperColors), darkTheme) @@ -83,14 +156,11 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) { seed } val camSeed = Cam.fromInt(seedArgb) - val hue = camSeed.hue - val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA) - val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt()) - accent1 = Shades.of(hue, chroma).toList() - accent2 = Shades.of(hue, ACCENT2_CHROMA).toList() - accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList() - neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList() - neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList() + accent1 = style.coreSpec.a1.shades(camSeed) + accent2 = style.coreSpec.a2.shades(camSeed) + accent3 = style.coreSpec.a3.shades(camSeed) + neutral1 = style.coreSpec.n1.shades(camSeed) + neutral2 = style.coreSpec.n2.shades(camSeed) } override fun toString(): String { @@ -225,6 +295,20 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) { } } + public fun wrapDegreesDouble(degrees: Double): Double { + return when { + degrees < 0 -> { + (degrees % 360) + 360 + } + degrees >= 360 -> { + degrees % 360 + } + else -> { + degrees + } + } + } + private fun hueDiff(a: Float, b: Float): Float { return 180f - ((a - b).absoluteValue - 180f).absoluteValue } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java index 8cd7d94d89529..5a06048838637 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java @@ -100,4 +100,34 @@ public class ColorSchemeTest extends SysuiTestCase { Cam cam = Cam.fromInt(tertiaryMid); Assert.assertEquals(cam.getHue(), 50.0, 10.0); } + + @Test + public void testSpritz() { + int colorInt = 0xffB3588A; // H350 C50 T50 + ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */, + Style.SPRITZ /* style */); + int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2); + Cam cam = Cam.fromInt(primaryMid); + Assert.assertEquals(cam.getChroma(), 4.0, 1.0); + } + + @Test + public void testVibrant() { + int colorInt = 0xffB3588A; // H350 C50 T50 + ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */, + Style.VIBRANT /* style */); + int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2); + Cam cam = Cam.fromInt(neutralMid); + Assert.assertEquals(cam.getChroma(), 8.0, 1.0); + } + + @Test + public void testExpressive() { + int colorInt = 0xffB3588A; // H350 C50 T50 + ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */, + Style.EXPRESSIVE /* style */); + int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2); + Cam cam = Cam.fromInt(neutralMid); + Assert.assertEquals(cam.getChroma(), 16.0, 1.0); + } }