Unverified Commit e28b5fea authored by Kirill Grouchnikov's avatar Kirill Grouchnikov Committed by GitHub

Add bindings for SkImageFilters::RuntimeShader (#520)

For https://github.com/JetBrains/skiko/issues/519
parent 82bfb2d6
...@@ -381,6 +381,47 @@ class ImageFilter internal constructor(ptr: NativePointer) : RefCnt(ptr) { ...@@ -381,6 +381,47 @@ class ImageFilter internal constructor(ptr: NativePointer) : RefCnt(ptr) {
} }
} }
fun makeRuntimeShader(runtimeShaderBuilder: RuntimeShaderBuilder, shaderName: String, input: ImageFilter?): ImageFilter {
return try {
Stats.onNativeCall()
interopScope {
ImageFilter(
_nMakeRuntimeShader(
runtimeShaderBuilderPtr = getPtr(runtimeShaderBuilder),
childShaderName = toInterop(shaderName),
input = getPtr(input)
)
)
}
} finally {
reachabilityBarrier(runtimeShaderBuilder)
reachabilityBarrier(input)
}
}
fun makeRuntimeShader(runtimeShaderBuilder: RuntimeShaderBuilder, shaderNames: Array<String>, inputs: Array<ImageFilter?>): ImageFilter {
return try {
Stats.onNativeCall()
require(shaderNames.size == inputs.size)
interopScope {
val inputPtrs = NativePointerArray(inputs.size)
for (i in inputs.indices) inputPtrs[i] = getPtr(inputs[i])
ImageFilter(
_nMakeRuntimeShaderFromArray(
runtimeShaderBuilderPtr = getPtr(runtimeShaderBuilder),
childShaderNames = toInterop(shaderNames),
inputs = toInterop(inputPtrs),
inputLength = inputPtrs.size
)
)
}
} finally {
reachabilityBarrier(runtimeShaderBuilder)
reachabilityBarrier(inputs)
}
}
fun makeTile(src: Rect, dst: Rect, input: ImageFilter?): ImageFilter { fun makeTile(src: Rect, dst: Rect, input: ImageFilter?): ImageFilter {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -753,6 +794,10 @@ private external fun _nMakeOffset(dx: Float, dy: Float, input: NativePointer, cr ...@@ -753,6 +794,10 @@ private external fun _nMakeOffset(dx: Float, dy: Float, input: NativePointer, cr
private external fun _nMakePaint(paint: NativePointer, crop: InteropPointer): NativePointer private external fun _nMakePaint(paint: NativePointer, crop: InteropPointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakePicture") @ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakePicture")
private external fun _nMakePicture(picture: NativePointer, l: Float, t: Float, r: Float, b: Float): NativePointer private external fun _nMakePicture(picture: NativePointer, l: Float, t: Float, r: Float, b: Float): NativePointer
@ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakeRuntimeShader")
private external fun _nMakeRuntimeShader(runtimeShaderBuilderPtr: NativePointer, childShaderName: InteropPointer, input: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakeRuntimeShaderFromArray")
private external fun _nMakeRuntimeShaderFromArray(runtimeShaderBuilderPtr: NativePointer, childShaderNames: InteropPointer, inputs: InteropPointer, inputLength: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakeTile") @ExternalSymbolName("org_jetbrains_skia_ImageFilter__1nMakeTile")
private external fun _nMakeTile( private external fun _nMakeTile(
l0: Float, l0: Float,
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.Managed
import org.jetbrains.skia.impl.Native.Companion.NullPointer
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
import org.jetbrains.skia.impl.reachabilityBarrier
class RuntimeShaderBuilder internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR) {
companion object {
init {
staticLoad()
}
}
constructor(effect: RuntimeEffect) : this(_nMakeFromRuntimeEffect(effect._ptr)) {
Stats.onNativeCall()
reachabilityBarrier(effect)
}
private object _FinalizerHolder {
val PTR = RuntimeShaderBuilder_nGetFinalizer()
}
fun uniform(name: String, value: Int) {
Stats.onNativeCall()
interopScope {
_nUniformInt(_ptr, toInterop(name), value)
}
}
fun uniform(name: String, value: Float) {
Stats.onNativeCall()
interopScope {
_nUniformFloat(_ptr, toInterop(name), value)
}
}
}
@ExternalSymbolName("org_jetbrains_skia_RuntimeShaderBuilder__1nMakeFromRuntimeEffect")
private external fun _nMakeFromRuntimeEffect(effectPtr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_RuntimeShaderBuilder__1nGetFinalizer")
private external fun RuntimeShaderBuilder_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_RuntimeShaderBuilder__1nUniformInt")
private external fun _nUniformInt(builderPtr: NativePointer, uniformName: InteropPointer, uniformValue: Int)
@ExternalSymbolName("org_jetbrains_skia_RuntimeShaderBuilder__1nUniformFloat")
private external fun _nUniformFloat(builderPtr: NativePointer, uniformName: InteropPointer, uniformValue: Float)
...@@ -3,7 +3,6 @@ package org.jetbrains.skia ...@@ -3,7 +3,6 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.use import org.jetbrains.skia.impl.use
import org.jetbrains.skia.util.assertContentDifferent import org.jetbrains.skia.util.assertContentDifferent
import org.jetbrains.skiko.tests.runTest import org.jetbrains.skiko.tests.runTest
import kotlin.test.Ignore
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
...@@ -224,6 +223,67 @@ class ImageFilterTest { ...@@ -224,6 +223,67 @@ class ImageFilterTest {
) )
} }
@Test
fun makeRuntimeShader() = imageFilterTest {
// A simple Skia shader that bumps up the red channel of every non-transparent
// pixel to full intensity, and leaves green and blue channels unchanged.
val redSksl = """
uniform shader content;
vec4 main(vec2 coord) {
vec4 c = content.eval(coord);
return vec4(1.0 * c.a, c.g * c.a, c.b * c.a, c.a);
}
"""
val redRuntimeEffect = RuntimeEffect.makeForShader(redSksl)
val redShaderBuilder = RuntimeShaderBuilder(redRuntimeEffect)
ImageFilter.makeRuntimeShader(
runtimeShaderBuilder = redShaderBuilder,
shaderName = "content",
input = null
)
}
@Test
fun makeRuntimeShaderFromArrays() = imageFilterTest {
// A Skia shader that has two children shaders - one that applies our custom shader logic
// on the underlying render node content, and another that is the built in blur. This
// shader also has a float uniform that is used to decide which one of these two children
// shaders to apply on a given pixel, based on the X coordinate.
// This test covers not only ImageFilter.makeRuntimeShader API, but also
// RuntimeShaderBuilder.uniform.
val compositeSksl = """
uniform shader content;
uniform shader blurred;
uniform float cutoff;
vec4 main(vec2 coord) {
vec4 c = content.eval(coord);
vec4 b = blurred.eval(coord);
if (coord.x > cutoff) {
return vec4(1.0 * c.a, c.g * c.a, c.b * c.a, c.a);
} else {
return b;
}
}
"""
val compositeRuntimeEffect = RuntimeEffect.makeForShader(compositeSksl)
val compositeShaderBuilder = RuntimeShaderBuilder(compositeRuntimeEffect)
// Pass a float uniform into our shader
compositeShaderBuilder.uniform("cutoff", 10.0f)
// And use ImageFilter.makeBlur as the second child input to our composite shader
val blurImageFilter = ImageFilter.makeBlur(sigmaX = 2.0f, sigmaY = 2.0f, mode = FilterTileMode.DECAL)
ImageFilter.makeRuntimeShader(
runtimeShaderBuilder = compositeShaderBuilder,
shaderNames = arrayOf("content", "blurred"),
inputs = arrayOf(null, blurImageFilter)
)
}
@Test @Test
fun makeTile() = imageFilterTest { fun makeTile() = imageFilterTest {
ImageFilter.makeTile( ImageFilter.makeTile(
......
...@@ -160,6 +160,37 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMake ...@@ -160,6 +160,37 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMake
return reinterpret_cast<jlong>(ptr); return reinterpret_cast<jlong>(ptr);
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMakeRuntimeShader
(JNIEnv* env, jclass jclass, jlong runtimeShaderBuilderPtr, jstring childShaderName, jlong inputPtr) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(static_cast<uintptr_t>(runtimeShaderBuilderPtr));
SkImageFilter* input = reinterpret_cast<SkImageFilter*>(static_cast<uintptr_t>(inputPtr));
SkImageFilter* ptr = SkImageFilters::RuntimeShader(*runtimeShaderBuilder, skString(env, childShaderName).c_str(), sk_ref_sp(input)).release();
return reinterpret_cast<jlong>(ptr);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMakeRuntimeShaderFromArray
(JNIEnv* env, jclass jclass, jlong runtimeShaderBuilderPtr, jobjectArray childShaderNamesArr, jlongArray inputPtrsArray, jint _inputCount) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(static_cast<uintptr_t>(runtimeShaderBuilderPtr));
jsize inputCount = env->GetArrayLength(inputPtrsArray);
jlong* inputPtrs = env->GetLongArrayElements(inputPtrsArray, 0);
std::vector<sk_sp<SkImageFilter>> inputChildren(inputCount);
for (int i = 0; i < inputCount; ++i) {
SkImageFilter* filter = reinterpret_cast<SkImageFilter*>(static_cast<uintptr_t>(inputPtrs[i]));
inputChildren[i] = sk_ref_sp(filter);
}
env->ReleaseLongArrayElements(inputPtrsArray, inputPtrs, 0);
std::vector<SkString> childShaderNameStrings = skStringVector(env, childShaderNamesArr);
std::vector<const char*> childShaderNames(childShaderNameStrings.size());
for (int i = 0; i < inputCount; ++i)
childShaderNames[i] = childShaderNameStrings[i].c_str();
SkImageFilter* ptr = SkImageFilters::RuntimeShader(*runtimeShaderBuilder, childShaderNames.data(), inputChildren.data(), inputCount).release();
return reinterpret_cast<jlong>(ptr);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMakeTile extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_ImageFilterKt__1nMakeTile
(JNIEnv* env, jclass jclass, jfloat l0, jfloat t0, jfloat r0, jfloat b0, jfloat l1, jfloat t1, jfloat r1, jfloat b1, jlong inputPtr) { (JNIEnv* env, jclass jclass, jfloat l0, jfloat t0, jfloat r0, jfloat b0, jfloat l1, jfloat t1, jfloat r1, jfloat b1, jlong inputPtr) {
SkImageFilter* input = reinterpret_cast<SkImageFilter*>(static_cast<uintptr_t>(inputPtr)); SkImageFilter* input = reinterpret_cast<SkImageFilter*>(static_cast<uintptr_t>(inputPtr));
......
#include <jni.h>
#include "SkRuntimeEffect.h"
#include "interop.hh"
static void deleteRuntimeShaderBuilder(SkRuntimeShaderBuilder* builder) {
delete builder;
}
extern "C" JNIEXPORT jlong JNICALL
Java_org_jetbrains_skia_RuntimeShaderBuilderKt_RuntimeShaderBuilder_1nGetFinalizer(JNIEnv* env, jclass jclass) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deleteRuntimeShaderBuilder));
}
extern "C" JNIEXPORT jlong JNICALL
Java_org_jetbrains_skia_RuntimeShaderBuilderKt__1nMakeFromRuntimeEffect
(JNIEnv* env, jclass jclass, jlong effectPtr) {
sk_sp<SkRuntimeEffect> runtimeEffect = sk_ref_sp<SkRuntimeEffect>(reinterpret_cast<SkRuntimeEffect*>(static_cast<uintptr_t>(effectPtr)));
SkRuntimeShaderBuilder* builder = new SkRuntimeShaderBuilder(runtimeEffect);
return reinterpret_cast<jlong>(builder);
}
extern "C" JNIEXPORT void JNICALL
Java_org_jetbrains_skia_RuntimeShaderBuilderKt__1nUniformInt
(JNIEnv* env, jclass jclass, jlong builderPtr, jstring uniformName, jint uniformValue) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = jlongToPtr<SkRuntimeShaderBuilder*>(builderPtr);
runtimeShaderBuilder->uniform(skString(env, uniformName).c_str()) = uniformValue;
}
extern "C" JNIEXPORT void JNICALL
Java_org_jetbrains_skia_RuntimeShaderBuilderKt__1nUniformFloat
(JNIEnv* env, jclass jclass, jlong builderPtr, jstring uniformName, jfloat uniformValue) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = jlongToPtr<SkRuntimeShaderBuilder*>(builderPtr);
runtimeShaderBuilder->uniform(skString(env, uniformName).c_str()) = uniformValue;
}
...@@ -160,6 +160,35 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakePicture ...@@ -160,6 +160,35 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakePicture
return reinterpret_cast<KNativePointer>(ptr); return reinterpret_cast<KNativePointer>(ptr);
} }
SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakeRuntimeShader
(KNativePointer runtimeShaderBuilderPtr, KInteropPointer childShaderName, KNativePointer inputPtr) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(runtimeShaderBuilderPtr);
sk_sp<SkImageFilter> input = sk_ref_sp<SkImageFilter>(reinterpret_cast<SkImageFilter*>(inputPtr));
SkImageFilter* ptr = SkImageFilters::RuntimeShader(*runtimeShaderBuilder, reinterpret_cast<char *>(childShaderName), input).release();
return reinterpret_cast<KNativePointer>(ptr);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakeRuntimeShaderFromArray
(KNativePointer runtimeShaderBuilderPtr, KInteropPointerArray childShaderNamesArr, KNativePointerArray inputPtrsArray, KInt inputCount) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(runtimeShaderBuilderPtr);
KNativePointer* inputPtrs = reinterpret_cast<KNativePointer*>(inputPtrsArray);
std::vector<sk_sp<SkImageFilter>> inputChildren(inputCount);
for (size_t i = 0; i < inputCount; i++) {
SkImageFilter* si = reinterpret_cast<SkImageFilter*>(inputPtrs[i]);
inputChildren[i] = sk_ref_sp(si);
}
std::vector<SkString> childShaderNameStrings = skStringVector(childShaderNamesArr, inputCount);
std::vector<const char*> childShaderNames(childShaderNameStrings.size());
for (int i = 0; i < childShaderNames.size(); ++i)
childShaderNames[i] = childShaderNameStrings[i].c_str();
SkImageFilter* ptr = SkImageFilters::RuntimeShader(*runtimeShaderBuilder, childShaderNames.data(), inputChildren.data(), inputCount).release();
return reinterpret_cast<KNativePointer>(ptr);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakeTile SKIKO_EXPORT KNativePointer org_jetbrains_skia_ImageFilter__1nMakeTile
(KFloat l0, KFloat t0, KFloat r0, KFloat b0, KFloat l1, KFloat t1, KFloat r1, KFloat b1, KNativePointer inputPtr) { (KFloat l0, KFloat t0, KFloat r0, KFloat b0, KFloat l1, KFloat t1, KFloat r1, KFloat b1, KNativePointer inputPtr) {
SkImageFilter* input = reinterpret_cast<SkImageFilter*>((inputPtr)); SkImageFilter* input = reinterpret_cast<SkImageFilter*>((inputPtr));
......
#include <iostream>
#include "SkRuntimeEffect.h"
#include "common.h"
static void deleteRuntimeShaderBuilder(SkRuntimeShaderBuilder* builder) {
delete builder;
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_RuntimeShaderBuilder__1nGetFinalizer() {
return reinterpret_cast<KNativePointer>((&deleteRuntimeShaderBuilder));
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_RuntimeShaderBuilder__1nMakeFromRuntimeEffect
(KNativePointer effectPtr) {
sk_sp<SkRuntimeEffect> runtimeEffect = sk_ref_sp<SkRuntimeEffect>(reinterpret_cast<SkRuntimeEffect*>(effectPtr));
SkRuntimeShaderBuilder* builder = new SkRuntimeShaderBuilder(runtimeEffect);
return reinterpret_cast<KNativePointer>(builder);
}
SKIKO_EXPORT void org_jetbrains_skia_RuntimeShaderBuilder__1nUniformInt
(KNativePointer builderPtr, KInteropPointer uniformName, KInt uniformValue) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr);
runtimeShaderBuilder->uniform(skString(uniformName).c_str()) = uniformValue;
}
SKIKO_EXPORT void org_jetbrains_skia_RuntimeShaderBuilder__1nUniformFloat
(KNativePointer builderPtr, KInteropPointer uniformName, KFloat uniformValue) {
SkRuntimeShaderBuilder* runtimeShaderBuilder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr);
runtimeShaderBuilder->uniform(skString(uniformName).c_str()) = uniformValue;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment