Android
RenderScript
Recherche…
Introduction
RenderScript est un langage de script qui vous permet d'écrire un rendu graphique haute performance et un code de calcul brut. Il fournit un moyen d'écrire du code critique de performance que le système compile ultérieurement en code natif pour le processeur sur lequel il peut être exécuté. Cela pourrait être le CPU, un CPU multi-core, ou même le GPU. Cela dépend de nombreux facteurs qui ne sont pas facilement accessibles au développeur, mais dépend également de l’architecture prise en charge par le compilateur interne.
Commencer
RenderScript est un framework permettant des calculs parallèles hautes performances sur Android. Les scripts que vous écrivez seront exécutés en parallèle sur tous les processeurs disponibles (par ex. CPU, GPU, etc.), ce qui vous permettra de vous concentrer sur la tâche que vous souhaitez réaliser au lieu de la planifier et de l’exécuter.
Les scripts sont écrits dans un langage C99 (C99 étant une ancienne version du standard du langage de programmation C). Pour chaque script, une classe Java est créée qui vous permet d'interagir facilement avec RenderScript dans votre code Java.
Mise en place de votre projet
Il existe deux manières différentes d’accéder à RenderScript dans votre application, avec les bibliothèques Android Framework ou la bibliothèque de support. Même si vous ne souhaitez pas cibler les périphériques avant le niveau 11 de l'API, vous devez toujours utiliser l'implémentation de la bibliothèque de support car elle garantit la compatibilité des périphériques sur de nombreux périphériques différents. Pour utiliser l'implémentation de la bibliothèque de support, vous devez utiliser au moins les outils de construction version 18.1.0
!
Configurez maintenant le fichier build.gradle de votre application:
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
defaultConfig {
minSdkVersion 8
targetSdkVersion 24
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
}
-
renderscriptTargetApi
: Cela doit être défini sur le niveau de l'API le plus ancien de la version qui fournit toutes les fonctionnalités RenderScript dont vous avez besoin. -
renderscriptSupportModeEnabled
: active l'utilisation de l'implémentation RenderScript de la bibliothèque de support.
Comment fonctionne RenderScript
Un script Render typique se compose de deux éléments: les noyaux et les fonctions. Une fonction est exactement ce que cela ressemble - elle accepte une entrée, fait quelque chose avec cette entrée et renvoie une sortie. Un noyau est l'endroit d'où vient la puissance réelle de RenderScript.
Un noyau est une fonction exécutée sur chaque élément d'une Allocation
. Une Allocation
peut être utilisée pour transmettre des données comme un Bitmap
ou un tableau d' byte
à un RenderScript
et elles sont également utilisées pour obtenir un résultat d'un noyau. Les noyaux peuvent soit prendre une Allocation
en entrée et une autre en sortie, soit modifier les données dans une seule Allocation
.
Vous pouvez écrire vos noyaux, mais il existe également de nombreux noyaux prédéfinis que vous pouvez utiliser pour effectuer des opérations courantes telles qu'un flou d'image gaussien.
Comme déjà mentionné pour chaque fichier RenderScript, une classe est générée pour interagir avec elle. Ces classes commencent toujours par le préfixe ScriptC_
suivi du nom du fichier RenderScript. Par exemple, si votre fichier RenderScript est appelé example
la classe Java générée s'appellera ScriptC_example
. Tous les scripts prédéfinis commencent par le préfixe Script
- par exemple, le script gaussien de l'image est appelé ScriptIntrinsicBlur
.
Écrire votre premier script RenderScript
L'exemple suivant est basé sur un exemple sur GitHub. Il effectue une manipulation d'image de base en modifiant la saturation d'une image. Vous pouvez trouver le code source ici et le vérifier si vous voulez jouer avec lui-même. Voici un aperçu rapide de ce à quoi le résultat est censé ressembler:
RenderScript Boilerplate
Les fichiers RenderScript résident dans le dossier src/main/rs
de votre projet. Chaque fichier a l'extension de fichier .rs
et doit contenir deux instructions #pragma
en haut:
#pragma version(1)
#pragma rs java_package_name(your.package.name)
#pragma version(1)
: Ceci peut être utilisé pour définir la version de RenderScript que vous utilisez. Actuellement, il n'y a que la version 1.#pragma rs java_package_name(your.package.name)
: Ceci peut être utilisé pour définir le nom du paquet de la classe Java générée pour interagir avec ce RenderScript particulier.
Il y a un autre #pragma
vous devriez normalement définir dans chacun de vos fichiers RenderScript et il est utilisé pour définir la précision en virgule flottante. Vous pouvez définir la précision en virgule flottante sur trois niveaux différents:
-
#pragma rs_fp_full
: C'est le paramètre le plus strict avec la plus grande précision et c'est aussi la valeur par défaut si rien n'est spécifié. Vous devriez l'utiliser si vous avez besoin d'une précision élevée en virgule flottante. -
#pragma rs_fp_relaxed
: Ceci garantit non seulement une précision en virgule flottante aussi élevée, mais sur certaines architectures cela permet un tas d'optimisations qui peuvent accélérer l'exécution de vos scripts. -
#pragma rs_fp_imprecise
: Ceci garantit encore moins de précision et devrait être utilisé si la précision en virgule flottante n'a pas vraiment d'importance pour votre script.
La plupart des scripts peuvent simplement utiliser #pragma rs_fp_relaxed
sauf si vous avez vraiment besoin d'une précision élevée en virgule flottante.
Variables globales
Maintenant, tout comme en code C, vous pouvez définir des variables globales ou des constantes:
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
float saturationLevel = 0.0f;
La variable gMonoMult
est de type float3
. Cela signifie que c'est un vecteur composé de 3 nombres flottants. L'autre variable float
appelée saturationValue
n'est pas constante, vous pouvez donc la définir à l'exécution à une valeur qui vous plait. Vous pouvez utiliser des variables comme celle-ci dans vos noyaux ou fonctions et elles constituent donc un autre moyen de fournir des entrées ou des sorties à vos scripts Render. Pour chaque variable non constante, une méthode getter et setter sera générée sur la classe Java associée.
Graines
Mais maintenant, commençons à implémenter le noyau. Pour les besoins de cet exemple, je ne vais pas expliquer les calculs utilisés dans le noyau pour modifier la saturation de l'image, mais plutôt comment implémenter un noyau et comment l'utiliser. À la fin de ce chapitre, j'expliquerai rapidement ce que fait le code dans ce noyau.
Noyaux en général
Regardons d'abord le code source:
uchar4 __attribute__((kernel)) saturation(uchar4 in) {
float4 f4 = rsUnpackColor8888(in);
float3 dotVector = dot(f4.rgb, gMonoMult);
float3 newColor = mix(dotVector, f4.rgb, saturationLevel);
return rsPackColorTo8888(newColor);
}
Comme vous pouvez le voir, cela ressemble à une fonction C normale avec une exception: le __attribute__((kernel))
entre le type de retour et le nom de la méthode. C'est ce qui dit à RenderScript que cette méthode est un noyau. Une autre chose que vous pouvez remarquer est que cette méthode accepte un paramètre uchar4
et renvoie une autre valeur uchar4
. uchar4
est - comme la variable float3
nous avons parlé dans le chapitre précédent - un vecteur. Il contient 4 valeurs uchar
qui ne sont que des valeurs d'octet comprises entre 0 et 255.
Vous pouvez accéder à ces valeurs individuelles de différentes manières, par exemple, in.r
renverrait l'octet qui correspond au canal rouge d'un pixel. Nous utilisons un uchar4
puisque chaque pixel est composé de 4 valeurs - r
pour le rouge, g
pour le vert, b
pour le bleu et a
pour alpha - et vous pouvez y accéder avec ce raccourci. RenderScript vous permet également de prendre n'importe quel nombre de valeurs d'un vecteur et de créer un autre vecteur avec elles. Par exemple, in.rgb
renverrait une valeur uchar3
contenant uniquement les parties rouge, verte et bleue du pixel sans la valeur alpha.
A l'exécution, RenderScript appellera cette méthode du noyau pour chaque pixel d'une image, ce qui explique pourquoi la valeur de retour et le paramètre ne représentent qu'une seule valeur uchar4
. RenderScript exécutera plusieurs de ces appels en parallèle sur tous les processeurs disponibles, raison pour laquelle RenderScript est si puissant. Cela signifie également que vous n'avez pas à vous soucier des threads ou de la sécurité des threads, vous pouvez simplement implémenter ce que vous voulez faire pour chaque pixel et RenderScript se charge du reste.
Lorsque vous appelez un noyau en Java, vous devez fournir deux variables d’ Allocation
, l’une qui contient les données en entrée et l’autre qui reçoit la sortie. Votre méthode du noyau sera appelée pour chaque valeur de l’ Allocation
entrée et écrira le résultat à l’ Allocation
sortie.
Méthodes de RenderScript Runtime API
Dans le noyau ci-dessus quelques méthodes sont utilisées qui sont fournies hors de la boîte. RenderScript fournit beaucoup de ces méthodes et elles sont essentielles pour presque tout ce que vous allez faire avec RenderScript. Parmi celles-ci, il y a des méthodes pour effectuer des opérations mathématiques comme sin()
et des méthodes auxiliaires comme mix()
qui mélange deux valeurs selon d'autres valeurs. Mais il existe également des méthodes pour des opérations plus complexes avec des vecteurs, des quaternions et des matrices.
La référence officielle de RenderScript Runtime API est la meilleure ressource disponible si vous souhaitez en savoir plus sur une méthode particulière ou si vous recherchez une méthode spécifique qui effectue une opération commune telle que le calcul du produit scalaire d'une matrice. Vous pouvez trouver cette documentation ici .
Implémentation du noyau
Voyons maintenant les spécificités de ce que fait le noyau. Voici la première ligne du noyau:
float4 f4 = rsUnpackColor8888(in);
La première ligne appelle la méthode rsUnpackColor8888()
qui transforme la valeur uchar4
valeur float4
. Chaque canal de couleur est également transformé dans la plage 0.0f - 1.0f
où 0.0f
correspond à une valeur d'octet de 0
et 1.0f
à 255
. Le but principal de cette opération est de simplifier tous les calculs dans ce noyau.
float3 dotVector = dot(f4.rgb, gMonoMult);
Cette ligne suivante utilise la méthode intégrée dot()
pour calculer le produit scalaire de deux vecteurs. gMonoMult
est une valeur constante que nous avons définie ci-dessus. Puisque les deux vecteurs doivent avoir la même longueur pour calculer le produit scalaire et aussi parce que nous voulons simplement affecter les canaux de couleur et non le canal alpha d’un pixel, nous utilisons le raccourci .rgb
pour obtenir un nouveau vecteur float3
canaux de couleur rouge, vert et bleu. Ceux d'entre nous qui se souviennent encore de la façon dont fonctionne le produit scalaire remarqueront rapidement que le produit scalaire ne devrait renvoyer qu'une valeur et non un vecteur. Pourtant, dans le code ci-dessus, nous float3
le résultat à un vecteur float3
. Ceci est encore une fonctionnalité de RenderScript. Lorsque vous attribuez un numéro à une dimension à un vecteur, tous les éléments du vecteur seront définis sur cette valeur. Par exemple, l'extrait suivant attribue 2.0f
à chacune des trois valeurs du vecteur float3
:
float3 example = 2.0f;
Ainsi, le résultat du produit scalaire ci-dessus est attribué à chaque élément du vecteur float3
ci-dessus.
Maintenant vient la partie dans laquelle nous utilisons effectivement la variable globale saturationLevel
pour modifier la saturation de l'image:
float3 newColor = mix(dotVector, f4.rgb, saturationLevel);
Cela utilise la méthode intégrée mix()
pour mélanger la couleur originale avec le vecteur de produit scalaire que nous avons créé ci-dessus. La façon dont ils sont mélangés est déterminée par la variable globale saturationLevel
. Ainsi, un saturationLevel
de 0.0f
ne donnera aucune partie des valeurs de couleur d'origine à la couleur résultante et consistera uniquement en des valeurs dans le dotVector
ce qui dotVector
une image en noir et blanc ou grisée. Une valeur de 1.0f
fera que la couleur résultante sera complètement composée des valeurs de couleur d'origine et que les valeurs supérieures à 1.0f
multiplieront les couleurs d'origine pour les rendre plus lumineuses et plus intenses.
return rsPackColorTo8888(newColor);
C'est la dernière partie du noyau. rsPackColorTo8888()
transforme le vecteur float3
en une valeur uchar4
qui est ensuite renvoyée. Les valeurs d'octet résultantes sont bloquées dans une plage comprise entre 0 et 255; les valeurs flottantes supérieures à 1.0f
entraînent une valeur d'octet de 255 et des valeurs inférieures à 0.0
entraînent une valeur d'octet de 0
.
Et c'est toute l'implémentation du noyau. Il ne reste qu'une partie: comment appeler un noyau en Java.
Appeler RenderScript en Java
Les bases
Comme cela a déjà été mentionné ci-dessus pour chaque fichier RenderScript, une classe Java est générée qui vous permet d'interagir avec vos scripts. Ces fichiers ont le préfixe ScriptC_
suivi du nom du fichier RenderScript. Pour créer une instance de ces classes, vous devez d’abord disposer d’une instance de la classe RenderScript
:
final RenderScript renderScript = RenderScript.create(context);
La méthode statique create()
peut être utilisée pour créer une occurrence RenderScript
partir d'un Context
. Vous pouvez ensuite instancier la classe Java générée pour votre script. Si vous appelez le fichier RenderScript saturation.rs
alors la classe s'appellera ScriptC_saturation
:
final ScriptC_saturation script = new ScriptC_saturation(renderScript);
Sur cette classe, vous pouvez maintenant définir le niveau de saturation et appeler le noyau. Le setter qui a été généré pour la variable saturationLevel
aura le préfixe set_
suivi du nom de la variable:
script.set_saturationLevel(1.0f);
Il y a aussi un getter préfixé par get_
qui vous permet d'obtenir le niveau de saturation actuellement défini:
float saturationLevel = script.get_saturationLevel();
Les noyaux que vous définissez dans votre script RenderScript sont préfixés par forEach_
suivi du nom de la méthode du noyau. Le noyau que nous avons écrit attend une Allocation
entrée et une Allocation
sortie comme paramètres:
script.forEach_saturation(inputAllocation, outputAllocation);
L' Allocation
entrée doit contenir l'image d'entrée et, une fois la méthode forEach_saturation
terminée, l'allocation de sortie contiendra les données d'image modifiées.
Une fois que vous avez une instance Allocation
, vous pouvez copier des données depuis et vers ces Allocations
en utilisant les méthodes copyFrom()
et copyTo()
. Par exemple, vous pouvez copier une nouvelle image dans votre entrée `Allocation en appelant:
inputAllocation.copyFrom(inputBitmap);
De la même façon, vous pouvez récupérer l'image de résultat en appelant copyTo()
sur la sortie Allocation
:
outputAllocation.copyTo(outputBitmap);
Création d'instances d'allocation
Il existe plusieurs façons de créer une Allocation
. Une fois que vous avez une instance Allocation
, vous pouvez copier les nouvelles données depuis et vers ces Allocations
avec copyTo()
et copyFrom()
comme expliqué ci-dessus, mais pour les créer initialement, vous devez savoir avec quel type de données vous travaillez. Commençons par l' Allocation
entrée:
Nous pouvons utiliser la méthode statique createFromBitmap()
pour créer rapidement notre Allocation
entrée à partir d'un Bitmap
:
final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, image);
Dans cet exemple, l'image d'entrée ne change jamais, nous n'avons donc jamais besoin de modifier l' Allocation
entrée. Nous pouvons le réutiliser à chaque fois que saturationLevel
change pour créer un nouveau Bitmap
sortie.
Création de la sortie L' Allocation
est un peu plus complexe. Nous devons d'abord créer ce qu'on appelle un Type
. Un Type
est utilisé pour indiquer à une Allocation
avec quel type de données il a affaire. Généralement, on utilise la classe Type.Builder
pour créer rapidement un Type
approprié. Regardons d'abord le code:
final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
.setX(inputBitmap.getWidth())
.setY(inputBitmap.getHeight())
.create();
Nous travaillons avec un Bitmap
normal de 32 bits (ou 4 octets) par pixel avec 4 canaux de couleur. C'est pourquoi nous choisissons Element.RGBA_8888
pour créer le Type
. Ensuite, nous utilisons les méthodes setX()
et setY()
pour définir la largeur et la hauteur de l'image de sortie sur la même taille que l'image d'entrée. La méthode create()
crée alors le Type
avec les paramètres que nous avons spécifiés.
Une fois que nous avons le bon Type
nous pouvons créer la sortie Allocation
avec la méthode statique createTyped()
:
final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);
Maintenant, nous avons presque terminé. Nous avons également besoin d'un Bitmap
sortie dans lequel nous pouvons copier les données de l' Allocation
sortie. Pour ce faire, nous utilisons la méthode statique createBitmap()
pour créer un nouveau Bitmap
vide ayant la même taille et la même configuration que le Bitmap
entrée.
final Bitmap outputBitmap = Bitmap.createBitmap(
inputBitmap.getWidth(),
inputBitmap.getHeight(),
inputBitmap.getConfig()
);
Et avec cela, nous avons toutes les pièces du puzzle pour exécuter notre script Render.
Exemple complet
Maintenant, mettons tout cela ensemble dans un exemple:
// Create the RenderScript instance
final RenderScript renderScript = RenderScript.create(context);
// Create the input Allocation
final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, inputBitmap);
// Create the output Type.
final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
.setX(inputBitmap.getWidth())
.setY(inputBitmap.getHeight())
.create();
// And use the Type to create am output Allocation
final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);
// Create an empty output Bitmap from the input Bitmap
final Bitmap outputBitmap = Bitmap.createBitmap(
inputBitmap.getWidth(),
inputBitmap.getHeight(),
inputBitmap.getConfig()
);
// Create an instance of our script
final ScriptC_saturation script = new ScriptC_saturation(renderScript);
// Set the saturation level
script.set_saturationLevel(2.0f);
// Execute the Kernel
script.forEach_saturation(inputAllocation, outputAllocation);
// Copy the result data to the output Bitmap
outputAllocation.copyTo(outputBitmap);
// Display the result Bitmap somewhere
someImageView.setImageBitmap(outputBitmap);
Conclusion
Avec cette introduction, vous devriez être prêt à écrire vos propres noyaux RenderScript pour une manipulation simple des images. Cependant, il y a quelques choses à garder à l'esprit:
- RenderScript ne fonctionne que dans les projets d'application : Actuellement, les fichiers RenderScript ne peuvent pas faire partie d'un projet de bibliothèque.
- Attention à la mémoire : RenderScript est très rapide, mais il peut également nécessiter beaucoup de mémoire. Il ne devrait jamais y avoir plus d'une instance de
RenderScript
à tout moment. Vous devriez également réutiliser autant que possible. Normalement, il vous suffit de créer vos instances d'Allocation
une fois et de les réutiliser ultérieurement. Il en va de même pour lesBitmaps
sortie ou vos instances de script. Réutiliser autant que possible. - Faites votre travail en arrière-plan : à nouveau, RenderScript est très rapide, mais pas instantané. Tout noyau, en particulier les noyaux complexes, devrait être exécuté à partir du thread d'interface utilisateur dans un
AsyncTask
ou quelque chose de similaire. Cependant, la plupart du temps, vous n'avez pas à vous soucier des fuites de mémoire. Toutes les classes liées à RenderScript utilisent uniquement leContext
application et ne provoquent donc pas de fuite de mémoire. Mais vous devez toujours vous inquiéter des choses habituelles comme la fuite deView
,Activity
ou de toute instance deContext
que vous utilisez vous-même! - Utilisez des éléments intégrés : il existe de nombreux scripts prédéfinis qui effectuent des tâches telles que le flou, la fusion, la conversion et le redimensionnement des images. Et il y a beaucoup d'autres méthodes intégrées qui vous aident à implémenter vos noyaux. Les chances sont que si vous voulez faire quelque chose, il existe soit un script ou une méthode qui fait déjà ce que vous essayez de faire. Ne réinventez pas la roue.
Si vous voulez rapidement commencer à jouer avec du code, je vous recommande de regarder l'exemple de projet GitHub qui implémente l'exemple exact dont il est question dans ce tutoriel. Vous pouvez trouver le projet ici . Amusez-vous avec RenderScript!
Flou une image
Cet exemple montre comment utiliser l'API Renderscript pour flouter une image (en utilisant Bitmap). Cet exemple utilise ScriptInstrinsicBlur fourni par l'API Android Renderscript (API> = 17).
public class BlurProcessor {
private RenderScript rs;
private Allocation inAllocation;
private Allocation outAllocation;
private int width;
private int height;
private ScriptIntrinsicBlur blurScript;
public BlurProcessor(RenderScript rs) {
this.rs = rs;
}
public void initialize(int width, int height) {
blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
blurScript.setRadius(7f); // Set blur radius. 25 is max
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
// Bitmap must have ARGB_8888 config for this type
Type bitmapType = new Type.Builder(rs, Element.RGBA_8888(rs))
.setX(width)
.setY(height)
.setMipmaps(false) // We are using MipmapControl.MIPMAP_NONE
.create();
// Create output allocation
outAllocation = Allocation.createTyped(rs, bitmapType);
// Create input allocation with same type as output allocation
inAllocation = Allocation.createTyped(rs, bitmapType);
}
public void release() {
if (blurScript != null) {
blurScript.destroy();
blurScript = null;
}
if (inAllocation != null) {
inAllocation.destroy();
inAllocation = null;
}
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
}
public Bitmap process(Bitmap bitmap, boolean createNewBitmap) {
if (bitmap.getWidth() != width || bitmap.getHeight() != height) {
// Throw error if required
return null;
}
// Copy data from bitmap to input allocations
inAllocation.copyFrom(bitmap);
// Set input for blur script
blurScript.setInput(inAllocation);
// process and set data to the output allocation
blurScript.forEach(outAllocation);
if (createNewBitmap) {
Bitmap returnVal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
outAllocation.copyTo(returnVal);
return returnVal;
}
outAllocation.copyTo(bitmap);
return bitmap;
}
}
Chaque script a un noyau qui traite les données et il est généralement forEach
via la méthode forEach
.
public class BlurActivity extends AppCompatActivity {
private BlurProcessor blurProcessor;
@Override
public void onCreate(Bundle savedInstanceState) {
// setup layout and other stuff
blurProcessor = new BlurProcessor(Renderscript.create(getApplicationContext()));
}
private void loadImage(String path) {
// Load image to bitmap
Bitmap bitmap = loadBitmapFromPath(path);
// Initialize processor for this bitmap
blurProcessor.release();
blurProcessor.initialize(bitmap.getWidth(), bitmap.getHeight());
// Blur image
Bitmap blurImage = blurProcessor.process(bitmap, true); // Use newBitamp as false if you don't want to create a new bitmap
}
}
Ceci a conclu l'exemple ici. Il est conseillé de faire le traitement dans un thread d'arrière-plan.
Flou une vue
BlurBitmapTask.java
public class BlurBitmapTask extends AsyncTask<Bitmap, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private final RenderScript renderScript;
private boolean shouldRecycleSource = false;
public BlurBitmapTask(@NonNull Context context, @NonNull ImageView imageView) {
// Use a WeakReference to ensure
// the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
renderScript = RenderScript.create(context);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Bitmap... params) {
Bitmap bitmap = params[0];
return blurBitmap(bitmap);
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap == null || isCancelled()) {
return;
}
final ImageView imageView = imageViewReference.get();
if (imageView == null) {
return;
}
imageView.setImageBitmap(bitmap);
}
public Bitmap blurBitmap(Bitmap bitmap) {
// https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz
//Let's create an empty bitmap with the same size of the bitmap we want to blur
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
//Instantiate a new Renderscript
//Create an Intrinsic Blur Script using the Renderscript
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
//Create the in/out Allocations with the Renderscript and the in/out bitmaps
Allocation allIn = Allocation.createFromBitmap(renderScript, bitmap);
Allocation allOut = Allocation.createFromBitmap(renderScript, outBitmap);
//Set the radius of the blur
blurScript.setRadius(25.f);
//Perform the Renderscript
blurScript.setInput(allIn);
blurScript.forEach(allOut);
//Copy the final bitmap created by the out Allocation to the outBitmap
allOut.copyTo(outBitmap);
// recycle the original bitmap
// nope, we are using the original bitmap as well :/
if (shouldRecycleSource) {
bitmap.recycle();
}
//After finishing everything, we destroy the Renderscript.
renderScript.destroy();
return outBitmap;
}
public boolean isShouldRecycleSource() {
return shouldRecycleSource;
}
public void setShouldRecycleSource(boolean shouldRecycleSource) {
this.shouldRecycleSource = shouldRecycleSource;
}
}
Usage:
ImageView imageViewOverlayOnViewToBeBlurred
.setImageDrawable(ContextCompat.getDrawable(this, android.R.color.transparent));
View viewToBeBlurred.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);
viewToBeBlurred.setDrawingCacheEnabled(true);
BlurBitmapTask blurBitmapTask = new BlurBitmapTask(this, imageViewOverlayOnViewToBeBlurred);
blurBitmapTask.execute(Bitmap.createBitmap(viewToBeBlurred.getDrawingCache()));
viewToBeBlurred.setDrawingCacheEnabled(false);