Zoeken…


Invoering

RenderScript is een scripttaal waarmee u grafische weergave met hoge prestaties en onbewerkte rekencode kunt schrijven. Het biedt een manier om kritieke prestatiecode te schrijven die het systeem later compileert naar native code voor de processor waarop het kan draaien. Dit kan de CPU, een multi-core CPU of zelfs de GPU zijn. Waar het uiteindelijk op draait, hangt af van vele factoren die niet direct beschikbaar zijn voor de ontwikkelaar, maar hangt ook af van welke architectuur de interne platformcompiler ondersteunt.

Ermee beginnen

RenderScript is een raamwerk om parallelle berekeningen met hoge prestaties op Android mogelijk te maken. Scripts die u schrijft, worden parallel uitgevoerd op alle beschikbare processors (bijv. CPU, GPU enz.), Zodat u zich kunt concentreren op de taak die u wilt bereiken in plaats van hoe deze is gepland en uitgevoerd.

Scripts zijn geschreven in een op C99 gebaseerde taal (C99 is een oude versie van de standaard voor programmeertaal C). Voor elk script wordt een Java-klasse gemaakt waarmee u eenvoudig kunt communiceren met RenderScript in uw Java-code.

Uw project opzetten

Er zijn twee verschillende manieren om toegang te krijgen tot RenderScript in uw app, met de Android Framework-bibliotheken of de ondersteuningsbibliotheek. Zelfs als u apparaten vóór API-niveau 11 niet wilt targeten, moet u altijd de implementatie van de ondersteuningsbibliotheek gebruiken omdat deze de compatibiliteit van apparaten op veel verschillende apparaten garandeert. Om de implementatie van de ondersteuningsbibliotheek te gebruiken, moet u minimaal build tools versie 18.1.0 !

Laten we nu het build.gradle-bestand van uw toepassing instellen:

android {
    compileSdkVersion 24
    buildToolsVersion '24.0.1'

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 24

        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true
    }
}
  • renderscriptTargetApi : Dit moet worden ingesteld op het vroegste versie-API-niveau dat alle benodigde RenderScript-functionaliteit biedt.
  • renderscriptSupportModeEnabled : Dit maakt het gebruik van de RenderScript-implementatie van de ondersteuningsbibliotheek mogelijk.

Hoe RenderScript werkt

Een typische RenderScript bestaat uit twee dingen: kernels en functies. Een functie is precies hoe het klinkt - het accepteert een invoer, doet iets met die invoer en retourneert een uitvoer. Een kernel is waar de echte kracht van RenderScript vandaan komt.

Een kernel is een functie die wordt uitgevoerd tegen elk element binnen een Allocation . Een Allocation kan worden gebruikt om gegevens zoals een Bitmap of een byte array door te geven aan een RenderScript en ze worden ook gebruikt om een resultaat van een kernel te krijgen. Kernels kunnen de ene Allocation als invoer en de andere als uitvoer gebruiken of ze kunnen de gegevens in slechts één Allocation .

U kunt uw eigen kernels schrijven, maar er zijn ook veel vooraf gedefinieerde kernels die u kunt gebruiken om algemene bewerkingen uit te voeren, zoals een Gaussiaans beeldvervaging.

Zoals reeds vermeld voor elk RenderScript-bestand wordt een klasse gegenereerd om ermee te communiceren. Deze klassen beginnen altijd met het voorvoegsel ScriptC_ gevolgd door de naam van het RenderScript-bestand. Als uw RenderScript-bestand example wordt genoemd, wordt de gegenereerde Java-klasse ScriptC_example . Alle vooraf gedefinieerde Scripts beginnen gewoon met het voorvoegsel Script - bijvoorbeeld het Gaussian Image Blur Script wordt ScriptIntrinsicBlur .

Uw eerste RenderScript schrijven

Het volgende voorbeeld is gebaseerd op een voorbeeld op GitHub. Het voert basisbeeldmanipulatie uit door de verzadiging van een beeld te wijzigen. Je kunt de broncode hier vinden en bekijken als je er zelf mee wilt spelen. Hier is een korte gif van hoe het resultaat eruit zou moeten zien:

