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"))
}
}