77import android .graphics .ColorMatrixColorFilter ;
88import android .graphics .Paint ;
99import android .graphics .Rect ;
10+ import android .os .Handler ;
11+ import android .os .Looper ;
1012import android .view .View ;
1113
14+ import java .util .concurrent .ExecutorService ;
15+ import java .util .concurrent .Executors ;
16+
1217public class FastBlurUtility {
1318
14- // Tỉ lệ thu nhỏ ảnh để xử lý nhanh (1/10 giúp giảm 100 lần số pixel cần tính toán)
1519 private static final float SCALE_FACTOR = 0.10f ;
1620 private static final int BLUR_RADIUS = 8 ;
21+
22+ // Executor để xử lý các tác vụ nặng ngoài UI Thread
23+ private static final ExecutorService executor = Executors .newSingleThreadExecutor ();
24+ private static final Handler mainHandler = new Handler (Looper .getMainLooper ());
1725
18- /**
19- * Chụp màn hình và làm mờ (Dùng làm phương án dự phòng khi không lấy được Wallpaper)
20- */
21- public static Bitmap getBlurBackgroundDrawer (Activity activity ) {
22- Bitmap bmp = takeScreenShot (activity );
23- return startBlurBackground (bmp );
26+ public interface BlurCallback {
27+ void onBlurCompleted (Bitmap bitmap );
2428 }
2529
2630 /**
27- * Quy trình xử lý: Thu nhỏ -> Làm mờ -> Phóng to & Nhuộm tối (Dim)
28- * Đảm bảo mượt mà từ SDK 23 trở lên.
31+ * Phương thức chính: Chụp và làm mờ không gây lag UI
2932 */
30- public static Bitmap startBlurBackground (Bitmap bkg ) {
33+ public static void getBlurBackgroundAsync (Activity activity , BlurCallback callback ) {
34+ // 1. Chụp màn hình phải thực hiện trên UI Thread
35+ Bitmap screenshot = takeScreenShot (activity );
36+
37+ if (screenshot == null ) {
38+ callback .onBlurCompleted (null );
39+ return ;
40+ }
41+
42+ // 2. Đẩy việc xử lý làm mờ vào Background Thread
43+ executor .execute (() -> {
44+ Bitmap blurred = startBlurProcess (screenshot );
45+
46+ // 3. Trả kết quả về Main Thread qua Callback
47+ mainHandler .post (() -> callback .onBlurCompleted (blurred ));
48+ });
49+ }
50+
51+ private static Bitmap startBlurProcess (Bitmap bkg ) {
3152 if (bkg == null || bkg .isRecycled ()) return null ;
3253
33- // 1. Tính toán kích thước thu nhỏ
34- int width = Math .round (bkg .getWidth () * SCALE_FACTOR );
35- int height = Math .round (bkg .getHeight () * SCALE_FACTOR );
54+ int originWidth = bkg .getWidth ();
55+ int originHeight = bkg .getHeight ();
56+
57+ // Tính toán kích thước thu nhỏ
58+ int width = Math .round (originWidth * SCALE_FACTOR );
59+ int height = Math .round (originHeight * SCALE_FACTOR );
3660
3761 if (width <= 0 || height <= 0 ) return bkg ;
3862
39- // 2. Thu nhỏ ảnh (Sử dụng bộ lọc Bilinear để ảnh mượt hơn)
63+ // Thu nhỏ ảnh
4064 Bitmap smallBitmap = Bitmap .createScaledBitmap (bkg , width , height , true );
65+
66+ // Giải phóng ảnh gốc ngay để tiết kiệm RAM
67+ if (bkg != smallBitmap ) {
68+ bkg .recycle ();
69+ }
4170
42- // 3. Làm mờ bằng thuật toán StackBlur (CPU-based, cực kỳ ổn định)
71+ // Làm mờ bằng thuật toán StackBlur
4372 Bitmap blurred = fastBlur (smallBitmap , BLUR_RADIUS );
4473
45- // 4. Phóng to về kích thước gốc và áp dụng bộ lọc màu tối
46- return scaleAndDim (blurred , bkg . getWidth (), bkg . getHeight () );
74+ // Phóng to và làm tối
75+ return scaleAndDim (blurred , originWidth , originHeight );
4776 }
4877
49- /**
50- * Chụp ảnh màn hình an toàn trên SDK 23+
51- */
5278 private static Bitmap takeScreenShot (Activity activity ) {
5379 try {
5480 View view = activity .getWindow ().getDecorView ();
5581 if (view .getWidth () <= 0 || view .getHeight () <= 0 ) return null ;
5682
57- Bitmap bitmap = Bitmap .createBitmap (view .getWidth (), view .getHeight (), Bitmap .Config .ARGB_8888 );
83+ // Dùng RGB_565 để tiết kiệm 50% bộ nhớ so với ARGB_8888 (phù hợp SDK 23+)
84+ Bitmap bitmap = Bitmap .createBitmap (view .getWidth (), view .getHeight (), Bitmap .Config .RGB_565 );
5885 Canvas canvas = new Canvas (bitmap );
5986 view .draw (canvas );
6087 return bitmap ;
@@ -63,46 +90,34 @@ private static Bitmap takeScreenShot(Activity activity) {
6390 }
6491 }
6592
66- /**
67- * Phóng to ảnh và áp dụng ColorMatrix để làm tối nền (Dim)
68- */
6993 private static Bitmap scaleAndDim (Bitmap bitmap , int targetW , int targetH ) {
70- Bitmap output = Bitmap .createBitmap (targetW , targetH , Bitmap .Config .ARGB_8888 );
94+ Bitmap output = Bitmap .createBitmap (targetW , targetH , Bitmap .Config .RGB_565 );
7195 Canvas canvas = new Canvas (output );
7296
73- // Paint với bộ lọc chống răng cưa và lọc bitmap khi scale
7497 Paint paint = new Paint (Paint .FILTER_BITMAP_FLAG | Paint .ANTI_ALIAS_FLAG );
7598
76- // Tạo bộ lọc màu để giảm độ sáng (contrast 0.85f ~ giảm 15% độ sáng)
99+ // Giảm độ sáng 20%
77100 ColorMatrix cm = new ColorMatrix ();
78101 float contrast = 0.8f ;
79- cm .set (new float []{
80- contrast , 0 , 0 , 0 , 0 ,
81- 0 , contrast , 0 , 0 , 0 ,
82- 0 , 0 , contrast , 0 , 0 ,
83- 0 , 0 , 0 , 1 , 0 });
102+ cm .setScale (contrast , contrast , contrast , 1.0f );
84103 paint .setColorFilter (new ColorMatrixColorFilter (cm ));
85104
86- // Vẽ ảnh từ vùng nguồn (nhỏ) ra vùng đích (toàn màn hình)
87105 Rect src = new Rect (0 , 0 , bitmap .getWidth (), bitmap .getHeight ());
88106 Rect dst = new Rect (0 , 0 , targetW , targetH );
89107 canvas .drawBitmap (bitmap , src , dst , paint );
90108
91- // Giải phóng bitmap tạm sau khi đã vẽ xong
92- if (bitmap != null && !bitmap .isRecycled ()) {
109+ if (!bitmap .isRecycled ()) {
93110 bitmap .recycle ();
94111 }
95112
96113 return output ;
97114 }
98115
99116 /**
100- * Thuật toán StackBlur (Multi-pass box blur) - Tối ưu cho hiệu năng CPU
101- * Hỗ trợ hoàn hảo cho các thiết bị từ cũ đến mới.
117+ * Thuật toán StackBlur tối ưu hóa
102118 */
103119 private static Bitmap fastBlur (Bitmap sentBitmap , int radius ) {
104120 Bitmap bitmap = sentBitmap .copy (sentBitmap .getConfig (), true );
105-
106121 if (radius < 1 ) return null ;
107122
108123 int w = bitmap .getWidth ();
@@ -151,48 +166,27 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
151166 gsum += sir [1 ] * rbs ;
152167 bsum += sir [2 ] * rbs ;
153168 if (i > 0 ) {
154- rinsum += sir [0 ];
155- ginsum += sir [1 ];
156- binsum += sir [2 ];
169+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
157170 } else {
158- routsum += sir [0 ];
159- goutsum += sir [1 ];
160- boutsum += sir [2 ];
171+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
161172 }
162173 }
163174 stackpointer = radius ;
164-
165175 for (x = 0 ; x < w ; x ++) {
166- r [yi ] = dv [rsum ];
167- g [yi ] = dv [gsum ];
168- b [yi ] = dv [bsum ];
169- rsum -= routsum ;
170- gsum -= goutsum ;
171- bsum -= boutsum ;
176+ r [yi ] = dv [rsum ]; g [yi ] = dv [gsum ]; b [yi ] = dv [bsum ];
177+ rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
172178 stackstart = stackpointer - radius + div ;
173179 sir = stack [stackstart % div ];
174- routsum -= sir [0 ];
175- goutsum -= sir [1 ];
176- boutsum -= sir [2 ];
180+ routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
177181 if (y == 0 ) vmin [x ] = Math .min (x + radius + 1 , wm );
178182 p = pix [yw + vmin [x ]];
179- sir [0 ] = (p & 0xff0000 ) >> 16 ;
180- sir [1 ] = (p & 0x00ff00 ) >> 8 ;
181- sir [2 ] = (p & 0x0000ff );
182- rinsum += sir [0 ];
183- ginsum += sir [1 ];
184- binsum += sir [2 ];
185- rsum += rinsum ;
186- gsum += ginsum ;
187- bsum += binsum ;
183+ sir [0 ] = (p & 0xff0000 ) >> 16 ; sir [1 ] = (p & 0x00ff00 ) >> 8 ; sir [2 ] = (p & 0x0000ff );
184+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
185+ rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
188186 stackpointer = (stackpointer + 1 ) % div ;
189187 sir = stack [(stackpointer ) % div ];
190- routsum += sir [0 ];
191- goutsum += sir [1 ];
192- boutsum += sir [2 ];
193- rinsum -= sir [0 ];
194- ginsum -= sir [1 ];
195- binsum -= sir [2 ];
188+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
189+ rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
196190 yi ++;
197191 }
198192 yw += w ;
@@ -203,59 +197,38 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
203197 for (i = -radius ; i <= radius ; i ++) {
204198 yi = Math .max (0 , yp ) + x ;
205199 sir = stack [i + radius ];
206- sir [0 ] = r [yi ];
207- sir [1 ] = g [yi ];
208- sir [2 ] = b [yi ];
200+ sir [0 ] = r [yi ]; sir [1 ] = g [yi ]; sir [2 ] = b [yi ];
209201 rbs = r1 - Math .abs (i );
210- rsum += r [yi ] * rbs ;
211- gsum += g [yi ] * rbs ;
212- bsum += b [yi ] * rbs ;
202+ rsum += r [yi ] * rbs ; gsum += g [yi ] * rbs ; bsum += b [yi ] * rbs ;
213203 if (i > 0 ) {
214- rinsum += sir [0 ];
215- ginsum += sir [1 ];
216- binsum += sir [2 ];
204+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
217205 } else {
218- routsum += sir [0 ];
219- goutsum += sir [1 ];
220- boutsum += sir [2 ];
206+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
221207 }
222208 if (i < hm ) yp += w ;
223209 }
224210 yi = x ;
225211 stackpointer = radius ;
226212 for (y = 0 ; y < h ; y ++) {
227213 pix [yi ] = (0xff000000 & pix [yi ]) | (dv [rsum ] << 16 ) | (dv [gsum ] << 8 ) | dv [bsum ];
228- rsum -= routsum ;
229- gsum -= goutsum ;
230- bsum -= boutsum ;
214+ rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
231215 stackstart = stackpointer - radius + div ;
232216 sir = stack [stackstart % div ];
233- routsum -= sir [0 ];
234- goutsum -= sir [1 ];
235- boutsum -= sir [2 ];
217+ routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
236218 if (x == 0 ) vmin [y ] = Math .min (y + r1 , hm ) * w ;
237219 p = x + vmin [y ];
238- sir [0 ] = r [p ];
239- sir [1 ] = g [p ];
240- sir [2 ] = b [p ];
241- rinsum += sir [0 ];
242- ginsum += sir [1 ];
243- binsum += sir [2 ];
244- rsum += rinsum ;
245- gsum += ginsum ;
246- bsum += binsum ;
220+ sir [0 ] = r [p ]; sir [1 ] = g [p ]; sir [2 ] = b [p ];
221+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
222+ rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
247223 stackpointer = (stackpointer + 1 ) % div ;
248224 sir = stack [stackpointer ];
249- routsum += sir [0 ];
250- goutsum += sir [1 ];
251- boutsum += sir [2 ];
252- rinsum -= sir [0 ];
253- ginsum -= sir [1 ];
254- binsum -= sir [2 ];
225+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
226+ rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
255227 yi += w ;
256228 }
257229 }
258230 bitmap .setPixels (pix , 0 , w , 0 , 0 , w , h );
231+ sentBitmap .recycle (); // Giải phóng bitmap cũ sau khi copy
259232 return bitmap ;
260233 }
261234}
0 commit comments