From c352ec75c90656c85d5743318a996b12d995a6bc Mon Sep 17 00:00:00 2001 From: Jordan Demeulenaere Date: Wed, 4 Dec 2024 10:44:27 +0100 Subject: Convert interpolated colors to original color space This CL fixes a crash that can happen when animating a Color using animateElementColorAsState(): the interpolated Color will end up in the Oklab color space (instead of the original color space), which is not supported by the Android platform. Bug: 382043554 Test: atest AnimatedSharedAsStateTest Flag: com.android.systemui.scene_container Change-Id: I6b32af10534f6112560bc174e734c00ce442688b --- .../android/compose/animation/scene/AnimateSharedAsState.kt | 8 ++++---- .../compose/animation/scene/AnimatedSharedAsStateTest.kt | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 0fc88b22a4d0..a4237f36ab58 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -225,7 +225,7 @@ fun ElementScope<*>.animateElementColorAsState(value: Color, key: ValueKey): Ani return animateElementValueAsState(value, key, SharedColorType, canOverflow = false) } -private object SharedColorType : SharedValueType { +internal object SharedColorType : SharedValueType { override val unspecifiedValue: Color = Color.Unspecified override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f) @@ -255,17 +255,17 @@ private object SharedColorType : SharedValueType { alpha = aOklab.alpha + b.alpha * bWeight, colorSpace = ColorSpaces.Oklab, ) - .convert(aOklab.colorSpace) + .convert(a.colorSpace) } } /** - * Represents the diff between two colors in the same color space. + * Represents the diff between two colors in the Oklab color space. * * Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor * is internal. */ -private class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float) +internal class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float) @Composable internal fun animateSharedValueAsState( diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt index 3644b3069fb3..2fd1d8d8573a 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt @@ -18,7 +18,10 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect @@ -495,4 +498,13 @@ class AnimatedSharedAsStateTest { assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f) assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f) } + + @Test + fun interpolatedColor() { + val a = Color.Red + val b = Color.Green + val delta = SharedColorType.diff(b, a) // b - a + val interpolated = SharedColorType.addWeighted(a, delta, 0.5f) // a + (b - a) * 0.5f + rule.setContent { Box(Modifier.fillMaxSize().background(interpolated)) } + } } -- cgit v1.2.3-59-g8ed1b