CIELab and CIELch colour util

Former-commit-id: f8b0413223c2c968e4627e7c251220d32e2c6bf5
Former-commit-id: 2bce3479a8ad95ac06fbbd6c35cf73967a49568d
This commit is contained in:
Song Minjae
2016-09-01 21:36:44 +09:00
parent 6e51b0c751
commit b735335a99
10 changed files with 228 additions and 53 deletions

View File

@@ -0,0 +1,99 @@
package net.torvald.colourutil
import com.jme3.math.FastMath
import org.newdawn.slick.Color
/**
* RGB in this code is always sRGB.
* reference: http://www.brucelindbloom.com/index.html?Equations.html
*
* Created by minjaesong on 16-09-01.
*/
object CIELabUtil {
fun Color.brighterLab(scale: Float): Color {
val brighten = scale + 1f
val lab = this.toLab()
lab.L *= brighten
return lab.toRGB()
}
fun Color.darkerLab(scale: Float): Color {
val darken = 1f - scale
val lab = this.toLab()
lab.L *= darken
return lab.toRGB()
}
/** Sweet Lab linear gradient */
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLab()
val to = toCol.toLab()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newA = FastMath.interpolateLinear(scale, from.a, to.a)
val newB = FastMath.interpolateLinear(scale, from.b, to.b)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIELab(newL, newA, newB, newAlpha).toRGB()
}
private fun Color.toLab() = this.toXYZ().toLab()
private fun CIELab.toRGB() = this.toXYZ().toRGB()
fun Color.toXYZ(): CIEXYZ {
val x = 0.4124564f * r + 0.3575761f * g + 0.1804375f * b
val y = 0.2126729f * r + 0.7151522f * g + 0.0721750f * b
val z = 0.0193339f * r + 0.1191920f * g + 0.9503041f * b
return CIEXYZ(x, y, z, a)
}
fun CIEXYZ.toRGB(): Color {
val r = 3.2404542f * x + -1.5371385f * y + -0.4985314f * z
val g = -0.9692660f * x + 1.8760108f * y + 0.0415560f * z
val b = 0.0556434f * x + -0.2040259f * y + 1.0572252f * z
return Color(r, g, b, alpha)
}
fun CIEXYZ.toLab(): CIELab {
val x = pivotXYZ(x / whitePoint.x)
val y = pivotXYZ(y / whitePoint.y)
val z = pivotXYZ(z / whitePoint.z)
val L = Math.max(0f, 116 * y - 16)
val a = 500 * (x - y)
val b = 200 * (y - z)
return CIELab(L, a, b, alpha)
}
fun CIELab.toXYZ(): CIEXYZ {
val y = L.plus(16).div(116f)
val x = a / 500f + y
val z = y - b / 200f
val x3 = x.cube()
val z3 = z.cube()
return CIEXYZ(
whitePoint.x * if (x3 > epsilon) x3 else (x - 16f / 116f) / 7.787f,
whitePoint.y * if (L > kappa * epsilon) (L.plus(16f) / 116f).cube() else L / kappa,
whitePoint.z * if (z3 > epsilon) z3 else (z - 16f / 116f) / 7.787f,
alpha
)
}
private fun pivotXYZ(n: Float) = if (n > epsilon) n.cbrt() else (kappa * n + 16f) / 116f
val epsilon = 0.008856f
val kappa = 903.3f
val whitePoint = CIEXYZ(95.047f, 100f, 108.883f)
private fun Float.cbrt() = FastMath.pow(this, 1f / 3f)
private fun Float.cube() = this * this * this
}
data class CIEXYZ(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f, val alpha: Float = 1f)
data class CIELab(var L: Float = 0f, var a: Float = 0f, var b: Float = 0f, val alpha: Float = 1f)

View File

@@ -0,0 +1,65 @@
package net.torvald.colourutil
import com.jme3.math.FastMath
import net.torvald.colourutil.CIELabUtil.toLab
import net.torvald.colourutil.CIELabUtil.toRGB
import net.torvald.colourutil.CIELabUtil.toXYZ
import org.newdawn.slick.Color
/**
* RGB in this code is always sRGB.
* reference: http://www.brucelindbloom.com/index.html?Equations.html
*
* Created by minjaesong on 16-09-01.
*/
object CIELchUtil {
/** Sweet Lch linear gradient */
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLch()
val to = toCol.toLch()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newC = FastMath.interpolateLinear(scale, from.c, to.c)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
val newH: Float
if ((from.h - to.h).abs() == FastMath.PI) // exact opposite colour
return CIELabUtil.getGradient(scale, fromCol, toCol)
else if ((from.h - to.h).abs() > FastMath.PI) // reflex angle
newH = FastMath.interpolateLinear(scale, from.h, to.h + FastMath.TWO_PI)
else
newH = FastMath.interpolateLinear(scale, from.h, to.h)
return CIELch(newL, newC, newH, newAlpha).toRGB()
}
fun CIELab.toLch(): CIELch {
val c = (a.sqr() + b.sqr()).sqrt()
val h = FastMath.atan2(b, a)
return CIELch(L, c, h, alpha)
}
fun CIELch.toLab(): CIELab {
val a = c * FastMath.cos(h)
val b = c * FastMath.sin(h)
return CIELab(L, a, b, alpha)
}
private fun Color.toLch() = this.toXYZ().toLab().toLch()
private fun CIELch.toRGB() = this.toLab().toXYZ().toRGB()
private fun Float.sqr() = this * this
private fun Float.sqrt() = Math.sqrt(this.toDouble()).toFloat()
private fun Float.abs() = FastMath.abs(this)
}
/**
* @param L : Luminosity in 0.0 - 1.0
* @param c : Chroma (saturation) in 0.0 - 1.0
* @param h : Hue in radian (-pi to pi)
*/
data class CIELch(var L: Float = 0f, var c: Float = 0f, var h: Float = 0f, var alpha: Float = 1f)

View File

@@ -1,5 +1,6 @@
package net.torvald.colourutil
import com.jme3.math.FastMath
import org.newdawn.slick.Color
/**
@@ -7,4 +8,16 @@ import org.newdawn.slick.Color
*/
object ColourUtil {
fun toSlickColor(r: Int, g: Int, b: Int) = Color(r.shl(16) or g.shl(8) or b)
/**
* Use CIELabUtil.getGradient for natural-looking colour
*/
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val r = FastMath.interpolateLinear(scale, fromCol.r, toCol.r)
val g = FastMath.interpolateLinear(scale, fromCol.g, toCol.g)
val b = FastMath.interpolateLinear(scale, fromCol.b, toCol.b)
val a = FastMath.interpolateLinear(scale, fromCol.a, toCol.a)
return Color(r, g, b, a)
}
}

View File

@@ -1,15 +0,0 @@
package net.torvald.colourutil
/**
* Created by minjaesong on 16-03-10.
*/
/**
* @param h : Hue 0-359
* @param s : Saturation 0-1
* @param v : Value (brightness in Adobe Photoshop(TM)) 0-1
*/
data class HSV(
var h: Float,
var s: Float,
var v: Float
)

View File

@@ -4,6 +4,8 @@ import com.jme3.math.FastMath
import org.newdawn.slick.Color
/**
* OBSOLETE; use CIELchUtil for natural-looking colour
*
* Created by minjaesong on 16-01-16.
*/
object HSVUtil {
@@ -20,7 +22,7 @@ object HSVUtil {
* *
* @link http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
*/
fun toRGB(H: Float, S: Float, V: Float): Color {
fun toRGB(H: Float, S: Float, V: Float, alpha: Float = 1f): Color {
var H = H
H %= 360f
@@ -64,12 +66,11 @@ object HSVUtil {
B_prime = X
}
return Color(
R_prime + m, G_prime + m, B_prime + m)
return Color(R_prime + m, G_prime + m, B_prime + m, alpha)
}
fun toRGB(hsv: HSV): Color {
return toRGB(hsv.h, hsv.s, hsv.v)
return toRGB(hsv.h * 360, hsv.s, hsv.v, hsv.alpha)
}
fun fromRGB(color: Color): HSV {
@@ -104,7 +105,14 @@ object HSVUtil {
h *= 60f
if (h < 0) h += 360f
return HSV(h, s, v)
return HSV(h.div(360f), s, v, color.a)
}
}
/**
* @param h : Hue in 0.0 - 1.0 (360 deg)
* @param s : Saturation in 0.0 - 1.0
* @param v : Value in 0.0 - 1.0
*/
data class HSV(var h: Float = 0f, var s: Float = 0f, var v: Float = 0f, var alpha: Float = 1f)