Suche…


Einführung

RenderScript ist eine Skriptsprache, mit der Sie leistungsstarkes Grafik-Rendering und reinen Rechencode schreiben können. Es bietet die Möglichkeit, leistungskritischen Code zu schreiben, den das System später in nativen Code für den Prozessor kompiliert, auf dem es ausgeführt werden kann. Dies kann die CPU, eine Multi-Core-CPU oder sogar die GPU sein. Worauf es letztlich läuft, hängt von vielen Faktoren ab, die dem Entwickler nicht ohne weiteres zur Verfügung stehen, sondern auch von der Architektur, die der interne Plattformcompiler unterstützt.

Fertig machen

RenderScript ist ein Framework für die parallele Hochleistungsberechnung unter Android. Skripts, die Sie schreiben, werden auf allen verfügbaren Prozessoren (z. B. CPU, GPU usw.) parallel ausgeführt, sodass Sie sich auf die Aufgabe konzentrieren können, die Sie ausführen möchten, anstatt wie geplant und ausgeführt wird.

Skripts werden in einer C99-Sprache geschrieben (C99 ist eine alte Version des C-Programmiersprachenstandards). Für jedes Skript wird eine Java-Klasse erstellt, mit der Sie problemlos mit RenderScript in Ihrem Java-Code interagieren können.

Einrichten Ihres Projekts

Es gibt zwei verschiedene Möglichkeiten, auf RenderScript in Ihrer App zuzugreifen, mit den Android Framework-Bibliotheken oder der Support Library. Auch wenn Sie keine Geräte vor API Level 11 ansprechen möchten, sollten Sie immer die Support Library-Implementierung verwenden, da die Gerätekompatibilität für viele verschiedene Geräte gewährleistet ist. Um die Support Library-Implementierung zu verwenden, müssen Sie mindestens Build-Tools Version 18.1.0 !

Jetzt können Sie die build.gradle -Datei Ihrer Anwendung einrichten:

android {
    compileSdkVersion 24
    buildToolsVersion '24.0.1'

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 24

        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true
    }
}
  • renderscriptTargetApi : Dies sollte auf die früheste API-Version festgelegt werden, die alle erforderlichen RenderScript-Funktionen bietet.
  • renderscriptSupportModeEnabled : Dies ermöglicht die Verwendung der RenderScript-Implementierung der Support Library.

Wie RenderScript funktioniert

Ein typisches RenderScript besteht aus zwei Dingen: Kerneln und Funktionen. Eine Funktion ist genau das, was sie sich anhört - sie akzeptiert eine Eingabe, macht etwas mit dieser Eingabe und gibt eine Ausgabe zurück. Ein Kernel ist, woher die wirkliche Kraft von RenderScript kommt.

Ein Kernel ist eine Funktion, die für jedes Element in einer Allocation . Eine Allocation kann verwendet werden, um Daten wie eine Bitmap oder ein byte Array an ein RenderScript und sie werden auch verwendet, um ein Ergebnis aus einem Kernel zu erhalten. Kernel können entweder eine Allocation als Eingabe und eine andere als Ausgabe verwenden oder sie können die Daten in nur einer Allocation ändern.

Sie können Ihre eigenen Kernels schreiben, aber es gibt auch viele vordefinierte Kernel, mit denen Sie gängige Operationen wie Gaußsche Bildunschärfe ausführen können.

Wie bereits erwähnt, wird für jede RenderScript-Datei eine Klasse generiert, um damit zu interagieren. Diese Klassen beginnen immer mit dem Präfix ScriptC_ gefolgt vom Namen der RenderScript-Datei. Wenn Ihre RenderScript-Datei beispielsweise als example wird, heißt die generierte Java-Klasse ScriptC_example . Alle vordefinierten Skripts beginnen einfach mit dem Präfix Script - beispielsweise heißt das Gaußsche Bild-Unschärfeskript ScriptIntrinsicBlur .

Schreiben Sie Ihr erstes RenderScript

Das folgende Beispiel basiert auf einem Beispiel für GitHub. Es führt grundlegende Bildmanipulationen durch, indem die Sättigung eines Bildes geändert wird. Sie können den Quellcode hier finden und herausfinden, ob Sie selbst damit herumspielen wollen. Hier ist ein kurzer Hinweis darauf, wie das Ergebnis aussehen soll:

Demo-Bild

RenderScript-Zwischenablage

RenderScript-Dateien befinden sich im Ordner src/main/rs in Ihrem Projekt. Jede Datei hat die Dateierweiterung .rs und muss oben zwei #pragma Anweisungen enthalten:

#pragma version(1)
#pragma rs java_package_name(your.package.name)
  • #pragma version(1) : Hier können Sie die Version von RenderScript festlegen, die Sie verwenden. Derzeit gibt es nur Version 1.

  • #pragma rs java_package_name(your.package.name) : Hiermit kann der Paketname der generierten Java-Klasse festgelegt werden, um mit diesem bestimmten RenderScript zu interagieren.

Es gibt ein weiteres #pragma Sie normalerweise in jeder Ihrer RenderScript-Dateien #pragma sollten. Mit diesem Parameter wird die Gleitkomma-Genauigkeit festgelegt. Sie können die Gleitkommazahl auf drei verschiedene Ebenen einstellen:

  • #pragma rs_fp_full : Dies ist die strengste Einstellung mit der höchsten Genauigkeit, und es ist auch der Standardwert, wenn nichts angegeben wird. Sie sollten dies verwenden, wenn Sie eine hohe Gleitkomma-Genauigkeit benötigen.
  • #pragma rs_fp_relaxed : Dies gewährleistet zwar eine nicht ganz so hohe Gleitkomma-Genauigkeit, aber bei manchen Architekturen ermöglicht es eine Reihe von Optimierungen, die dazu führen können, dass Ihre Skripts schneller ausgeführt werden.
  • #pragma rs_fp_imprecise : Dies sorgt für eine noch geringere Genauigkeit und sollte verwendet werden, wenn die Gleitkomma-Genauigkeit für Ihr Skript keine Rolle spielt.

Die meisten Skripts können nur #pragma rs_fp_relaxed sei denn, Sie benötigen eine hohe Gleitkomma-Genauigkeit.

Globale Variablen

Wie in C-Code können Sie nun globale Variablen oder Konstanten definieren:

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

float saturationLevel = 0.0f;

Die Variable gMonoMult ist vom Typ float3 . Dies bedeutet, dass es sich um einen Vektor handelt, der aus 3 Float-Nummern besteht. Die andere float Variable mit dem Namen saturationValue ist nicht konstant. Daher können Sie sie zur Laufzeit auf einen gewünschten Wert setzen. Sie können Variablen wie diese in Ihren Kerneln oder Funktionen verwenden. Sie sind daher eine andere Möglichkeit, Eingaben in Ihre RenderScripts zu geben oder von ihnen zu empfangen. Für jede nicht konstante Variable wird eine Getter- und Setter-Methode für die zugehörige Java-Klasse generiert.

Kernel

Aber jetzt lass uns den Kernel implementieren. Für die Zwecke dieses Beispiels werde ich nicht die im Kernel verwendete Mathematik erläutern, um die Sättigung des Bildes zu ändern, sondern mich darauf konzentrieren, wie ein Kernel implementiert wird und wie er verwendet wird. Am Ende dieses Kapitels werde ich schnell erklären, was der Code in diesem Kernel tatsächlich tut.

Kernel im Allgemeinen

Schauen wir uns zuerst den Quellcode an:

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

Wie Sie sehen, sieht es aus wie eine normale C-Funktion mit einer Ausnahme: Das __attribute__((kernel)) zwischen dem Rückgabetyp und dem Methodennamen. Dies ist, was RenderScript sagt, dass diese Methode ein Kernel ist. Möglicherweise werden Sie auch feststellen, dass diese Methode einen uchar4 Parameter akzeptiert und einen anderen uchar4 Wert uchar4 . uchar4 ist - wie die Variable float3 , die wir im float3 Kapitel besprochen haben - ein Vektor. Es enthält 4 uchar Werte, bei denen es sich lediglich um Byte-Werte im Bereich von 0 bis 255 handelt.

Sie können auf diese einzelnen Werte auf viele verschiedene Arten zugreifen, z. B. würde in.r das Byte zurückgeben, das dem roten Kanal eines Pixels entspricht. Wir verwenden ein uchar4 da jedes Pixel aus 4 Werten besteht - r für Rot, g für Grün, b für Blau und a für Alpha - und Sie können mit dieser Abkürzung darauf zugreifen. Mit RenderScript können Sie auch eine beliebige Anzahl von Werten aus einem Vektor übernehmen und daraus einen anderen Vektor erstellen. Beispielsweise würde in.rgb einen uchar3 Wert zurückgeben, der nur den roten, grünen und blauen Teil des Pixels ohne den Alpha-Wert enthält.

Zur Laufzeit ruft RenderScript diese Kernel-Methode für jedes Pixel eines Bildes auf, weshalb der Rückgabewert und der Parameter nur ein uchar4 Wert sind. RenderScript führt viele dieser Aufrufe parallel auf allen verfügbaren Prozessoren aus, weshalb RenderScript so leistungsfähig ist. Dies bedeutet auch, dass Sie sich nicht um Threading oder Thread-Sicherheit sorgen müssen. Sie können einfach alles, was Sie tun möchten, für jedes Pixel implementieren, und RenderScript kümmert sich um den Rest.

Wenn Sie einen Kernel in Java aufrufen, geben Sie zwei Allocation Variablen an, eine, die die Eingabedaten enthält, und eine, die die Ausgabe erhält. Ihre Kernel-Methode wird für jeden Wert in der Input Allocation aufgerufen und schreibt das Ergebnis in die Output Allocation .

RenderScript-Laufzeit-API-Methoden

Im obigen Kernel werden einige Methoden verwendet, die im Lieferumfang enthalten sind. RenderScript bietet viele solcher Methoden und sie sind für fast alles, was Sie mit RenderScript machen werden, von entscheidender Bedeutung. Darunter befinden sich Methoden für mathematische Operationen wie sin() und Hilfsmethoden wie mix() die zwei Werte nach anderen Werten mischen. Es gibt aber auch Methoden für komplexere Operationen im Umgang mit Vektoren, Quaternionen und Matrizen.

Die offizielle RenderScript-Laufzeit-API-Referenz ist die beste Ressource, wenn Sie mehr über eine bestimmte Methode erfahren möchten oder nach einer bestimmten Methode suchen, die eine allgemeine Operation wie das Berechnen des Punktprodukts einer Matrix durchführt. Diese Dokumentation finden Sie hier .

Kernel-Implementierung

Schauen wir uns nun die Besonderheiten des Kernels an. Hier ist die erste Zeile im Kernel:

float4 f4 = rsUnpackColor8888(in);

Die erste Zeile ruft die eingebaute Methode rsUnpackColor8888() die den uchar4 Wert in einen float4 Wert float4 . Jeder Farbkanal wird auch in den Bereich 0.0f - 1.0f wobei 0.0f einem Byte-Wert von 0 und 1.0f bis 255 . Der Hauptzweck besteht darin, die gesamte Mathematik in diesem Kernel viel einfacher zu machen.

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

Diese nächste Zeile verwendet die integrierte Methode dot() , um das Punktprodukt zweier Vektoren zu berechnen. gMonoMult ist ein konstanter Wert, den wir oben ein paar Kapitel definiert haben. Da beide Vektoren dieselbe Länge haben müssen, um das Punktprodukt zu berechnen, und da wir nur die Farbkanäle und nicht den Alphakanal eines Pixels beeinflussen möchten, verwenden wir die .rgb , um einen neuen float3 Vektor zu erhalten, der nur den rote, grüne und blaue Farbkanäle. Diejenigen von uns, die sich noch an die Schule erinnern, wie das Dot-Produkt funktioniert, werden schnell feststellen, dass das Dot-Produkt nur einen Wert und nicht einen Vektor liefern soll. Im obigen Code weisen wir das Ergebnis jedoch einem float3 Vektor zu. Dies ist wieder eine Funktion von RenderScript. Wenn Sie einem Vektor eine eindimensionale Zahl zuweisen, werden alle Elemente im Vektor auf diesen Wert gesetzt. Das folgende Snippet weist 2.0f jedem der drei Werte im float3 Vektor float3 :

float3 example = 2.0f;

Das Ergebnis des obigen Punktprodukts wird also jedem Element im darüber float3 Vektor float3 zugewiesen.

Jetzt kommt der Teil, in dem wir tatsächlich die globale Variable saturationLevel , um die Sättigung des Bildes zu ändern:

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

Hierbei wird die eingebaute Methode mix() um die Originalfarbe mit dem oben erstellten Produktvektor zu mischen. Wie sie miteinander vermischt werden, hängt von der globalen Variable saturationLevel . Ein saturationLevel von 0.0f , dass die resultierende Farbe keinen Teil der ursprünglichen Farbwerte hat und nur aus Werten im dotVector die zu einem Schwarzweiß- oder ausgegrauten Bild führen. Ein Wert von 1.0f , dass die resultierende Farbe vollständig aus den ursprünglichen Farbwerten besteht. Werte über 1.0f multiplizieren die Originalfarben, um sie heller und intensiver zu machen.

return rsPackColorTo8888(newColor);

Dies ist der letzte Teil im Kernel. rsPackColorTo8888() wandelt den float3 Vektor zurück in einen uchar4 Wert, der zurückgegeben wird. Die resultierenden Bytewerte werden auf einen Bereich zwischen 0 und 255 festgelegt, sodass Float-Werte über 1.0f zu einem Byte-Wert von 255 führen und Werte unter 0.0 für einen Byte-Wert von 0 .

Und das ist die gesamte Kernel-Implementierung. Jetzt ist nur noch ein Teil übrig: Wie ruft man einen Kernel in Java auf?

RenderScript in Java aufrufen

Grundlagen

Wie bereits oben erwähnt, wird für jede RenderScript-Datei eine Java-Klasse generiert, mit der Sie mit Ihren Skripts interagieren können. Diese Dateien haben das Präfix ScriptC_ gefolgt vom Namen der RenderScript-Datei. Um eine Instanz dieser Klassen zu erstellen, benötigen Sie zunächst eine Instanz der RenderScript Klasse:

final RenderScript renderScript = RenderScript.create(context);

Die statische Methode create() kann verwendet werden, um eine RenderScript Instanz aus einem Context zu erstellen. Sie können dann die Java-Klasse instanziieren, die für Ihr Skript generiert wurde. Wenn Sie die RenderScript-Datei saturation.rs aufgerufen ScriptC_saturation wird die Klasse ScriptC_saturation :

final ScriptC_saturation script = new ScriptC_saturation(renderScript);

In dieser Klasse können Sie nun den Sättigungsgrad einstellen und den Kernel aufrufen. Der Setter, der für die Variable saturationLevel generiert wurde, hat das Präfix set_ gefolgt vom Namen der Variablen:

script.set_saturationLevel(1.0f);

Es gibt auch einen Getter mit dem Präfix get_ , mit dem Sie den aktuell eingestellten Sättigungspegel get_ können:

float saturationLevel = script.get_saturationLevel();

Kerneln, die Sie in Ihrem RenderScript definieren, sind forEach_ gefolgt vom Namen der Kernel-Methode vorangestellt. Der Kernel, den wir geschrieben haben, erwartet als Parameter eine Input Allocation und eine Output Allocation :

script.forEach_saturation(inputAllocation, outputAllocation);

Die Allocation muss das Eingangsbild enthalten, und nach dem forEach_saturation Verfahren die Ausgangszuweisung enthält die geänderten Bilddaten beendet ist.

Sobald Sie eine Allocation Instanz haben, können Sie Daten von und zu diesen Allocations kopieren, indem Sie die Methoden copyFrom() und copyTo() . Zum Beispiel können Sie ein neues Bild in Ihre Eingabe `Allocation kopieren, indem Sie Folgendes aufrufen:

inputAllocation.copyFrom(inputBitmap);

Auf dieselbe Weise können Sie das Ergebnisbild copyTo() indem Sie copyTo() in der Ausgabe Allocation aufrufen:

outputAllocation.copyTo(outputBitmap);

Allocation-Instanzen erstellen

Es gibt viele Möglichkeiten, eine Allocation zu erstellen. Sobald Sie eine Allocation Instanz haben, können Sie neue Daten von und zu diesen Allocations mit copyTo() und copyFrom() wie oben beschrieben, aber um sie zu erstellen, müssen Sie zunächst wissen, mit welchen Daten Sie genau arbeiten. Beginnen wir mit der Eingabe Allocation :

Wir können die statische Methode createFromBitmap() , um schnell unsere Eingabe erstellen Allocation aus einer Bitmap :

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

In diesem Beispiel nie das Eingangsbild ändert brauchen wir so nie die Eingabe ändern Allocation wieder. Wir können es jedes Mal wieder verwenden die saturationLevel ändert eine neue Ausgabe erstellen Bitmap .

Erstellen der Ausgabe Die Allocation ist etwas komplexer. Zuerst müssen wir einen sogenannten Type erstellen. Ein Type wird verwendet, um einer Allocation mitzuteilen, mit welchen Daten es sich handelt. Normalerweise verwendet man die Type.Builder Klasse, um schnell einen geeigneten Type zu erstellen. Schauen wir uns zuerst den Code an:

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

Wir arbeiten mit einer normalen 32-Bit- Bitmap (oder mit anderen Worten 4 Byte) mit 4 Farbkanälen. Deshalb wählen wir Element.RGBA_8888 , um den Type zu erstellen. Dann verwenden wir die Methoden setX() und setY() , um die Breite und Höhe des Ausgabebildes auf dieselbe Größe wie das Eingabebild festzulegen. Die Methode create() erstellt dann den Type mit den von uns angegebenen Parametern.

Sobald wir den richtigen Type haben, können wir die Ausgabe- Allocation mit der statischen Methode createTyped() :

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

Jetzt sind wir fast fertig. Wir brauchen auch einen Ausgang Bitmap , in dem wir die Daten aus der Ausgabe kopieren Allocation . Dazu müssen wir die statische Methode createBitmap() einen neuen leeren erstellen Bitmap mit der gleichen Größe und Konfiguration wie der Eingang Bitmap .

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

Und damit haben wir alle Puzzleteile, um unser RenderScript auszuführen.

Vollständiges Beispiel

Lassen Sie uns dies in einem Beispiel zusammenfassen:

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

Fazit

Mit dieser Einführung sollten Sie alle eigenen RenderScript-Kernel für die einfache Bildbearbeitung schreiben. Es gibt jedoch ein paar Dinge, die Sie beachten sollten:

  • RenderScript funktioniert nur in Anwendungsprojekten : Derzeit können RenderScript-Dateien nicht Teil eines Bibliotheksprojekts sein.
  • Achten Sie auf Speicher : RenderScript ist sehr schnell, kann jedoch auch speicherintensiv sein. RenderScript keinem Zeitpunkt sollte es mehr als eine Instanz von RenderScript . Sie sollten auch so viel wie möglich wiederverwenden. Normalerweise müssen Sie Ihre Allocation Instanzen nur einmal erstellen und können sie in Zukunft wiederverwenden. Das gleiche gilt für die Ausgabe Bitmaps oder Script - Instanzen. Verwenden Sie so viel wie möglich.
  • Machen Sie Ihre Arbeit im Hintergrund : Wieder ist RenderScript sehr schnell, aber in keiner Weise sofort. Jeder Kernel, besonders komplexe, sollten in einer AsyncTask oder etwas Ähnlichem vom UI-Thread ausgeführt werden. In den meisten Fällen müssen Sie sich jedoch nicht um Speicherlecks kümmern. Alle renderbezogenen Klassen nur die Anwendung verwenden Context und damit verursachen keine Speicherlecks. Sie müssen sich jedoch immer noch um die üblichen Dinge wie auslaufendes View , Activity oder eine Context kümmern, die Sie selbst verwenden.
  • Verwenden Sie integrierte Funktionen : Es gibt viele vordefinierte Skripts, die Aufgaben wie Verwischen, Verwischen, Konvertieren und Ändern der Größe von Bildern ausführen. Es gibt viele weitere integrierte Methoden, die Sie bei der Implementierung Ihrer Kernel unterstützen. Wenn Sie etwas tun wollen, gibt es wahrscheinlich ein Skript oder eine Methode, die bereits das tut, was Sie versuchen. Das Rad nicht neu erfinden.

Wenn Sie schnell loslegen und mit aktuellem Code herumspielen möchten, sollten Sie sich das GitHub-Beispielprojekt ansehen, in dem das in diesem Tutorial beschriebene Beispiel genau implementiert ist. Das Projekt finden Sie hier . Viel Spaß mit RenderScript!

Verwischen Sie ein Bild

In diesem Beispiel wird veranschaulicht, wie Sie mit der Renderscript-API ein Bild verwischen (mit Bitmap). In diesem Beispiel wird ScriptInstrinsicBlur verwendet, das von der Android Renderscript-API (API> = 17) bereitgestellt wird.

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

Jedes Skript verfügt über einen Kern, der die Daten verarbeitet, und es wird im Allgemeinen über die Methode forEach aufgerufen.

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

Damit ist das Beispiel hier abgeschlossen. Es wird empfohlen, die Verarbeitung in einem Hintergrundthread durchzuführen.

Verwischen Sie eine Ansicht

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

Verwendungszweck:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow