Android
RenderScript
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:
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 vonRenderScript
. Sie sollten auch so viel wie möglich wiederverwenden. Normalerweise müssen Sie IhreAllocation
Instanzen nur einmal erstellen und können sie in Zukunft wiederverwenden. Das gleiche gilt für die AusgabeBitmaps
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 verwendenContext
und damit verursachen keine Speicherlecks. Sie müssen sich jedoch immer noch um die üblichen Dinge wie auslaufendesView
,Activity
oder eineContext
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);