mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
rotate warm artwork hues out of the brown/olive range for dark tones
This commit is contained in:
@@ -53,6 +53,7 @@ fun artworkColorsFromSeeds(
|
|||||||
): ArtworkColors {
|
): ArtworkColors {
|
||||||
val (hue, saturation, _) = Color(primarySeed or 0xFF000000.toInt()).toHsl()
|
val (hue, saturation, _) = Color(primarySeed or 0xFF000000.toInt()).toHsl()
|
||||||
val s = saturation.coerceAtMost(0.85f)
|
val s = saturation.coerceAtMost(0.85f)
|
||||||
|
val darkHue = harmonizeDarkHue(hue)
|
||||||
|
|
||||||
fun tone(
|
fun tone(
|
||||||
h: Float,
|
h: Float,
|
||||||
@@ -61,7 +62,7 @@ fun artworkColorsFromSeeds(
|
|||||||
) = Color.hsl(h % 360f, sat.coerceIn(0f, 1f), l.coerceIn(0f, 1f))
|
) = Color.hsl(h % 360f, sat.coerceIn(0f, 1f), l.coerceIn(0f, 1f))
|
||||||
|
|
||||||
val primary = tone(hue, s, 0.62f).withMinLuminance(PRIMARY_MIN_LUMINANCE)
|
val primary = tone(hue, s, 0.62f).withMinLuminance(PRIMARY_MIN_LUMINANCE)
|
||||||
val background = tone(hue, (s * 0.35f).coerceAtMost(0.20f), 0.06f)
|
val background = tone(darkHue, (s * 0.35f).coerceAtMost(0.20f), 0.06f)
|
||||||
val onBackground = tone(hue, (s * 0.10f).coerceAtMost(0.08f), 0.95f)
|
val onBackground = tone(hue, (s * 0.10f).coerceAtMost(0.08f), 0.95f)
|
||||||
|
|
||||||
val secondary =
|
val secondary =
|
||||||
@@ -75,14 +76,14 @@ fun artworkColorsFromSeeds(
|
|||||||
} ?: tone(hue, s, 0.40f)
|
} ?: tone(hue, s, 0.40f)
|
||||||
|
|
||||||
val gradientSat = if (s > 0.05f) s.coerceAtLeast(MIN_GRADIENT_SATURATION) else s
|
val gradientSat = if (s > 0.05f) s.coerceAtLeast(MIN_GRADIENT_SATURATION) else s
|
||||||
val gradientHigh = tone(hue, (gradientSat * 0.85f).coerceAtMost(0.70f), 0.26f)
|
val gradientHigh = tone(darkHue, (gradientSat * 0.85f).coerceAtMost(0.70f), 0.26f)
|
||||||
val gradientLow = tone(hue, gradientSat * 0.55f, 0.12f)
|
val gradientLow = tone(darkHue, gradientSat * 0.55f, 0.12f)
|
||||||
val accents =
|
val accents =
|
||||||
listOf(
|
listOf(
|
||||||
tone(hue, gradientSat, 0.72f).withMaxLuminance(ACCENT_MAX_LUMINANCE),
|
tone(darkHue, gradientSat, 0.72f).withMaxLuminance(ACCENT_MAX_LUMINANCE),
|
||||||
secondary.withMaxLuminance(ACCENT_MAX_LUMINANCE),
|
secondary.withMaxLuminance(ACCENT_MAX_LUMINANCE),
|
||||||
)
|
)
|
||||||
val darkPrimary = tone(hue, gradientSat * 0.75f, 0.10f)
|
val darkPrimary = tone(darkHue, gradientSat * 0.75f, 0.10f)
|
||||||
|
|
||||||
val backdrop = maxOf(gradientHigh.luminance(), gradientLow.luminance(), accents.maxOf { it.luminance() })
|
val backdrop = maxOf(gradientHigh.luminance(), gradientLow.luminance(), accents.maxOf { it.luminance() })
|
||||||
val onSurfaceVariant =
|
val onSurfaceVariant =
|
||||||
@@ -108,15 +109,15 @@ fun artworkColorsFromSeeds(
|
|||||||
surface = background,
|
surface = background,
|
||||||
onSurface = onBackground,
|
onSurface = onBackground,
|
||||||
surfaceTint = primary,
|
surfaceTint = primary,
|
||||||
surfaceVariant = tone(hue, s * 0.25f, 0.17f),
|
surfaceVariant = tone(darkHue, s * 0.25f, 0.17f),
|
||||||
onSurfaceVariant = onSurfaceVariant,
|
onSurfaceVariant = onSurfaceVariant,
|
||||||
surfaceContainerLowest = tone(hue, s * 0.25f, 0.04f),
|
surfaceContainerLowest = tone(darkHue, s * 0.25f, 0.04f),
|
||||||
surfaceContainerLow = tone(hue, s * 0.25f, 0.08f),
|
surfaceContainerLow = tone(darkHue, s * 0.25f, 0.08f),
|
||||||
surfaceContainer = tone(hue, s * 0.25f, 0.10f),
|
surfaceContainer = tone(darkHue, s * 0.25f, 0.10f),
|
||||||
surfaceContainerHigh = tone(hue, s * 0.22f, 0.14f),
|
surfaceContainerHigh = tone(darkHue, s * 0.22f, 0.14f),
|
||||||
surfaceContainerHighest = tone(hue, s * 0.20f, 0.18f),
|
surfaceContainerHighest = tone(darkHue, s * 0.20f, 0.18f),
|
||||||
outline = tone(hue, s * 0.15f, 0.55f),
|
outline = tone(hue, s * 0.15f, 0.55f),
|
||||||
outlineVariant = tone(hue, s * 0.18f, 0.28f),
|
outlineVariant = tone(darkHue, s * 0.18f, 0.28f),
|
||||||
)
|
)
|
||||||
|
|
||||||
return ArtworkColors(
|
return ArtworkColors(
|
||||||
@@ -174,6 +175,11 @@ private fun blendHue(
|
|||||||
return (from + delta * fraction + 360f) % 360f
|
return (from + delta * fraction + 360f) % 360f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun harmonizeDarkHue(hue: Float): Float {
|
||||||
|
if (hue !in 0f..120f) return hue
|
||||||
|
return 358f - hue * (40f / 120f)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberArtworkColors(
|
fun rememberArtworkColors(
|
||||||
ref: CoverArtRef?,
|
ref: CoverArtRef?,
|
||||||
|
|||||||
@@ -155,6 +155,44 @@ class AppScreenshotTest {
|
|||||||
composeRule.onRoot().captureRoboImage("src/test/screenshots/library_with_mini_player.png")
|
composeRule.onRoot().captureRoboImage("src/test/screenshots/library_with_mini_player.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun libraryWithMiniPlayerWarm() {
|
||||||
|
composeRule.setContent {
|
||||||
|
SubtracksTheme {
|
||||||
|
val artwork = artworkColorsFromSeed(Color.rgb(222, 150, 30))
|
||||||
|
Column(Modifier.fillMaxSize()) {
|
||||||
|
LibraryScreen(
|
||||||
|
selectedTab = LibraryTab.Albums,
|
||||||
|
onTabSelected = {},
|
||||||
|
albums = remember { flowOf(PagingData.from(Fixtures.albums)) }.collectAsLazyPagingItems(),
|
||||||
|
artists = remember { flowOf(PagingData.from(Fixtures.artists)) }.collectAsLazyPagingItems(),
|
||||||
|
songs = remember { flowOf(PagingData.from(Fixtures.songItems)) }.collectAsLazyPagingItems(),
|
||||||
|
playlists = remember { flowOf(PagingData.from(Fixtures.playlists)) }.collectAsLazyPagingItems(),
|
||||||
|
coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } },
|
||||||
|
onAlbumClick = {},
|
||||||
|
onArtistClick = {},
|
||||||
|
onPlaylistClick = {},
|
||||||
|
onSongClick = {},
|
||||||
|
onSync = {},
|
||||||
|
onOpenSettings = {},
|
||||||
|
artwork = artwork,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
MiniPlayer(
|
||||||
|
state = Fixtures.playbackState(),
|
||||||
|
coverArt = CoverArtRef("art-al-kid-a", "test:art-al-kid-a"),
|
||||||
|
artwork = artwork,
|
||||||
|
onExpand = {},
|
||||||
|
onPlayPause = {},
|
||||||
|
onNext = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
awaitTag(ALBUM_COVER_TAG)
|
||||||
|
composeRule.onRoot().captureRoboImage("src/test/screenshots/library_with_mini_player_warm.png")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun albumDetail() {
|
fun albumDetail() {
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
|
|||||||
@@ -59,6 +59,19 @@ class ArtworkColorsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun warmHuesKeepDarkSurfacesOutOfBrown() {
|
||||||
|
val warmSeeds = listOf(0xFFE6C822.toInt(), 0xFFE68A22.toInt(), 0xFFD53A3A.toInt())
|
||||||
|
for (seed in warmSeeds) {
|
||||||
|
val colors = artworkColorsFromSeed(seed)
|
||||||
|
assertTrue("gradientHigh for $seed", colors.gradientHigh.blue >= colors.gradientHigh.green)
|
||||||
|
assertTrue(
|
||||||
|
"surfaceContainerHigh for $seed",
|
||||||
|
colors.scheme.surfaceContainerHigh.blue >= colors.scheme.surfaceContainerHigh.green,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun contrast(
|
private fun contrast(
|
||||||
a: Color,
|
a: Color,
|
||||||
b: Color,
|
b: Color,
|
||||||
|
|||||||
Reference in New Issue
Block a user