一、效果图
二、实现
StackBlurManager.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * StackBlur v1.0 for Android * * @Author: Enrique López Mañas <eenriquelopez@gmail.com> * http://www.neo-tech.es * * Author of the original algorithm: Mario Klingemann <mario.quasimondo.com> * * 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. * * 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. * * @copyright: Enrique López Mañas * @license: Apache License 2.0 */ import android.graphics.Bitmap; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class StackBlurManager { static final int EXECUTOR_THREADS = Runtime.getRuntime().availableProcessors(); static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(EXECUTOR_THREADS); /** * Original image */ private final Bitmap _image; /** * Method of blurring */ private final JavaBlurProcess _blurProcess; /** * Most recent result of blurring */ private Bitmap _result; /** * Constructor method (basic initialization and construction of the pixel array) * * @param image The image that will be analyed */ public StackBlurManager(Bitmap image) { _image = image; _blurProcess = new JavaBlurProcess(); } /** * Process the image on the given radius. Radius must be at least 1 * * @param radius */ public Bitmap process(int radius) { _result = _blurProcess.blur(_image, radius); return _result; } /** * Returns the blurred image as a bitmap * * @return blurred image */ public Bitmap returnBlurredImage() { return _result; } /** * Save the image into the file system * * @param path The path where to save the image */ public void saveIntoFile(String path) { try { FileOutputStream out = new FileOutputStream(path); _result.compress(Bitmap.CompressFormat.PNG, 90, out); } catch (Exception e) { e.printStackTrace(); } } /** * Returns the original image as a bitmap * * @return the original bitmap image */ public Bitmap getImage() { return this._image; } public static class JavaBlurProcess { private static final short[] stackblur_mul = { 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 }; private static final byte[] stackblur_shr = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 }; private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) { int x, y, xp, yp, i; int sp; int stack_start; int stack_i; int src_i; int dst_i; long sum_r, sum_g, sum_b, sum_in_r, sum_in_g, sum_in_b, sum_out_r, sum_out_g, sum_out_b; int wm = w - 1; int hm = h - 1; int div = (radius * 2) + 1; int mul_sum = stackblur_mul[radius]; byte shr_sum = stackblur_shr[radius]; int[] stack = new int[div]; if (step == 1) { int minY = core * h / cores; int maxY = (core + 1) * h / cores; for (y = minY; y < maxY; y++) { sum_r = sum_g = sum_b = sum_in_r = sum_in_g = sum_in_b = sum_out_r = sum_out_g = sum_out_b = 0; src_i = w * y; // start of line (0,y) for (i = 0; i <= radius; i++) { stack_i = i; stack[stack_i] = src[src_i]; sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1); sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1); sum_b += (src[src_i] & 0xff) * (i + 1); sum_out_r += ((src[src_i] >>> 16) & 0xff); sum_out_g += ((src[src_i] >>> 8) & 0xff); sum_out_b += (src[src_i] & 0xff); } for (i = 1; i <= radius; i++) { if (i <= wm) src_i += 1; stack_i = i + radius; stack[stack_i] = src[src_i]; sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i); sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i); sum_b += (src[src_i] & 0xff) * (radius + 1 - i); sum_in_r += ((src[src_i] >>> 16) & 0xff); sum_in_g += ((src[src_i] >>> 8) & 0xff); sum_in_b += (src[src_i] & 0xff); } sp = radius; xp = radius; if (xp > wm) xp = wm; src_i = xp + y * w; // img.pix_ptr(xp, y); dst_i = y * w; // img.pix_ptr(0, y); for (x = 0; x < w; x++) { src[dst_i] = (int) ((src[dst_i] & 0xff000000) | ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) | ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) | ((((sum_b * mul_sum) >>> shr_sum) & 0xff))); dst_i += 1; sum_r -= sum_out_r; sum_g -= sum_out_g; sum_b -= sum_out_b; stack_start = sp + div - radius; if (stack_start >= div) stack_start -= div; stack_i = stack_start; sum_out_r -= ((stack[stack_i] >>> 16) & 0xff); sum_out_g -= ((stack[stack_i] >>> 8) & 0xff); sum_out_b -= (stack[stack_i] & 0xff); if (xp < wm) { src_i += 1; ++xp; } stack[stack_i] = src[src_i]; sum_in_r += ((src[src_i] >>> 16) & 0xff); sum_in_g += ((src[src_i] >>> 8) & 0xff); sum_in_b += (src[src_i] & 0xff); sum_r += sum_in_r; sum_g += sum_in_g; sum_b += sum_in_b; ++sp; if (sp >= div) sp = 0; stack_i = sp; sum_out_r += ((stack[stack_i] >>> 16) & 0xff); sum_out_g += ((stack[stack_i] >>> 8) & 0xff); sum_out_b += (stack[stack_i] & 0xff); sum_in_r -= ((stack[stack_i] >>> 16) & 0xff); sum_in_g -= ((stack[stack_i] >>> 8) & 0xff); sum_in_b -= (stack[stack_i] & 0xff); } } } // step 2 else if (step == 2) { int minX = core * w / cores; int maxX = (core + 1) * w / cores; for (x = minX; x < maxX; x++) { sum_r = sum_g = sum_b = sum_in_r = sum_in_g = sum_in_b = sum_out_r = sum_out_g = sum_out_b = 0; src_i = x; // x,0 for (i = 0; i <= radius; i++) { stack_i = i; stack[stack_i] = src[src_i]; sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1); sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1); sum_b += (src[src_i] & 0xff) * (i + 1); sum_out_r += ((src[src_i] >>> 16) & 0xff); sum_out_g += ((src[src_i] >>> 8) & 0xff); sum_out_b += (src[src_i] & 0xff); } for (i = 1; i <= radius; i++) { if (i <= hm) src_i += w; // +stride stack_i = i + radius; stack[stack_i] = src[src_i]; sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i); sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i); sum_b += (src[src_i] & 0xff) * (radius + 1 - i); sum_in_r += ((src[src_i] >>> 16) & 0xff); sum_in_g += ((src[src_i] >>> 8) & 0xff); sum_in_b += (src[src_i] & 0xff); } sp = radius; yp = radius; if (yp > hm) yp = hm; src_i = x + yp * w; // img.pix_ptr(x, yp); dst_i = x; // img.pix_ptr(x, 0); for (y = 0; y < h; y++) { src[dst_i] = (int) ((src[dst_i] & 0xff000000) | ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) | ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) | ((((sum_b * mul_sum) >>> shr_sum) & 0xff))); dst_i += w; sum_r -= sum_out_r; sum_g -= sum_out_g; sum_b -= sum_out_b; stack_start = sp + div - radius; if (stack_start >= div) stack_start -= div; stack_i = stack_start; sum_out_r -= ((stack[stack_i] >>> 16) & 0xff); sum_out_g -= ((stack[stack_i] >>> 8) & 0xff); sum_out_b -= (stack[stack_i] & 0xff); if (yp < hm) { src_i += w; // stride ++yp; } stack[stack_i] = src[src_i]; sum_in_r += ((src[src_i] >>> 16) & 0xff); sum_in_g += ((src[src_i] >>> 8) & 0xff); sum_in_b += (src[src_i] & 0xff); sum_r += sum_in_r; sum_g += sum_in_g; sum_b += sum_in_b; ++sp; if (sp >= div) sp = 0; stack_i = sp; sum_out_r += ((stack[stack_i] >>> 16) & 0xff); sum_out_g += ((stack[stack_i] >>> 8) & 0xff); sum_out_b += (stack[stack_i] & 0xff); sum_in_r -= ((stack[stack_i] >>> 16) & 0xff); sum_in_g -= ((stack[stack_i] >>> 8) & 0xff); sum_in_b -= (stack[stack_i] & 0xff); } } } } /** * Process the given image, blurring by the supplied radius. * If radius is 0, this will return original * * @param original the bitmap to be blurred * @param radius the radius in pixels to blur the image * @return the blurred version of the image. */ public Bitmap blur(Bitmap original, float radius) { int w = original.getWidth(); int h = original.getHeight(); int[] currentPixels = new int[w * h]; original.getPixels(currentPixels, 0, w, 0, 0, w, h); int cores = StackBlurManager.EXECUTOR_THREADS; ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores); ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores); for (int i = 0; i < cores; i++) { horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1)); vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2)); } try { StackBlurManager.EXECUTOR.invokeAll(horizontal); } catch (InterruptedException e) { return null; } try { StackBlurManager.EXECUTOR.invokeAll(vertical); } catch (InterruptedException e) { return null; } return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888); } private static class BlurTask implements Callable<Void> { private final int[] _src; private final int _w; private final int _h; private final int _radius; private final int _totalCores; private final int _coreIndex; private final int _round; public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) { _src = src; _w = w; _h = h; _radius = radius; _totalCores = totalCores; _coreIndex = coreIndex; _round = round; } @Override public Void call() throws Exception { blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round); return null; } } } }
2.布局文件activity_blur.xml
<ScrollView android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@drawable/coc" android:scaleType="centerInside" android:layout_margin="15dp"/> <ImageView android:id="@+id/blur_image" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerInside" android:layout_margin="15dp"/> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="更改毛玻璃效果"/> </LinearLayout> </ScrollView>
3.java代码
public class BlurActivity extends AppCompatActivity { private ImageView blurImage; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_blur); blurImage = (ImageView) findViewById(R.id.blur_image); button = (Button) findViewById(R.id.button); BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.coc); final StackBlurManager stackBlurManager = new StackBlurManager(bitmapDrawable.getBitmap()); blurImage.setImageBitmap(stackBlurManager.process(100)); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { blurImage.setImageBitmap(stackBlurManager.process(new Random().nextInt(100)+1));//粗糙代码 } }); } }
4.注意事项
<1>图片不能过大,否则会抛出异常;
<2>process(int)至少为1;