RenderEffect in Android 12
Learn how to use the new RenderEffect API in Android 12 to efficiently add custom styles to your views like blurs, saturation, offset, and more. By Diayan Siat.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
RenderEffect in Android 12
20 mins
- Getting Started
- Adding Effects Before RenderEffect
- Understanding RenderEffect
- Rendering in Android
- Understanding RenderNode
- Understanding the Display List
- Understanding the Canvas
- Adding Some Awesome Effects
- Adding Blur
- Adjusting the Blur
- Adding Color Filter Effects
- Adjusting the Color Filter
- Using the Offset Effect
- Adjusting Offset
- Applying Chain Effects
- Adjusting the Chain Effect
- Applying Fullscreen Blur
- Where to Go From Here?
With so many social media and photo-sharing apps, it’s quite common nowadays to apply filters to images before sharing them. Having the ability to do that within the Android OS makes things easier and more efficient. Prior to Android 12, the process was much more complicated, as you had to define RenderNodes by interacting with the Canvas and then apply the effects directly with transformations and algorithms.
In Android 12, however, Google introduced the RenderEffect API. This enables developers to effortlessly apply graphic effects such as blurs, color filters and more to Views.
In this tutorial, you’ll create an app that lets a user apply different graphic effects to a photo. During the process, you’ll:
- Learn about the RenderEffect API.
- Explore key concepts like RenderNodes, Display List and Canvas.
- Learn how to use RenderEffect to implement simple graphic effects like blur, color filters and offset.
- Use multiple effects to create chain effects.
- Implement a full-screen blur.
If you’re new to Kotlin, check out our Kotlin introduction tutorial. If you’re completely new to Android development, familiarize yourself with our Beginning Android Development tutorials first.
Since Android 12 is still in beta at the time of writing, you can read on how to Set up an Android emulator with an Android 12 system image.
If you’re new to Kotlin, check out our Kotlin introduction tutorial. If you’re completely new to Android development, familiarize yourself with our Beginning Android Development tutorials first.
Since Android 12 is still in beta at the time of writing, you can read on how to Set up an Android emulator with an Android 12 system image.
Getting Started
Download the project materials by clicking Download Materials at the top or bottom of this tutorial. Open the starter project in Android Studio.
The app, called Instafilter, allows you to apply graphic effects to a photo of an adorable dog. Right now, it can’t do much, but you’ll add functionality to perform several types of effects soon.
Build and run. You’ll see the following:
The main files in the project are:
- MainActivity: The entry point of your app. It’s where you’ll write all your code.
- activity_main.xml: Contains all of the app’s user interface (UI) design.
Adding Effects Before RenderEffect
Adding graphic effects to Views was possible before the introduction of the RenderEffect API. The two main ways to achieve this were:
- Using third-party libraries like Glide and Blurry.
- Using the RenderScript API, which involves writing your own algorithm.
Two issues you may have to deal with if using these methods are:
- Potential random bugs caused by external libraries.
- Poor performance on certain devices caused by CPU, GPU and memory limitations.
Luckily, RenderEffect solves these issues at the GPU level since the Android OS itself handles the rendering.
Understanding RenderEffect
RenderEffect is an intermediary used to render drawing commands with corresponding visual effects. It allows users to apply effects such as blurs, color filters, shader effects and more to Views and rendering hierarchies. This means the effects are added to Views when they’re drawn on the screen at the GPU level.
It does this by leveraging the existing rendering pipeline to minimize excess calculation, resulting in better performance.
Android uses RenderNodes to render all its Views on the GPU to improve performance. RenderEffect hooks into this implementation to apply effects on the hardware-accelerated rendering pipeline.
Rendering in Android
Android renders views on the GPU, and it uses RenderNodes to build hardware-accelerated rendering hierarchies to ensure better performance. Each RenderNode contains display lists as well as a set of properties that affect the rendering of the display list.
The display list is produced on the main thread and then synced with the RenderThread. The RenderThread issues the display list operations to the GPU, which then renders the view.
A typical GPU Rendering graph of most apps prior to this API will show lots of inefficiencies:
To learn more about rendering in Android, check out this video from Google I/O.
Understanding RenderNode
Introduced in API 29, Android uses RenderNodes to build hardware-accelerated rendering hierarchies.
RenderNodes break down complex scenes, rendering content into smaller chunks that can individually be updated more cheaply. Instead of redrawing an entire scene, updating a portion of it requires updating the display list or properties of a small number of RenderNodes. Only when the content of a RenderNode has to be modified does its display list need to be rerecorded.
For example, when a user clicks an item in a RecyclerView, only that item’s display list is updated instead of rerecording the entire RecyclerView’s RenderNode. Another example is a TextView that has different paragraphs in it. Only an updated paragraph’s display list will be updated and the changes applied.
Understanding the Display List
A display list is a structure that stores the rendering information. This means that it stores rendering commands for Views. Graphic commands in Canvas — like drawBackground()
, drawDrawable()
and drawLine()
— all end up as operations in a display list. So the display list is a compact way of representing those operations and the parameters to the operations.
Understanding the Canvas
A Canvas is a 2D drawing surface that provides drawing commands for drawing to a bitmap. The Canvas provides graphic commands/operations like drawBackground()
, drawDrawable()
and drawText()
of views that end up as operations in a display list in the rendering pipeline.
Adding Some Awesome Effects
Enough of the boring theory, it’s now time to apply some effects to your cute dog photo. To add any effects using RenderEffect, you need to follow three main steps:
- Create an instance of RenderEffect.
- Implement RenderEffect’s factory static method.
- Then, set the effect to the appropriate view.
You’ll be working through a bunch of TODOs to complete this tutorial.
Adding Blur
To blur a view, you first need to implement the createBlurEffect
static factory method, which RenderEffect provides. Do this by replacing the TODO()
under //TODO 1: Add blur effect
with:
return RenderEffect.createBlurEffect(radiusX, radiusY, shader)
The code above returns a blur RenderEffect object by implementing the createBlurEffect
factory method, which takes three arguments. The first two arguments, radiusX
and radiusY
, specify the horizontal and vertical radii to which the blur effect should be rendered. Then, the third parameter indicates how to render TileMode
at the edges.
Now, replace //TODO 4: Add blur effect
with:
//1
if (isChecked) {
binding.saturationCheck.isClickable = false
binding.offsetEffectsCheck.isClickable = false
binding.chainEffectCheck.isClickable = false
//2
binding.blurSlider.isEnabled = true
//3
val blurEffect = createBlurEffect(DEFAULT_BLUR, DEFAULT_BLUR, Shader.TileMode.MIRROR)
//4
binding.imageView.setRenderEffect(blurEffect)
} else {
//5
binding.imageView.setRenderEffect(null)
//6
binding.blurSlider.isEnabled = false
binding.saturationCheck.isClickable = true
binding.offsetEffectsCheck.isClickable = true
binding.chainEffectCheck.isClickable = true
}
In the code above, here’s what’s happening:
- Determine if
blurCheckBox
is checked. - If it’s checked, enable the blur slider.
- Create a blur object by passing in default values for the X and Y radii.
- Apply the blur effect to the view.
- If
blurCheckBox
isn’t checked, remove the blur from the view. Removing blur is as easy as setting the RenderEffect tonull
. - Disable blur slider.
Build and run. Check Blur, and you’ll see something like this: