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:

image démo

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.0f0.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 les Bitmaps 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 le Context application et ne provoquent donc pas de fuite de mémoire. Mais vous devez toujours vous inquiéter des choses habituelles comme la fuite de View , Activity ou de toute instance de Context 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);


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow