• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.Shader.TileMode;
22 
23 import libcore.util.NativeAllocationRegistry;
24 
25 /**
26  * Intermediate rendering step used to render drawing commands with a corresponding
27  * visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through
28  * {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through
29  * {@link Canvas#drawRenderNode(RenderNode)}.
30  * Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
31  * {@link android.view.View#setRenderEffect(RenderEffect)}
32  */
33 @android.ravenwood.annotation.RavenwoodKeepWholeClass
34 public final class RenderEffect {
35 
36     private static class RenderEffectHolder {
37         public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
38                 NativeAllocationRegistry.createMalloced(
39                         RenderEffect.class.getClassLoader(), nativeGetFinalizer());
40     }
41 
42     /**
43      * Create a {@link RenderEffect} instance that will offset the drawing content
44      * by the provided x and y offset.
45      * @param offsetX offset along the x axis in pixels
46      * @param offsetY offset along the y axis in pixels
47      */
48     @NonNull
createOffsetEffect(float offsetX, float offsetY)49     public static RenderEffect createOffsetEffect(float offsetX, float offsetY) {
50         return new RenderEffect(nativeCreateOffsetEffect(offsetX, offsetY, 0));
51     }
52 
53     /**
54      * Create a {@link RenderEffect} instance with the provided x and y offset
55      * @param offsetX offset along the x axis in pixels
56      * @param offsetY offset along the y axis in pixels
57      * @param input target RenderEffect used to render in the offset coordinates.
58      */
59     @NonNull
createOffsetEffect( float offsetX, float offsetY, @NonNull RenderEffect input )60     public static RenderEffect createOffsetEffect(
61             float offsetX,
62             float offsetY,
63             @NonNull RenderEffect input
64     ) {
65         return new RenderEffect(nativeCreateOffsetEffect(
66                     offsetX,
67                     offsetY,
68                     input.getNativeInstance()
69                 )
70         );
71     }
72 
73     /**
74      * Create a {@link RenderEffect} that blurs the contents of the optional input RenderEffect
75      * with the specified radius along the x and y axis. If no input RenderEffect is provided
76      * then all drawing commands issued with a {@link android.graphics.RenderNode} that this
77      * RenderEffect is installed in will be blurred
78      * @param radiusX Radius of blur along the X axis
79      * @param radiusY Radius of blur along the Y axis
80      * @param inputEffect Input RenderEffect that provides the content to be blurred, can be null
81      *                    to indicate that the drawing commands on the RenderNode are to be
82      *                    blurred instead of the input RenderEffect
83      * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
84      */
85     @NonNull
createBlurEffect( float radiusX, float radiusY, @NonNull RenderEffect inputEffect, @NonNull TileMode edgeTreatment )86     public static RenderEffect createBlurEffect(
87             float radiusX,
88             float radiusY,
89             @NonNull RenderEffect inputEffect,
90             @NonNull TileMode edgeTreatment
91     ) {
92         long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0;
93         return new RenderEffect(
94                 nativeCreateBlurEffect(
95                         radiusX,
96                         radiusY,
97                         nativeInputEffect,
98                         edgeTreatment.nativeInt
99                 )
100             );
101     }
102 
103     /**
104      * Create a {@link RenderEffect} that blurs the contents of the
105      * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
106      * specified radius along the x and y axis.
107      * @param radiusX Radius of blur along the X axis
108      * @param radiusY Radius of blur along the Y axis
109      * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
110      */
111     @NonNull
createBlurEffect( float radiusX, float radiusY, @NonNull TileMode edgeTreatment )112     public static RenderEffect createBlurEffect(
113             float radiusX,
114             float radiusY,
115             @NonNull TileMode edgeTreatment
116     ) {
117         return new RenderEffect(
118                 nativeCreateBlurEffect(
119                         radiusX,
120                         radiusY,
121                         0,
122                         edgeTreatment.nativeInt
123                 )
124             );
125     }
126 
127     /**
128      * Create a {@link RenderEffect} that renders the contents of the input {@link Bitmap}.
129      * This is useful to create an input for other {@link RenderEffect} types such as
130      * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
131      * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}
132      *
133      * @param bitmap The source bitmap to be rendered by the created {@link RenderEffect}
134      */
135     @NonNull
createBitmapEffect(@onNull Bitmap bitmap)136     public static RenderEffect createBitmapEffect(@NonNull Bitmap bitmap) {
137         float right = bitmap.getWidth();
138         float bottom = bitmap.getHeight();
139         return new RenderEffect(
140                 nativeCreateBitmapEffect(
141                         bitmap.getNativeInstance(),
142                         0f,
143                         0f,
144                         right,
145                         bottom,
146                         0f,
147                         0f,
148                         right,
149                         bottom
150                 )
151         );
152     }
153 
154     /**
155      * Create a {@link RenderEffect} that renders the contents of the input {@link Bitmap}.
156      * This is useful to create an input for other {@link RenderEffect} types such as
157      * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
158      * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}
159      *
160      * @param bitmap The source bitmap to be rendered by the created {@link RenderEffect}
161      * @param src Optional subset of the bitmap to be part of the rendered output. If null
162      *            is provided, the entire bitmap bounds are used.
163      * @param dst Bounds of the destination which the bitmap is translated and scaled to be
164      *            drawn into within the bounds of the {@link RenderNode} this RenderEffect is
165      *            installed on
166      */
167     @NonNull
createBitmapEffect( @onNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst )168     public static RenderEffect createBitmapEffect(
169             @NonNull Bitmap bitmap,
170             @Nullable Rect src,
171             @NonNull Rect dst
172     ) {
173         long bitmapHandle = bitmap.getNativeInstance();
174         int left = src == null ? 0 : src.left;
175         int top = src == null ? 0 : src.top;
176         int right = src == null ? bitmap.getWidth() : src.right;
177         int bottom = src == null ? bitmap.getHeight() : src.bottom;
178         return new RenderEffect(
179                 nativeCreateBitmapEffect(
180                         bitmapHandle,
181                         left,
182                         top,
183                         right,
184                         bottom,
185                         dst.left,
186                         dst.top,
187                         dst.right,
188                         dst.bottom
189                 )
190         );
191     }
192 
193     /**
194      * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
195      *
196      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
197      * @param renderEffect Source to be transformed by the specified {@link ColorFilter}
198      */
199     @NonNull
createColorFilterEffect( @onNull ColorFilter colorFilter, @NonNull RenderEffect renderEffect )200     public static RenderEffect createColorFilterEffect(
201             @NonNull ColorFilter colorFilter,
202             @NonNull RenderEffect renderEffect
203     ) {
204         return new RenderEffect(
205                 nativeCreateColorFilterEffect(
206                     colorFilter.getNativeInstance(),
207                     renderEffect.getNativeInstance()
208                 )
209             );
210     }
211 
212     /**
213      * Create a {@link RenderEffect} that applies the color filter to the contents of the
214      * {@link android.graphics.RenderNode} that this RenderEffect is installed on
215      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
216      */
217     @NonNull
createColorFilterEffect(@onNull ColorFilter colorFilter)218     public static RenderEffect createColorFilterEffect(@NonNull ColorFilter colorFilter) {
219         return new RenderEffect(
220                 nativeCreateColorFilterEffect(
221                         colorFilter.getNativeInstance(),
222                         0
223                 )
224         );
225     }
226 
227     /**
228      * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
229      * combined by the specified {@link BlendMode}
230      *
231      * @param dst The Dst pixels used in blending
232      * @param src The Src pixels used in blending
233      * @param blendMode The {@link BlendMode} to be used to combine colors from the two
234      *                  {@link RenderEffect}s
235      */
236     @NonNull
createBlendModeEffect( @onNull RenderEffect dst, @NonNull RenderEffect src, @NonNull BlendMode blendMode )237     public static RenderEffect createBlendModeEffect(
238             @NonNull RenderEffect dst,
239             @NonNull RenderEffect src,
240             @NonNull BlendMode blendMode
241     ) {
242         return new RenderEffect(
243                 nativeCreateBlendModeEffect(
244                         dst.getNativeInstance(),
245                         src.getNativeInstance(),
246                         blendMode.getXfermode().porterDuffMode
247                 )
248         );
249     }
250 
251     /**
252      * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
253      * 'inner' are treated as the source bitmap passed to 'outer', i.e.
254      *
255      * <pre>
256      * {@code
257      * result = outer(inner(source))
258      * }
259      * </pre>
260      *
261      * Consumers should favor explicit chaining of {@link RenderEffect} instances at creation time
262      * rather than using chain effect. Chain effects are useful for situations where the input or
263      * output are provided from elsewhere and the input or output {@link RenderEffect} need to be
264      * changed.
265      *
266      * @param outer {@link RenderEffect} that consumes the output of {@param inner} as its input
267      * @param inner {@link RenderEffect} that is consumed as input by {@param outer}
268      */
269     @NonNull
createChainEffect( @onNull RenderEffect outer, @NonNull RenderEffect inner )270     public static RenderEffect createChainEffect(
271             @NonNull RenderEffect outer,
272             @NonNull RenderEffect inner
273     ) {
274         return new RenderEffect(
275                 nativeCreateChainEffect(
276                     outer.getNativeInstance(),
277                     inner.getNativeInstance()
278                 )
279             );
280     }
281 
282     /**
283      * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
284      * This is useful to create an input for other {@link RenderEffect} types such as
285      * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
286      * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
287      * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
288      */
289     @NonNull
createShaderEffect(@onNull Shader shader)290     public static RenderEffect createShaderEffect(@NonNull Shader shader) {
291         return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
292     }
293 
294     /**
295      * Create a {@link RenderEffect} that executes the provided {@link RuntimeShader} and passes
296      * the contents of the {@link android.graphics.RenderNode} that this RenderEffect is installed
297      * on as an input to the shader.
298      * @param shader the runtime shader that will bind the inputShaderName to the RenderEffect input
299      * @param uniformShaderName the uniform name defined in the RuntimeShader's program to which
300      *                         the contents of the RenderNode will be bound
301      */
302     @NonNull
createRuntimeShaderEffect( @onNull RuntimeShader shader, @NonNull String uniformShaderName)303     public static RenderEffect createRuntimeShaderEffect(
304             @NonNull RuntimeShader shader, @NonNull String uniformShaderName) {
305         return new RenderEffect(
306                 nativeCreateRuntimeShaderEffect(shader.getNativeShaderBuilder(),
307                         uniformShaderName));
308     }
309 
310     private final long mNativeRenderEffect;
311 
312     /* only constructed from static factory methods */
RenderEffect(long nativeRenderEffect)313     private RenderEffect(long nativeRenderEffect) {
314         mNativeRenderEffect = nativeRenderEffect;
315         RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
316                 this, mNativeRenderEffect);
317     }
318 
319     /**
320      * Obtain the pointer to the underlying RenderEffect to be configured
321      * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
322      */
getNativeInstance()323     /* package */ long getNativeInstance() {
324         return mNativeRenderEffect;
325     }
326 
nativeCreateOffsetEffect( float offsetX, float offsetY, long nativeInput)327     private static native long nativeCreateOffsetEffect(
328             float offsetX, float offsetY, long nativeInput);
nativeCreateBlurEffect( float radiusX, float radiusY, long nativeInput, int edgeTreatment)329     private static native long nativeCreateBlurEffect(
330             float radiusX, float radiusY, long nativeInput, int edgeTreatment);
nativeCreateBitmapEffect( long bitmapHandle, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom)331     private static native long nativeCreateBitmapEffect(
332             long bitmapHandle, float srcLeft, float srcTop, float srcRight, float srcBottom,
333             float dstLeft, float dstTop, float dstRight, float dstBottom);
nativeCreateColorFilterEffect(long colorFilter, long nativeInput)334     private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
nativeCreateBlendModeEffect(long dst, long src, int blendmode)335     private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
nativeCreateChainEffect(long outer, long inner)336     private static native long nativeCreateChainEffect(long outer, long inner);
nativeCreateShaderEffect(long shader)337     private static native long nativeCreateShaderEffect(long shader);
nativeCreateRuntimeShaderEffect( long shaderBuilder, String inputShaderName)338     private static native long nativeCreateRuntimeShaderEffect(
339             long shaderBuilder, String inputShaderName);
nativeGetFinalizer()340     private static native long nativeGetFinalizer();
341 }
342