compute and persist the artist name busy flag with the artwork seed

- ArtworkSeedStore now carries the flag; artwork_seeds gains a nullable nameBusy column
  (DB v12, migration 11->12) so it survives cold starts with no decode
- resolve() computes the flag from the same bitmap it extracts the colour seed from, so
  there is no second decode, and persists it alongside the seed
- overlaidNameBusy reuses the seed job and reads the cached flag synchronously, so the
  scrim is known before the art shows; drop the fade
- bottomBandBusy now uses perceptual (gamma-space) luma, edge density and a bright
  fraction, which catches busy art on dark backgrounds (issue #40) that mean/variance missed
This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent 658b7f9452
commit f4ca0fd6d4
8 changed files with 916 additions and 48 deletions
@@ -26,6 +26,11 @@ class ArtworkNameBandTest {
assertFalse(ArtworkSeedCache.bottomBandBusy(bitmap { _, _ -> Color.rgb(24, 20, 34) }))
}
@Test
fun smoothDarkGradientNeedsNoScrim() {
assertFalse(ArtworkSeedCache.bottomBandBusy(bitmap { x, _ -> Color.rgb(x * 2, x * 2, x * 2) }))
}
@Test
fun brightBandNeedsAScrim() {
assertTrue(ArtworkSeedCache.bottomBandBusy(bitmap { _, _ -> Color.rgb(235, 230, 220) }))
@@ -35,4 +40,13 @@ class ArtworkNameBandTest {
fun noisyBandNeedsAScrim() {
assertTrue(ArtworkSeedCache.bottomBandBusy(bitmap { x, y -> if ((x + y) % 2 == 0) Color.WHITE else Color.BLACK }))
}
@Test
fun darkBandWithBrightStrokesNeedsAScrim() {
assertTrue(
ArtworkSeedCache.bottomBandBusy(
bitmap { x, y -> if (x % 9 == 0 || y % 9 == 0) Color.rgb(210, 40, 40) else Color.BLACK },
),
)
}
}