diff --git a/CHANGES.md b/CHANGES.md index 4a158086a2..f0bae1aa32 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ Changes in Element 1.0.9 (2020-XX-XX) Features ✨: - Search messages in a room - phase 1 (#2110) - Hide encrypted history (before user is invited). Can be shown if wanted in developer settings + - Changed rainbow algorithm Improvements 🙌: - Wording differentiation for direct rooms (#2176) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt index 7bed9f8e64..7a64d437c5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt @@ -18,8 +18,10 @@ package im.vector.app.features.home.room.detail.composer.rainbow import im.vector.app.core.utils.splitEmoji import javax.inject.Inject -import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.pow import kotlin.math.roundToInt +import kotlin.math.sin /** * Inspired from React-Sdk @@ -29,7 +31,7 @@ class RainbowGenerator @Inject constructor() { fun generate(text: String): String { val split = text.splitEmoji() - val frequency = 360f / split.size + val frequency = 360.0 / split.size return split .mapIndexed { idx, letter -> @@ -37,53 +39,54 @@ class RainbowGenerator @Inject constructor() { if (letter == " ") { "$letter" } else { - val dashColor = hueToRGB(idx * frequency, 1.0f, 0.5f).toDashColor() + val (a, b) = generateAB(idx * frequency, 1f) + val dashColor = labToRGB(75, a, b).toDashColor() "$letter" } } .joinToString(separator = "") } - private fun hueToRGB(h: Float, s: Float, l: Float): RgbColor { - val c = s * (1 - abs(2 * l - 1)) - val x = c * (1 - abs((h / 60) % 2 - 1)) - val m = l - c / 2 + private fun generateAB(hue: Double, chroma: Float): Pair { + val radians = Math.toRadians(hue) - var r = 0f - var g = 0f - var b = 0f + val a = chroma * 127 * cos(radians) + val b = chroma * 127 * sin(radians) - when { - h < 60f -> { - r = c - g = x - } - h < 120f -> { - r = x - g = c - } - h < 180f -> { - g = c - b = x - } - h < 240f -> { - g = x - b = c - } - h < 300f -> { - r = x - b = c - } - else -> { - r = c - b = x - } + return Pair(a, b) + } + + private fun labToRGB(l: Int, a: Double, b: Double): RgbColor { + var y = (l + 16) / 116.0 + val x = adjustXYZ(y + a / 500) * 0.9505 + val z = adjustXYZ(y - b / 200) * 1.0890 + + y = adjustXYZ(y) + + val red = 3.24096994 * x - 1.53738318 * y - 0.49861076 * z + val green = -0.96924364 * x + 1.8759675 * y + 0.04155506 * z + val blue = 0.05563008 * x - 0.20397696 * y + 1.05697151 * z + + return RgbColor(adjustRGB(red), adjustRGB(green), adjustRGB(blue)) + } + + private fun adjustXYZ(value: Double): Double { + if (value > 0.2069) { + return value.pow(3) } + return 0.1284 * value - 0.01771 + } - return RgbColor( - ((r + m) * 255).roundToInt(), - ((g + m) * 255).roundToInt(), - ((b + m) * 255).roundToInt() - ) + private fun gammaCorrection(value: Double): Double { + if (value <= 0.0031308) { + return 12.92 * value + } + return 1.055 * value.pow(1 / 2.4) - 0.055 + } + + private fun adjustRGB(value: Double): Int { + return (gammaCorrection(value) + .coerceIn(0.0, 1.0) * 255) + .roundToInt() } } diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt index 0e46d67860..20b041fd1e 100644 --- a/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt +++ b/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt @@ -32,14 +32,14 @@ class RainbowGeneratorTest { @Test fun testAscii1() { - assertEquals("""a""", rainbowGenerator.generate("a")) + assertEquals("""a""", rainbowGenerator.generate("a")) } @Test fun testAscii2() { val expected = """ - a - b + a + b """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("ab")) @@ -48,24 +48,24 @@ class RainbowGeneratorTest { @Test fun testAscii3() { val expected = """ - T - h - i - s + T + h + i + s - i - s + i + s - a + a - r - a - i - n - b - o - w - ! + r + a + i + n + b + o + w + ! """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("This is a rainbow!")) @@ -73,19 +73,19 @@ class RainbowGeneratorTest { @Test fun testEmoji1() { - assertEquals("""🤞""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🤞 + assertEquals("""🤞""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🤞 } @Test fun testEmoji2() { - assertEquals("""🤞""", rainbowGenerator.generate("🤞")) + assertEquals("""🤞""", rainbowGenerator.generate("🤞")) } @Test fun testEmoji3() { val expected = """ - 🤞 - 🙂 + 🤞 + 🙂 """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("🤞🙂")) @@ -94,20 +94,20 @@ class RainbowGeneratorTest { @Test fun testEmojiMix1() { val expected = """ - H - e - l - l - o + H + e + l + l + o - 🤞 + 🤞 - w - o - r - l - d - ! + w + o + r + l + d + ! """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("Hello 🤞 world!")) @@ -116,8 +116,8 @@ class RainbowGeneratorTest { @Test fun testEmojiMix2() { val expected = """ - a - 🤞 + a + 🤞 """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("a🤞")) @@ -126,8 +126,8 @@ class RainbowGeneratorTest { @Test fun testEmojiMix3() { val expected = """ - 🤞 - a + 🤞 + a """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("🤞a")) @@ -135,6 +135,6 @@ class RainbowGeneratorTest { @Test fun testError1() { - assertEquals("\uD83E", rainbowGenerator.generate("\uD83E")) + assertEquals("\uD83E", rainbowGenerator.generate("\uD83E")) } }