[orx-shade-styles] Add OKLab color space support to gradients
This commit is contained in:
@@ -9,7 +9,8 @@ sourceSets {
|
||||
}
|
||||
dependencies {
|
||||
api project(":orx-parameters")
|
||||
|
||||
implementation project(":orx-color")
|
||||
implementation project(":orx-shader-phrases")
|
||||
demoImplementation("org.openrndr:openrndr-core:$openrndrVersion")
|
||||
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
|
||||
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
|
||||
|
||||
34
orx-shade-styles/src/demo/kotlin/DemoLinearGradient.kt
Normal file
34
orx-shade-styles/src/demo/kotlin/DemoLinearGradient.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extensions.SingleScreenshot
|
||||
import org.openrndr.extra.shadestyles.linearGradient
|
||||
|
||||
import org.openrndr.extra.shadestyles.radialGradient
|
||||
import org.openrndr.extras.color.spaces.toOKLABa
|
||||
import kotlin.math.cos
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
program {
|
||||
if (System.getProperty("takeScreenshot") == "true") {
|
||||
extend(SingleScreenshot()) {
|
||||
this.outputFile = System.getProperty("screenshotPath")
|
||||
}
|
||||
}
|
||||
extend {
|
||||
drawer.shadeStyle = linearGradient(
|
||||
ColorRGBa.RED.toOKLABa(),
|
||||
ColorRGBa.BLUE.toOKLABa(),
|
||||
)
|
||||
drawer.rectangle(120.0, 40.0, 200.0, 400.0)
|
||||
|
||||
drawer.shadeStyle = linearGradient(
|
||||
ColorRGBa.RED,
|
||||
ColorRGBa.BLUE
|
||||
)
|
||||
|
||||
drawer.rectangle(120.0+200.0, 40.0, 200.0, 400.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
orx-shade-styles/src/main/kotlin/ColorspaceHelper.kt
Normal file
13
orx-shade-styles/src/main/kotlin/ColorspaceHelper.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal fun generateColorTransform(kClass: KClass<*>): String {
|
||||
return when (kClass) {
|
||||
ColorRGBa::class -> """"""
|
||||
ColorOKLABa::class -> """gradient = linear_rgb_to_srgb(oklab_to_linear_rgb(gradient));"""
|
||||
else -> error("color space not supported $kClass")
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,32 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.*
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.extra.shaderphrases.preprocess
|
||||
import org.openrndr.extras.color.phrases.ColorPhraseBook
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Description("Linear gradient")
|
||||
class LinearGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0) : ShadeStyle() {
|
||||
open class LinearGradientBase<C>(
|
||||
color0: C,
|
||||
color1: C,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
) : ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
|
||||
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0: ColorRGBa by Parameter()
|
||||
var color0: C by Parameter()
|
||||
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1: ColorRGBa by Parameter()
|
||||
var color1: C by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
|
||||
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
|
||||
@@ -29,15 +36,19 @@ class LinearGradient(
|
||||
var exponent: Double by Parameter()
|
||||
|
||||
init {
|
||||
ColorPhraseBook.register()
|
||||
this.color0 = color0
|
||||
this.color1 = color1
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.exponent = exponent
|
||||
|
||||
fragmentPreamble = """
|
||||
|#pragma import color.oklab_to_linear_rgb
|
||||
|#pragma import color.linear_rgb_to_srgb""".trimMargin().preprocess()
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset);
|
||||
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
@@ -45,29 +56,48 @@ class LinearGradient(
|
||||
float f = clamp(rc.y + 0.5, 0.0, 1.0);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_color1;
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
${generateColorTransform(color0::class)}
|
||||
x_fill *= gradient;
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class LinearGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientBase<ColorRGBa>(color0, color1, offset, rotation, exponent)
|
||||
|
||||
class LinearGradientOKLab(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientBase<ColorOKLABa>(color0, color1, offset, rotation, exponent)
|
||||
|
||||
|
||||
fun linearGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): ShadeStyle {
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradient {
|
||||
return LinearGradient(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
|
||||
fun linearGradient(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientOKLab {
|
||||
return LinearGradientOKLab(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
|
||||
@Description("N-Point gradient")
|
||||
class NPointGradient(
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.AlgebraicColor
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.ConvertibleToColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Multicolor linear gradient")
|
||||
class NPointLinearGradient(
|
||||
colors: Array<ColorRGBa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0) : ShadeStyle() {
|
||||
open class NPointLinearGradientBase<C>(
|
||||
colors: Array<C>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C : CastableToVector4 {
|
||||
|
||||
var colors: Array<C> by Parameter()
|
||||
|
||||
var colors: Array<ColorRGBa> by Parameter()
|
||||
// Sorted normalized values defining relative positions of colors
|
||||
var points: Array<Double> by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
@@ -37,17 +44,14 @@ class NPointLinearGradient(
|
||||
while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; }
|
||||
|
||||
vec4 color0 = p_colors[i];
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_colors[i+1];
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]);
|
||||
vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
${generateColorTransform(colors[0]::class)}
|
||||
|
||||
x_fill *= gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
@@ -55,3 +59,19 @@ class NPointLinearGradient(
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class NPointLinearGradient(
|
||||
colors: Array<ColorRGBa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : NPointLinearGradientBase<ColorRGBa>(colors, points, offset, rotation)
|
||||
|
||||
class NPointLinearGradientOKLab(
|
||||
colors: Array<ColorOKLABa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : NPointLinearGradientBase<ColorOKLABa>(colors, points, offset, rotation)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@ class NPointRadialGradient(
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0) : ShadeStyle() {
|
||||
length: Double = 1.0) : ShadeStyle()
|
||||
|
||||
|
||||
{
|
||||
|
||||
var colors: Array<ColorRGBa> by Parameter()
|
||||
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.*
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.draw.shadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Radial gradient")
|
||||
class RadialGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
open class RadialGradientBase<C>(
|
||||
color0: C,
|
||||
color1: C,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0) : ShadeStyle() {
|
||||
|
||||
exponent: Double = 1.0)
|
||||
: ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0 : ColorRGBa by Parameter()
|
||||
var color0 : C by Parameter()
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1 : ColorRGBa by Parameter()
|
||||
var color1 : C by Parameter()
|
||||
var offset : Vector2 by Parameter()
|
||||
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
|
||||
var rotation : Double by Parameter()
|
||||
@@ -47,23 +50,34 @@ class RadialGradient(
|
||||
float f = clamp(p_length * length(rc), 0.0, 1.0);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_color1;
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
${generateColorTransform(color0::class)}
|
||||
|
||||
x_fill *= gradient;
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class RadialGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientBase<ColorRGBa>(color0, color1, offset, rotation, length, exponent)
|
||||
|
||||
class RadialGradientOKLab(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientBase<ColorOKLABa>(color0, color1, offset, rotation, length, exponent)
|
||||
|
||||
fun radialGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
@@ -71,6 +85,19 @@ fun radialGradient(
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): ShadeStyle {
|
||||
): RadialGradient {
|
||||
return RadialGradient(color0, color1, offset, rotation, length, exponent)
|
||||
}
|
||||
|
||||
fun radialGradient(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientOKLab {
|
||||
return RadialGradientOKLab(color0, color1, offset, rotation, length, exponent)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
|
||||
newSymbols
|
||||
)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
println(source)
|
||||
error("field \"$fieldName\" not found in \"#pragma import $packageClass.$fieldName\" on line ${index + 1}")
|
||||
}
|
||||
}
|
||||
@@ -137,6 +138,7 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
|
||||
throw IllegalArgumentException("class $packageClass has no ShaderPhrases annotation")
|
||||
}
|
||||
} catch (e: ClassNotFoundException) {
|
||||
println(source)
|
||||
error("class \"$packageClass\" not found in \"#pragma import $packageClass\" on line ${index + 1}")
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user