Поиск…


Вступление

RenderScript - это язык сценариев, который позволяет писать высокопроизводительный графический рендеринг и исходный вычислительный код. Он предоставляет средства для написания критического кода производительности, который система затем компилирует в собственный код для процессора, на котором он может работать. Это может быть процессор, многоядерный процессор или даже графический процессор. В конечном счете это зависит от многих факторов, которые не всегда доступны разработчику, но также зависит от архитектуры, которую поддерживает внутренний компилятор платформы.

Начиная

RenderScript - это среда для высокопроизводительных параллельных вычислений на Android. Сценарии, которые вы пишете, будут выполняться на всех доступных процессорах (например, CPU, GPU и т. Д.) Параллельно, позволяя вам сосредоточиться на задаче, которую вы хотите достичь, а не на том, как она запланирована и выполнена.

Сценарии написаны на языке C99 (C99 - старая версия стандарта языка программирования C). Для каждого скрипта создается Java-класс, который позволяет вам легко взаимодействовать с RenderScript в вашем Java-коде.

Настройка вашего проекта

Существуют два разных способа доступа к RenderScript в приложении: библиотеки Android Framework или Библиотека поддержки. Даже если вы не хотите настраивать таргетинг на устройства до уровня API 11, вы всегда должны использовать реализацию библиотеки поддержки, поскольку он обеспечивает совместимость устройств на разных устройствах. Чтобы использовать реализацию библиотеки поддержки, вам нужно использовать хотя бы инструменты для сборки версии 18.1.0 !

Теперь давайте настроим файл build.gradle вашего приложения:

android {
    compileSdkVersion 24
    buildToolsVersion '24.0.1'

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 24

        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true
    }
}
  • renderscriptTargetApi : этот параметр должен быть установлен на самый ранний уровень API версии, который предоставляет все необходимые функции RenderScript.
  • renderscriptSupportModeEnabled : Это позволяет использовать реализацию RenderScript библиотеки поддержки.

Как работает RenderScript

Типичный RenderScript состоит из двух вещей: ядра и функции. Функция - это то, на что это похоже - она ​​принимает вход, делает что-то с этим входом и возвращает результат. Ядро - это реальная сила RenderScript.

Ядро - это функция, выполняемая против каждого элемента внутри Allocation . Allocation может использоваться для передачи данных, таких как Bitmap или byte массив, в RenderScript и они также используются для получения результата из ядра. Ядра могут либо принимать одно Allocation качестве входных данных, либо другое как вывод, или они могут изменять данные внутри всего одного Allocation .

Вы можете написать одно ядро, но есть также множество предопределенных ядер, которые вы можете использовать для выполнения общих операций, таких как Gaussian Image Blur.

Как уже упоминалось для каждого файла RenderScript, для него взаимодействует класс. Эти классы всегда начинаются с префикса ScriptC_ за которым следует имя файла RenderScript. Например, если ваш RenderScript-файл называется example тогда сгенерированный класс Java будет называться ScriptC_example . Все предопределенные скрипты начинаются с префикса Script - например, Gaussian Image Blur Script называется ScriptIntrinsicBlur .

Написание первого RenderScript

Следующий пример основан на примере GitHub. Он выполняет базовые манипуляции с изображениями, изменяя насыщенность изображения. Вы можете найти исходный код здесь и проверить его, если вы хотите поиграть с ним самостоятельно. Вот краткое описание того, как должен выглядеть результат:

демонстрационный снимок

RilerScript Boilerplate

Файлы RenderScript находятся в папке src/main/rs в вашем проекте. Каждый файл имеет расширение файла .rs и должен содержать два оператора #pragma в верхней части:

#pragma version(1)
#pragma rs java_package_name(your.package.name)
  • #pragma version(1) : Это можно использовать для установки версии RenderScript, которую вы используете. В настоящее время существует только версия 1.

  • #pragma rs java_package_name(your.package.name) : это может использоваться для установки имени пакета класса Java, сгенерированного для взаимодействия с этим конкретным RenderScript.

