[orx-shade-styles] Add luma gradient

This commit is contained in:
Edwin Jakobs
2025-03-07 16:20:17 +01:00
parent a0c471f817
commit 499bf3d1c0
4 changed files with 152 additions and 0 deletions

View File

@@ -34,6 +34,7 @@ open class GradientBase<C>(
var spreadMethod: Int by Parameter() var spreadMethod: Int by Parameter()
var fillUnits: Int by Parameter() var fillUnits: Int by Parameter()
var fillFit: Int by Parameter() var fillFit: Int by Parameter()
var resetFill: Boolean by Parameter()
init { init {
this.quantization = 0 this.quantization = 0
@@ -42,7 +43,9 @@ open class GradientBase<C>(
this.fillUnits = FillUnits.BOUNDS.ordinal this.fillUnits = FillUnits.BOUNDS.ordinal
this.spreadMethod = SpreadMethod.PAD.ordinal this.spreadMethod = SpreadMethod.PAD.ordinal
this.fillFit = FillFit.STRETCH.ordinal this.fillFit = FillFit.STRETCH.ordinal
this.resetFill = false
fragmentPreamble = """ fragmentPreamble = """
|vec4 g_fill;
|vec2 rotate2D(vec2 x, float angle){ |vec2 rotate2D(vec2 x, float angle){
| float rad = angle / 180.0 * $PI; | float rad = angle / 180.0 * $PI;
| mat2 m = mat2(cos(rad),-sin(rad), sin(rad),cos(rad)); | mat2 m = mat2(cos(rad),-sin(rad), sin(rad),cos(rad));
@@ -56,6 +59,7 @@ open class GradientBase<C>(
|""".trimMargin() |""".trimMargin()
fragmentTransform = """ fragmentTransform = """
g_fill = x_fill;
vec2 coord = vec2(0.0); vec2 coord = vec2(0.0);
if (p_fillUnits == 0) { // BOUNDS if (p_fillUnits == 0) { // BOUNDS
@@ -140,6 +144,9 @@ open class GradientBase<C>(
if (gradient.a > 0.0) { if (gradient.a > 0.0) {
gradient.rgb /= gradient.a; gradient.rgb /= gradient.a;
} }
if (p_resetFill) {
x_fill.rgb = vec3(1.0);
}
x_fill *= gradient; x_fill *= gradient;
""" """
} }

View File

@@ -31,6 +31,18 @@ class GradientBuilder<C>(val colorType: KClass<C>): StyleParameters
var gradientFunction = """float gradientFunction(vec2 coord) { return 0.0; }""" var gradientFunction = """float gradientFunction(vec2 coord) { return 0.0; }"""
var quantization = 0 var quantization = 0
/**
* Specifies whether to reset the fill state when building a gradient.
*
* If set to `true`, it ensures that the fill color is overridden with
* the defined gradient fill during the rendering process. When set to `false`,
* the previous fill state persists, allowing the gradient to blend with
* other graphical elements.
*
* The default value is `false`
*/
var resetFill = false
private fun setBaseParameters(style: GradientBase<C>) { private fun setBaseParameters(style: GradientBase<C>) {
style.parameterTypes.putAll(parameterTypes) style.parameterTypes.putAll(parameterTypes)
style.parameterValues.putAll(parameterValues) style.parameterValues.putAll(parameterValues)
@@ -38,6 +50,7 @@ class GradientBuilder<C>(val colorType: KClass<C>): StyleParameters
style.spreadMethod = spreadMethod.ordinal style.spreadMethod = spreadMethod.ordinal
style.fillUnits = fillUnits.ordinal style.fillUnits = fillUnits.ordinal
style.fillFit = fillFit.ordinal style.fillFit = fillFit.ordinal
style.resetFill = resetFill
} }
@PublishedApi @PublishedApi
@@ -67,11 +80,29 @@ class GradientBuilder<C>(val colorType: KClass<C>): StyleParameters
gradientFunction = RadialGradient.gradientFunction gradientFunction = RadialGradient.gradientFunction
} }
/**
* Configures an elliptical gradient by applying the provided builder block.
*
* @param builder A lambda function used to define the properties of the elliptical gradient.
*/
fun elliptic(builder: EllipticalGradientBuilder<C>.() -> Unit) { fun elliptic(builder: EllipticalGradientBuilder<C>.() -> Unit) {
shadeStyleBuilder = EllipticalGradientBuilder(this).apply { builder() } shadeStyleBuilder = EllipticalGradientBuilder(this).apply { builder() }
gradientFunction = EllipticalGradient.gradientFunction gradientFunction = EllipticalGradient.gradientFunction
} }
/**
* Configures a luma gradient by applying the provided builder block.
*
* @param builder A lambda function used to define the properties of the luma gradient.
* The builder block allows customization of attributes such as luminance-based
* transformations, including specifying minimum and maximum luminance levels.
*/
fun luma(builder: LumaGradientBuilder<C>.() -> Unit) {
shadeStyleBuilder = LumaGradientBuilder(this).apply { builder() }
gradientFunction = LumaGradient.gradientFunction
resetFill = true
}
/** /**
* Configures a conic gradient by applying the provided builder block. * Configures a conic gradient by applying the provided builder block.
* *

View File

@@ -0,0 +1,79 @@
package org.openrndr.extra.shadestyles.fills.gradients
import org.openrndr.color.AlgebraicColor
import org.openrndr.color.ConvertibleToColorRGBa
import org.openrndr.math.CastableToVector4
import org.openrndr.math.Vector4
import kotlin.reflect.KClass
open class LumaGradient<C>(
colorType: KClass<C>,
minLevel: Double,
maxLevel: Double,
colors: Array<Vector4>,
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
structure: GradientBaseStructure
) : GradientBase<C>(
colorType,
colors,
points,
structure
)
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C : CastableToVector4 {
var minLevel: Double by Parameter()
var maxLevel: Double by Parameter()
init {
this.minLevel = minLevel
this.maxLevel = maxLevel
}
companion object {
val gradientFunction = """
float gradientFunction(vec2 coord) {
float f = 0.2126 * g_fill.r + 0.7152 * g_fill.g + 0.0722 * g_fill.b;
f = (f - p_minLevel) / (p_maxLevel - p_minLevel);
return f;
}
""".trimIndent()
}
}
class LumaGradientBuilder<C>(private val gradientBuilder: GradientBuilder<C>) : GradientShadeStyleBuilder<C>
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C : CastableToVector4 {
/**
* Specifies the minimum luminance level for manipulating the gradient's color representation.
* This value determines the lower bound of the luminance range applied to the gradient transformation.
* It is used in combination with `maxLevel` to define the range of luminance levels that
* affect the final gradient appearance.
*
* The default value is `0.0`, which represents the minimum possible luminance level.
*/
var minLevel = 0.0
/**
* Specifies the maximum luminance level for manipulating the gradient's color representation.
* This value determines the upper bound of the luminance range applied to the gradient transformation.
* It is used in combination with `minLevel` to define the range of luminance levels that affect
* the final gradient appearance.
*
* The default value is `1.0`, which represents the maximum possible luminance level.
*/
var maxLevel = 1.0
override fun build(): GradientBase<C> {
val (stops, colors) = gradientBuilder.extractStepsUnzip()
return LumaGradient(
gradientBuilder.colorType,
minLevel,
maxLevel,
colors,
stops,
gradientBuilder.structure()
)
}
}

View File

@@ -0,0 +1,35 @@
package gradients
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.loadImage
import org.openrndr.extra.color.presets.CRIMSON
import org.openrndr.extra.color.presets.DODGER_BLUE
import org.openrndr.extra.color.presets.LIME_GREEN
import org.openrndr.extra.imageFit.imageFit
import org.openrndr.extra.shadestyles.fills.SpreadMethod
import org.openrndr.extra.shadestyles.fills.gradients.gradient
fun main() = application {
configure {
width = 720
height = 720
}
program {
val image = loadImage("demo-data/images/image-001.png")
extend {
drawer.shadeStyle = gradient<ColorRGBa> {
stops[0.0] = ColorRGBa.CRIMSON
stops[0.7] = ColorRGBa.DODGER_BLUE
stops[1.0] = ColorRGBa.LIME_GREEN
spreadMethod = SpreadMethod.REFLECT
luma {
minLevel = 0.1
maxLevel = 0.9
}
}
drawer.imageFit(image, drawer.bounds.offsetEdges(-10.0))
}
}
}