blob: ad4c3fe86175d8685e9ab2ed7a61e3ff74de0844 [file] [log] [blame]
/*
* 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.
*
* <pre>
* {@code
* result = outer(inner(source))
* }
* </pre>
*
* 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()));
}
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 nativeGetFinalizer();
}