Add orx-poisson-fill
This commit is contained in:
122
orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt
Normal file
122
orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt
Normal file
@@ -0,0 +1,122 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.filter.blend.passthrough
|
||||
import org.openrndr.math.IntVector2
|
||||
import org.openrndr.resourceUrl
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.min
|
||||
|
||||
internal class Downscale(filterUrl: String = "/shaders/gl3/poisson/downscale.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var h1: FloatArray by parameters
|
||||
init {
|
||||
h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Upscale(filterUrl: String = "/shaders/gl3/poisson/upscale.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var h1: FloatArray by parameters
|
||||
var h2: Float by parameters
|
||||
var g: FloatArray by parameters
|
||||
|
||||
init {
|
||||
h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
h2 = 0.0f
|
||||
g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Convolution(filterUrl: String = "/shaders/gl3/poisson/filter.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var g: FloatArray by parameters
|
||||
|
||||
init {
|
||||
g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConvolutionPyramid(width: Int, height: Int,
|
||||
private val padding: Int = 0, cutOff: Int = 10000,
|
||||
private val downscale: Downscale = Downscale(),
|
||||
private val upscale: Upscale = Upscale(),
|
||||
private val filter: Convolution = Convolution(),
|
||||
val type: ColorType = ColorType.FLOAT32) {
|
||||
var h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
var h2 = 0.0f
|
||||
var g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
|
||||
private val size = 5
|
||||
private val resolution = IntVector2(width + 2 * padding, height + 2 * padding)
|
||||
private val minResolution = min(resolution.x, resolution.y)
|
||||
private val depth = min(cutOff, ceil(log2(minResolution.toDouble())).toInt())
|
||||
|
||||
private val levelsIn = mutableListOf<RenderTarget>()
|
||||
private val levelsOut = mutableListOf<RenderTarget>()
|
||||
|
||||
private val result = colorBuffer(width, height, type = type)
|
||||
|
||||
init {
|
||||
var levelWidth = resolution.x + 2 * size
|
||||
var levelHeight = resolution.y + 2 * size
|
||||
|
||||
for (i in 0 until depth) {
|
||||
levelsIn.add(renderTarget(levelWidth, levelHeight) {
|
||||
colorBuffer(type = type)
|
||||
})
|
||||
|
||||
levelsOut.add(renderTarget(levelWidth, levelHeight) {
|
||||
colorBuffer(type = type)
|
||||
})
|
||||
|
||||
levelWidth /= 2
|
||||
levelHeight /= 2
|
||||
levelWidth += 2 * size
|
||||
levelHeight += 2 * size
|
||||
}
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
for (l in levelsIn) {
|
||||
l.clearColor(0, ColorRGBa.TRANSPARENT)
|
||||
}
|
||||
|
||||
for (l in levelsOut) {
|
||||
l.clearColor(0, ColorRGBa.TRANSPARENT)
|
||||
}
|
||||
|
||||
downscale.h1 = h1
|
||||
|
||||
upscale.g = g
|
||||
upscale.h1 = h1
|
||||
upscale.h2 = h2
|
||||
|
||||
filter.g = g
|
||||
|
||||
passthrough.padding = (levelsIn[0].width - input.width) / 2
|
||||
passthrough.apply(input, levelsIn[0].colorBuffer(0))
|
||||
passthrough.padding = 0
|
||||
|
||||
for (i in 1 until levelsIn.size) {
|
||||
downscale.padding = 0
|
||||
downscale.apply(levelsIn[i - 1].colorBuffer(0),
|
||||
levelsIn[i].colorBuffer(0)
|
||||
)
|
||||
}
|
||||
|
||||
filter.apply(levelsIn.last().colorBuffer(0), levelsOut.last().colorBuffer(0))
|
||||
|
||||
for (i in levelsOut.size - 2 downTo 0) {
|
||||
upscale.padding = 0
|
||||
upscale.apply(arrayOf(levelsIn[i].colorBuffer(0), levelsOut[i + 1].colorBuffer(0)), arrayOf(levelsOut[i].colorBuffer(0)))
|
||||
}
|
||||
|
||||
passthrough.padding = -size - padding
|
||||
passthrough.apply(levelsOut[0].colorBuffer(0), result)
|
||||
passthrough.padding = 0
|
||||
return result
|
||||
}
|
||||
}
|
||||
46
orx-poisson-fill/src/main/kotlin/LaplacianIntegrator.kt
Normal file
46
orx-poisson-fill/src/main/kotlin/LaplacianIntegrator.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class PassthroughNoAlpha : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/passthrough-noalpha.frag")))
|
||||
|
||||
/**
|
||||
* Laplacian filter
|
||||
*/
|
||||
class Laplacian : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/laplacian.frag")))
|
||||
|
||||
class LaplacianIntegrator(width: Int, height: Int, downscaling: Int = 1, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(2 + width / downscaling, 2 + height / downscaling, type = type)
|
||||
private val h1 = floatArrayOf(0.15f, 0.5f, 0.7f, 0.5f, 0.15f)
|
||||
private val h2 = 1.0f
|
||||
private val g = floatArrayOf(0.175f, 0.547f, 0.175f)
|
||||
private val preproc = colorBuffer(width + 2, height + 2, type = type)
|
||||
private val combined = colorBuffer(width, height)
|
||||
private val passthrough = PassthroughNoAlpha()
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
preproc.fill(ColorRGBa.TRANSPARENT)
|
||||
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
|
||||
passthrough.padding = 1
|
||||
passthrough.apply(input, preproc)
|
||||
passthrough.padding = 0
|
||||
|
||||
val result = pyramid.process(preproc)
|
||||
passthrough.padding = -1
|
||||
passthrough.apply(result, combined)
|
||||
passthrough.padding = 0
|
||||
return combined
|
||||
}
|
||||
}
|
||||
55
orx-poisson-fill/src/main/kotlin/PoissonBlender.kt
Normal file
55
orx-poisson-fill/src/main/kotlin/PoissonBlender.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.filter.blend.subtract
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class BlendBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-boundary.frag")))
|
||||
class AlphaToBitmap : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/alpha-to-bitmap.frag")))
|
||||
|
||||
internal class BlendCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-combine.frag"))) {
|
||||
var softMaskGain: Double by parameters
|
||||
init {
|
||||
softMaskGain = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
internal class Clamp : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/clamp.frag"))) {
|
||||
var minValue: Double by parameters
|
||||
var maxValue: Double by parameters
|
||||
}
|
||||
|
||||
class PoissonBlender(width: Int, height: Int, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(width, height, 0, type = type)
|
||||
private val preprocess = colorBuffer(width, height, type = type)
|
||||
private val combined = colorBuffer(width, height, type = type)
|
||||
|
||||
private val fillBoundary = BlendBoundary()
|
||||
private val fillCombine = BlendCombine()
|
||||
|
||||
private val difference = colorBuffer(width, height, type = type)
|
||||
|
||||
private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f)
|
||||
private val h2 = 0.0269546f
|
||||
private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f)
|
||||
|
||||
private val clamp = Clamp()
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(target: ColorBuffer, source: ColorBuffer, mask: ColorBuffer,
|
||||
softMask: ColorBuffer = mask, softMaskGain: Double = 1.0): ColorBuffer {
|
||||
subtract.apply(arrayOf(target, source), difference)
|
||||
clamp.minValue = -0.50
|
||||
clamp.maxValue = 0.50
|
||||
fillBoundary.apply(arrayOf(difference, mask), preprocess)
|
||||
val result = pyramid.process(preprocess)
|
||||
fillCombine.softMaskGain = softMaskGain
|
||||
fillCombine.apply(arrayOf(result, target, source, mask, softMask), arrayOf(combined))
|
||||
return combined
|
||||
}
|
||||
}
|
||||
32
orx-poisson-fill/src/main/kotlin/PoissonFiller.kt
Normal file
32
orx-poisson-fill/src/main/kotlin/PoissonFiller.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class FillBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-boundary.frag")))
|
||||
internal class FillCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-combine.frag")))
|
||||
|
||||
class PoissonFiller(width: Int, height: Int, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(width, height, 0, type = type)
|
||||
private val preproc = colorBuffer(width, height, type = type)
|
||||
private val combined = colorBuffer(width, height, type = type)
|
||||
private val fillBoundary = FillBoundary()
|
||||
private val fillCombine = FillCombine()
|
||||
|
||||
private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f)
|
||||
private val h2 = 0.0269546f
|
||||
private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f)
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
fillBoundary.apply(input, preproc)
|
||||
val result = pyramid.process(preproc)
|
||||
fillCombine.apply(arrayOf(result, input), arrayOf(combined))
|
||||
return combined
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user