diff options
author | 2024-11-12 19:58:03 +0000 | |
---|---|---|
committer | 2024-11-13 22:38:57 +0000 | |
commit | be71b3eda74abedb643fdf9d0761715980294193 (patch) | |
tree | 846aaac2b6b7dbad91e8b4e14e361b54f8175a7e | |
parent | a9774d7702dc4fe08933f71abee869d1fa65618f (diff) |
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
-rw-r--r-- | core/api/current.txt | 19 | ||||
-rw-r--r-- | graphics/java/android/graphics/BlendMode.java | 6 | ||||
-rw-r--r-- | graphics/java/android/graphics/ComposeShader.java | 5 | ||||
-rw-r--r-- | graphics/java/android/graphics/Paint.java | 32 | ||||
-rw-r--r-- | graphics/java/android/graphics/PorterDuffXfermode.java | 3 | ||||
-rw-r--r-- | graphics/java/android/graphics/RuntimeXfermode.java | 312 | ||||
-rw-r--r-- | graphics/java/android/graphics/Xfermode.java | 9 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/jni/Paint.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/jni/RuntimeXfermode.cpp | 117 |
11 files changed, 496 insertions, 18 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index dc7ccd4efaa9..ccecb9601a1b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17491,6 +17491,25 @@ package android.graphics { method public void setIntUniform(@NonNull String, @NonNull int[]); } + @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public class RuntimeXfermode extends android.graphics.Xfermode { + ctor public RuntimeXfermode(@NonNull String); + method public void setColorUniform(@NonNull String, @ColorInt int); + method public void setColorUniform(@NonNull String, @ColorLong long); + method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color); + method public void setFloatUniform(@NonNull String, float); + method public void setFloatUniform(@NonNull String, float, float); + method public void setFloatUniform(@NonNull String, float, float, float); + method public void setFloatUniform(@NonNull String, float, float, float, float); + method public void setFloatUniform(@NonNull String, @NonNull float[]); + method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter); + method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader); + method public void setIntUniform(@NonNull String, int); + method public void setIntUniform(@NonNull String, int, int); + method public void setIntUniform(@NonNull String, int, int, int); + method public void setIntUniform(@NonNull String, int, int, int, int); + method public void setIntUniform(@NonNull String, @NonNull int[]); + } + public class Shader { ctor @Deprecated public Shader(); method public boolean getLocalMatrix(@NonNull android.graphics.Matrix); diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java index 5c294aa72ce8..c6ae680d01bf 100644 --- a/graphics/java/android/graphics/BlendMode.java +++ b/graphics/java/android/graphics/BlendMode.java @@ -571,10 +571,10 @@ public enum BlendMode { } @NonNull - private final Xfermode mXfermode; + private final PorterDuffXfermode mXfermode; BlendMode(int mode) { - mXfermode = new Xfermode(); + mXfermode = new PorterDuffXfermode(); mXfermode.porterDuffMode = mode; } @@ -582,7 +582,7 @@ public enum BlendMode { * @hide */ @NonNull - public Xfermode getXfermode() { + public PorterDuffXfermode getXfermode() { return mXfermode; } } diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java index 977aeaa2f4d9..e7145686247e 100644 --- a/graphics/java/android/graphics/ComposeShader.java +++ b/graphics/java/android/graphics/ComposeShader.java @@ -40,9 +40,12 @@ public class ComposeShader extends Shader { * @param mode The mode that combines the colors from the two shaders. If mode * is null, then SRC_OVER is assumed. */ + //TODO(358126864): allow a ComposeShader to accept a RuntimeXfermode @Deprecated public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) { - this(shaderA, shaderB, mode.porterDuffMode); + this(shaderA, shaderB, + mode instanceof PorterDuffXfermode ? ((PorterDuffXfermode) mode).porterDuffMode + : BlendMode.SRC_OVER.getXfermode().porterDuffMode); } /** diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 56bb0f0d12d5..2c166c32ba50 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -71,6 +71,7 @@ public class Paint { private long mNativePaint; private long mNativeShader; private long mNativeColorFilter; + private long mNativeXfermode; // Use a Holder to allow static initialization of Paint in the boot image. private static class NoImagePreloadHolder { @@ -735,6 +736,7 @@ public class Paint { mPathEffect = null; mShader = null; mNativeShader = 0; + mNativeXfermode = 0; mTypeface = null; mXfermode = null; @@ -780,6 +782,7 @@ public class Paint { mNativeShader = paint.mNativeShader; mTypeface = paint.mTypeface; mXfermode = paint.mXfermode; + mNativeXfermode = paint.mNativeXfermode; mHasCompatScaling = paint.mHasCompatScaling; mCompatScaling = paint.mCompatScaling; @@ -815,7 +818,7 @@ public class Paint { * * Note: Although this method is |synchronized|, this is simply so it * is not thread-hostile to multiple threads calling this method. It - * is still unsafe to attempt to change the Shader/ColorFilter while + * is still unsafe to attempt to change the Shader/ColorFilter/Xfermode while * another thread attempts to access the native object. * * @hide @@ -833,6 +836,13 @@ public class Paint { mNativeColorFilter = newNativeColorFilter; nSetColorFilter(mNativePaint, mNativeColorFilter); } + if (mXfermode instanceof RuntimeXfermode) { + long newNativeXfermode = ((RuntimeXfermode) mXfermode).createNativeInstance(); + if (newNativeXfermode != mNativeXfermode) { + mNativeXfermode = newNativeXfermode; + nSetXfermode(mNativePaint, mNativeXfermode); + } + } return mNativePaint; } @@ -1427,16 +1437,17 @@ public class Paint { } /** - * Get the paint's blend mode object. + * Get the paint's blend mode object. Will return null if there is a Xfermode applied that + * cannot be represented by a blend mode (i.e. a custom {@code RuntimeXfermode} * * @return the paint's blend mode (or null) */ @Nullable public BlendMode getBlendMode() { - if (mXfermode == null) { + if (mXfermode == null || !(mXfermode instanceof PorterDuffXfermode)) { return null; } else { - return BlendMode.fromValue(mXfermode.porterDuffMode); + return BlendMode.fromValue(((PorterDuffXfermode) mXfermode).porterDuffMode); } } @@ -1459,8 +1470,15 @@ public class Paint { @Nullable private Xfermode installXfermode(Xfermode xfermode) { - int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT; - int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT; + if (xfermode instanceof RuntimeXfermode) { + mXfermode = xfermode; + nSetXfermode(mNativePaint, ((RuntimeXfermode) xfermode).createNativeInstance()); + return xfermode; + } + int newMode = (xfermode instanceof PorterDuffXfermode) + ? ((PorterDuffXfermode) xfermode).porterDuffMode : PorterDuffXfermode.DEFAULT; + int curMode = (mXfermode instanceof PorterDuffXfermode) + ? ((PorterDuffXfermode) mXfermode).porterDuffMode : PorterDuffXfermode.DEFAULT; if (newMode != curMode) { nSetXfermode(mNativePaint, newMode); } @@ -3823,6 +3841,8 @@ public class Paint { @CriticalNative private static native void nSetXfermode(long paintPtr, int xfermode); @CriticalNative + private static native void nSetXfermode(long paintPtr, long xfermodePtr); + @CriticalNative private static native long nSetPathEffect(long paintPtr, long effect); @CriticalNative private static native long nSetMaskFilter(long paintPtr, long maskfilter); diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java index ff9ff8b0069d..83d0507a5074 100644 --- a/graphics/java/android/graphics/PorterDuffXfermode.java +++ b/graphics/java/android/graphics/PorterDuffXfermode.java @@ -29,6 +29,9 @@ public class PorterDuffXfermode extends Xfermode { * * @param mode The porter-duff mode that is applied */ + static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt; + int porterDuffMode = DEFAULT; + PorterDuffXfermode() {} public PorterDuffXfermode(PorterDuff.Mode mode) { porterDuffMode = mode.nativeInt; } diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java new file mode 100644 index 000000000000..f5a656862bf9 --- /dev/null +++ b/graphics/java/android/graphics/RuntimeXfermode.java @@ -0,0 +1,312 @@ +/* + * Copyright 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. + */ + +package android.graphics; + +import android.annotation.ColorInt; +import android.annotation.ColorLong; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; + +import com.android.graphics.hwui.flags.Flags; + +import libcore.util.NativeAllocationRegistry; + + +/** + * <p>A {@link RuntimeXfermode} calculates a per-pixel color based on the output of a user + * * defined Android Graphics Shading Language (AGSL) function.</p> + * + * <p>This AGSL function takes in two input colors to be operated on. These colors are in sRGB + * * and the output is also interpreted as sRGB. The AGSL function signature expects a single input + * * of color (packed as a half4 or float4 or vec4).</p> + * + * <pre class="prettyprint"> + * vec4 main(half4 src, half4 dst); + * </pre> + */ +@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS) +public class RuntimeXfermode extends Xfermode { + + private static class NoImagePreloadHolder { + public static final NativeAllocationRegistry sRegistry = + NativeAllocationRegistry.createMalloced( + RuntimeXfermode.class.getClassLoader(), nativeGetFinalizer()); + } + + private long mBuilderNativeInstance; + + /** + * Creates a new RuntimeBlender. + * + * @param agsl The text of AGSL color filter program to run. + */ + public RuntimeXfermode(@NonNull String agsl) { + if (agsl == null) { + throw new NullPointerException("RuntimeShader requires a non-null AGSL string"); + } + mBuilderNativeInstance = nativeCreateBlenderBuilder(agsl); + RuntimeXfermode.NoImagePreloadHolder.sRegistry.registerNativeAllocation( + this, mBuilderNativeInstance); + } + /** + * Sets the uniform color value corresponding to this color filter. If the effect does not have + * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4 + * and corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL program + * @param color the provided sRGB color + */ + public void setColorUniform(@NonNull String uniformName, @ColorInt int color) { + setUniform(uniformName, Color.valueOf(color).getComponents(), true); + } + + /** + * Sets the uniform color value corresponding to this color filter. If the effect does not have + * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4 + * and corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL program + * @param color the provided sRGB color + */ + public void setColorUniform(@NonNull String uniformName, @ColorLong long color) { + Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); + setUniform(uniformName, exSRGB.getComponents(), true); + } + + /** + * Sets the uniform color value corresponding to this color filter. If the effect does not have + * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4 + * and corresponding layout(color) annotation then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the color uniform declared in the AGSL program + * @param color the provided sRGB color + */ + public void setColorUniform(@NonNull String uniformName, @NonNull Color color) { + if (color == null) { + throw new NullPointerException("The color parameter must not be null"); + } + Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); + setUniform(uniformName, exSRGB.getComponents(), true); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than a float or + * float[1] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setFloatUniform(@NonNull String uniformName, float value) { + setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than a vec2 or + * float[2] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setFloatUniform(@NonNull String uniformName, float value1, float value2) { + setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than a vec3 or + * float[3] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3) { + setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3); + + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than a vec4 or + * float[4] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3, float value4) { + setFloatUniform(uniformName, value1, value2, value3, value4, 4); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than a float + * (for N=1), vecN, or float[N] where N is the length of the values param then an + * IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) { + setUniform(uniformName, values, false); + } + + private void setFloatUniform(@NonNull String uniformName, float value1, float value2, + float value3, float value4, int count) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + nativeUpdateUniforms(mBuilderNativeInstance, uniformName, value1, value2, value3, value4, + count); + } + + private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + if (values == null) { + throw new NullPointerException("The uniform values parameter must not be null"); + } + nativeUpdateUniforms(mBuilderNativeInstance, uniformName, values, isColor); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than an int or int[1] + * then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setIntUniform(@NonNull String uniformName, int value) { + setIntUniform(uniformName, value, 0, 0, 0, 1); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than an ivec2 or + * int[2] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2) { + setIntUniform(uniformName, value1, value2, 0, 0, 2); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than an ivec3 or + * int[3] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) { + setIntUniform(uniformName, value1, value2, value3, 0, 3); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than an ivec4 or + * int[4] then an IllegalArgumentException is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setIntUniform(@NonNull String uniformName, int value1, int value2, + int value3, int value4) { + setIntUniform(uniformName, value1, value2, value3, value4, 4); + } + + /** + * Sets the uniform value corresponding to this color filter. If the effect does not have a + * uniform with that name or if the uniform is declared with a type other than an int (for N=1), + * ivecN, or int[N] where N is the length of the values param then an IllegalArgumentException + * is thrown. + * + * @param uniformName name matching the uniform declared in the AGSL program + */ + public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + if (values == null) { + throw new NullPointerException("The uniform values parameter must not be null"); + } + nativeUpdateUniforms(mBuilderNativeInstance, uniformName, values); + } + + private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3, + int value4, int count) { + if (uniformName == null) { + throw new NullPointerException("The uniformName parameter must not be null"); + } + nativeUpdateUniforms(mBuilderNativeInstance, uniformName, value1, value2, value3, value4, + count); + } + + /** + * Assigns the uniform shader to the provided shader parameter. If the shader program does not + * have a uniform shader with that name then an IllegalArgumentException is thrown. + * + * @param shaderName name matching the uniform declared in the AGSL program + * @param shader shader passed into the AGSL program for sampling + */ + public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) { + if (shaderName == null) { + throw new NullPointerException("The shaderName parameter must not be null"); + } + if (shader == null) { + throw new NullPointerException("The shader parameter must not be null"); + } + nativeUpdateChild(mBuilderNativeInstance, shaderName, shader.getNativeInstance()); + } + + /** + * Assigns the uniform color filter to the provided color filter parameter. If the shader + * program does not have a uniform color filter with that name then an IllegalArgumentException + * is thrown. + * + * @param filterName name matching the uniform declared in the AGSL program + * @param colorFilter filter passed into the AGSL program for sampling + */ + public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) { + if (filterName == null) { + throw new NullPointerException("The filterName parameter must not be null"); + } + if (colorFilter == null) { + throw new NullPointerException("The colorFilter parameter must not be null"); + } + nativeUpdateChild(mBuilderNativeInstance, filterName, colorFilter.getNativeInstance()); + } + + /** @hide */ + public long createNativeInstance() { + return nativeCreateNativeInstance(mBuilderNativeInstance); + } + + /** @hide */ + private static native long nativeGetFinalizer(); + private static native long nativeCreateBlenderBuilder(String agsl); + private static native long nativeCreateNativeInstance(long builder); + private static native void nativeUpdateUniforms( + long builder, String uniformName, float[] uniforms, boolean isColor); + private static native void nativeUpdateUniforms( + long builder, String uniformName, float value1, float value2, float value3, + float value4, int count); + private static native void nativeUpdateUniforms( + long builder, String uniformName, int[] uniforms); + private static native void nativeUpdateUniforms( + long builder, String uniformName, int value1, int value2, int value3, + int value4, int count); + private static native void nativeUpdateChild(long builder, String childName, long child); + +} diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java index 6bb22a12280e..fb689e4cb9c2 100644 --- a/graphics/java/android/graphics/Xfermode.java +++ b/graphics/java/android/graphics/Xfermode.java @@ -21,9 +21,6 @@ package android.graphics; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; - /** * Xfermode is the base class for objects that are called to implement custom * "transfer-modes" in the drawing pipeline. The static function Create(Modes) @@ -31,8 +28,4 @@ import android.os.Build; * specified in the Modes enum. When an Xfermode is assigned to a Paint, then * objects drawn with that paint have the xfermode applied. */ -public class Xfermode { - static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - int porterDuffMode = DEFAULT; -} +public class Xfermode {} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index fcb7efc35c94..e2db2c9e3827 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -355,6 +355,7 @@ cc_defaults { "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 15b2bac50c79..56de56805be4 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 @@ extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env); 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 da237928e5e1..a7d855d7e8ca 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -906,6 +906,13 @@ namespace PaintGlue { 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 @@ static const JNINativeMethod methods[] = { {"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 000000000000..c1c8964bf5eb --- /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; +} |