Distance fields using orx-jumpflood

Prerequisites

Assuming you are working on an openrndr-template based project, all you have to do is to add implementation(orx.jumpflood) to the dependencies block in build.gradle.kts and reload Gradle, as described in ORX.

Distance field visualization

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorType
import org.openrndr.draw.loadImage
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.compositor.post
import org.openrndr.extra.jumpfill.DistanceField
import org.openrndr.extra.jumpfill.fx.InnerGlow
import org.openrndr.extra.jumpfill.fx.OuterGlow
import kotlin.math.cos

fun main() = application {
    program {

        val image = loadImage("data/images/cheeta.jpg")

        val c = compose {
            layer {
                colorType = ColorType.FLOAT32

                draw {
                    drawer.image(image)
                }
                post(DistanceField()) {
                    threshold = cos(seconds) * 0.5 + 0.5
                    distanceScale = 0.008
                }
            }
        }
        extend {
            // FIXME
            //c.draw(drawer)
        }
    }
}

Link to the full example

Outer glow

orx-jumpflood comes with a filter that creates Photoshop-style outer glow effect.

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorType
import org.openrndr.draw.loadImage
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.compositor.post
import org.openrndr.extra.jumpfill.DistanceField
import org.openrndr.extra.jumpfill.fx.InnerGlow
import org.openrndr.extra.jumpfill.fx.OuterGlow
import kotlin.math.cos

fun main() = application {
    program {
        val c = compose {
            layer {
                draw {
                    drawer.fill = ColorRGBa.PINK
                    drawer.circle(width / 2.0, height / 2.0, 200.0)
                }
                post(OuterGlow()) {
                    this.width = (cos(seconds) * 0.5 + 0.5) * 100.0
                }
            }
        }
        extend {
            // FIXME
            //c.draw(drawer)
        }
    }
}

Link to the full example

Inner glow

Similar to the outer glow effect, but the glow is placed in the inside of the shape.

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorType
import org.openrndr.draw.loadImage
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.compositor.post
import org.openrndr.extra.jumpfill.DistanceField
import org.openrndr.extra.jumpfill.fx.InnerGlow
import org.openrndr.extra.jumpfill.fx.OuterGlow
import kotlin.math.cos

fun main() = application {
    program {
        val c = compose {
            layer {
                draw {
                    drawer.fill = ColorRGBa.PINK
                    drawer.circle(width / 2.0, height / 2.0, 200.0)
                }
                post(InnerGlow()) {
                    this.width = (cos(seconds) * 0.5 + 0.5) * 100.0
                }
            }
        }
        extend {
            // FIXME
            //c.draw(drawer)
        }
    }
}

Link to the full example

Sampling distance

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorType
import org.openrndr.draw.loadImage
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.compositor.post
import org.openrndr.extra.jumpfill.DistanceField
import org.openrndr.extra.jumpfill.fx.InnerGlow
import org.openrndr.extra.jumpfill.fx.OuterGlow
import kotlin.math.cos

fun main() = application {
    program {

        val image = loadImage("data/images/cheeta.jpg")

        val c = compose {
            // -- make sure accumulation is done in float32
            colorType = ColorType.FLOAT32
            layer {
                colorType = ColorType.FLOAT32
                draw {
                    drawer.image(image)
                }
                post(DistanceField()) {
                    threshold = cos(seconds) * 0.5 + 0.5
                    distanceScale = 1.0
                }
            }
            layer {
                draw {
                    // -- use the accumulation buffer to get the distance field
                    accumulation?.let {
                        val s = it.shadow
                        s.download()
                        drawer.fill = ColorRGBa.PINK
                        drawer.stroke = null
                        for (y in 0 until height step 10) {
                            for (x in 0 until width step 10) {
                                val distance = s[x, y].r
                                drawer.circle(x * 1.0, y * 1.0, distance * 0.05)
                            }
                        }
                        // -- clear the accumulated contents
                        it.fill(ColorRGBa.TRANSPARENT)
                    }
                }
            }
        }
        extend {
            // FIXME
            //c.draw(drawer)
        }
    }
}

Link to the full example

edit on GitHub