demo foto

RenderScript-ketelplaat

RenderScript-bestanden bevinden zich in de map src/main/rs in uw project. Elk bestand heeft de bestandsextensie .rs en moet bovenaan twee #pragma instructies bevatten:

#pragma version(1)
#pragma rs java_package_name(your.package.name)
  • #pragma version(1) : dit kan worden gebruikt om de versie van RenderScript in te stellen die u gebruikt. Momenteel is er alleen versie 1.

  • #pragma rs java_package_name(your.package.name) : dit kan worden gebruikt om de pakketnaam in te stellen van de Java-klasse die wordt gegenereerd voor interactie met deze specifieke RenderScript.

Er is nog een #pragma u gewoonlijk in elk van uw RenderScript-bestanden moet instellen en deze wordt gebruikt om de zwevende puntprecisie in te stellen. U kunt de zwevende puntprecisie instellen op drie verschillende niveaus:

  • #pragma rs_fp_full : dit is de strengste instelling met de hoogste precisie en het is ook de standaardwaarde als u niets opgeeft. U moet dit gebruiken als u een hoge drijvende komma precisie nodig hebt.
  • #pragma rs_fp_relaxed : dit zorgt voor niet zo hoge precisie met een drijvend punt, maar op sommige architecturen maakt het een aantal optimalisaties mogelijk die ervoor kunnen zorgen dat uw scripts sneller worden uitgevoerd.
  • #pragma rs_fp_imprecise : dit zorgt voor nog minder precisie en moet worden gebruikt als precisie met drijvende komma niet echt belangrijk is voor uw script.

De meeste scripts kunnen gewoon #pragma rs_fp_relaxed tenzij je echt een hoge drijvende komma-precisie nodig hebt.

Globale variabelen

Nu kunt u net als in C-code globale variabelen of constanten definiëren:

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

float saturationLevel = 0.0f;

De variabele gMonoMult is van het type float3 . Dit betekent dat het een vector is die bestaat uit 3 floatnummers. De andere float naam saturationValue is niet constant, daarom kunt u deze tijdens runtime instellen op een waarde die u leuk vindt. U kunt dergelijke variabelen gebruiken in uw kernels of functies en daarom zijn ze een andere manier om invoer te geven aan of te ontvangen van uw RenderScripts. Voor elke niet-constante variabele worden een getter- en settermethode gegenereerd op de bijbehorende Java-klasse.

kernels

Maar laten we nu beginnen met het implementeren van de kernel. Voor de doeleinden van dit voorbeeld ga ik de wiskunde die in de kernel wordt gebruikt om de verzadiging van de afbeelding te wijzigen niet verklaren, maar in plaats daarvan zal ik me concentreren op hoe een kernel te implementeren en hoe deze te gebruiken. Aan het einde van dit hoofdstuk zal ik snel uitleggen wat de code in deze kernel eigenlijk doet.

Pitten in het algemeen

Laten we eerst de broncode bekijken:

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

Zoals je kunt zien, lijkt het op een normale C-functie met één uitzondering: het __attribute__((kernel)) tussen het __attribute__((kernel)) en de naam van de methode. Dit vertelt RenderScript dat deze methode een kernel is. Een ander ding dat je misschien opvalt is dat deze methode een parameter uchar4 accepteert en een andere waarde voor uchar4 retourneert. uchar4 is - net als de variabele float3 die we eerder in het hoofdstuk hebben besproken - een vector. Het bevat 4 uchar waarden die gewoon bytewaarden zijn in het bereik van 0 tot 255.

U kunt op verschillende manieren toegang krijgen tot deze individuele waarden, bijvoorbeeld in.r retourneert de byte die overeenkomt met het rode kanaal van een pixel. We gebruiken een uchar4 omdat elke pixel bestaat uit 4 waarden - r voor rood, g voor groen, b voor blauw en a voor alfa - en u kunt ze openen met deze steno. Met RenderScript kunt u ook een willekeurig aantal waarden uit een vector nemen en er een andere vector mee maken. in.rgb zou bijvoorbeeld een uchar3 waarde retourneren die alleen de rode, groene en blauwe delen van de pixel bevat zonder de alfawaarde.

