[orx-compositor] Add explicit contract checks for lambdas with callsInPlace in layer-related functions

This commit is contained in:
Edwin Jakobs
2025-08-20 21:12:50 +02:00
parent ad31a9ab02
commit af64323229

View File

@@ -8,6 +8,9 @@ import org.openrndr.extra.fx.blend.SourceIn
import org.openrndr.extra.fx.blend.SourceOut import org.openrndr.extra.fx.blend.SourceOut
import org.openrndr.extra.parameters.BooleanParameter import org.openrndr.extra.parameters.BooleanParameter
import org.openrndr.extra.parameters.Description import org.openrndr.extra.parameters.Description
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmRecord import kotlin.jvm.JvmRecord
@@ -200,24 +203,50 @@ open class Layer internal constructor(
} }
/** /**
* create a layer within the composition * Creates a new layer within the current layer, allowing for hierarchical composition of drawings.
* The newly created layer inherits properties such as color type and multisample from the parent layer
* unless explicitly overridden. A custom lambda function can be applied to configure the new layer.
*
* @param colorType The color type for the new layer. Defaults to the color type of the parent layer.
* @param multisample Specifies the multisampling mode for the new layer to control antialiasing.
* Defaults to [BufferMultisample.Disabled].
* @param function A configuration block where the properties and behavior of the new layer
* can be defined.
* @return The newly created layer, which is also added as a child of the parent layer.
*/ */
@OptIn(ExperimentalContracts::class)
fun Layer.layer( fun Layer.layer(
colorType: ColorType = this.colorType, colorType: ColorType = this.colorType,
multisample: BufferMultisample = BufferMultisample.Disabled, multisample: BufferMultisample = BufferMultisample.Disabled,
function: Layer.() -> Unit function: Layer.() -> Unit
): Layer { ): Layer {
contract {
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
}
val layer = Layer(LayerType.LAYER, multisample).apply { function() } val layer = Layer(LayerType.LAYER, multisample).apply { function() }
layer.colorType = colorType layer.colorType = colorType
children.add(layer) children.add(layer)
return layer return layer
} }
/**
* Creates a new `Layer` of type `ASIDE` as a child of the current layer, applies the specified function
* to configure it, and returns the created layer.
*
* @param colorType The color type for the new layer. Defaults to the color type of the parent layer.
* @param multisample Multisampling configuration for the new layer. Defaults to `BufferMultisample.Disabled`.
* @param function Configuration function applied to the newly created layer.
* @return The newly created `Layer` of type `ASIDE`.
*/
@OptIn(ExperimentalContracts::class)
fun Layer.aside( fun Layer.aside(
colorType: ColorType = this.colorType, colorType: ColorType = this.colorType,
multisample: BufferMultisample = BufferMultisample.Disabled, multisample: BufferMultisample = BufferMultisample.Disabled,
function: Layer.() -> Unit function: Layer.() -> Unit
): Layer { ): Layer {
contract {
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
}
val layer = Layer(LayerType.ASIDE, multisample).apply { function() } val layer = Layer(LayerType.ASIDE, multisample).apply { function() }
layer.colorType = colorType layer.colorType = colorType
children.add(layer) children.add(layer)
@@ -348,9 +377,17 @@ class Composite(val session: Session?) : Layer(LayerType.LAYER), AutoCloseable {
} }
/** /**
* create a layered composition * Creates a `Composite` object and allows configuration of its layers and effects within the provided `function`.
*
* @param function the lambda function used to configure the `Composite`. It is invoked with the `Composite` instance as the receiver.
* @return the configured `Composite` object.
*/ */
@OptIn(ExperimentalContracts::class)
fun compose(function: Composite.() -> Unit): Composite { fun compose(function: Composite.() -> Unit): Composite {
contract {
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
}
val session = Session.active.fork() val session = Session.active.fork()
val root = Composite(session) val root = Composite(session)
root.function() root.function()