Android
RenderScript
Buscar..
Introducción
RenderScript es un lenguaje de scripting que le permite escribir renderizado gráfico de alto rendimiento y código computacional en bruto. Proporciona un medio para escribir código crítico de rendimiento que el sistema compila posteriormente en código nativo para el procesador en el que puede ejecutarse. Esto podría ser la CPU, una CPU de varios núcleos o incluso la GPU. Lo que finalmente se ejecuta depende de muchos factores que no están disponibles para el desarrollador, pero también depende de la arquitectura que admita el compilador de la plataforma interna.
Empezando
RenderScript es un marco para permitir la computación paralela de alto rendimiento en Android. Los scripts que escriba se ejecutarán en todos los procesadores disponibles (por ejemplo, CPU, GPU, etc.) en paralelo, lo que le permitirá concentrarse en la tarea que desea lograr en lugar de cómo se programa y se ejecuta.
Los scripts están escritos en un lenguaje basado en C99 (C99 es una versión antigua del estándar de lenguaje de programación C). Para cada Script se crea una clase Java que le permite interactuar fácilmente con RenderScript en su código Java.
Configurando tu proyecto
Existen dos formas diferentes de acceder a RenderScript en su aplicación, con las bibliotecas de Android Framework o la Biblioteca de soporte. Incluso si no desea apuntar a dispositivos antes del nivel de API 11, siempre debe usar la implementación de la biblioteca de soporte porque garantiza la compatibilidad de los dispositivos en muchos dispositivos diferentes. Para usar la implementación de la biblioteca de soporte, debe usar al menos herramientas de compilación versión 18.1.0
.
Ahora permite configurar el archivo build.gradle de su aplicación:
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
defaultConfig {
minSdkVersion 8
targetSdkVersion 24
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
}
-
renderscriptTargetApi
: debe establecerse en el nivel de API más antiguo de la versión, que proporciona toda la funcionalidad de RenderScript que necesita. -
renderscriptSupportModeEnabled
: Esto permite el uso de la implementación RenderScript de la biblioteca de soporte.
Cómo funciona RenderScript
Un RenderScript típico consta de dos cosas: Kernels y Funciones. Una función es exactamente lo que suena: acepta una entrada, hace algo con esa entrada y devuelve una salida. Un Kernel es de donde viene el verdadero poder de RenderScript.
Un Kernel es una función que se ejecuta contra cada elemento dentro de una Allocation
. Se puede usar una Allocation
para pasar datos como un Bitmap
o una matriz de byte
a un RenderScript
y también se usan para obtener un resultado de un núcleo. Los kernels pueden tomar una Allocation
como entrada y otra como salida o pueden modificar los datos dentro de una Allocation
.
Puede escribir sus Kernels, pero también hay muchos Kernels predefinidos que puede usar para realizar operaciones comunes como un Desenfoque de Imagen Gaussian.
Como ya se mencionó para cada archivo RenderScript, se genera una clase para interactuar con él. Estas clases siempre comienzan con el prefijo ScriptC_
seguido del nombre del archivo RenderScript. Por ejemplo, si su archivo RenderScript se llama example
, la clase Java generada se llamará ScriptC_example
. Todas las secuencias de comandos predefinidas comienzan con la Script
prefijo, por ejemplo, la secuencia de comandos de desenfoque de imagen gaussiana se llama ScriptIntrinsicBlur
.
Escribiendo tu primer RenderScript
El siguiente ejemplo se basa en un ejemplo en GitHub. Realiza la manipulación básica de la imagen modificando la saturación de una imagen. Puede encontrar el código fuente aquí y verificarlo si quiere jugar con él. Aquí hay un gif rápido de cómo se verá el resultado:
Plantilla de RenderScript
Los archivos RenderScript residen en la carpeta src/main/rs
en su proyecto. Cada archivo tiene la extensión de archivo .rs
y debe contener dos declaraciones #pragma
en la parte superior:
#pragma version(1)
#pragma rs java_package_name(your.package.name)
#pragma version(1)
: se puede usar para configurar la versión de RenderScript que está utilizando. Actualmente solo hay versión 1.#pragma rs java_package_name(your.package.name)
: se puede usar para establecer el nombre del paquete de la clase Java generada para interactuar con este RenderScript en particular.
Hay otro #pragma
que normalmente debe establecer en cada uno de sus archivos RenderScript y se usa para establecer la precisión del punto flotante. Puede establecer la precisión de punto flotante en tres niveles diferentes:
-
#pragma rs_fp_full
: esta es la configuración más estricta con la mayor precisión y también es el valor predeterminado si no se especifica nada. Debe usar esto si necesita alta precisión de punto flotante. -
#pragma rs_fp_relaxed
: Esto no garantiza una precisión de punto flotante tan alta, pero en algunas arquitecturas permite un montón de optimizaciones que pueden hacer que sus scripts se ejecuten más rápido. -
#pragma rs_fp_imprecise
: Esto asegura incluso menos precisión y debe usarse si la precisión de punto flotante no es realmente importante para su script.
La mayoría de los scripts solo pueden usar #pragma rs_fp_relaxed
menos que realmente necesite una alta precisión de punto flotante.
Variables globales
Ahora, al igual que en el código C, puede definir variables globales o constantes:
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
float saturationLevel = 0.0f;
La variable gMonoMult
es de tipo float3
. Esto significa que es un vector que consta de 3 números flotantes. La otra variable float
llamada saturationValue
no es constante, por lo tanto, puede establecerla en el tiempo de ejecución en el valor que desee. Puede usar variables como esta en sus Kernels o funciones y, por lo tanto, son otra forma de dar entrada o recibir salida de sus RenderScripts. Para cada variable no constante, se generará un método de obtención y establecimiento en la clase Java asociada.
Kernels
Pero ahora vamos a empezar a implementar el Kernel. Para los propósitos de este ejemplo, no voy a explicar las matemáticas utilizadas en el Kernel para modificar la saturación de la imagen, sino que me centraré en cómo implementar un Kernel y cómo usarlo. Al final de este capítulo, explicaré rápidamente lo que realmente está haciendo el código en este Kernel.
Kernels en general
Veamos primero el código fuente:
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);
}
Como puede ver, parece una función normal de C con una excepción: el __attribute__((kernel))
entre el tipo de retorno y el nombre del método. Esto es lo que le dice a RenderScript que este método es un Kernel. Otra cosa que podría notar es que este método acepta un parámetro uchar4
y devuelve otro valor uchar4
. uchar4
es, como la variable float3
que analizamos en el capítulo anterior, un vector. Contiene 4 uchar
valores que son solo valores de bytes en el rango de 0 a 255.
Puede acceder a estos valores individuales de muchas maneras diferentes, por ejemplo, in.r
devolverá el byte que corresponde al canal rojo de un píxel. Usamos un uchar4
ya que cada píxel se compone de 4 valores: r
para rojo, g
para verde, b
para azul y a
para alfa, y puedes acceder a ellos con esta taquigrafía. RenderScript también le permite tomar cualquier número de valores de un vector y crear otro vector con ellos. Por ejemplo, in.rgb
devolvería un valor uchar3
que solo contiene las partes roja, verde y azul del píxel sin el valor alfa.
En tiempo de ejecución, RenderScript llamará a este método Kernel para cada píxel de una imagen, por lo que el valor de retorno y el parámetro son solo un valor de uchar4
. RenderScript ejecutará muchas de estas llamadas en paralelo en todos los procesadores disponibles, por lo que RenderScript es tan poderoso. Esto también significa que no tiene que preocuparse por los hilos o la seguridad de los hilos, simplemente puede implementar lo que quiera hacer en cada píxel y RenderScript se encarga del resto.
Cuando llama a un Kernel en Java, proporciona dos variables de Allocation
, una que contiene los datos de entrada y otra que recibirá la salida. Se llamará al método Kernel para cada valor en la Allocation
entrada y se escribirá el resultado en la Allocation
salida.
Métodos de la API de RenderScript Runtime
En el Kernel anterior se utilizan algunos métodos que se proporcionan de forma inmediata. RenderScript proporciona muchos de estos métodos y son vitales para casi cualquier cosa que vaya a hacer con RenderScript. Entre ellos se encuentran métodos para realizar operaciones matemáticas como sin()
y métodos auxiliares como mix()
que mezclan dos valores de acuerdo con otros valores. Pero también hay métodos para operaciones más complejas cuando se trata de vectores, cuaterniones y matrices.
La referencia oficial de la API de RenderScript Runtime es el mejor recurso que existe si desea saber más acerca de un método en particular o si está buscando un método específico que realice una operación común, como calcular el producto de puntos de una matriz. Puedes encontrar esta documentación aquí .
Implementacion de Kernel
Ahora echemos un vistazo a los detalles de lo que está haciendo este núcleo. Aquí está la primera línea en el Kernel:
float4 f4 = rsUnpackColor8888(in);
La primera línea llama al método construida en rsUnpackColor8888()
que transforma la uchar4
valor a un float4
valores. Cada canal de color también se transforma en el rango de 0.0f - 1.0f
donde 0.0f
corresponde a un valor de byte de 0
y 1.0f
a 255
. El propósito principal de esto es hacer que todas las matemáticas en este Kernel sean mucho más simples.
float3 dotVector = dot(f4.rgb, gMonoMult);
La siguiente línea utiliza el método incorporado en dot()
para calcular el producto de punto de dos vectores. gMonoMult
es un valor constante que definimos en algunos capítulos anteriores. Dado que ambos vectores deben ser de la misma longitud para calcular el producto de puntos y también como solo queremos afectar a los canales de color y no al canal alfa de un píxel, usamos la abreviatura .rgb
para obtener un nuevo vector float3
que solo contiene el Canales de color rojo, verde y azul. Aquellos de nosotros que aún recordamos de la escuela cómo funciona el producto de puntos notaremos rápidamente que el producto de puntos debe devolver solo un valor y no un vector. Sin embargo, en el código anterior estamos asignando el resultado a un vector float3
. Esto es de nuevo una característica de RenderScript. Cuando asigna un número unidimensional a un vector, todos los elementos del vector se ajustarán a este valor. Por ejemplo, el siguiente fragmento de 2.0f
asignará 2.0f
a cada uno de los tres valores en el vector float3
:
float3 example = 2.0f;
Por lo tanto, el resultado del producto punto anterior se asigna a cada elemento en el vector float3
anterior.
Ahora viene la parte en la que realmente usamos la variable global saturationLevel
para modificar la saturación de la imagen:
float3 newColor = mix(dotVector, f4.rgb, saturationLevel);
Esto utiliza el método integrado mix()
para mezclar el color original con el vector de producto de puntos que creamos anteriormente. La forma en que se mezclan entre sí está determinada por la variable global saturationLevel
. Por lo tanto, un nivel de saturationLevel
de 0.0f
hará que el color resultante no forme parte de los valores de color originales y solo constará de valores en el dotVector
que dan como resultado una imagen en blanco y negro o en gris. Un valor de 1.0f
hará que el color resultante se componga completamente de los valores de color originales y los valores superiores a 1.0f
multiplicarán los colores originales para hacerlos más brillantes e intensos.
return rsPackColorTo8888(newColor);
Esta es la última parte en el núcleo. rsPackColorTo8888()
transforma el vector float3
nuevo a un valor de uchar4
que luego se devuelve. Los valores de byte resultantes se fijan en un rango entre 0 y 255, por lo que los valores flotantes superiores a 1.0f
darán como resultado un valor de byte de 255 y los valores inferiores a 0.0
darán como resultado un valor de byte de 0
.
Y esa es toda la implementación del Kernel. Ahora solo queda una parte: Cómo llamar a un kernel en Java.
Llamando a RenderScript en Java
Lo esencial
Como ya se mencionó anteriormente para cada archivo RenderScript, se genera una clase Java que le permite interactuar con sus scripts. Estos archivos tienen el prefijo ScriptC_
seguido del nombre del archivo RenderScript. Para crear una instancia de estas clases, primero necesita una instancia de la clase RenderScript
:
final RenderScript renderScript = RenderScript.create(context);
El método estático create()
se puede usar para crear una instancia de RenderScript
desde un Context
. Luego puede crear una instancia de la clase Java que se generó para su script. Si llamó al archivo RenderScript saturation.rs
, la clase se llamará ScriptC_saturation
:
final ScriptC_saturation script = new ScriptC_saturation(renderScript);
En esta clase, ahora puede establecer el nivel de saturación y llamar al Kernel. El setter que se generó para la variable saturationLevel
tendrá el prefijo set_
seguido del nombre de la variable:
script.set_saturationLevel(1.0f);
También hay un prefijo getter con get_
que le permite obtener el nivel de saturación establecido actualmente:
float saturationLevel = script.get_saturationLevel();
Los kernels que defina en su RenderScript tienen el prefijo forEach_
seguido del nombre del método Kernel. El Kernel que hemos escrito espera una Allocation
entrada y una Allocation
salida como sus parámetros:
script.forEach_saturation(inputAllocation, outputAllocation);
La Allocation
entrada debe contener la imagen de entrada, y una vez que el método forEach_saturation
haya finalizado, la asignación de salida contendrá los datos de imagen modificados.
Una vez que tenga una instancia de Allocation
, puede copiar datos desde y hacia esas Allocations
utilizando los métodos copyFrom()
y copyTo()
. Por ejemplo, puede copiar una nueva imagen en su entrada `Asignación llamando:
inputAllocation.copyFrom(inputBitmap);
De la misma manera que puede recuperar la imagen de resultado llamando a copyTo()
en la Allocation
salida:
outputAllocation.copyTo(outputBitmap);
Creación de instancias de asignación
Hay muchas formas de crear una Allocation
. Una vez que tenga una instancia de Allocation
, puede copiar nuevos datos desde y hacia esas Allocations
con copyTo()
y copyFrom()
como se explicó anteriormente, pero para crearlos inicialmente debe saber con qué tipo de datos está trabajando exactamente. Vamos a empezar con la Allocation
entrada:
Podemos usar el método estático createFromBitmap()
para crear rápidamente nuestra Allocation
entrada desde un Bitmap
:
final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, image);
En este ejemplo, la imagen de entrada nunca cambia, por lo que nunca necesitamos modificar la Allocation
entrada nuevamente. Podemos reutilizarlo cada vez que el nivel de saturationLevel
cambie para crear un nuevo Bitmap
salida.
Crear la Allocation
salida es un poco más complejo. Primero necesitamos crear lo que se llama un Type
. Un Type
se usa para indicar una Allocation
con qué tipo de datos está tratando. Por lo general, uno usa la clase Type.Builder
para crear rápidamente un Type
apropiado. Echemos un vistazo al código primero:
final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
.setX(inputBitmap.getWidth())
.setY(inputBitmap.getHeight())
.create();
Estamos trabajando con un Bitmap
normal de 32 bits (o en otras palabras, 4 bytes) por píxel con 4 canales de color. Es por eso que estamos eligiendo Element.RGBA_8888
para crear el Type
. Luego usamos los métodos setX()
y setY()
para establecer el ancho y la altura de la imagen de salida en el mismo tamaño que la imagen de entrada. El método create()
luego crea el Type
con los parámetros que especificamos.
Una vez que tengamos el Type
correcto, podemos crear la Allocation
salida con el método estático createTyped()
:
final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);
Ahora casi hemos terminado. También necesitamos un Bitmap
salida en el que podamos copiar los datos de la Allocation
salida. Para hacer esto, usamos el método estático createBitmap()
para crear un nuevo Bitmap
vacío con el mismo tamaño y configuración que el Bitmap
entrada.
final Bitmap outputBitmap = Bitmap.createBitmap(
inputBitmap.getWidth(),
inputBitmap.getHeight(),
inputBitmap.getConfig()
);
Y con eso tenemos todas las piezas de rompecabezas para ejecutar nuestro RenderScript.
Ejemplo completo
Ahora pongamos todo esto junto en un ejemplo:
// 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);
Conclusión
Con esta introducción, debería estar listo para escribir sus propios núcleos RenderScript para la manipulación simple de imágenes. Sin embargo, hay algunas cosas que debes tener en cuenta:
- RenderScript solo funciona en proyectos de aplicación : actualmente, los archivos RenderScript no pueden formar parte de un proyecto de biblioteca.
- Tenga cuidado con la memoria : RenderScript es muy rápido, pero también puede requerir mucha memoria. Nunca debe haber más de una instancia de
RenderScript
en cualquier momento. También debe reutilizar lo más posible. Normalmente solo necesita crear sus instancias deAllocation
una vez y puede reutilizarlas en el futuro. Lo mismo ocurre con losBitmaps
salida o sus instancias de script. Reutiliza tanto como sea posible. - Realice su trabajo en segundo plano : una vez más, RenderScript es muy rápido, pero no instantáneo de ninguna manera. Cualquier Kernel, especialmente los complejos, deben ejecutarse fuera del hilo de la IU en una
AsyncTask
o algo similar. Sin embargo, en su mayor parte, no tiene que preocuparse por las fugas de memoria. Todas las clases relacionadas con RenderScript solo usan elContext
la aplicación y, por lo tanto, no producen pérdidas de memoria. ¡Pero todavía tiene que preocuparse por las cosas habituales, como la pérdida deView
,Activity
o cualquier instancia deContext
que use usted mismo! - Use cosas integradas : hay muchos scripts predefinidos que realizan tareas como difuminar, combinar, convertir y cambiar el tamaño de la imagen. Y hay muchos más métodos incorporados que te ayudan a implementar tus kernels. Lo más probable es que si quieres hacer algo, ya sea un script o un método que ya hace lo que estás tratando de hacer. No reinventes la rueda.
Si quieres comenzar rápidamente y jugar con el código real, te recomiendo que eches un vistazo al ejemplo del proyecto GitHub que implementa el ejemplo exacto del que se habla en este tutorial. Puedes encontrar el proyecto aquí . ¡Diviértete con RenderScript!
Desenfocar una imagen
Este ejemplo muestra cómo usar la API de Renderscript para desenfocar una imagen (usando Bitmap). Este ejemplo utiliza ScriptInstrinsicBlur proporcionado por la API de Renderscript de Android (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;
}
}
Cada script tiene un núcleo que procesa los datos y generalmente se invoca a través del método 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
}
}
Esto concluyó el ejemplo aquí. Se aconseja hacer el procesamiento en un hilo de fondo.
Desenfocar 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);