Tijdens runtime roept RenderScript deze kernelmethode aan voor elke pixel van een afbeelding. Daarom zijn de retourwaarde en parameter slechts één uchar4 waarde. RenderScript zal veel van deze aanroepen parallel op alle beschikbare processors uitvoeren. Daarom is RenderScript zo krachtig. Dit betekent ook dat u zich geen zorgen hoeft te maken over draadsnijden of draadveiligheid, u kunt gewoon implementeren wat u voor elke pixel wilt doen en RenderScript zorgt voor de rest.

Wanneer u een kernel in Java oproept, levert u twee Allocation , een die de invoergegevens bevat en een andere die de uitvoer ontvangt. Uw kernelmethode wordt voor elke waarde in de Allocation aangeroepen en schrijft het resultaat naar de Allocation .

RenderScript Runtime API-methoden

In de kernel hierboven worden een paar methoden gebruikt die uit de doos worden geleverd. RenderScript biedt veel van dergelijke methoden en ze zijn van vitaal belang voor bijna alles wat u met RenderScript gaat doen. Onder hen zijn methoden om wiskundige bewerkingen uit te voeren, zoals sin() en hulpmethoden zoals mix() die twee waarden volgens andere waarden mengt. Maar er zijn ook methoden voor complexere bewerkingen bij het omgaan met vectoren, quaternions en matrices.

De officiële RenderScript Runtime API Reference is de beste bron die er is als u meer wilt weten over een bepaalde methode of op zoek bent naar een specifieke methode die een algemene bewerking uitvoert, zoals het berekenen van het puntproduct van een matrix. U kunt deze documentatie hier vinden .

Kernel-implementatie

Laten we nu eens kijken naar de details van wat deze kernel doet. Hier is de eerste regel in de kernel:

float4 f4 = rsUnpackColor8888(in);

De eerste regel roept de ingebouwde methode rsUnpackColor8888() die de uchar4 waarde uchar4 in een float4 waarde. Elk kleurkanaal wordt ook getransformeerd naar het bereik 0.0f - 1.0f waarbij 0.0f overeenkomt met een bytewaarde van 0 en 1.0f tot 255 . Het belangrijkste doel hiervan is om alle wiskunde in deze kernel een stuk eenvoudiger te maken.

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

Deze volgende regel gebruikt de ingebouwde methode dot() om het puntproduct van twee vectoren te berekenen. gMonoMult is een constante waarde die we een paar hoofdstukken hierboven hebben gedefinieerd. Omdat beide vectoren dezelfde lengte moeten hebben om het puntproduct te berekenen en ook omdat we alleen de kleurkanalen willen beïnvloeden en niet het alfakanaal van een pixel, gebruiken we de .rgb om een nieuwe float3 vector te krijgen die alleen de rode, groene en blauwe kleurkanalen. Degenen onder ons die zich nog van school herinneren hoe het dot-product werkt, zullen snel merken dat het dot-product slechts één waarde moet retourneren en geen vector. In de bovenstaande code wijzen we het resultaat toe aan een float3 vector. Dit is opnieuw een functie van RenderScript. Wanneer u een eendimensionaal nummer aan een vector toewijst, worden alle elementen in de vector op deze waarde ingesteld. Het volgende fragment zal bijvoorbeeld 2.0f toewijzen aan elk van de drie waarden in de float3 vector:

float3 example = 2.0f;

Het resultaat van het bovenstaande puntproduct wordt dus toegewezen aan elk element in de float3 vector hierboven.

Nu komt het gedeelte waarin we de globale variabele saturationLevel gebruiken om de verzadiging van de afbeelding te wijzigen:

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

