add runtime color filter support to platform
Flag: com.android.graphics.hwui.flags.runtime_color_filters_blenders
Test: atest CtsUiRenderingTestCases:RuntimeColorFilterTests
Bug: b/358126864
Change-Id: If569957489f5352455c419074219125f6e9cc43e
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index 0b95148..20301d2 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -18,7 +18,9 @@
#include "ColorFilter.h"
#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
#include "SkBlendMode.h"
+#include "include/effects/SkRuntimeEffect.h"
namespace android {
@@ -89,6 +91,78 @@
filter->setMatrix(getMatrixFromJFloatArray(env, jarray));
}
}
+
+ static jlong RuntimeColorFilter_createColorFilter(JNIEnv* env, jobject, jstring agsl) {
+ ScopedUtfChars strSksl(env, agsl);
+ auto result = SkRuntimeEffect::MakeForColorFilter(SkString(strSksl.c_str()),
+ SkRuntimeEffect::Options{});
+ if (result.effect.get() == nullptr) {
+ doThrowIAE(env, result.errorText.c_str());
+ return 0;
+ }
+ auto builder = new SkRuntimeEffectBuilder(std::move(result.effect));
+ auto* runtimeColorFilter = new RuntimeColorFilter(builder);
+ runtimeColorFilter->incStrong(nullptr);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(runtimeColorFilter));
+ }
+
+ static void RuntimeColorFilter_updateUniformsFloatArray(JNIEnv* env, jobject,
+ jlong colorFilterPtr,
+ jstring uniformName,
+ jfloatArray uniforms,
+ jboolean isColor) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length(),
+ isColor);
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsFloats(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring uniformName, jfloat value1,
+ jfloat value2, jfloat value3, jfloat value4,
+ jint count) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), values, count, false);
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsIntArray(JNIEnv* env, jobject,
+ jlong colorFilterPtr, jstring uniformName,
+ jintArray uniforms) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaIntArray autoValues(env, uniforms, 0);
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length());
+ }
+ }
+
+ static void RuntimeColorFilter_updateUniformsInts(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring uniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, uniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ if (filter) {
+ filter->updateUniforms(env, name.c_str(), values, count);
+ }
+ }
+
+ static void RuntimeColorFilter_updateChild(JNIEnv* env, jobject, jlong colorFilterPtr,
+ jstring childName, jlong childPtr) {
+ auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+ ScopedUtfChars name(env, childName);
+ auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
+ if (filter && child) {
+ filter->updateChild(env, name.c_str(), child);
+ }
+ }
};
static const JNINativeMethod colorfilter_methods[] = {
@@ -107,6 +181,20 @@
{"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter},
{"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}};
+static const JNINativeMethod runtime_color_filter_methods[] = {
+ {"nativeCreateRuntimeColorFilter", "(Ljava/lang/String;)J",
+ (void*)ColorFilterGlue::RuntimeColorFilter_createColorFilter},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloatArray},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloats},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsIntArray},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts},
+ {"nativeUpdateChild", "(JLjava/lang/String;J)V",
+ (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}};
+
int register_android_graphics_ColorFilter(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
NELEM(colorfilter_methods));
@@ -118,7 +206,10 @@
NELEM(lighting_methods));
android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
colormatrix_methods, NELEM(colormatrix_methods));
-
+ android::RegisterMethodsOrDie(env, "android/graphics/RuntimeColorFilter",
+ runtime_color_filter_methods,
+ NELEM(runtime_color_filter_methods));
+
return 0;
}
diff --git a/libs/hwui/jni/RuntimeEffectUtils.cpp b/libs/hwui/jni/RuntimeEffectUtils.cpp
new file mode 100644
index 0000000..46db863
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RuntimeEffectUtils.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const float values[], int count, bool isColor) {
+ SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const int values[], int count) {
+ SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+ SkFlattenable* childEffect) {
+ SkRuntimeShaderBuilder::BuilderChild builderChild = builder->child(childName);
+ if (builderChild.fChild == nullptr) {
+ ThrowIAEFmt(env, "unable to find shader named %s", childName);
+ return;
+ }
+
+ builderChild = sk_ref_sp(childEffect);
+}
+
+} // namespace uirenderer
+} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/jni/RuntimeEffectUtils.h b/libs/hwui/jni/RuntimeEffectUtils.h
new file mode 100644
index 0000000..75623c0
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RUNTIMEEFFECTUTILS_H
+#define RUNTIMEEFFECTUTILS_H
+
+#include "GraphicsJNI.h"
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const float values[], int count, bool isColor);
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+ const int values[], int count);
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+ SkFlattenable* childEffect);
+} // namespace uirenderer
+} // namespace android
+
+#endif // MAIN_RUNTIMEEFFECTUTILS_H