diff options
author | 2020-11-02 19:26:27 -0800 | |
---|---|---|
committer | 2020-11-05 12:59:05 -0800 | |
commit | 0081a9c61d7402b102012c17cb0dcf5030987898 (patch) | |
tree | edcac45dda639143054e8022c0c7bb1673a9e3e2 | |
parent | e31e5987f9de30efc2ab55aa5335ddff91d9840c (diff) |
Introduce more RenderEffect APIs
Add support for the following RenderEffects:
--RenderEffect.createColorFilterEffect
--RenderEffect.createBitmapEffect
--RenderEffect.createComposeEffect
--RenderEffect.createCombineEffect
Bug: 143468037
Test: Added new CTS tests to RenderNodeTests
Change-Id: I0c1398986d93c17020a4cf770f9782cdc10253bc
-rw-r--r-- | api/current.txt | 14 | ||||
-rw-r--r-- | core/api/current.txt | 14 | ||||
-rw-r--r-- | graphics/java/android/graphics/RenderEffect.java | 161 | ||||
-rw-r--r-- | graphics/java/android/graphics/RenderNode.java | 2 | ||||
-rw-r--r-- | libs/hwui/jni/RenderEffect.cpp | 73 |
5 files changed, 258 insertions, 6 deletions
diff --git a/api/current.txt b/api/current.txt index 6459eb4c548a..879959817053 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15599,6 +15599,19 @@ package android.graphics { method public final boolean next(android.graphics.Rect); } + public final class RenderEffect { + method @NonNull public static android.graphics.RenderEffect createBitmapEffect(@NonNull android.graphics.Bitmap); + method @NonNull public static android.graphics.RenderEffect createBitmapEffect(@NonNull android.graphics.Bitmap, @Nullable android.graphics.Rect, @NonNull android.graphics.Rect); + method @NonNull public static android.graphics.RenderEffect createBlendModeEffect(@NonNull android.graphics.RenderEffect, @NonNull android.graphics.RenderEffect, @NonNull android.graphics.BlendMode); + method @NonNull public static android.graphics.RenderEffect createBlurEffect(float, float, @NonNull android.graphics.RenderEffect, @NonNull android.graphics.Shader.TileMode); + method @NonNull public static android.graphics.RenderEffect createBlurEffect(float, float, @NonNull android.graphics.Shader.TileMode); + method @NonNull public static android.graphics.RenderEffect createChainEffect(@NonNull android.graphics.RenderEffect, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter); + method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float); + method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect); + } + public final class RenderNode { ctor public RenderNode(@Nullable String); method @NonNull public android.graphics.RecordingCanvas beginRecording(int, int); @@ -15658,6 +15671,7 @@ package android.graphics { method public boolean setPosition(@NonNull android.graphics.Rect); method public boolean setProjectBackwards(boolean); method public boolean setProjectionReceiver(boolean); + method public void setRenderEffect(@Nullable android.graphics.RenderEffect); method public boolean setRotationX(float); method public boolean setRotationY(float); method public boolean setRotationZ(float); diff --git a/core/api/current.txt b/core/api/current.txt index 05b57af1dccd..0ec1531a742a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -15581,6 +15581,19 @@ package android.graphics { method public final boolean next(android.graphics.Rect); } + public final class RenderEffect { + method @NonNull public static android.graphics.RenderEffect createBitmapEffect(@NonNull android.graphics.Bitmap); + method @NonNull public static android.graphics.RenderEffect createBitmapEffect(@NonNull android.graphics.Bitmap, @Nullable android.graphics.Rect, @NonNull android.graphics.Rect); + method @NonNull public static android.graphics.RenderEffect createBlendModeEffect(@NonNull android.graphics.RenderEffect, @NonNull android.graphics.RenderEffect, @NonNull android.graphics.BlendMode); + method @NonNull public static android.graphics.RenderEffect createBlurEffect(float, float, @NonNull android.graphics.RenderEffect, @NonNull android.graphics.Shader.TileMode); + method @NonNull public static android.graphics.RenderEffect createBlurEffect(float, float, @NonNull android.graphics.Shader.TileMode); + method @NonNull public static android.graphics.RenderEffect createChainEffect(@NonNull android.graphics.RenderEffect, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter); + method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float); + method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect); + } + public final class RenderNode { ctor public RenderNode(@Nullable String); method @NonNull public android.graphics.RecordingCanvas beginRecording(int, int); @@ -15640,6 +15653,7 @@ package android.graphics { method public boolean setPosition(@NonNull android.graphics.Rect); method public boolean setProjectBackwards(boolean); method public boolean setProjectionReceiver(boolean); + method public void setRenderEffect(@Nullable android.graphics.RenderEffect); method public boolean setRotationX(float); method public boolean setRotationY(float); method public boolean setRotationZ(float); diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java index 9fc0c8eb9d90..8b9e36a3b6b1 100644 --- a/graphics/java/android/graphics/RenderEffect.java +++ b/graphics/java/android/graphics/RenderEffect.java @@ -17,6 +17,7 @@ package android.graphics; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Shader.TileMode; import libcore.util.NativeAllocationRegistry; @@ -24,8 +25,6 @@ import libcore.util.NativeAllocationRegistry; /** * Intermediate rendering step used to render drawing commands with a corresponding * visual effect - * - * @hide */ public final class RenderEffect { @@ -99,7 +98,7 @@ public final class RenderEffect { /** * Create a {@link RenderEffect} that blurs the contents of the * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the - * specified radius along hte x and y axis. + * specified radius along the x and y axis. * @param radiusX Radius of blur along the X axis * @param radiusY Radius of blur along the Y axis * @param edgeTreatment Policy for how to blur content near edges of the blur kernel @@ -117,9 +116,159 @@ public final class RenderEffect { 0, edgeTreatment.nativeInt ) + ); + } + + /** + * Create a {@link RenderEffect} that renders the contents of the input {@link Bitmap}. + * This is useful to create an input for other {@link RenderEffect} types such as + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or + * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)} + * + * @param bitmap The source bitmap to be rendered by the created {@link RenderEffect} + */ + @NonNull + public static RenderEffect createBitmapEffect(@NonNull Bitmap bitmap) { + float right = bitmap.getWidth(); + float bottom = bitmap.getHeight(); + return new RenderEffect( + nativeCreateBitmapEffect( + bitmap.getNativeInstance(), + 0f, + 0f, + right, + bottom, + 0f, + 0f, + right, + bottom + ) ); } + /** + * Create a {@link RenderEffect} that renders the contents of the input {@link Bitmap}. + * This is useful to create an input for other {@link RenderEffect} types such as + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or + * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)} + * + * @param bitmap The source bitmap to be rendered by the created {@link RenderEffect} + * @param src Optional subset of the bitmap to be part of the rendered output. If null + * is provided, the entire bitmap bounds are used. + * @param dst Bounds of the destination which the bitmap is translated and scaled to be + * drawn into + */ + @NonNull + public static RenderEffect createBitmapEffect( + @NonNull Bitmap bitmap, + @Nullable Rect src, + @NonNull Rect dst + ) { + long bitmapHandle = bitmap.getNativeInstance(); + int left = src == null ? 0 : src.left; + int top = src == null ? 0 : src.top; + int right = src == null ? bitmap.getWidth() : src.right; + int bottom = src == null ? bitmap.getHeight() : src.bottom; + return new RenderEffect( + nativeCreateBitmapEffect( + bitmapHandle, + left, + top, + right, + bottom, + dst.left, + dst.top, + dst.right, + dst.bottom + ) + ); + } + + /** + * Create a filter that applies the color filter to the provided RenderEffect + * + * @param colorFilter ColorFilter applied to the content in the input RenderEffect + * @param renderEffect Source to be transformed by the specified {@link ColorFilter} + */ + @NonNull + public static RenderEffect createColorFilterEffect( + @NonNull ColorFilter colorFilter, + @NonNull RenderEffect renderEffect + ) { + return new RenderEffect( + nativeCreateColorFilterEffect( + colorFilter.getNativeInstance(), + renderEffect.getNativeInstance() + ) + ); + } + + /** + * Create a filter that applies the color filter to the contents of the + * {@link android.graphics.RenderNode} that this RenderEffect is installed on + * @param colorFilter ColorFilter applied to the content in the input RenderEffect + */ + @NonNull + public static RenderEffect createColorFilterEffect(@NonNull ColorFilter colorFilter) { + return new RenderEffect( + nativeCreateColorFilterEffect( + colorFilter.getNativeInstance(), + 0 + ) + ); + } + + /** + * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances + * combined by the specified {@link BlendMode} + * + * @param dst The Dst pixels used in blending, if null the source bitmap is used. + * @param src The Src pixels used in blending, if null the source bitmap is use + * @param blendMode The {@link BlendMode} to be used to combine colors from the two + * {@link RenderEffect}s + */ + @NonNull + public static RenderEffect createBlendModeEffect( + @NonNull RenderEffect dst, + @NonNull RenderEffect src, + @NonNull BlendMode blendMode + ) { + return new RenderEffect( + nativeCreateBlendModeEffect( + dst.getNativeInstance(), + src.getNativeInstance(), + blendMode.getXfermode().porterDuffMode + ) + ); + } + + /** + * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are + * treated as the source bitmap passed to 'outer', i.e. + * + * result = outer(inner(source)). + * + * Consumers should favor explicit chaining of {@link RenderEffect} instances at creation time + * rather than using chain effect. Chain effects are useful for situations where the input or + * output are provided from elsewhere and the input or output {@link RenderEffect} need to be + * changed. + * + * @param outer {@link RenderEffect} that consumes the output of {@param inner} as its input + * @param inner {@link RenderEffect} that is consumed as input by {@param outer} + */ + @NonNull + public static RenderEffect createChainEffect( + @NonNull RenderEffect outer, + @NonNull RenderEffect inner + ) { + return new RenderEffect( + nativeCreateChainEffect( + outer.getNativeInstance(), + inner.getNativeInstance() + ) + ); + } + private final long mNativeRenderEffect; /* only constructed from static factory methods */ @@ -141,5 +290,11 @@ public final class RenderEffect { float offsetX, float offsetY, long nativeInput); private static native long nativeCreateBlurEffect( float radiusX, float radiusY, long nativeInput, int edgeTreatment); + private static native long nativeCreateBitmapEffect( + long bitmapHandle, float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom); + private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput); + private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode); + private static native long nativeCreateChainEffect(long outer, long inner); private static native long nativeGetFinalizer(); } diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index d812c1a5595d..001ebacfd2cd 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -858,8 +858,6 @@ public final class RenderNode { * be blurred when this RenderNode is drawn into the destination. * @param renderEffect to be applied to the RenderNode. Passing null clears all previously * configured RenderEffects - * - * @hide */ public void setRenderEffect(@Nullable RenderEffect renderEffect) { nSetRenderEffect(mNativeRenderNode, diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp index 0ebd0ca720d8..97c40d695f97 100644 --- a/libs/hwui/jni/RenderEffect.cpp +++ b/libs/hwui/jni/RenderEffect.cpp @@ -48,6 +48,73 @@ static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX, return reinterpret_cast<jlong>(blurFilter.release()); } +static jlong createBitmapEffect( + JNIEnv* env, + jobject, + jlong bitmapHandle, + jfloat srcLeft, + jfloat srcTop, + jfloat srcRight, + jfloat srcBottom, + jfloat dstLeft, + jfloat dstTop, + jfloat dstRight, + jfloat dstBottom +) { + sk_sp<SkImage> image = android::bitmap::toBitmap(bitmapHandle).makeImage(); + SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); + SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); + sk_sp<SkImageFilter> bitmapFilter = + SkImageFilters::Image(image, srcRect, dstRect, kLow_SkFilterQuality); + return reinterpret_cast<jlong>(bitmapFilter.release()); +} + +static jlong createColorFilterEffect( + JNIEnv* env, + jobject, + jlong colorFilterHandle, + jlong inputFilterHandle +) { + auto* colorFilter = reinterpret_cast<const SkColorFilter*>(colorFilterHandle); + auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle); + sk_sp<SkImageFilter> colorFilterImageFilter = SkImageFilters::ColorFilter( + sk_ref_sp(colorFilter), sk_ref_sp(inputFilter), nullptr); + return reinterpret_cast<jlong>(colorFilterImageFilter.release()); +} + +static jlong createBlendModeEffect( + JNIEnv* env, + jobject, + jlong backgroundImageFilterHandle, + jlong foregroundImageFilterHandle, + jint blendmodeHandle +) { + auto* backgroundFilter = reinterpret_cast<const SkImageFilter*>(backgroundImageFilterHandle); + auto* foregroundFilter = reinterpret_cast<const SkImageFilter*>(foregroundImageFilterHandle); + SkBlendMode blendMode = static_cast<SkBlendMode>(blendmodeHandle); + sk_sp<SkImageFilter> xfermodeFilter = SkImageFilters::Blend( + blendMode, + sk_ref_sp(backgroundFilter), + sk_ref_sp(foregroundFilter) + ); + return reinterpret_cast<jlong>(xfermodeFilter.release()); +} + +static jlong createChainEffect( + JNIEnv* env, + jobject, + jlong outerFilterHandle, + jlong innerFilterHandle +) { + auto* outerImageFilter = reinterpret_cast<const SkImageFilter*>(outerFilterHandle); + auto* innerImageFilter = reinterpret_cast<const SkImageFilter*>(innerFilterHandle); + sk_sp<SkImageFilter> composeFilter = SkImageFilters::Compose( + sk_ref_sp(outerImageFilter), + sk_ref_sp(innerImageFilter) + ); + return reinterpret_cast<jlong>(composeFilter.release()); +} + static void RenderEffect_safeUnref(SkImageFilter* filter) { SkSafeUnref(filter); } @@ -59,7 +126,11 @@ static jlong getRenderEffectFinalizer(JNIEnv*, jobject) { static const JNINativeMethod gRenderEffectMethods[] = { {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer}, {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect}, - {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect} + {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect}, + {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect}, + {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect}, + {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect}, + {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect} }; int register_android_graphics_RenderEffect(JNIEnv* env) { |