Dit maakt gebruik van de ingebouwde methode mix() om de originele kleur te combineren met de puntproductvector die we hierboven hebben gemaakt. Hoe ze met elkaar worden gemengd, wordt bepaald door de variabele global saturationLevel . Dus een saturationLevel van 0.0f zorgt ervoor dat de resulterende kleur geen deel uitmaakt van de oorspronkelijke kleurwaarden en alleen uit waarden in de dotVector wat resulteert in een zwart-wit of grijs beeld. Een waarde van 1.0f zorgt ervoor dat de resulterende kleur volledig bestaat uit de oorspronkelijke kleurwaarden en waarden boven 1.0f vermenigvuldigen de oorspronkelijke kleuren om ze helderder en intenser te maken.

return rsPackColorTo8888(newColor);

Dit is het laatste deel in de kernel. rsPackColorTo8888() transformeert de float3 vector terug naar een uchar4 waarde die vervolgens wordt geretourneerd. De resulterende bytewaarden worden geklemd tot een bereik tussen 0 en 255, dus 1.0f hoger dan 1.0f resulteren in een bytewaarde van 255 en waarden lager dan 0.0 zullen resulteren in een bytewaarde van 0 .

En dat is de hele kernelimplementatie. Nu is er nog maar één deel over: Hoe een kernel op Java te bellen.

RenderScript aanroepen in Java

Basics

Zoals hierboven al werd vermeld voor elk RenderScript-bestand, wordt een Java-klasse gegenereerd waarmee u kunt communiceren met uw scripts. Deze bestanden hebben het voorvoegsel ScriptC_ gevolgd door de naam van het RenderScript-bestand. Om een instantie van deze klassen te maken, hebt u eerst een instantie van de RenderScript klasse nodig:

final RenderScript renderScript = RenderScript.create(context);

De statische methode create() kan worden gebruikt om een RenderScript instantie van een Context . U kunt vervolgens de Java-klasse instantiëren die voor uw script is gegenereerd. Als je het RenderScript-bestand saturation.rs wordt de klasse ScriptC_saturation :

final ScriptC_saturation script = new ScriptC_saturation(renderScript);

In deze klasse kunt u nu het verzadigingsniveau instellen en de kernel oproepen. De setter die is gegenereerd voor de variabele saturationLevel heeft het voorvoegsel set_ gevolgd door de naam van de variabele:

script.set_saturationLevel(1.0f);

Er is ook een getter voorafgegaan door get_ waarmee je het momenteel ingestelde verzadigingsniveau kunt krijgen:

float saturationLevel = script.get_saturationLevel();

Kernels die u in uw RenderScript definieert, worden voorafgegaan door forEach_ gevolgd door de naam van de Kernel-methode. De kernel die we hebben geschreven verwacht een Allocation en een Allocation als zijn parameters:

script.forEach_saturation(inputAllocation, outputAllocation);

De ingang Allocation moet het ingangsbeeld bevatten, en na de forEach_saturation methode is voltooid zal de uitgang toewijzing de gewijzigde beeldgegevens bevatten.