Существует еще одна # #pragma вы обычно должны устанавливать в каждом из ваших файлов RenderScript, и используется для установки точности с плавающей запятой. Вы можете установить точность с плавающей запятой на три разных уровня:

  • #pragma rs_fp_full : это самый строгий параметр с максимальной точностью, а также значение по умолчанию, если ничего не указывать. Вы должны использовать это, если вам нужна высокая точность с плавающей точкой.
  • #pragma rs_fp_relaxed : Это обеспечивает не совсем высокую точность с плавающей запятой, но на некоторых архитектурах она позволяет кучу оптимизаций, которые могут привести к тому, что ваши скрипты будут работать быстрее.
  • #pragma rs_fp_imprecise : Это обеспечивает еще меньшую точность и должно использоваться, если точность с плавающей запятой не имеет особого значения для вашего скрипта.

Большинство скриптов могут просто использовать #pragma rs_fp_relaxed если вам действительно не нужна высокая точность с плавающей точкой.

Глобальные переменные

Теперь, как и в коде C, вы можете определить глобальные переменные или константы:

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

float saturationLevel = 0.0f;

Переменная gMonoMult имеет тип float3 . Это означает, что это вектор, состоящий из 3 чисел с плавающей точкой. Другая переменная float называемая saturationValue , не является постоянной, поэтому вы можете установить ее во время выполнения на нужное вам значение. Вы можете использовать такие переменные в своих ядрах или функциях, и поэтому они являются другим способом ввода или получения результатов из ваших RenderScripts. Для каждой не постоянной переменной метод getter и setter будет сгенерирован в соответствующем Java-классе.

Ядра

Но теперь давайте начнем внедрять Ядро. Для целей этого примера я не буду объяснять математику, используемую в ядре, чтобы изменить насыщенность изображения, но вместо этого сосредоточимся на том, как реализовать Ядро и как его использовать. В конце этой главы я быстро объясню, что на самом деле делает код в этом ядре.

Ядра в целом

Давайте сначала посмотрим на исходный код:

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

Как вы видите, это выглядит как обычная функция C с одним исключением: __attribute__((kernel)) между типом возвращаемого значения и именем метода. Это то, что говорит RenderScript, что этот метод является ядром. Другое дело, что вы можете заметить, что этот метод принимает параметр uchar4 и возвращает другое значение uchar4 . uchar4 - как переменная float3 мы обсуждали в главе раньше - вектор. Он содержит 4 значения uchar которые являются только байтовыми значениями в диапазоне от 0 до 255.

Вы можете получить доступ к этим отдельным значениям различными способами, например in.r вернет байт, соответствующий красному каналу пикселя. Мы используем uchar4 , поскольку каждый пиксель состоит из 4 значений - r для красного, g для зеленых, b для синего и для альфа - и вы можете получить доступ к ним с этим стенографией. a RenderScript также позволяет вам принимать любое количество значений из вектора и создавать с ними другой вектор. Например, in.rgb вернет значение uchar3 которое просто содержит красную, зеленую и синюю части пикселя без альфа-значения.

Во время выполнения RenderScript вызовет этот метод Kernel для каждого пикселя изображения, поэтому возвращаемое значение и параметр являются только одним значением uchar4 . RenderScript будет запускать многие из этих вызовов параллельно на всех доступных процессорах, поэтому RenderScript настолько мощный. Это также означает, что вам не нужно беспокоиться о потоковой или потоковой безопасности, вы можете просто реализовать все, что хотите сделать для каждого пикселя, и RenderScript позаботится об остальном.

При вызове ядра на Java вы предоставляете две переменные Allocation , которые содержат входные данные, и другую, которая будет получать результат. Ваш метод Kernel будет вызываться для каждого значения во входном Allocation и будет записывать результат в Output Allocation .

Методы API RenderScript Runtime

В ядре выше используется несколько методов, которые предоставляются из коробки. RenderScript предоставляет множество таких методов, и они жизненно важны для всего, что вы собираетесь делать с RenderScript. Среди них методы для математических операций, таких как sin() и вспомогательные методы, такие как mix() который смешивает два значения в соответствии с другими значениями. Но существуют также методы более сложных операций при работе с векторами, кватернионами и матрицами.

Официальная ссылка Runtime Runtime API Reference является лучшим ресурсом, если вы хотите узнать больше о конкретном методе или ищете конкретный метод, который выполняет общую операцию, например вычисление точечного произведения матрицы. Вы можете найти эту документацию здесь .

Реализация ядра

Теперь давайте рассмотрим особенности того, что делает это Ядро. Вот первая строка в ядре:

float4 f4 = rsUnpackColor8888(in);

Первая строка вызывает встроенный метод rsUnpackColor8888() , который преобразует uchar4 значение до float4 значений. Каждый цветной канал также преобразуется в диапазон 0.0f - 1.0f где 0.0f соответствует 0.0f значению 0 и от 1.0f до 255 . Основная цель этого - сделать математику в этом ядре намного проще.

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

В следующей строке используется встроенный метод dot() для вычисления точечного произведения двух векторов. gMonoMult - это постоянное значение, которое мы определили в нескольких главах выше. Поскольку оба вектора должны иметь одинаковую длину для вычисления точечного произведения, а также, поскольку мы просто хотим влиять на цветовые каналы, а не на альфа-канал пикселя, мы используем сокращенное .rgb для получения нового вектора float3 который просто содержит красный, зеленый и синий. Те из нас, кто все еще помнит из школы, как работает точечный продукт, быстро заметят, что точечный продукт должен возвращать только одно значение, а не вектор. Однако в приведенном выше коде мы присваиваем результат вектору float3 . Это опять-таки особенность RenderScript. Когда вы назначаете одномерное число вектору, все элементы в векторе будут установлены в это значение. Например, следующий фрагмент присваивает 2.0f каждому из трех значений в векторе float3 :

float3 example = 2.0f;

Таким образом, результат указанного выше точечного продукта присваивается каждому элементу в float3 элементе float3 .

Теперь наступает часть, в которой мы фактически используем глобальную переменную saturationLevel для изменения насыщенности изображения:

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

Это использует встроенный метод mix() для смешивания исходного цвета с вектором продукта dot, который мы создали выше. Как они смешиваются вместе, определяется глобальной переменной saturationLevel . Таким образом, saturationLevel 0.0f приведет к тому, что результирующий цвет не будет иметь 0.0f значений цвета и будет состоять только из значений в dotVector что приведет к получению черно-белого или серого изображения. Значение 1.0f приведет к тому, что полученный цвет будет полностью составлен из исходных значений цвета, а значения выше 1.0f будут умножать исходные цвета, чтобы сделать их более яркими и интенсивными.

return rsPackColorTo8888(newColor);

Это последняя часть в ядре. rsPackColorTo8888() преобразует вектор float3 обратно в значение uchar4 которое затем возвращается. Результирующие байтовые значения зажимаются в диапазоне от 0 до 255, поэтому значения float выше 1.0f приведут к 1.0f значению 255, а значения ниже 0.0 приведут к байтовому значению 0 .

И это целая реализация ядра. Теперь остается только одна часть: Как вызвать ядро ​​на Java.

Вызов RenderScript в Java

основы

Как уже упоминалось выше для каждого файла RenderScript генерируется класс Java, который позволяет вам взаимодействовать с вашими сценариями. Эти файлы имеют префикс ScriptC_ за которым следует имя файла RenderScript. Чтобы создать экземпляр этих классов, вам сначала понадобится экземпляр класса RenderScript :

final RenderScript renderScript = RenderScript.create(context);

Статический метод create() может использоваться для создания экземпляра RenderScript из Context . Затем вы можете создать экземпляр класса Java, который был сгенерирован для вашего скрипта. Если вы вызвали файл saturation.rs файла RenderScript, класс будет называться ScriptC_saturation :

final ScriptC_saturation script = new ScriptC_saturation(renderScript);

В этом классе вы можете установить уровень насыщенности и вызвать ядро. Установщик, который был сгенерирован для переменной saturationLevel будет иметь префикс set_ за которым следует имя переменной:

script.set_saturationLevel(1.0f);

Также есть getter с префиксом get_ который позволяет вам получить уровень насыщенности, установленный в настоящий момент:

float saturationLevel = script.get_saturationLevel();

Ядра, которые вы определяете в своем RenderScript, имеют префикс forEach_ за которым следует имя метода Kernel. Ядро, которое мы написали, ожидает, что в качестве параметров ввода и Allocation выходов будет Allocation :

script.forEach_saturation(inputAllocation, outputAllocation);

Входное Allocation должно содержать входное изображение, и после forEach_saturation метода forEach_saturation распределение вывода будет содержать измененные данные изображения.

После того, как у вас есть экземпляр Allocation вы можете скопировать данные из этих Allocations с помощью этих методов с помощью методов copyFrom() и copyTo() . Например, вы можете скопировать новое изображение в свой ввод «Выделение путем вызова:

inputAllocation.copyFrom(inputBitmap);

Точно так же вы можете получить результат изображения, вызвав copyTo() на выходе Allocation :

outputAllocation.copyTo(outputBitmap);

Создание экземпляров распределения

Существует много способов создания Allocation . После того, как у вас есть экземпляр Allocation вы можете скопировать новые данные и с этими Allocations с помощью copyTo() и copyFrom() как описано выше, но для их создания изначально вы должны знать, с какими данными вы работаете. Начнем с ввода Allocation :

Мы можем использовать статический метод createFromBitmap() чтобы быстро создать наш вход. Allocation из Bitmap :

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

В этом примере входное изображение никогда не изменяется, поэтому нам больше не нужно снова изменять Allocation ввода. Мы можем повторно использовать его каждый раз, когда saturationLevel изменяется для создания нового выходного Bitmap .

Создание вывода Allocation немного сложнее. Сначала нам нужно создать так называемый Type . Type используется, чтобы рассказать о Allocation с какими данными он имеет дело. Обычно используется класс Type.Builder для быстрого создания соответствующего Type . Давайте сначала посмотрим на код:

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

Мы работаем с обычным 32-битным (или, другими словами, 4 байтами) на пиксель Bitmap с 4-мя цветными каналами. Вот почему мы выбираем Element.RGBA_8888 для создания Type . Затем мы используем методы setX() и setY() чтобы установить ширину и высоту выходного изображения того же размера, что и входное изображение. Затем метод create() создает Type с указанными параметрами.

Как только у нас будет правильный Type мы можем создать выход Allocation со статическим методом createTyped() :

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

Теперь мы почти закончили. Нам также нужен выходной Bitmap в котором мы можем скопировать данные из вывода Allocation . Для этого мы используем статический метод createBitmap() для создания нового пустого Bitmap с тем же размером и конфигурацией, что и входной Bitmap .

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

И вместе с этим у нас есть все части головоломки для выполнения нашего RenderScript.

Полный пример

Теперь давайте соединим все это в одном примере:

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

Заключение

С этим введением вы должны быть готовы написать свои собственные ядра RenderScript для простой обработки изображений. Однако есть несколько вещей, которые вы должны иметь в виду:

  • RenderScript работает только в проектах приложений : в настоящее время файлы RenderScript не могут быть частью проекта библиотеки.
  • Следите за памятью : RenderScript работает очень быстро, но он также может быть интенсивным в памяти. В любой момент никогда не должно быть более одного экземпляра RenderScript . Вы должны также использовать его как можно больше. Обычно вам просто нужно создать экземпляры « Allocation один раз и использовать их в будущем. То же самое касается выходных Bitmaps или экземпляров скриптов. Повторно используйте как можно больше.
  • Сделайте свою работу в фоновом режиме : снова RenderScript работает очень быстро, но не мгновенно. Любое Ядро, особенно сложные, должно быть выполнено из потока пользовательского интерфейса в AsyncTask или что-то подобное. Однако по большей части вам не нужно беспокоиться о утечке памяти. Все классы, связанные с RenderScript, используют только Context приложения и, следовательно, не вызывают утечки памяти. Но вам все равно придется беспокоиться о обычных вещах, таких как утечка View , Activity или любого экземпляра Context который вы используете сами!
  • Используйте встроенный материал : существует множество предопределенных скриптов, которые выполняют такие задачи, как размытие изображения, смешивание, преобразование, изменение размера. И есть еще много встроенных методов, которые помогут вам реализовать свои ядра. Скорее всего, если вы хотите что-то сделать, есть либо скрипт, либо метод, который уже делает то, что вы пытаетесь сделать. Не изобретайте велосипед.

Если вы хотите быстро начать работу и поиграть с фактическим кодом, я рекомендую вам взглянуть на пример проекта GitHub, который реализует точный пример, о котором говорится в этом уроке. Вы можете найти проект здесь . Получайте удовольствие от RenderScript!

Размытие изображения

В этом примере показано, как использовать Renderscript API для размытия изображения (с использованием растрового изображения). В этом примере используется ScriptInstrinsicBlur, предоставляемый API android Renderscript API (API> = 17).

public class BlurProcessor {
    
    private RenderScript rs;
    private Allocation inAllocation;
    private Allocation outAllocation;
    private int width;
    private int height;

    private ScriptIntrinsicBlur blurScript;

    public BlurProcessor(RenderScript rs) {
        this.rs = rs;
    }
    
    public void initialize(int width, int height) {
        blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        blurScript.setRadius(7f); // Set blur radius. 25 is max

        if (outAllocation != null) {
            outAllocation.destroy();
            outAllocation = null;
        }
        
        // Bitmap must have ARGB_8888 config for this type
        Type bitmapType = new Type.Builder(rs, Element.RGBA_8888(rs))
            .setX(width)
            .setY(height)
            .setMipmaps(false) // We are using MipmapControl.MIPMAP_NONE
            .create();
        
        // Create output allocation
        outAllocation = Allocation.createTyped(rs, bitmapType);

        // Create input allocation with same type as output allocation
        inAllocation = Allocation.createTyped(rs, bitmapType);
    }

    public void release() {
        
        if (blurScript != null) {
            blurScript.destroy();
            blurScript = null;
        }
    
        if (inAllocation != null) {
            inAllocation.destroy();
            inAllocation = null;
        }

        if (outAllocation != null) {
            outAllocation.destroy();
            outAllocation = null;
        }
    }

    public Bitmap process(Bitmap bitmap, boolean createNewBitmap) {
        if (bitmap.getWidth() != width || bitmap.getHeight() != height) {
            // Throw error if required
            return null;
        }
        
        // Copy data from bitmap to input allocations
        inAllocation.copyFrom(bitmap);

        // Set input for blur script
        blurScript.setInput(inAllocation);

        // process and set data to the output allocation
        blurScript.forEach(outAllocation);
        
        if (createNewBitmap) {
            Bitmap returnVal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            outAllocation.copyTo(returnVal);
            return returnVal;
        }

        outAllocation.copyTo(bitmap);
        return bitmap;
    }
}    

Каждый скрипт имеет ядро, которое обрабатывает данные и обычно вызывается методом 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
    }
}

Здесь был приведен пример. Рекомендуется выполнять обработку в фоновом потоке.

Размытие

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

Использование:

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow