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:

imagen de demostración

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 de Allocation una vez y puede reutilizarlas en el futuro. Lo mismo ocurre con los Bitmaps 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 el Context 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 de View , Activity o cualquier instancia de Context 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);


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow