-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add CSS color parsing (and also write a few bonus tests) * Fix codecov path fixes * Use string prefix to determine parsing technique * Remove accidental double blank line * Add tests for the Random extensions
- Loading branch information
1 parent
2cd38aa
commit 52c530a
Showing
10 changed files
with
361 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.juul.krayon.color | ||
|
||
private const val HEX = "[a-f0-9]" | ||
|
||
private val RGB = """($HEX)($HEX)($HEX)""".toRegex(RegexOption.IGNORE_CASE) | ||
private val RGBA = """($HEX)($HEX)($HEX)($HEX)""".toRegex(RegexOption.IGNORE_CASE) | ||
private val RRGGBB = """($HEX{2})($HEX{2})($HEX{2})""".toRegex(RegexOption.IGNORE_CASE) | ||
private val RRGGBBAA = """($HEX{2})($HEX{2})($HEX{2})($HEX{2})""".toRegex(RegexOption.IGNORE_CASE) | ||
|
||
public fun String.toColor(): Color = | ||
when { | ||
this.startsWith("#") -> parseHexNotation(this) | ||
else -> keywordMap[this.toLowerCase()] | ||
} ?: throw IllegalArgumentException("Unknown color: `$this`.") | ||
|
||
public fun String.toColorOrNull(): Color? = | ||
try { | ||
toColor() | ||
} catch (e: IllegalArgumentException) { | ||
null | ||
} | ||
|
||
private fun parseHexNotation(text: String): Color { | ||
require(text.startsWith("#")) { "Hex notation must start with a pound sign (#)." } | ||
val hexText = text.substring(1) | ||
val match = when (hexText.length) { | ||
3 -> RGB.matchEntire(hexText) | ||
4 -> RGBA.matchEntire(hexText) | ||
6 -> RRGGBB.matchEntire(hexText) | ||
8 -> RRGGBBAA.matchEntire(hexText) | ||
else -> throw IllegalArgumentException("Incorrect length. Hex notation must contain exactly 3, 4, 6, or 8 hexadecimal characters.") | ||
} ?: throw IllegalArgumentException("Text is not a hexadecimal string. Each character must match the regex [a-fA-F0-9].") | ||
val red = match.groupValues[1] | ||
val green = match.groupValues[2] | ||
val blue = match.groupValues[3] | ||
val alpha = match.groupValues.getOrNull(4) ?: "ff" | ||
return Color(parseHexComponent(alpha), parseHexComponent(red), parseHexComponent(green), parseHexComponent(blue)) | ||
} | ||
|
||
private fun parseHexComponent(component: String): Int = | ||
if (component.length == 1) { | ||
// Per MDN, "The three-digit notation (#RGB) is a shorter version of the six-digit form (#RRGGBB)." | ||
// Achieve that by just calling this function again with the component repeated. | ||
parseHexComponent(component + component) | ||
} else { | ||
component.toInt(HEX_BASE) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.juul.krayon.color | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
|
||
class OperationsTests { | ||
|
||
@Test | ||
fun lerp_fromWhiteToBlack_isGray() { | ||
assertEquals(gray, lerp(white, black, 0.5f)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.juul.krayon.color | ||
|
||
import kotlin.random.Random | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
|
||
class RandomTests { | ||
|
||
private val saturatedRandom = object : Random() { | ||
override fun nextBits(bitCount: Int): Int = 0xFFFFFFFF.toInt() ushr (32 - bitCount) | ||
} | ||
|
||
private val zeroRandom = object : Random() { | ||
override fun nextBits(bitCount: Int): Int = 0 | ||
} | ||
|
||
@Test | ||
fun nextColor_withSaturatedRandom_isWhite() { | ||
assertEquals(white.rgb, saturatedRandom.nextColor().rgb) | ||
} | ||
|
||
@Test | ||
fun nextColor_withDefaultArg_isOpaque() { | ||
assertEquals(0xFF, zeroRandom.nextColor().alpha) | ||
} | ||
|
||
@Test | ||
fun nextColor_withOpaqueFalse_canProduceTransparency() { | ||
assertEquals(0, zeroRandom.nextColor(isOpaque = false).alpha) | ||
} | ||
} |
Oops, something went wrong.