add RuntimeXfermode API to the graphics platform

Flag: com.android.graphics.hwui.flags.runtime_color_filters_blenders

Test: atest CtsUiRenderingTestCases:RuntimeXfermodeTests
Bug: b/358126864

Change-Id: Idd15f937707feec51387b513b75cfadfaf0336a3
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index fcb7efc..e2db2c9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -355,6 +355,7 @@
         "jni/AnimatedImageDrawable.cpp",
         "jni/Bitmap.cpp",
         "jni/BitmapRegionDecoder.cpp",
+        "jni/RuntimeXfermode.cpp",
         "jni/BufferUtils.cpp",
         "jni/HardwareBufferHelpers.cpp",
         "jni/BitmapFactory.cpp",
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 15b2bac..56de568 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -28,6 +28,7 @@
 extern int register_android_graphics_Bitmap(JNIEnv*);
 extern int register_android_graphics_BitmapFactory(JNIEnv*);
 extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
+extern int register_android_graphics_RuntimeXfermode(JNIEnv*);
 extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
 extern int register_android_graphics_Camera(JNIEnv* env);
 extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
@@ -107,6 +108,7 @@
             REG_JNI(register_android_graphics_Bitmap),
             REG_JNI(register_android_graphics_BitmapFactory),
             REG_JNI(register_android_graphics_BitmapRegionDecoder),
+            REG_JNI(register_android_graphics_RuntimeXfermode),
             REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
             REG_JNI(register_android_graphics_Camera),
             REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index da23792..a7d855d 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -906,6 +906,13 @@
         paint->setBlendMode(mode);
     }
 
+    static void setRuntimeXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle,
+                                   jlong xfermodeHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        SkBlender* blender = reinterpret_cast<SkBlender*>(xfermodeHandle);
+        paint->setBlender(sk_ref_sp(blender));
+    }
+
     static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
@@ -1233,6 +1240,7 @@
         {"nSetShader", "(JJ)J", (void*)PaintGlue::setShader},
         {"nSetColorFilter", "(JJ)J", (void*)PaintGlue::setColorFilter},
         {"nSetXfermode", "(JI)V", (void*)PaintGlue::setXfermode},
+        {"nSetXfermode", "(JJ)V", (void*)PaintGlue::setRuntimeXfermode},
         {"nSetPathEffect", "(JJ)J", (void*)PaintGlue::setPathEffect},
         {"nSetMaskFilter", "(JJ)J", (void*)PaintGlue::setMaskFilter},
         {"nSetTypeface", "(JJ)V", (void*)PaintGlue::setTypeface},
diff --git a/libs/hwui/jni/RuntimeXfermode.cpp b/libs/hwui/jni/RuntimeXfermode.cpp
new file mode 100644
index 0000000..c1c8964
--- /dev/null
+++ b/libs/hwui/jni/RuntimeXfermode.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
+#include "SkBlender.h"
+
+using namespace android::uirenderer;
+
+static void SkRuntimeEffectBuilder_delete(SkRuntimeEffectBuilder* builder) {
+    delete builder;
+}
+
+static jlong RuntimeXfermode_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeEffectBuilder_delete));
+}
+
+static jlong RuntimeXfermode_createBuilder(JNIEnv* env, jobject, jstring sksl) {
+    ScopedUtfChars strSksl(env, sksl);
+    auto result =
+            SkRuntimeEffect::MakeForBlender(SkString(strSksl.c_str()), SkRuntimeEffect::Options{});
+    if (result.effect.get() == nullptr) {
+        doThrowIAE(env, result.errorText.c_str());
+        return 0;
+    }
+    return reinterpret_cast<jlong>(new SkRuntimeEffectBuilder(std::move(result.effect)));
+}
+
+static jlong RuntimeXfermode_create(JNIEnv* env, jobject, jlong builderPtr) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    sk_sp<SkBlender> blender = builder->makeBlender();
+    if (!blender) {
+        doThrowIAE(env);
+    }
+    return reinterpret_cast<jlong>(blender.release());
+}
+
+static void RuntimeXfermode_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong builderPtr,
+                                                     jstring uniformName, jfloatArray uniforms,
+                                                     jboolean isColor) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    ScopedUtfChars name(env, uniformName);
+    AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
+    UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
+}
+
+static void RuntimeXfermode_updateFloatUniforms(JNIEnv* env, jobject, jlong builderPtr,
+                                                jstring uniformName, jfloat value1, jfloat value2,
+                                                jfloat value3, jfloat value4, jint count) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    ScopedUtfChars name(env, uniformName);
+    const float values[4] = {value1, value2, value3, value4};
+    UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
+}
+
+static void RuntimeXfermode_updateIntArrayUniforms(JNIEnv* env, jobject, jlong builderPtr,
+                                                   jstring uniformName, jintArray uniforms) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    ScopedUtfChars name(env, uniformName);
+    AutoJavaIntArray autoValues(env, uniforms, 0);
+    UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
+}
+
+static void RuntimeXfermode_updateIntUniforms(JNIEnv* env, jobject, jlong builderPtr,
+                                              jstring uniformName, jint value1, jint value2,
+                                              jint value3, jint value4, jint count) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    ScopedUtfChars name(env, uniformName);
+    const int values[4] = {value1, value2, value3, value4};
+    UpdateIntUniforms(env, builder, name.c_str(), values, count);
+}
+
+static void RuntimeXfermode_updateChild(JNIEnv* env, jobject, jlong builderPtr, jstring childName,
+                                        jlong childPtr) {
+    auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr);
+    ScopedUtfChars name(env, childName);
+    auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
+    if (child) {
+        UpdateChild(env, builder, name.c_str(), child);
+    }
+}
+
+static const JNINativeMethod gRuntimeXfermodeMethods[] = {
+        {"nativeGetFinalizer", "()J", (void*)RuntimeXfermode_getNativeFinalizer},
+        {"nativeCreateBlenderBuilder", "(Ljava/lang/String;)J",
+         (void*)RuntimeXfermode_createBuilder},
+        {"nativeCreateNativeInstance", "(J)J", (void*)RuntimeXfermode_create},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+         (void*)RuntimeXfermode_updateFloatArrayUniforms},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+         (void*)RuntimeXfermode_updateFloatUniforms},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+         (void*)RuntimeXfermode_updateIntArrayUniforms},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+         (void*)RuntimeXfermode_updateIntUniforms},
+        {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeXfermode_updateChild},
+};
+
+int register_android_graphics_RuntimeXfermode(JNIEnv* env) {
+    android::RegisterMethodsOrDie(env, "android/graphics/RuntimeXfermode", gRuntimeXfermodeMethods,
+                                  NELEM(gRuntimeXfermodeMethods));
+
+    return 0;
+}