Transparent blurry view which blurs layout underneath

后端 未结 5 486
[愿得一人]
[愿得一人] 2020-11-30 18:27

I\'ve got a Linearlayout which I\'ve made transparent, and now I\'m looking for a way to give it a Blur effect, so what\'s ever underneath it gets blurry. Just like the Wind

相关标签:
5条回答
  • 2020-11-30 19:17

    Here is a good way to "Blur Images Efficiently using Renderscript" by GDE Mario Viviani.

    Here is the copy/paste:

    Blurring images is an effect a lot of developers need to achieve, and it may require some time and efforts to be implemented. Also, since a lot of image manipulation is required, if it's not appropriately coded it can be really a pain in terms of CPU and memory usage.

    There's a quick and efficient solution to blur images, which is Renderscript.

    Available since API 11 (Honeycomb), Renderscript allows to take advantage of the GPU acceleration and is targeted at high-performance 3D rendering and compute operations. Renderscript is a really complex and articulated product, and allows deep configuration and coding using native C99 language, which allows portability, performance and usability.

    However, since API 17 (4.2.2) Renderscript offer some built-in functions that perform well-defined operations, called Intrinsics. Intrinsics are pre-written scripts that allow, to perform operations like Blur, Blen, Matrix Convolution and more, without the need to write Renderscript code.

    Here's a simple method I wrote to easily end efficiently apply a Blur filter to a Bitmap:

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

    RenderScript Blurred Image

    And...voilà! Blurred bitmap! :-)

    Remember that to run the previous code you need minimum API 17 (4.2.2).

    Here's a Gist of this method: https://gist.github.com/Mariuxtheone/903c35b4927c0df18cf8

    If you want to discover more about Intrinsics, check out this post on Android Developers Blog: http://android-developers.blogspot.it/2013/08/renderscript-intrinsics.html

    If you're interested in know more about Renderscript, check out these links: http://android-developers.blogspot.it/2011/02/introducing-renderscript.html http://android-developers.blogspot.it/2011/03/renderscript.html

    0 讨论(0)
  • 2020-11-30 19:19

    Blurring in real time on android is still a hurdle. Here's a comprehensive comparison between some of the viable mechanisms:

    StackBlur (already listed in the answer by Onur under the moniker fastBlur):

    How it looks (at radius 20):

    enter image description here

    Logged time(ms) to generate each 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
    

    Average => ~ 44.1 ms => 22 drawables per second

    RenderScript:

    ScriptIntrinsicBlur provides a consistently fast blur. Its available api 8 onwards using the support library.

    What it looks like (at radius 20):

    enter image description here

    Logged time(ms) to generate each 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
    

    Average => ~ 15.6 ms => 64 drawables per second

    RenderScript (radius = 3) + Scaling (20%):

    This is another way of getting a decent(?) but fast blur. What we do is scale the bitmap to a fraction of its size - say 20% - and apply the blur algorithm on the scaled down version. Once that is done, we scale the bitmap to its original size. Results are not as good as using the blur algorithm on the original, but they are passable. Also notice that the radius value shouldn't be too high or the resulting bitmap will be indiscernible.

    What it looks like:

    enter image description here

    Logged time(ms) to generate each 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
    

    Average => ~ 7.9 ms => 126 drawables per second

    StackBlur (radius = 3) + Scaling (20%):

    Same concept as #3 above. Reduce size to 20% and apply stackblur on the scaled bitmap.

    enter image description here

    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
    

    Average => ~ 7.4 ms => 135 drawables per second

    Tests carried out on Nex4. Bitmap size - 200px x 200px

    Another tip: methods buildDrawingCache() and getDrawingCache() themselves take a long time. An alternative to this is to create a Bitmap using the dimensions of the view you need to blur:

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

    The view is now drawn on toDrawOn and you can use it however you please.

    This, in my experience is much faster than generating and accessing a view's drawing cache.

    If you need help implementing any of the 4 methods listed above, let me know in comments.

    Do keep in mind that the gifs above have been downscaled and whatnot. If you'd like to see original screen-capture (mp4) files - check this Link.

    0 讨论(0)
  • 2020-11-30 19:23

    It is not possible to do it easily, using layout features.

    I would suggest drawing parent view to canvas as described here: How to make any view to draw to canvas? with the foreground view hidden. Then, blur the canvas and draw the foreground view using this data as a background, or a part of it.

    It will be very resource intensive operation if you want this effect to be live. You should at least try caching the blur result.

    0 讨论(0)
  • 2020-11-30 19:24

    This was on my mind for some time, and I just implemented it thanks to your question.

    To be able to do this, we need to draw the layout that is beneath our blur layout into a bitmap. Than by using a blurring algorithm, we need to blur that bitmap and finally draw blurred bitmap as our blur layout's background.

    Luckily android has cached drawing mechanism, so first part is easy. We can simply enable cached drawing for our beneath layout and use getDrawingCache() to acquire the bitmap from it.

    Now we need a fast blurring algorithm. I used this https://stackoverflow.com/a/10028267/3133545

    Here it is.

    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://stackoverflow.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 <mario@quasimondo.com>
         */
        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);
        }
    
    }
    

    Usage :

    View beneathView = //the view that beneath blur view
    View blurView= //blur View
    
    BlurDrawable blurDrawable = new BlurDrawable(beneathView, radius);
    
    blurView.setBackgroundDrawable(blurDrawable);
    

    And how my test application looked like:

    test application

    I decided not to use this tho, because it is too hacky and not looking as cool as i thought it would be in first place.

    0 讨论(0)
  • 2020-11-30 19:26

    step 1. cut the part of background image in bitmap which is to be blured.

    step 2. Blur that part of bitmap.

    step 3. set bitmap as a background.

    java method

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

    ...

    now call this function:

    calling method

    applyBlur(detail_main_image, titleLayout);

    ...

    //where detail_main_image is image on which i have to show blur part //and titleLayout is view on which i have to set background blur.

    0 讨论(0)
提交回复
热议问题