java - imagen - efecto blur online
Vista borrosa transparente que difumina el diseƱo debajo (6)
Tengo un Linearlayout que he hecho transparente, y ahora estoy buscando una manera de darle un efecto de desenfoque, por lo que lo que está debajo de él se vuelve borroso. Al igual que el aspecto Aero 7 de Windows (vea la captura de pantalla).
Sé que puedes hacer un efecto de desenfoque así:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
Pero esto solo se aplica a desenfocar el fondo cuando aparece un diálogo.
Estuve buscando en Google casi una hora y no encuentro nada. ¿Alguien tiene alguna sugerencia sobre cómo hacer esto?
Gracias
Difuminar en tiempo real en Android sigue siendo un obstáculo. Aquí hay una comparación exhaustiva entre algunos de los mecanismos viables:
StackBlur (ya figura en la respuesta de
Onur
bajo el nombre defastBlur
):
Cómo se ve (en el radio 20):
BitmapDrawable
registrado (ms) para generar cada BitmapDrawable
:
I/(10266): Total time taken: 35
I/(10266): Total time taken: 54
I/(10266): Total time taken: 48
I/(10266): Total time taken: 36
I/(10266): Total time taken: 48
I/(10266): Total time taken: 39
I/(10266): Total time taken: 49
I/(10266): Total time taken: 50
I/(10266): Total time taken: 35
I/(10266): Total time taken: 47
Promedio => ~ 44.1 ms => 22 dibujables por segundo
RenderScript :
ScriptIntrinsicBlur
proporciona un desenfoque constantemente rápido. Está disponible la api 8 en adelante usando la biblioteca de soporte.
Cómo se ve (en el radio 20):
BitmapDrawable
registrado (ms) para generar cada BitmapDrawable
:
I/(9342): Total time taken: 14
I/(9342): Total time taken: 16
I/(9342): Total time taken: 13
I/(9342): Total time taken: 28
I/(9342): Total time taken: 14
I/(9342): Total time taken: 12
I/(9342): Total time taken: 14
I/(9342): Total time taken: 19
I/(9342): Total time taken: 13
I/(9342): Total time taken: 13
Promedio => ~ 15.6 ms => 64 dibujables por segundo
RenderScript (radio = 3) + Escala (20%) :
Esta es otra forma de obtener un desenfoque decente (?) Pero rápido . Lo que hacemos es escalar el mapa de bits a una fracción de su tamaño, digamos 20%, y aplicar el algoritmo de desenfoque en la versión reducida. Una vez hecho esto, escalamos el mapa de bits a su tamaño original. Los resultados no son tan buenos como usar el algoritmo de desenfoque en el original, pero son pasables. También tenga en cuenta que el valor del radio no debe ser demasiado alto o el mapa de bits resultante será indiscernible.
Como se ve:
BitmapDrawable
registrado (ms) para generar cada BitmapDrawable
:
I/(11631): Total time taken: 5
I/(11631): Total time taken: 19
I/(11631): Total time taken: 3
I/(11631): Total time taken: 7
I/(11631): Total time taken: 7
I/(11631): Total time taken: 5
I/(11631): Total time taken: 7
I/(11631): Total time taken: 17
I/(11631): Total time taken: 5
I/(11631): Total time taken: 4
Promedio => ~ 7.9 ms => 126 consumibles por segundo
StackBlur (radio = 3) + escala (20%) :
Mismo concepto que el # 3 anterior. Reduzca el tamaño al 20% y aplique stackblur en el mapa de bits escalado.
I/(12690): Total time taken: 4
I/(12690): Total time taken: 20
I/(12690): Total time taken: 4
I/(12690): Total time taken: 4
I/(12690): Total time taken: 5
I/(12690): Total time taken: 5
I/(12690): Total time taken: 4
I/(12690): Total time taken: 21
I/(12690): Total time taken: 3
I/(12690): Total time taken: 4
Promedio => ~ 7.4 ms => 135 dibujables por segundo
Pruebas realizadas en Nex4. Tamaño de mapa de bits: 200 px x 200 px
Otro consejo : los métodos buildDrawingCache()
y getDrawingCache()
llevan mucho tiempo. Una alternativa a esto es crear un mapa de bits usando las dimensiones de la vista que necesita desenfocar:
Bitmap toDrawOn = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
// Create a canvas - assign `toDrawOn` as an offscreen bitmap to it
Canvas holdingCanvas = new Canvas(toDrawOn);
// Now, let the view draw itself on this canvas
yourView.draw(holdingCanvas);
La vista ahora se dibuja en toDrawOn
y puede usarlo como lo desee.
Esto, según mi experiencia, es mucho más rápido que generar y acceder a la memoria caché de dibujo de una vista.
Si necesita ayuda para implementar cualquiera de los 4 métodos mencionados anteriormente, avísenos en los comentarios.
Tenga en cuenta que los gifs anteriores se han reducido y otras cosas. Si desea ver archivos originales de captura de pantalla (mp4), consulte este Link .
Esto estuvo en mi mente por algún tiempo, y acabo de implementarlo gracias a su pregunta.
Para poder hacer esto, necesitamos dibujar el diseño que está debajo de nuestro diseño de desenfoque en un mapa de bits. En lugar de utilizar un algoritmo de desenfoque, debemos difuminar ese mapa de bits y finalmente dibujar un mapa de bits borroso como fondo de nuestro diseño de desenfoque.
Afortunadamente, Android tiene un mecanismo de dibujo en caché, por lo que la primera parte es fácil. Simplemente podemos habilitar el dibujo en caché para nuestro diseño inferior y usar getDrawingCache()
para adquirir el mapa de bits a partir de él.
Ahora necesitamos un algoritmo de borrado rápido. Utilicé esto https://.com/a/10028267/3133545
Aquí está.
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import java.lang.ref.WeakReference;
import java.util.InputMismatchException;
/**
* A drawable that draws the target view as blurred using fast blur
* <p/>
* <p/>
* TODO:we might use setBounds() to draw only part a of the target view
* <p/>
* Created by 10uR on 24.5.2014.
*/
public class BlurDrawable extends Drawable {
private WeakReference<View> targetRef;
private Bitmap blurred;
private Paint paint;
private int radius;
public BlurDrawable(View target) {
this(target, 10);
}
public BlurDrawable(View target, int radius) {
this.targetRef = new WeakReference<View>(target);
setRadius(radius);
target.setDrawingCacheEnabled(true);
target.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO);
paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
}
@Override
public void draw(Canvas canvas) {
if (blurred == null) {
View target = targetRef.get();
if (target != null) {
Bitmap bitmap = target.getDrawingCache(true);
if (bitmap == null) return;
blurred = fastBlur(bitmap, radius);
}
}
if (blurred != null && !blurred.isRecycled())
canvas.drawBitmap(blurred, 0, 0, paint);
}
/**
* Set the bluring radius that will be applied to target view''s bitmap
*
* @param radius should be 0-100
*/
public void setRadius(int radius) {
if (radius < 0 || radius > 100)
throw new InputMismatchException("Radius must be 0 <= radius <= 100 !");
this.radius = radius;
if (blurred != null) {
blurred.recycle();
blurred = null;
}
invalidateSelf();
}
public int getRadius() {
return radius;
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
* from https://.com/a/10028267/3133545
* <p/>
* <p/>
* <p/>
* Stack Blur v1.0 from
* http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
* <p/>
* Java Author: Mario Klingemann <mario at quasimondo.com>
* http://incubator.quasimondo.com
* created Feburary 29, 2004
* Android port : Yahel Bouaziz <yahel at kayenko.com>
* http://www.kayenko.com
* ported april 5th, 2012
* <p/>
* This is a compromise between Gaussian Blur and Box blur
* It creates much better looking blurs than Box Blur, but is
* 7x faster than my Gaussian Blur implementation.
* <p/>
* I called it Stack Blur because this describes best how this
* filter works internally: it creates a kind of moving stack
* of colors whilst scanning through the image. Thereby it
* just has to add one new block of color to the right side
* of the stack and remove the leftmost color. The remaining
* colors on the topmost layer of the stack are either added on
* or reduced by one, depending on if they are on the right or
* on the left side of the stack.
* <p/>
* If you are using this algorithm in your code please add
* the following line:
* <p/>
* Stack Blur Algorithm by Mario Klingemann <[email protected]>
*/
private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
if (radius < 1) {
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
Log.e("pix", w + " " + h + " " + pix.length);
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int dv[] = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return (bitmap);
}
}
Uso:
View beneathView = //the view that beneath blur view
View blurView= //blur View
BlurDrawable blurDrawable = new BlurDrawable(beneathView, radius);
blurView.setBackgroundDrawable(blurDrawable);
Y cómo se veía mi aplicación de prueba:
Decidí no usar esto, porque es demasiado hacky y no se ve tan genial como pensé que estaría en primer lugar.
Intenta usar esta biblioteca. Creo que solo necesitas pasar la vista y funcionará ...
No es posible hacerlo fácilmente, usando características de diseño.
Sugiero que dibuje la vista principal en el lienzo como se describe aquí: ¿Cómo hacer una vista para dibujar en el lienzo? con la vista en primer plano oculta. Luego, difumine el lienzo y dibuje la vista en primer plano usando estos datos como fondo o como parte de él.
Será una operación muy intensiva en recursos si desea que este efecto sea en vivo. Al menos debería tratar de almacenar en caché el resultado del desenfoque.
paso 1. cortar la parte de la imagen de fondo en el mapa de bits que se va a difuminar.
paso 2. Desenfoque esa parte del mapa de bits.
paso 3. establecer mapa de bits como fondo.
método de java
private void applyBlur(final View image, final View layout) {
image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
image.getViewTreeObserver().removeOnPreDrawListener(this);
image.buildDrawingCache();
Bitmap bmp = image.getDrawingCache();
Bitmap overlay = Bitmap.createBitmap((int) (layout.getMeasuredWidth()),
(int) (layout.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-layout.getLeft(), -layout.getTop());
canvas.drawBitmap(bmp, 0, 0, null);
RenderScript rs = RenderScript.create(getActivity());
Allocation overlayAlloc = Allocation.createFromBitmap(
rs, overlay);
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(
rs, overlayAlloc.getElement());
blur.setInput(overlayAlloc);
blur.setRadius(20);
blur.forEach(overlayAlloc);
overlayAlloc.copyTo(overlay);
layout.setBackground(new BitmapDrawable(
getResources(), overlay));
rs.destroy();
return true;
}
});
}
...
ahora llama a esta función:
método de llamada
applyBlur (detail_main_image, titleLayout);
...
// donde detail_main_image es una imagen en la que tengo que mostrar el desenfoque de la parte // y titleLayout es una vista en la que tengo que configurar el difuminado de fondo.
Here hay una buena manera de "Desenfocar imágenes eficientemente usando Renderscript" por GDE Mario Viviani .
Aquí está el copiar / pegar:
Las imágenes borrosas son un efecto que muchos desarrolladores deben lograr y puede requerir tiempo y esfuerzo para implementarse. Además, dado que se requiere mucha manipulación de la imagen, si no está codificada adecuadamente, puede ser realmente un dolor en términos de uso de la CPU y la memoria.
Hay una solución rápida y eficiente para desenfocar imágenes, que es Renderscript .
Disponible desde la API 11 ( Honeycomb
), Renderscript
permite aprovechar la aceleración de la GPU y está orientado a la representación 3D de alto rendimiento y las operaciones informáticas. Renderscript
es un producto realmente complejo y articulado, y permite una configuración y codificación profunda usando el lenguaje nativo C99, que permite la portabilidad, el rendimiento y la facilidad de uso.
Sin embargo, desde API 17 (4.2.2) Renderscript
ofrece algunas funciones integradas que realizan operaciones bien definidas, llamadas Intrinsics. Intrinsics son scripts pre-escritos que permiten realizar operaciones como Blur, Blen, Matrix Convolution y más, sin la necesidad de escribir código Renderscript
.
Aquí hay un método simple que escribí para terminar fácilmente y aplicar de manera eficiente un filtro de desenfoque a un mapa de bits:
public Bitmap blurBitmap(Bitmap bitmap) {
//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(), Config.ARGB_8888);
//Instantiate a new Renderscript
RenderScript rs = RenderScript.create(getApplicationContext());
//Create an Intrinsic Blur Script using the Renderscript
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
//Create the in/out Allocations with the Renderscript and the in/out bitmaps
Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
Allocation allOut = Allocation.createFromBitmap(rs, 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
bitmap.recycle();
//After finishing everything, we destroy the Renderscript.
rs.destroy();
return outBitmap;
}
¡Y voilá! Mapa de bits borroso! :-)
Recuerde que para ejecutar el código anterior necesita una API mínima 17 (4.2.2).
Aquí hay una esencia de este método: https://gist.github.com/Mariuxtheone/903c35b4927c0df18cf8
Si desea descubrir más acerca de Intrinsics, consulte esta publicación en el Blog de Desarrolladores de Android: http://android-developers.blogspot.it/2013/08/renderscript-intrinsics.html
Si está interesado en saber más acerca de Renderscript, consulte estos enlaces: http://android-developers.blogspot.it/2011/02/introducing-renderscript.html http://android-developers.blogspot.it/2011/03/renderscript.html