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
diff --git a/api/current.txt b/api/current.txt
index 6459eb4..8799598 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15599,6 +15599,19 @@
     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 @@
     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 05b57af..0ec1531 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -15581,6 +15581,19 @@
     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 @@
     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 9fc0c8e..8b9e36a 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 @@
 /**
  * Intermediate rendering step used to render drawing commands with a corresponding
  * visual effect
- *
- * @hide
  */
 public final class RenderEffect {
 
@@ -99,7 +98,7 @@
     /**
      * 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 @@
                         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 @@
             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 d812c1a..001ebac 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -858,8 +858,6 @@
      * 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 0ebd0ca..97c40d6 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -48,6 +48,73 @@
     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 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) {