Als u eenmaal een Allocation instantie hebt, kunt u gegevens van en naar die Allocations kopiëren met behulp van de methoden copyFrom() en copyTo() . U kunt bijvoorbeeld een nieuwe afbeelding naar uw invoer `Allocatie kopiëren door te bellen naar:

inputAllocation.copyFrom(inputBitmap);

Op dezelfde manier kunt u de resultaatafbeelding ophalen door copyTo() aan te roepen in de output Allocation :

outputAllocation.copyTo(outputBitmap);

Toewijzingsinstanties maken

Er zijn veel manieren om een Allocation te maken. Als u eenmaal een Allocation instantie hebt, kunt u nieuwe gegevens van en naar die Allocations kopiëren met copyTo() en copyFrom() zoals hierboven uitgelegd, maar om ze in eerste instantie te maken, moet u weten met wat voor soort gegevens u precies werkt. Laten we beginnen met de invoer Allocation :

We kunnen de statische methode gebruikt createFromBitmap() om snel onze inbreng creëren Allocation van een Bitmap :

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

In dit voorbeeld van het ingevoerde beeld verandert nooit zodat we nooit nodig om de ingang te wijzigen Allocation weer. We kunnen het elke keer dat het saturationLevel verandert opnieuw gebruiken om een nieuwe output- Bitmap .

Het creëren van de output Allocation is een beetje ingewikkelder. Eerst moeten we een Type . Een Type wordt gebruikt om een Allocation te vertellen met wat voor soort gegevens het te maken heeft. Meestal gebruikt men de Type.Builder klasse om snel een geschikt Type . Laten we eerst de code bekijken:

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

We werken met een normale 32 bit (oftewel 4 byte) per pixel Bitmap met 4 kleurkanalen. Daarom kiezen we Element.RGBA_8888 om het Type te maken. Vervolgens gebruiken we de methoden setX() en setY() om de breedte en hoogte van de uitvoerafbeelding in te stellen op dezelfde grootte als de setY() . De methode create() vervolgens het Type met de parameters die we hebben opgegeven.

Zodra we de juiste Type kunnen we de uitvoer te maken Allocation met de statische methode createTyped() :

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

Nu zijn we bijna klaar. We hebben ook een output- Bitmap nodig waarin we de gegevens van de Allocation kunnen kopiëren. Om dit te doen gebruiken we de statische methode createBitmap() om een nieuwe lege Bitmap met dezelfde grootte en configuratie als de Bitmap .

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

En daarmee hebben we alle puzzelstukjes om onze RenderScript uit te voeren.

Volledig voorbeeld

Laten we dit allemaal samenvatten in één voorbeeld:

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

Conclusie

Met deze introductie zou u helemaal klaar moeten zijn om uw eigen RenderScript-kernels te schrijven voor eenvoudige beeldmanipulatie. Er zijn echter een paar dingen waar u rekening mee moet houden:

  • RenderScript werkt alleen in toepassingsprojecten : momenteel kunnen RenderScript-bestanden geen deel uitmaken van een bibliotheekproject.
  • Pas op voor geheugen : RenderScript is erg snel, maar het kan ook geheugenintensief zijn. Er mag nooit meer dan één exemplaar van RenderScript tegelijk zijn. Je moet ook zoveel mogelijk hergebruiken. Normaal gesproken hoeft u slechts eenmaal een Allocation instantie te maken en deze in de toekomst opnieuw te gebruiken. Hetzelfde geldt voor output Bitmaps of uw scriptinstanties. Hergebruik zoveel mogelijk.
  • Doe je werk op de achtergrond : Wederom is RenderScript erg snel, maar op geen enkele manier direct. Elke kernel, vooral complexe, moet worden uitgevoerd vanuit de UI-thread in een AsyncTask of iets dergelijks. Voor het grootste deel hoeft u zich echter geen zorgen te maken over geheugenlekken. Alle RenderScript-gerelateerde klassen gebruiken alleen de applicatie Context en veroorzaken daarom geen geheugenlekken. Maar u moet zich nog steeds zorgen maken over de gebruikelijke dingen zoals lekkende View , Activity of een Context die u zelf gebruikt!
  • Gebruik ingebouwde dingen : er zijn veel vooraf gedefinieerde scripts die taken uitvoeren zoals beeldvervaging, overvloeien, converteren, vergroten of verkleinen. En er zijn nog veel meer ingebouwde methoden die u helpen uw kernels te implementeren. De kans is groot dat als u iets wilt doen, er een script of methode is die al doet wat u probeert te doen. Vind het wiel niet opnieuw uit.

Als je snel aan de slag wilt gaan en met echte code wilt spelen, raad ik je aan een kijkje te nemen in het voorbeeld GitHub-project dat het exacte voorbeeld implementeert waarover in deze tutorial is gesproken. Je kunt het project hier vinden . Veel plezier met RenderScript!

Een afbeelding vervagen

Dit voorbeeld laat zien hoe u de Renderscript API kunt gebruiken om een afbeelding te vervagen (met Bitmap). In dit voorbeeld wordt ScriptInstrinsicBlur gebruikt van de 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;
    }
}    

Elk script heeft een kernel die de gegevens verwerkt en wordt meestal aangeroepen via de methode 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
    }
}

Hiermee is het voorbeeld hier afgesloten. Het wordt geadviseerd om de verwerking in een achtergrondthread te doen.

Een weergave vervagen

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

Gebruik:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow