Ricerca…


introduzione

RenderScript è un linguaggio di scripting che consente di scrivere rendering grafico ad alte prestazioni e codice di calcolo raw. Fornisce un mezzo per scrivere codice critico delle prestazioni che il sistema compila successivamente in codice nativo per il processore su cui può essere eseguito. Questa potrebbe essere la CPU, una CPU multi-core o anche la GPU. In definitiva, ciò dipende da molti fattori che non sono prontamente disponibili per lo sviluppatore, ma dipende anche da quale architettura supporta il compilatore di piattaforma interno.

Iniziare

RenderScript è un framework per consentire il calcolo parallelo ad alte prestazioni su Android. Gli script scritti verranno eseguiti su tutti i processori disponibili (ad es. CPU, GPU, ecc.) In parallelo, consentendo all'utente di concentrarsi sull'attività che si desidera ottenere anziché su come è pianificata ed eseguita.

Gli script sono scritti in un linguaggio basato su C99 (C99 è una vecchia versione dello standard del linguaggio di programmazione C). Per ogni script viene creata una classe Java che consente di interagire facilmente con RenderScript nel codice Java.

Impostazione del tuo progetto

Esistono due modi diversi per accedere a RenderScript nella tua app, con le librerie di Android Framework o la libreria di supporto. Anche se non desideri utilizzare i dispositivi di destinazione prima dell'API 11, devi sempre utilizzare l'implementazione della Libreria di supporto perché garantisce la compatibilità dei dispositivi su molti dispositivi diversi. Per utilizzare l'implementazione della libreria di supporto è necessario utilizzare almeno gli strumenti di compilazione versione 18.1.0 !

Ora consente di configurare il file build.gradle dell'applicazione:

android {
    compileSdkVersion 24
    buildToolsVersion '24.0.1'

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 24

        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true
    }
}
  • renderscriptTargetApi : questo dovrebbe essere impostato sulla versione più recente del livello API che fornisce tutte le funzionalità RenderScript richieste.
  • renderscriptSupportModeEnabled : abilita l'uso dell'implementazione RenderScript della libreria di supporto.

Come funziona RenderScript

Un tipico RenderScript consiste di due cose: i kernel e le funzioni. Una funzione è proprio quello che sembra: accetta un input, fa qualcosa con quell'input e restituisce un output. Un kernel è da dove proviene il vero potere di RenderScript.

Un kernel è una funzione che viene eseguita su ogni elemento all'interno di Allocation . Allocation può essere utilizzata per passare dati come una Bitmap o una matrice di byte a un RenderScript e vengono anche utilizzati per ottenere un risultato da un kernel. I kernel possono prendere una Allocation come input e un'altra come output oppure possono modificare i dati all'interno di una sola Allocation .

Puoi scrivere il tuo kernel, ma ci sono anche molti kernel predefiniti che puoi usare per eseguire operazioni comuni come un Gaussian Image Blur.

Come già accennato per ogni file RenderScript, viene generata una classe per interagire con esso. Queste classi iniziano sempre con il prefisso ScriptC_ seguito dal nome del file RenderScript. Ad esempio, se il tuo file RenderScript è chiamato example la classe Java generata verrà chiamata ScriptC_example . Tutti gli script predefiniti iniziano solo con lo Script prefisso, ad esempio lo script di sfocatura immagine gaussiana si chiama ScriptIntrinsicBlur .

Scrivi il tuo primo RenderScript

Il seguente esempio è basato su un esempio su GitHub. Esegue la manipolazione di base delle immagini modificando la saturazione di un'immagine. Puoi trovare il codice sorgente qui e verificarlo se vuoi giocarci da solo. Ecco una rapida gif di come dovrebbe apparire il risultato:

foto dimostrativa

RenderScript Boilerplate

I file RenderScript risiedono nella cartella src/main/rs nel tuo progetto. Ogni file ha l'estensione del file .rs e deve contenere due istruzioni #pragma nella parte superiore:

#pragma version(1)
#pragma rs java_package_name(your.package.name)
  • #pragma version(1) : può essere usato per impostare la versione di RenderScript che stai utilizzando. Attualmente c'è solo la versione 1.

  • #pragma rs java_package_name(your.package.name) : può essere usato per impostare il nome del pacchetto della classe Java generata per interagire con questo particolare RenderScript.

C'è un altro #pragma che dovresti normalmente impostare in ciascuno dei tuoi file RenderScript e che è usato per impostare la precisione in virgola mobile. È possibile impostare la precisione in virgola mobile su tre diversi livelli:

  • #pragma rs_fp_full : questa è l'impostazione più rigorosa con la massima precisione ed è anche il valore predefinito se non si specifica nulla. Dovresti usarlo se hai bisogno di una precisione in virgola mobile elevata.
  • #pragma rs_fp_relaxed : questo garantisce un'elevata precisione in virgola mobile, ma su alcune architetture consente una serie di ottimizzazioni che possono far sì che i tuoi script possano girare più velocemente.
  • #pragma rs_fp_imprecise : questo garantisce una precisione ancora minore e dovrebbe essere usato se la precisione in virgola mobile non è veramente importante per il tuo script.

La maggior parte degli script può usare #pragma rs_fp_relaxed meno che non sia veramente necessaria un'elevata precisione in virgola mobile.

Variabili globali

Ora, proprio come nel codice C, puoi definire variabili o costanti globali:

const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};

float saturationLevel = 0.0f;

La variabile gMonoMult è di tipo float3 . Ciò significa che è un vettore costituito da 3 numeri float. L'altra variabile float denominata saturationValue non è costante, pertanto è possibile impostarla in fase di esecuzione su un valore che ti piace. Puoi usare variabili come questa nei tuoi Kernel o funzioni e quindi sono un altro modo per dare input o ricevere output dai tuoi RenderScripts. Per ogni variabile non costante verrà generato un metodo getter e setter sulla classe Java associata.

Noccioli

Ma ora iniziamo ad implementare il Kernel. Per gli scopi di questo esempio non ho intenzione di spiegare la matematica usata nel kernel per modificare la saturazione dell'immagine, ma invece si concentrerà su come implementare un kernel e su come usarlo. Alla fine di questo capitolo spiegherò rapidamente cosa sta facendo il codice in questo kernel.

Kernels in generale

Diamo prima un'occhiata al codice sorgente:

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);
}

Come puoi vedere sembra una normale funzione C con un'eccezione: __attribute__((kernel)) tra il tipo di ritorno e il nome del metodo. Questo è ciò che dice a RenderScript che questo metodo è un kernel. Un'altra cosa che potresti notare è che questo metodo accetta un parametro uchar4 e restituisce un altro valore uchar4 . uchar4 è - come la variabile float3 discussa nel capitolo precedente - un vettore. Contiene 4 valori uchar che sono solo valori byte nell'intervallo compreso tra 0 e 255.

È possibile accedere a questi singoli valori in molti modi diversi, ad esempio in.r restituirebbe il byte che corrisponde al canale rosso di un pixel. Usiamo un uchar4 poiché ogni pixel è composto da 4 valori - r per rosso, g per verde, b per blu e a per alpha - e puoi accedervi con questa stenografia. RenderScript consente anche di prendere qualsiasi numero di valori da un vettore e creare un altro vettore con essi. Ad esempio in.rgb restituirebbe un valore uchar3 che contiene solo le parti rossa, verde e blu del pixel senza il valore alfa.

Durante il runtime RenderScript chiamerà questo metodo Kernel per ogni pixel di un'immagine, motivo per cui il valore restituito e il parametro sono solo un valore uchar4 . RenderScript eseguirà molte di queste chiamate in parallelo su tutti i processori disponibili, motivo per cui RenderScript è così potente. Ciò significa anche che non devi preoccuparti del thread o della sicurezza dei thread, puoi semplicemente implementare ciò che vuoi fare su ciascun pixel e RenderScript si prenderà cura di tutto il resto.

Quando si chiama un Kernel in Java vengono fornite due variabili di Allocation , una che contiene i dati di input e un'altra che riceverà l'output. Vostro metodo Kernel verrà chiamato per ogni valore nell'input Allocation e scriverà il risultato all'uscita Allocation .

Metodi dell'API Runtime di RenderScript

Nel kernel sopra sono usati alcuni metodi forniti fuori dalla scatola. RenderScript fornisce molti di questi metodi e sono fondamentali per qualsiasi cosa tu debba fare con RenderScript. Tra questi ci sono i metodi per fare operazioni matematiche come sin() e metodi helper come mix() che mescola due valori in base ad altri valori. Ma ci sono anche metodi per operazioni più complesse quando si tratta di vettori, quaternioni e matrici.

Il riferimento dell'API RenderScript Runtime ufficiale è la migliore risorsa là fuori se vuoi saperne di più su un particolare metodo o stai cercando un metodo specifico che esegua un'operazione comune come il calcolo del prodotto punto di una matrice. Puoi trovare questa documentazione qui .

Implementazione del kernel

Ora diamo un'occhiata alle specifiche di ciò che questo kernel sta facendo. Ecco la prima riga del kernel:

float4 f4 = rsUnpackColor8888(in);

Chiama la prima linea di costruzione metodo rsUnpackColor8888() che trasforma uchar4 valore di un float4 valori. Ogni canale di colore viene anche trasformato nell'intervallo 0.0f - 1.0f dove 0.0f corrisponde a un valore di byte di 0 e 1.0f a 255 . Lo scopo principale di questo è rendere molto più semplici tutti i calcoli in questo Kernel.

float3 dotVector = dot(f4.rgb, gMonoMult);

Questa riga successiva utilizza il metodo incorporato dot() per calcolare il prodotto punto di due vettori. gMonoMult è un valore costante abbiamo definito alcuni capitoli sopra. Dal momento che entrambi i vettori devono essere della stessa lunghezza per calcolare il prodotto punto e anche dal momento che vogliamo solo influenzare i canali di colore e non il canale alfa di un pixel usiamo la scorciatoia .rgb per ottenere un nuovo vettore float3 che contiene solo il canali di colore rosso, verde e blu. Quelli di noi che ancora ricordano a scuola come funziona il prodotto dot noteranno subito che il prodotto puntino dovrebbe restituire solo un valore e non un vettore. Tuttavia nel codice sopra stiamo assegnando il risultato a un vettore float3 . Questa è ancora una caratteristica di RenderScript. Quando assegni un numero unidimensionale a un vettore, tutti gli elementi nel vettore saranno impostati su questo valore. Ad esempio il seguente snippet assegnerà 2.0f a ciascuno dei tre valori nel vettore float3 :

float3 example = 2.0f;

Quindi il risultato del prodotto punto sopra è assegnato a ciascun elemento nel vettore float3 sopra.

Ora arriva la parte in cui effettivamente usiamo la variabile globale saturationLevel per modificare la saturazione dell'immagine:

float3 newColor = mix(dotVector, f4.rgb, saturationLevel);

Questo usa il metodo integrato mix() per mescolare insieme il colore originale con il vettore di punti prodotto che abbiamo creato sopra. Il modo in cui vengono mescolati insieme è determinato dalla variabile global saturationLevel . Quindi un livello di saturationLevel pari a 0.0f farà sì che il colore risultante non abbia parte dei valori di colore originali e consisterà solo di valori nel dotVector che si traduce in un'immagine in bianco e nero o in grigio. Un valore di 1.0f farà sì che il colore risultante sia completamente composto da valori di colore originali e valori superiori a 1.0f moltiplicheranno i colori originali per renderli più luminosi e intensi.

return rsPackColorTo8888(newColor);

Questa è l'ultima parte del kernel. rsPackColorTo8888() trasforma il vettore float3 in un valore uchar4 che viene quindi restituito. I valori di byte risultanti vengono bloccati su un intervallo compreso tra 0 e 255, pertanto valori float superiori a 1.0f generano un valore di byte pari a 255 e valori inferiori a 0.0 generano un valore di byte pari a 0 .

E questa è l'intera implementazione del kernel. Ora c'è solo una parte rimanente: come chiamare un kernel in Java.

Richiamo di RenderScript in Java

Nozioni di base

Come già detto sopra per ogni file RenderScript, viene generata una classe Java che consente di interagire con gli script. Questi file hanno il prefisso ScriptC_ seguito dal nome del file RenderScript. Per creare un'istanza di queste classi è necessario innanzitutto un'istanza della classe RenderScript :

final RenderScript renderScript = RenderScript.create(context);

Il metodo statico create() può essere utilizzato per creare un'istanza RenderScript da un Context . È quindi possibile creare un'istanza della classe Java che è stata generata per il tuo script. Se hai chiamato il file RenderScript saturation.rs la classe si chiamerà ScriptC_saturation :

final ScriptC_saturation script = new ScriptC_saturation(renderScript);

Su questa classe ora puoi impostare il livello di saturazione e chiamare il kernel. Il setter che è stato generato per la variabile saturationLevel avrà il prefisso set_ seguito dal nome della variabile:

script.set_saturationLevel(1.0f);

C'è anche un getter con prefisso get_ che ti permette di ottenere il livello di saturazione attualmente impostato:

float saturationLevel = script.get_saturationLevel();

I kernel definiti in RenderScript sono preceduti da forEach_ seguito dal nome del metodo Kernel. Il kernel che abbiamo scritto si aspetta Allocation input e Allocation output come parametri:

script.forEach_saturation(inputAllocation, outputAllocation);

L' Allocation input deve contenere l'immagine di input e, al termine del metodo forEach_saturation , l'allocazione di output conterrà i dati di immagine modificati.

Una volta Allocation un'istanza di Allocation è possibile copiare i dati da e verso tali Allocations utilizzando i metodi copyFrom() e copyTo() . Ad esempio è possibile copiare una nuova immagine nel proprio input `Assegnazione chiamando:

inputAllocation.copyFrom(inputBitmap);

Allo stesso modo è possibile recuperare l'immagine risultato chiamando copyTo() sull'uscita Allocation :

outputAllocation.copyTo(outputBitmap);

Creare istanze di allocazione

Esistono molti modi per creare Allocation . Una volta che hai un'istanza di Allocation puoi copiare nuovi dati da e verso quelle Allocations con copyTo() e copyFrom() come spiegato sopra, ma per crearli inizialmente devi sapere con che tipo di dati stai lavorando esattamente. Iniziamo con l' Allocation degli input:

Possiamo usare il metodo statico createFromBitmap() per creare rapidamente l' Allocation input da una Bitmap :

final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, image);

In questo esempio l'immagine di input non cambia mai, quindi non è più necessario modificare di nuovo l' Allocation input. Possiamo riutilizzarlo ogni volta che saturationLevel cambia per creare un nuovo Bitmap output.

Creare l'output L' Allocation è un po 'più complessa. Per prima cosa dobbiamo creare ciò che è chiamato un Type . Un Type è usato per dire Allocation con che tipo di dati sta trattando. Di solito si usa la classe Type.Builder per creare rapidamente un Type appropriato. Diamo prima un'occhiata al codice:

final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
        .setX(inputBitmap.getWidth())
        .setY(inputBitmap.getHeight())
        .create();

Stiamo lavorando con un normale Bitmap a 32 bit (o in altre parole 4 byte) per pixel con 4 canali di colore. Ecco perché stiamo scegliendo Element.RGBA_8888 per creare il Type . Quindi usiamo i metodi setX() e setY() per impostare la larghezza e l'altezza dell'immagine di output alla stessa dimensione dell'immagine di input. Il metodo create() crea quindi il Type con i parametri che abbiamo specificato.

Una volta che abbiamo il Type corretto possiamo creare l' Allocation dell'output con il metodo statico createTyped() :

final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);

Ora abbiamo quasi finito. Abbiamo anche bisogno di un output Bitmap in cui possiamo copiare i dati dalla uscita Allocation . Per fare questo usiamo il metodo statico createBitmap() per creare una nuova Bitmap vuota con le stesse dimensioni e la stessa configurazione della Bitmap input.

final Bitmap outputBitmap = Bitmap.createBitmap(
        inputBitmap.getWidth(),
        inputBitmap.getHeight(),
        inputBitmap.getConfig()
);

E con questo abbiamo tutti i pezzi del puzzle per eseguire il nostro RenderScript.

Esempio completo

Ora mettiamo insieme tutto questo in un solo esempio:

// 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);

Conclusione

Con questa introduzione dovresti essere pronto a scrivere i tuoi kernel RenderScript per una semplice manipolazione delle immagini. Tuttavia ci sono alcune cose da tenere a mente:

  • RenderScript funziona solo nei progetti di applicazione : attualmente i file RenderScript non possono far parte di un progetto di libreria.
  • Attenzione per la memoria : RenderScript è molto veloce, ma può anche richiedere un uso intensivo della memoria. Non ci dovrebbe mai essere più di una istanza di RenderScript in qualsiasi momento. Dovresti anche riutilizzare il più possibile. Normalmente è sufficiente creare le istanze di Allocation una sola volta e riutilizzarle in futuro. Lo stesso vale per le Bitmaps output o le istanze di script. Riutilizzare il più possibile.
  • Fai il tuo lavoro in background : Ancora RenderScript è molto veloce, ma non istantaneo in alcun modo. Qualsiasi kernel, specialmente quelli complessi, dovrebbe essere eseguito fuori dal thread dell'interfaccia utente in un AsyncTask o qualcosa di simile. Tuttavia per la maggior parte non devi preoccuparti di perdite di memoria. Tutte le classi correlate a RenderScript utilizzano solo l'applicazione Context e quindi non causano perdite di memoria. Ma devi ancora preoccuparti delle solite cose come perdite di View , Activity o qualsiasi istanza di Context che usi tu stesso!
  • Usa elementi incorporati : esistono numerosi script predefiniti che eseguono attività come sfocatura dell'immagine, fusione, conversione, ridimensionamento. E ci sono molti altri metodi incorporati che ti aiutano a implementare i tuoi kernel. È probabile che se vuoi fare qualcosa, c'è uno script o un metodo che fa già ciò che stai cercando di fare. Non reinventare la ruota.

Se vuoi iniziare subito a giocare con il codice reale ti consiglio di dare un'occhiata al progetto GitHub di esempio che implementa l'esatto esempio di cui si parla in questo tutorial. Puoi trovare il progetto qui . Divertiti con RenderScript!

Sfocare un'immagine

Questo esempio dimostra come usare l'API Renderscript per sfocare un'immagine (usando Bitmap). Questo esempio utilizza ScriptInstrinsicBlur fornito da Android Renderscript API (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;
    }
}    

Ogni script ha un kernel che elabora i dati ed è generalmente invocato tramite il metodo 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
    }
}

Questo ha concluso l'esempio qui. Si consiglia di eseguire l'elaborazione in un thread in background.

Sfocare una vista

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;
    }
}

Uso:

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow