Clean up orx-color

ColorOKHSLa#toRGBa result will now always be in SRGB linearity and will include the alpha value of the receiver.
ColorOKLABa#toRGBa result now includes the alpha value of the receiver
This commit is contained in:
vechro
2022-08-13 07:45:47 +03:00
committed by Edwin Jakobs
parent 23bca781ab
commit 2fdda2bf37
4 changed files with 101 additions and 100 deletions

View File

@@ -5,6 +5,7 @@ import org.openrndr.math.Vector4
import org.openrndr.math.mixAngle
import kotlin.math.*
@Suppress("LocalVariableName")
data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) :
ColorModel<ColorOKHSLa>,
HueShiftableColor<ColorOKHSLa>,
@@ -15,36 +16,37 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val
companion object {
fun fromColorRGBa(c: ColorRGBa): ColorOKHSLa {
val lab = c.toOKLABa()
val C = sqrt(lab.a * lab.a + lab.b * lab.b);
val a_ = lab.a / C;
val b_ = lab.b / C;
val C = sqrt(lab.a * lab.a + lab.b * lab.b)
val a_ = lab.a / C
val b_ = lab.b / C
val L = lab.l
val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI;
val cs = get_Cs(L, a_, b_)
val c0 = cs[0];
val cMid = cs[1];
val cMax = cs[2];
val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI
val (c0, cMid, cMax) = get_Cs(L, a_, b_)
val s = if (C < cMid) {
val k0 = 0;
val k1 = 0.8 * c0;
val k2 = (1 - k1 / cMid);
val k0 = 0
val k1 = 0.8 * c0
val k2 = (1 - k1 / cMid)
val t = (C - k0) / (k1 + k2 * (C - k0));
t * 0.8;
val t = (C - k0) / (k1 + k2 * (C - k0))
t * 0.8
} else {
val k0 = cMid;
val k1 = 0.2 * cMid * cMid * 1.25 * 1.25 / c0;
val k2 = (1 - (k1) / (cMax - cMid));
val k0 = cMid
val k1 = 0.2 * cMid * cMid * 1.25 * 1.25 / c0
val k2 = (1 - (k1) / (cMax - cMid))
val t = (C - k0) / (k1 + k2 * (C - k0));
0.8 + 0.2 * t;
val t = (C - k0) / (k1 + k2 * (C - k0))
0.8 + 0.2 * t
}
val l = toe(L);
return ColorOKHSLa(h * 360.0, if (s == s) s else 0.0, if (l == l) l else 0.0, c.a)
val l = toe(L)
return ColorOKHSLa(
h * 360.0,
if (s == s) s else 0.0,
if (l == l) l else 0.0,
c.alpha
)
}
}
@@ -52,52 +54,43 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val
val a = alpha
override fun toRGBa(): ColorRGBa {
if (l == 1.0) {
ColorRGBa(1.0, 1.0, 1.0, alpha)
} else if (l == 0.0) {
ColorRGBa(0.0, 0.0, 0.0, alpha)
if (l == 0.0 || l == 1.0) {
return ColorRGBa(l, l, l, alpha, Linearity.SRGB)
}
val a_ = cos(2 * PI * h / 360.0);
val b_ = sin(2 * PI * h / 360.0);
val L = toeInv(l);
val a_ = cos(2 * PI * h / 360.0)
val b_ = sin(2 * PI * h / 360.0)
val L = toeInv(l)
val Cs = get_Cs(L, a_, b_);
val C_0 = Cs[0];
val C_mid = Cs[1];
val C_max = Cs[2];
val Cs = get_Cs(L, a_, b_)
val C_0 = Cs[0]
val C_mid = Cs[1]
val C_max = Cs[2]
//let C, t, k_0, k_1, k_2;
val C: Double
val t: Double
val k_0: Double
val k_1: Double
val k_2: Double
if (s < 0.8) {
t = 1.25 * s;
k_0 = 0.0
k_1 = 0.8 * C_0;
k_2 = (1 - k_1 / C_mid);
val C = if (s < 0.8) {
val t = 1.25 * s
val k_0 = 0.0
val k_1 = 0.8 * C_0
val k_2 = (1 - k_1 / C_mid)
k_0 + t * k_1 / (1 - k_2 * t)
} else {
t = 5 * (s - 0.8);
k_0 = C_mid;
k_1 = 0.2 * C_mid * C_mid * 1.25 * 1.25 / C_0;
k_2 = (1 - (k_1) / (C_max - C_mid));
val t = 5 * (s - 0.8)
val k_0 = C_mid
val k_1 = 0.2 * C_mid * C_mid * 1.25 * 1.25 / C_0
val k_2 = (1 - (k_1) / (C_max - C_mid))
k_0 + t * k_1 / (1 - k_2 * t)
}
C = k_0 + t * k_1 / (1 - k_2 * t);
// If we would only use one of the Cs:
//C = s*C_0;
//C = s*1.25*C_mid;
//C = s*C_max;
// let rgb = oklab_to_linear_srgb(L, C*a_, C*b_);
// return [
// 255*srgb_transfer_function(rgb[0]),
// 255*srgb_transfer_function(rgb[1]),
// 255*srgb_transfer_function(rgb[2]),
// ]
return ColorOKLABa(if (L == L) L else 0.0, if (C == C) C * a_ else 0.0, if (C == C) C * b_ else 0.0).toRGBa().toSRGB()
return ColorOKLABa(
if (L == L) L else 0.0,
if (C == C) C * a_ else 0.0,
if (C == C) C * b_ else 0.0,
alpha
).toRGBa().toSRGB()
}
override fun shiftHue(shiftInDegrees: Double): ColorOKHSLa = copy(h = h + shiftInDegrees)
@@ -114,7 +107,8 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val
override fun plus(right: ColorOKHSLa) =
copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha)
override fun times(scale: Double): ColorOKHSLa = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale)
override fun times(scale: Double): ColorOKHSLa =
copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale)
override fun mix(other: ColorOKHSLa, factor: Double): ColorOKHSLa {
val sx = factor.coerceIn(0.0, 1.0)

View File

@@ -5,6 +5,7 @@ import org.openrndr.math.Vector4
import org.openrndr.math.mixAngle
import kotlin.math.*
@Suppress("LocalVariableName")
data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val alpha: Double = 1.0) :
ColorModel<ColorOKHSVa>,
HueShiftableColor<ColorOKHSVa>,
@@ -15,39 +16,39 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val
companion object {
fun fromColorRGBa(c: ColorRGBa): ColorOKHSVa {
val lab = c.toOKLABa()
var C = sqrt(lab.a * lab.a + lab.b * lab.b);
var C = sqrt(lab.a * lab.a + lab.b * lab.b)
val a_ = if (C != 0.0) lab.a / C else 0.0
val b_ = if (C != 0.0) lab.b / C else 0.0
var L = lab.l
val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI;
val h = 0.5 + 0.5 * atan2(-lab.b, -lab.a) / PI
val ST_max = get_ST_max(a_, b_);
val S_max = ST_max[0];
val S_0 = 0.5;
val T = ST_max[1];
val ST_max = get_ST_max(a_, b_)
val S_max = ST_max[0]
val S_0 = 0.5
val T = ST_max[1]
val k = if (S_max != 0.0) (1 - S_0 / S_max) else 0.0
val t = T / (C + L * T);
val L_v = t * L;
val C_v = t * C;
val t = T / (C + L * T)
val L_v = t * L
val C_v = t * C
val L_vt = toeInv(L_v);
val C_vt = C_v * L_vt / L_v;
val L_vt = toeInv(L_v)
val C_vt = C_v * L_vt / L_v
val rgb_scale = ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, c.a).toRGBa().toLinear()
val rgb_scale = ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, c.alpha).toRGBa().toLinear()
val scale_L = (1.0 / (max(rgb_scale.r, rgb_scale.g, rgb_scale.b, 0.0))).pow(1.0 / 3.0)
L = L / scale_L;
C = C / scale_L;
L /= scale_L
C /= scale_L
C = C * toe(L) / L;
L = toe(L);
C = C * toe(L) / L
L = toe(L)
val v = L / L_v;
val v = L / L_v
val s = (S_0 + T) * C_v / ((T * S_0) + T * k * C_v)
return ColorOKHSVa(h * 360.0, if (s == s) s else 0.0, if (v==v) v else 0.0, c.a)
return ColorOKHSVa(h * 360.0, if (s == s) s else 0.0, if (v == v) v else 0.0, c.alpha)
}
}
@@ -59,16 +60,16 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val
val b_ = sin(2 * PI * h / 360.0)
val ST_max = get_ST_max(a_, b_)
val S_max = ST_max[0];
val S_0 = 0.5;
val T = ST_max[1];
val k = 1 - S_0 / S_max;
val S_max = ST_max[0]
val S_0 = 0.5
val T = ST_max[1]
val k = 1 - S_0 / S_max
val L_v = 1 - s * S_0 / (S_0 + T - T * k * s)
val C_v = s * T * S_0 / (S_0 + T - T * k * s)
var L = v * L_v;
var C = v * C_v;
var L = v * L_v
var C = v * C_v
// to present steps along the way
//L = v;
@@ -76,25 +77,27 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val
//L = v*(1 - s*S_max/(S_max+T));
//C = v*s*S_max*T/(S_max+T);
val L_vt = toeInv(L_v);
val C_vt = C_v * L_vt / L_v;
val L_vt = toeInv(L_v)
val C_vt = C_v * L_vt / L_v
val L_new = toeInv(L); // * L_v/L_vt;
C = C * L_new / L;
L = L_new;
val L_new = toeInv(L) // * L_v/L_vt;
C = C * L_new / L
L = L_new
val rgb_scale =
ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, alpha).toRGBa().toLinear()// oklab_to_linear_srgb(L_vt,a_*C_vt,b_*C_vt);
val scale_L = (1.0 / (max(rgb_scale.r, rgb_scale.g, rgb_scale.b, 0.0))).pow(1.0 / 3.0)
// remove to see effect without rescaling
L *= scale_L;
C *= scale_L;
L *= scale_L
C *= scale_L
return ColorOKLABa(
if (L == L) L else 0.0,
if (C == C) C * a_ else 0.0,
if (C == C) C * b_ else 0.0).toRGBa().toSRGB()
if (C == C) C * b_ else 0.0,
alpha
).toRGBa().toSRGB()
}
override fun shiftHue(shiftInDegrees: Double): ColorOKHSVa = copy(h = h + shiftInDegrees)

View File

@@ -10,6 +10,7 @@ import kotlin.math.pow
* [a] = red (-1.0) to green (1.0),
* [b] = yellow (-1.0) to blue (1.0).
*/
@Suppress("LocalVariableName")
data class ColorOKLABa(val l: Double, val a: Double, val b: Double, override val alpha: Double = 1.0) :
ColorModel<ColorOKLABa>,
ShadableColor<ColorOKLABa>,
@@ -19,19 +20,19 @@ data class ColorOKLABa(val l: Double, val a: Double, val b: Double, override val
fun fromRGBa(rgba: ColorRGBa): ColorOKLABa {
// based on https://bottosson.github.io/posts/oklab/
val c = rgba.toLinear()
val l = 0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929f * c.b
val m = 0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566f * c.b
val s = 0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005f * c.b
val l = 0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b
val m = 0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b
val s = 0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b
val lnl = l.pow(1.0 / 3.0)
val mnl = m.pow(1.0 / 3.0)
val snl = s.pow(1.0 / 3.0)
val L = 0.2104542553f * lnl + 0.7936177850f * mnl - 0.0040720468f * snl
val a = 1.9779984951f * lnl - 2.4285922050f * mnl + 0.4505937099f * snl
val b = 0.0259040371f * lnl + 0.7827717662f * mnl - 0.8086757660f * snl
val L = 0.2104542553 * lnl + 0.7936177850 * mnl - 0.0040720468 * snl
val a = 1.9779984951 * lnl - 2.4285922050 * mnl + 0.4505937099 * snl
val b = 0.0259040371 * lnl + 0.7827717662 * mnl - 0.8086757660 * snl
return ColorOKLABa(L, a, b, alpha = c.a)
return ColorOKLABa(L, a, b, c.alpha)
}
}
@@ -46,10 +47,11 @@ data class ColorOKLABa(val l: Double, val a: Double, val b: Double, override val
val s = snl * snl * snl
return ColorRGBa(
4.0767416621 * l - 3.3077115913 * m + 0.2309699292f * s,
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965f * s,
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010f * s,
alpha, linearity = Linearity.LINEAR
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
alpha,
Linearity.LINEAR
)
}

View File

@@ -1,3 +1,5 @@
@file:Suppress("FunctionName", "LocalVariableName")
package org.openrndr.extra.color.spaces