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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("FunctionName", "LocalVariableName")
|
||||
|
||||
package org.openrndr.extra.color.spaces
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user