RuneColor
Easily convert RS2 HSL to/from(approximate) RGB
The problem
We often need to approximate a 16-bit RS2 HSL color given an RGB.
Typically, people simply used a direct translation from RGB to HSL, with a function like:
fun rgbToRS2HSL(r: Int, g: Int, b: Int): Short {
val hsb = Color.RGBtoHSB(r, g, b, null)
val hue = hsb[0]
val saturation = hsb[1]
val brightness = hsb[2]
val encodedHue = (hue * 63F).toInt() // to 6-bits
val encodedSaturation = (saturation * 7F).toInt() // to 3-bits
val encodedBrightness = (brightness * 127F).toInt() // to 7-bits
return ((encodedHue shl 10) + (encodedSaturation shl 7) + encodedBrightness).toShort()
}
This will then produce a result, for example:
val redHSL = rgbToRS2HSL(255, 0, 0)
println(redHSL) // HSL 1023, which is RGB color #ff0000
The problem is, within the RuneScape world, such a color does not actually exist, making for strange visuals:
The solution
RuneColor
allows you to easily build and search an RS2 color palette for the closest match to an RGB color.
val palette = RS2CacheHSLColorPalette.load("cache")
val redHSL = palette.closest(255, 0, 0).first() // HSLColor(hsl=960)
println("${redHSL.hsl}(#${redHSL.rgb.toString(16)})") // 960(#ff0202)
Using the HSL color we calculated with RuneColor
(960
), you can see it looks great when applied in-game:
Under the hood
Under the hood, by default RuneColor
by default loads all game model's face colors to use as the search palette.
This color palette is mapped into RGB, and then sorted by CIEDE2000 to find the closest matching RS2 HSL colors for the given RGB color.
It's possible and often that you might want to use a limited color palette, which you can easily pass a ShortArray
to create:
val palette = RS2HSLColorPalette(shortArray)
Or you can pass a vararg Int
:
val palette = RS2HSLColorPalette(1, 2, 3, 4, 5, ...)