/* * Copyright (C) 2020 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.NonNull; import android.annotation.Nullable; import android.graphics.Shader.TileMode; import libcore.util.NativeAllocationRegistry; /** * Intermediate rendering step used to render drawing commands with a corresponding * visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through * {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through * {@link Canvas#drawRenderNode(RenderNode)}. * Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through * {@link android.view.View#setRenderEffect(RenderEffect)} */ public final class RenderEffect { private static class RenderEffectHolder { public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY = NativeAllocationRegistry.createMalloced( RenderEffect.class.getClassLoader(), nativeGetFinalizer()); } /** * Create a {@link RenderEffect} instance that will offset the drawing content * by the provided x and y offset. * @param offsetX offset along the x axis in pixels * @param offsetY offset along the y axis in pixels */ @NonNull public static RenderEffect createOffsetEffect(float offsetX, float offsetY) { return new RenderEffect(nativeCreateOffsetEffect(offsetX, offsetY, 0)); } /** * Create a {@link RenderEffect} instance with the provided x and y offset * @param offsetX offset along the x axis in pixels * @param offsetY offset along the y axis in pixels * @param input target RenderEffect used to render in the offset coordinates. */ @NonNull public static RenderEffect createOffsetEffect( float offsetX, float offsetY, @NonNull RenderEffect input ) { return new RenderEffect(nativeCreateOffsetEffect( offsetX, offsetY, input.getNativeInstance() ) ); } /** * Create a {@link RenderEffect} that blurs the contents of the optional input RenderEffect * with the specified radius along the x and y axis. If no input RenderEffect is provided * then all drawing commands issued with a {@link android.graphics.RenderNode} that this * RenderEffect is installed in will be blurred * @param radiusX Radius of blur along the X axis * @param radiusY Radius of blur along the Y axis * @param inputEffect Input RenderEffect that provides the content to be blurred, can be null * to indicate that the drawing commands on the RenderNode are to be * blurred instead of the input RenderEffect * @param edgeTreatment Policy for how to blur content near edges of the blur kernel */ @NonNull public static RenderEffect createBlurEffect( float radiusX, float radiusY, @NonNull RenderEffect inputEffect, @NonNull TileMode edgeTreatment ) { long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0; return new RenderEffect( nativeCreateBlurEffect( radiusX, radiusY, nativeInputEffect, edgeTreatment.nativeInt ) ); } /** * 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 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 */ @NonNull public static RenderEffect createBlurEffect( float radiusX, float radiusY, @NonNull TileMode edgeTreatment ) { return new RenderEffect( nativeCreateBlurEffect( radiusX, radiusY, 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 within the bounds of the {@link RenderNode} this RenderEffect is * installed on */ @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 {@link RenderEffect} 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 {@link RenderEffect} 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 ) ); } /** * Create a {@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 * @param src The Src pixels used in blending * @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 {@link RenderEffect} that composes 'inner' with 'outer', such that the results of * 'inner' are treated as the source bitmap passed to 'outer', i.e. * *
* {@code * 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() ) ); } /** * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}. * This is useful to create an input for other {@link RenderEffect} types such as * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}. */ @NonNull public static RenderEffect createShaderEffect(@NonNull Shader shader) { return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance())); } /** * Create a {@link RenderEffect} that executes the provided {@link RuntimeShader} and passes * the contents of the {@link android.graphics.RenderNode} that this RenderEffect is installed * on as an input to the shader. * @param shader the runtime shader that will bind the inputShaderName to the RenderEffect input * @param uniformShaderName the uniform name defined in the RuntimeShader's program to which * the contents of the RenderNode will be bound */ @NonNull public static RenderEffect createRuntimeShaderEffect( @NonNull RuntimeShader shader, @NonNull String uniformShaderName) { return new RenderEffect( nativeCreateRuntimeShaderEffect(shader.getNativeShaderBuilder(), uniformShaderName)); } private final long mNativeRenderEffect; /* only constructed from static factory methods */ private RenderEffect(long nativeRenderEffect) { mNativeRenderEffect = nativeRenderEffect; RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation( this, mNativeRenderEffect); } /** * Obtain the pointer to the underlying RenderEffect to be configured * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)} */ /* package */ long getNativeInstance() { return mNativeRenderEffect; } private static native long nativeCreateOffsetEffect( 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 nativeCreateShaderEffect(long shader); private static native long nativeCreateRuntimeShaderEffect( long shaderBuilder, String inputShaderName); private static native long nativeGetFinalizer(); }