Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49fa723e authored by James O'Leary's avatar James O'Leary
Browse files

Match latest variant spec; update hue rotation logic

- Update a couple rotations to match spec (ex. rotation occurs at 151
degrees instead of 141)
- If hue rotation specs have an entry at 0 degrees, they also need an
entry at 360 degrees. There's no way to determine that, for example,
321 is between 315 and 0, 360 must be listed.
- Find hue rotation in a way that ensures ex. an angle isn't tested for
being between 0 and 360 degrees, as that will always return true.
Instead, use first element, (always 0 degrees), check if angle is
between and the next element, and stop once the next element is 360
degrees (always the last element in the array)
- Remove code that became unused after the above change.

Test: atest ColorSchemeTest (unit test); atest SystemPaletteTest (CTS).
Spent ~4 hours trying a variety of wallpapers, and seed colors, and
used dumpsys to verify results match UX spec. Create spreadsheet for UX
to fill out their spec, then another spreadsheet that contains the
dumpsys results for blue/red/yellow/green seed colors across all theme
variants, and the preset colors for Ice Cream and Fruit Salad variants.
Confirmed the runtime results, as reported by dumpsys, match the spec
in all cases.
Bug: 213314628
Change-Id: Id35faa294bf82c0e81c2690c65c42218a87c236a
parent c3cffc8b
Loading
Loading
Loading
Loading
+18 −47
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.graphics.Color
import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.cam.Cam
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.roundToInt

const val TAG = "ColorScheme"
@@ -43,13 +42,13 @@ internal interface Hue {
     *    second item in the pair is a hue rotation that should be applied
     */
    fun getHueRotation(sourceHue: Float, hueAndRotations: List<Pair<Int, Int>>): Double {
        for (i in 0..hueAndRotations.size) {
            val previousIndex = if (i == 0) hueAndRotations.size - 1 else i - 1
            val thisHue = hueAndRotations[i].first
            val previousHue = hueAndRotations[previousIndex].first
            if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
                return ColorScheme.wrapDegreesDouble(sourceHue.toDouble() +
                        hueAndRotations[previousIndex].second)
        val sanitizedSourceHue = (if (sourceHue < 0 || sourceHue >= 360) 0 else sourceHue).toFloat()
        for (i in 0..hueAndRotations.size - 2) {
            val thisHue = hueAndRotations[i].first.toFloat()
            val nextHue = hueAndRotations[i + 1].first.toFloat()
            if (thisHue <= sanitizedSourceHue && sanitizedSourceHue < nextHue) {
                return ColorScheme.wrapDegreesDouble(sanitizedSourceHue.toDouble() +
                        hueAndRotations[i].second)
            }
        }

@@ -79,7 +78,7 @@ internal class HueSubtract(val amountDegrees: Double) : Hue {

internal class HueVibrantSecondary() : Hue {
    val hueToRotations = listOf(Pair(0, 18), Pair(41, 15), Pair(61, 10), Pair(101, 12),
            Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12))
            Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12), Pair(360, 12))
    override fun get(sourceColor: Cam): Double {
        return getHueRotation(sourceColor.hue, hueToRotations)
    }
@@ -87,7 +86,7 @@ internal class HueVibrantSecondary() : Hue {

internal class HueVibrantTertiary() : Hue {
    val hueToRotations = listOf(Pair(0, 35), Pair(41, 30), Pair(61, 20), Pair(101, 25),
            Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25))
            Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25), Pair(360, 25))
    override fun get(sourceColor: Cam): Double {
        return getHueRotation(sourceColor.hue, hueToRotations)
    }
@@ -95,7 +94,7 @@ internal class HueVibrantTertiary() : Hue {

internal class HueExpressiveSecondary() : Hue {
    val hueToRotations = listOf(Pair(0, 45), Pair(21, 95), Pair(51, 45), Pair(121, 20),
            Pair(141, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45))
            Pair(151, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45), Pair(360, 45))
    override fun get(sourceColor: Cam): Double {
        return getHueRotation(sourceColor.hue, hueToRotations)
    }
@@ -103,7 +102,7 @@ internal class HueExpressiveSecondary() : Hue {

internal class HueExpressiveTertiary() : Hue {
    val hueToRotations = listOf(Pair(0, 120), Pair(21, 120), Pair(51, 20), Pair(121, 45),
            Pair(141, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120))
            Pair(151, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120), Pair(360, 120))
    override fun get(sourceColor: Cam): Double {
        return getHueRotation(sourceColor.hue, hueToRotations)
    }
@@ -111,34 +110,13 @@ internal class HueExpressiveTertiary() : Hue {

internal interface Chroma {
    fun get(sourceColor: Cam): Double

    /**
     * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the
     * hue fall betweens, and use the hue rotation of the lower hue.
     *
     * @param sourceHue hue of source color
     * @param hueAndChromas list of pairs, where the first item in a pair is a hue, and the
     *    second item in the pair is a chroma that should be applied
     */
    fun getSpecifiedChroma(sourceHue: Float, hueAndChromas: List<Pair<Int, Int>>): Double {
        for (i in 0..hueAndChromas.size) {
            val previousIndex = if (i == 0) hueAndChromas.size - 1 else i - 1
            val thisHue = hueAndChromas[i].first
            val previousHue = hueAndChromas[previousIndex].first
            if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
                return hueAndChromas[i].second.toDouble()
            }
}

        // If this statement executes, something is wrong, there should have been a rotation
        // found using the arrays.
        return sourceHue.toDouble()
    }
}

internal class ChromaMinimum(val chroma: Double) : Chroma {
internal class ChromaMaxOut : Chroma {
    override fun get(sourceColor: Cam): Double {
        return max(sourceColor.chroma.toDouble(), chroma)
        // Intentionally high. Gamut mapping from impossible HCT to sRGB will ensure that
        // the maximum chroma is reached, even if lower than this constant.
        return 130.0
    }
}

@@ -192,11 +170,11 @@ enum class Style(internal val coreSpec: CoreSpec) {
            n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
    )),
    VIBRANT(CoreSpec(
            a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
            a1 = TonalSpec(HueSource(), ChromaMaxOut()),
            a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
            a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
            n1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
            n2 = TonalSpec(HueSource(), ChromaConstant(14.0))
            n1 = TonalSpec(HueSource(), ChromaConstant(8.0)),
            n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
    )),
    EXPRESSIVE(CoreSpec(
            a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
@@ -416,13 +394,6 @@ class ColorScheme(
            return seeds
        }

        internal fun angleIsBetween(angle: Float, a: Int, b: Int): Boolean {
            if (a < b) {
                return a <= angle && angle <= b
            }
            return a <= angle || angle <= b
        }

        private fun wrapDegrees(degrees: Int): Int {
            return when {
                degrees < 0 -> {
+1 −1
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ public class ColorSchemeTest extends SysuiTestCase {
        // Expressive applies hue rotations to the theme color. The input theme color has hue
        // 117, ensuring the hue changed significantly is a strong signal styles are being applied.
        ColorScheme colorScheme = new ColorScheme(wallpaperColors, false, Style.EXPRESSIVE);
        Assert.assertEquals(Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 357.46, 0.1);
        Assert.assertEquals(357.77, Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 0.1);
    }