From 499bf3d1c0a6638ec8e958d7d1f0b92834d3cb45 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Fri, 7 Mar 2025 16:20:17 +0100 Subject: [PATCH] [orx-shade-styles] Add luma gradient --- .../kotlin/fills/gradients/GradientBase.kt | 7 ++ .../kotlin/fills/gradients/GradientBuilder.kt | 31 ++++++++ .../kotlin/fills/gradients/LumaGradient.kt | 79 +++++++++++++++++++ .../kotlin/gradients/DemoGradient09.kt | 35 ++++++++ 4 files changed, 152 insertions(+) create mode 100644 orx-shade-styles/src/commonMain/kotlin/fills/gradients/LumaGradient.kt create mode 100644 orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient09.kt diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt index 6a1a6f18..9292d7a7 100644 --- a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt +++ b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt @@ -34,6 +34,7 @@ open class GradientBase( var spreadMethod: Int by Parameter() var fillUnits: Int by Parameter() var fillFit: Int by Parameter() + var resetFill: Boolean by Parameter() init { this.quantization = 0 @@ -42,7 +43,9 @@ open class GradientBase( this.fillUnits = FillUnits.BOUNDS.ordinal this.spreadMethod = SpreadMethod.PAD.ordinal this.fillFit = FillFit.STRETCH.ordinal + this.resetFill = false fragmentPreamble = """ + |vec4 g_fill; |vec2 rotate2D(vec2 x, float angle){ | float rad = angle / 180.0 * $PI; | mat2 m = mat2(cos(rad),-sin(rad), sin(rad),cos(rad)); @@ -56,6 +59,7 @@ open class GradientBase( |""".trimMargin() fragmentTransform = """ + g_fill = x_fill; vec2 coord = vec2(0.0); if (p_fillUnits == 0) { // BOUNDS @@ -140,6 +144,9 @@ open class GradientBase( if (gradient.a > 0.0) { gradient.rgb /= gradient.a; } + if (p_resetFill) { + x_fill.rgb = vec3(1.0); + } x_fill *= gradient; """ } diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBuilder.kt b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBuilder.kt index e198e212..d9a794ba 100644 --- a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBuilder.kt +++ b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBuilder.kt @@ -31,6 +31,18 @@ class GradientBuilder(val colorType: KClass): StyleParameters var gradientFunction = """float gradientFunction(vec2 coord) { return 0.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) { style.parameterTypes.putAll(parameterTypes) style.parameterValues.putAll(parameterValues) @@ -38,6 +50,7 @@ class GradientBuilder(val colorType: KClass): StyleParameters style.spreadMethod = spreadMethod.ordinal style.fillUnits = fillUnits.ordinal style.fillFit = fillFit.ordinal + style.resetFill = resetFill } @PublishedApi @@ -67,11 +80,29 @@ class GradientBuilder(val colorType: KClass): StyleParameters 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.() -> Unit) { shadeStyleBuilder = EllipticalGradientBuilder(this).apply { builder() } 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.() -> Unit) { + shadeStyleBuilder = LumaGradientBuilder(this).apply { builder() } + gradientFunction = LumaGradient.gradientFunction + resetFill = true + } + /** * Configures a conic gradient by applying the provided builder block. * diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LumaGradient.kt b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LumaGradient.kt new file mode 100644 index 00000000..201beb98 --- /dev/null +++ b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LumaGradient.kt @@ -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( + colorType: KClass, + minLevel: Double, + maxLevel: Double, + colors: Array, + points: Array = Array(colors.size) { it / (colors.size - 1.0) }, + structure: GradientBaseStructure + +) : GradientBase( + colorType, + colors, + points, + structure +) + where C : ConvertibleToColorRGBa, C : AlgebraicColor, 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(private val gradientBuilder: GradientBuilder) : GradientShadeStyleBuilder + where C : ConvertibleToColorRGBa, C : AlgebraicColor, 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 { + val (stops, colors) = gradientBuilder.extractStepsUnzip() + return LumaGradient( + gradientBuilder.colorType, + minLevel, + maxLevel, + colors, + stops, + gradientBuilder.structure() + ) + } +} diff --git a/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient09.kt b/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient09.kt new file mode 100644 index 00000000..24770af9 --- /dev/null +++ b/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient09.kt @@ -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 { + 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)) + } + } +} \ No newline at end of file