[orx-poisson-fill] move to orx-jvm

This commit is contained in:
Edwin Jakobs
2021-06-29 14:28:44 +02:00
parent 0ead2a440d
commit 533166f678
20 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,139 @@
package org.openrndr.poissonfill
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extra.fx.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)
}
}
private val passthrough by lazy { Passthrough() }
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
}
fun destroy() {
result.destroy()
(levelsIn+levelsOut).forEach {
it.colorAttachments.forEach {
when(it) {
is ColorBufferAttachment -> it.colorBuffer.destroy()
is CubemapAttachment -> it.cubemap.destroy()
is ArrayTextureAttachment -> it.arrayTexture.destroy()
is ArrayCubemapAttachment -> it.arrayCubemap.destroy()
}
}
it.detachColorAttachments()
it.destroy()
}
}
}

View 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
}
}

View File

@@ -0,0 +1,117 @@
package org.openrndr.poissonfill
import org.openrndr.draw.*
import org.openrndr.extra.fx.blend.Passthrough
import org.openrndr.extra.fx.blend.Subtract
import org.openrndr.filter.color.delinearize
import org.openrndr.filter.color.linearize
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
}
private val passthrough by lazy { Passthrough() }
private val subtract by lazy { Subtract() }
class PoissonBlender(val width: Int, val 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)
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)
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
}
fun destroy() {
pyramid.destroy()
preprocess.destroy()
combined.destroy()
difference.destroy()
}
}
class PoissonBlend: Filter() {
private var blender: PoissonBlender? = null
val alphaToBitmap = AlphaToBitmap()
var mask: ColorBuffer? = null
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
if (target.isNotEmpty()) {
mask?.let {
if (it.width != target[0].width || it.height != target[0].height) {
it.destroy()
mask = null
}
}
if (mask == null) {
mask = colorBuffer(target[0].width, target[0].height)
}
blender?.let {
if (it.width != target[0].width || it.height != target[0].height) {
it.destroy()
blender = null
}
}
if (blender == null) {
blender = PoissonBlender(target[0].width, target[0].height)
}
mask?.let {
alphaToBitmap.apply(source[1], it)
}
blender?.let {
linearize.apply(source[0], source[0])
linearize.apply(source[1], source[1])
val result = it.process(source[0], source[1], mask ?: error("no mask"))
result.copyTo(target[0])
delinearize.apply(target[0], target[0])
}
}
}
}

View File

@@ -0,0 +1,65 @@
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(val width: Int, val 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
}
fun destroy() {
preproc.destroy()
combined.destroy()
}
}
/**
* Poison filling as a filter
*/
class PoissonFill : Filter() {
private var filler: PoissonFiller? = null
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
if (target.isNotEmpty()) {
filler?.let {
if (it.width != target[0].width || it.height != target[0].height) {
it.destroy()
filler = null
}
}
if (filler == null) {
filler = PoissonFiller(target[0].width, target[0].height)
}
filler?.let {
val result = it.process(source[0])
result.copyTo(target[0])
}
}
}
}