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