2024-01-04 13:53:43 +00:00
|
|
|
package dev.inmo.micro_utils.colors.common
|
|
|
|
|
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
import kotlin.jvm.JvmInline
|
|
|
|
import kotlin.math.floor
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper for RGBA colors. Receiving [UInt] in main constructor. Each part in main constructor
|
|
|
|
* configured with `0x00 - 0xff` range. Examples:
|
|
|
|
*
|
|
|
|
* * Red: `0xff0000ffu`
|
|
|
|
* * Red (0.5 capacity): `0xff000088u`
|
|
|
|
*
|
|
|
|
* Anyway it is recommended to use
|
2024-02-12 07:58:06 +00:00
|
|
|
*
|
2024-02-12 08:25:45 +00:00
|
|
|
* @param hexaUInt rgba [UInt] in format `0xFFEEBBAA` where FF - red, EE - green, BB - blue` and AA - alpha
|
2024-01-04 13:53:43 +00:00
|
|
|
*/
|
|
|
|
@Serializable
|
|
|
|
@JvmInline
|
|
|
|
value class HEXAColor (
|
2024-02-12 08:25:45 +00:00
|
|
|
val hexaUInt: UInt
|
2024-01-04 13:53:43 +00:00
|
|
|
) : Comparable<HEXAColor> {
|
2024-02-12 07:58:06 +00:00
|
|
|
/**
|
2024-02-12 08:25:45 +00:00
|
|
|
* @returns [hexaUInt] as a string with format `#FFEEBBAA` where FF - red, EE - green, BB - blue and AA - alpha
|
2024-02-12 07:58:06 +00:00
|
|
|
*/
|
2024-01-04 13:53:43 +00:00
|
|
|
val hexa: String
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = "#${hexaUInt.toString(16).padStart(8, '0')}"
|
2024-02-12 07:58:06 +00:00
|
|
|
|
|
|
|
/**
|
2024-02-12 08:25:45 +00:00
|
|
|
* @returns [hexaUInt] as a string with format `#FFEEBB` where FF - red, EE - green and BB - blue
|
2024-02-12 07:58:06 +00:00
|
|
|
*/
|
2024-01-04 13:53:43 +00:00
|
|
|
val hex: String
|
|
|
|
get() = hexa.take(7)
|
2024-02-12 07:58:06 +00:00
|
|
|
/**
|
2024-02-12 08:25:45 +00:00
|
|
|
* @returns [hexaUInt] as a string with format `#AAFFEEBB` where AA - alpha, FF - red, EE - green and BB - blue
|
2024-02-12 07:58:06 +00:00
|
|
|
*/
|
|
|
|
val ahex: String
|
|
|
|
get() = "#${a.toString(16).padStart(2, '2')}${hex.drop(1)}"
|
2024-01-04 14:20:55 +00:00
|
|
|
val rgba: String
|
|
|
|
get() = "rgba($r,$g,$b,${aOfOne.toString().take(5)})"
|
|
|
|
val rgb: String
|
|
|
|
get() = "rgb($r,$g,$b)"
|
2024-01-04 13:53:43 +00:00
|
|
|
val shortHex: String
|
|
|
|
get() = "#${r.shortPart()}${g.shortPart()}${b.shortPart()}"
|
|
|
|
val shortHexa: String
|
|
|
|
get() = "$shortHex${a.shortPart()}"
|
2024-02-12 08:25:45 +00:00
|
|
|
val rgbUInt: UInt
|
|
|
|
get() = (hexaUInt / 256u)
|
2024-01-04 13:53:43 +00:00
|
|
|
val rgbInt: Int
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = rgbUInt.toInt()
|
|
|
|
val ahexUInt
|
|
|
|
get() = (a * 0x1000000).toUInt() + rgbUInt
|
2024-01-04 13:53:43 +00:00
|
|
|
|
|
|
|
val r: Int
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = ((hexaUInt and 0xff000000u) / 0x1000000u).toInt()
|
2024-01-04 13:53:43 +00:00
|
|
|
val g: Int
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = ((hexaUInt and 0x00ff0000u) / 0x10000u).toInt()
|
2024-01-04 13:53:43 +00:00
|
|
|
val b: Int
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = ((hexaUInt and 0x0000ff00u) / 0x100u).toInt()
|
2024-01-04 13:53:43 +00:00
|
|
|
val a: Int
|
2024-02-12 08:25:45 +00:00
|
|
|
get() = ((hexaUInt and 0x000000ffu)).toInt()
|
2024-01-04 13:53:43 +00:00
|
|
|
val aOfOne: Float
|
|
|
|
get() = a.toFloat() / (0xff)
|
|
|
|
init {
|
2024-02-12 08:25:45 +00:00
|
|
|
require(hexaUInt in 0u ..0xffffffffu)
|
2024-01-04 13:53:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(r: Int, g: Int, b: Int, a: Int) : this(
|
|
|
|
((r * 0x1000000).toLong() + g * 0x10000 + b * 0x100 + a).toUInt()
|
|
|
|
) {
|
|
|
|
require(r in 0 ..0xff)
|
|
|
|
require(g in 0 ..0xff)
|
|
|
|
require(b in 0 ..0xff)
|
|
|
|
require(a in 0 ..0xff)
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(r: Int, g: Int, b: Int, aOfOne: Float = 1f) : this(
|
|
|
|
r = r, g = g, b = b, a = (aOfOne * 0xff).toInt()
|
|
|
|
)
|
|
|
|
|
|
|
|
override fun toString(): String {
|
|
|
|
return hexa
|
|
|
|
}
|
|
|
|
|
2024-02-12 08:25:45 +00:00
|
|
|
override fun compareTo(other: HEXAColor): Int = (hexaUInt - other.hexaUInt).coerceIn(Int.MIN_VALUE.toUInt(), Int.MAX_VALUE.toLong().toUInt()).toInt()
|
2024-01-04 13:53:43 +00:00
|
|
|
|
|
|
|
fun copy(
|
|
|
|
r: Int = this.r,
|
|
|
|
g: Int = this.g,
|
|
|
|
b: Int = this.b,
|
|
|
|
aOfOne: Float = this.aOfOne
|
2024-01-04 14:20:55 +00:00
|
|
|
) = HEXAColor(r = r, g = g, b = b, aOfOne = aOfOne)
|
2024-01-04 13:53:43 +00:00
|
|
|
fun copy(
|
|
|
|
r: Int = this.r,
|
|
|
|
g: Int = this.g,
|
|
|
|
b: Int = this.b,
|
|
|
|
a: Int
|
2024-01-04 14:20:55 +00:00
|
|
|
) = HEXAColor(r = r, g = g, b = b, a = a)
|
2024-01-04 13:53:43 +00:00
|
|
|
|
|
|
|
companion object {
|
|
|
|
/**
|
|
|
|
* Parsing color from [color]
|
|
|
|
*
|
|
|
|
* Supported formats samples (on Red color based):
|
|
|
|
*
|
|
|
|
* * `#f00`
|
|
|
|
* * `#f00f`
|
|
|
|
* * `#ff0000`
|
|
|
|
* * `#ff0000ff`
|
|
|
|
* * `rgb(255, 0, 0)`
|
|
|
|
* * `rgba(255, 0, 0, 1)`
|
|
|
|
*/
|
|
|
|
fun parseStringColor(color: String): HEXAColor = when {
|
|
|
|
color.startsWith("#") -> color.removePrefix("#").let { color ->
|
|
|
|
when (color.length) {
|
|
|
|
3 -> color.map { "$it$it" }.joinToString(separator = "", postfix = "ff")
|
|
|
|
4 -> color.take(3).map { "$it$it" }.joinToString(separator = "", postfix = color.takeLast(1).let { "${it}0" })
|
|
|
|
6 -> "${color}ff"
|
|
|
|
8 -> color
|
|
|
|
else -> error("Malfurmed color string: $color. It is expected that color started with # will contains 3, 6 or 8 valuable parts")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
color.startsWith("rgb(") -> color
|
|
|
|
.removePrefix("rgb(")
|
|
|
|
.removeSuffix(")")
|
|
|
|
.replace(Regex("\\s"), "")
|
|
|
|
.split(",")
|
2024-01-04 14:20:55 +00:00
|
|
|
.joinToString("", postfix = "ff") {
|
|
|
|
it.toInt().toString(16).padStart(2, '0')
|
|
|
|
}
|
2024-01-04 13:53:43 +00:00
|
|
|
color.startsWith("rgba(") -> color
|
|
|
|
.removePrefix("rgba(")
|
|
|
|
.removeSuffix(")")
|
|
|
|
.replace(Regex("\\s"), "")
|
|
|
|
.split(",").let {
|
2024-01-04 14:20:55 +00:00
|
|
|
it.take(3).map { it.toInt().toString(16).padStart(2, '0') } + (it.last().toFloat() * 0xff).toInt().toString(16).padStart(2, '0')
|
2024-01-04 13:53:43 +00:00
|
|
|
}
|
|
|
|
.joinToString("")
|
|
|
|
else -> color
|
|
|
|
}.lowercase().toUInt(16).let(::HEXAColor)
|
|
|
|
|
2024-02-12 07:58:06 +00:00
|
|
|
/**
|
|
|
|
* Creates [HEXAColor] from [uint] presume it is in format `0xFFEEBBAA` where FF - red, EE - green, BB - blue` and AA - alpha
|
|
|
|
*/
|
|
|
|
fun fromHexa(uint: UInt) = HEXAColor(uint)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates [HEXAColor] from [uint] presume it is in format `0xAAFFEEBB` where AA - alpha, FF - red, EE - green and BB - blue`
|
|
|
|
*/
|
|
|
|
fun fromAhex(uint: UInt) = HEXAColor(
|
|
|
|
a = ((uint and 0xff000000u) / 0x1000000u).toInt(),
|
|
|
|
r = ((uint and 0x00ff0000u) / 0x10000u).toInt(),
|
|
|
|
g = ((uint and 0x0000ff00u) / 0x100u).toInt(),
|
|
|
|
b = ((uint and 0x000000ffu)).toInt()
|
|
|
|
)
|
|
|
|
|
2024-01-04 13:53:43 +00:00
|
|
|
/**
|
|
|
|
* Parsing color from [color]
|
|
|
|
*
|
|
|
|
* Supported formats samples (on Red color based):
|
|
|
|
*
|
|
|
|
* * `#f00`
|
|
|
|
* * `#ff0000`
|
|
|
|
* * `#ff0000ff`
|
|
|
|
* * `rgb(255, 0, 0)`
|
|
|
|
* * `rgba(255, 0, 0, 1)`
|
|
|
|
*/
|
|
|
|
operator fun invoke(color: String) = parseStringColor(color)
|
|
|
|
|
|
|
|
private fun Int.shortPart(): String {
|
|
|
|
return (floor(toFloat() / 16)).toInt().toString(16)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|