1111
1212public class FastBlurUtility {
1313
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)
1415 private static final float SCALE_FACTOR = 0.10f ;
1516 private static final int BLUR_RADIUS = 8 ;
1617
@@ -19,44 +20,51 @@ public class FastBlurUtility {
1920 */
2021 public static Bitmap getBlurBackgroundDrawer (Activity activity ) {
2122 Bitmap bmp = takeScreenShot (activity );
22- if (bmp == null ) return null ;
23-
24- Bitmap blurredBmp = startBlurBackground (bmp );
25-
26- if (bmp != blurredBmp && !bmp .isRecycled ()) {
27- bmp .recycle ();
28- }
29- return blurredBmp ;
23+ return startBlurBackground (bmp );
3024 }
3125
3226 /**
33- * Quy trình xử lý: Thu nhỏ -> Làm mờ -> Phóng to & Dim
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.
3429 */
3530 public static Bitmap startBlurBackground (Bitmap bkg ) {
3631 if (bkg == null || bkg .isRecycled ()) return null ;
3732
38- // Đảm bảo kích thước tối thiểu 16px để tránh ảnh bị quá nhỏ gây mờ đục hoặc hỏng layout
39- int width = Math .max ( Math . round (bkg .getWidth () * SCALE_FACTOR ), 16 );
40- int height = Math .max ( Math . round (bkg .getHeight () * SCALE_FACTOR ), 16 );
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 );
4136
42- Bitmap smallBitmap = Bitmap . createScaledBitmap ( bkg , width , height , true ) ;
37+ if ( width <= 0 || height <= 0 ) return bkg ;
4338
44- Bitmap blurred = fastBlur (smallBitmap , BLUR_RADIUS );
39+ try {
40+ // 2. Thu nhỏ ảnh (Sử dụng bộ lọc Bilinear để ảnh mượt hơn)
41+ Bitmap smallBitmap = Bitmap .createScaledBitmap (bkg , width , height , true );
4542
46- if (smallBitmap != null && smallBitmap != blurred && !smallBitmap .isRecycled ()) {
47- smallBitmap .recycle ();
48- }
43+ // 3. Làm mờ bằng thuật toán StackBlur (CPU-based, cực kỳ ổn định)
44+ Bitmap blurred = fastBlur (smallBitmap , BLUR_RADIUS );
45+
46+ // FIX: smallBitmap chỉ là ảnh trung gian, luôn giải phóng sau khi dùng xong
47+ // (trừ trường hợp createScaledBitmap trả về chính bkg do width/height không đổi)
48+ if (smallBitmap != bkg && !smallBitmap .isRecycled ()) {
49+ smallBitmap .recycle ();
50+ }
4951
50- if (blurred == null ) return null ;
52+ // FIX: fastBlur() có thể trả về null (radius < 1), phải kiểm tra trước khi dùng
53+ if (blurred == null || blurred .isRecycled ()) {
54+ return bkg ;
55+ }
5156
52- return scaleAndDim (blurred , bkg .getWidth (), bkg .getHeight ());
57+ // 4. Phóng to về kích thước gốc và áp dụng bộ lọc màu tối
58+ return scaleAndDim (blurred , bkg .getWidth (), bkg .getHeight ());
59+ } catch (OutOfMemoryError e ) {
60+ return bkg ;
61+ }
5362 }
5463
5564 /**
56- * Chụp ảnh màn hình an toàn
65+ * Chụp ảnh màn hình an toàn trên SDK 23+
5766 */
5867 private static Bitmap takeScreenShot (Activity activity ) {
59- if (activity == null || activity .isFinishing ()) return null ;
6068 try {
6169 View view = activity .getWindow ().getDecorView ();
6270 if (view .getWidth () <= 0 || view .getHeight () <= 0 ) return null ;
@@ -71,29 +79,33 @@ private static Bitmap takeScreenShot(Activity activity) {
7179 }
7280
7381 /**
74- * Phóng to ảnh và áp dụng ColorMatrix làm tối (Dim)
82+ * Phóng to ảnh và áp dụng ColorMatrix để làm tối nền (Dim)
7583 */
7684 private static Bitmap scaleAndDim (Bitmap bitmap , int targetW , int targetH ) {
7785 if (bitmap == null || bitmap .isRecycled ()) return null ;
7886
7987 Bitmap output = Bitmap .createBitmap (targetW , targetH , Bitmap .Config .ARGB_8888 );
8088 Canvas canvas = new Canvas (output );
8189
90+ // Paint với bộ lọc chống răng cưa và lọc bitmap khi scale
8291 Paint paint = new Paint (Paint .FILTER_BITMAP_FLAG | Paint .ANTI_ALIAS_FLAG );
8392
93+ // Tạo bộ lọc màu để giảm độ sáng (contrast 0.85f ~ giảm 15% độ sáng)
8494 ColorMatrix cm = new ColorMatrix ();
85- float contrast = 0.80f ;
95+ float contrast = 0.80f ;
8696 cm .set (new float []{
8797 contrast , 0 , 0 , 0 , 0 ,
8898 0 , contrast , 0 , 0 , 0 ,
8999 0 , 0 , contrast , 0 , 0 ,
90100 0 , 0 , 0 , 1 , 0 });
91101 paint .setColorFilter (new ColorMatrixColorFilter (cm ));
92102
103+ // Vẽ ảnh từ vùng nguồn (nhỏ) ra vùng đích (toàn màn hình)
93104 Rect src = new Rect (0 , 0 , bitmap .getWidth (), bitmap .getHeight ());
94105 Rect dst = new Rect (0 , 0 , targetW , targetH );
95106 canvas .drawBitmap (bitmap , src , dst , paint );
96107
108+ // Giải phóng bitmap tạm sau khi đã vẽ xong
97109 if (!bitmap .isRecycled ()) {
98110 bitmap .recycle ();
99111 }
@@ -102,20 +114,15 @@ private static Bitmap scaleAndDim(Bitmap bitmap, int targetW, int targetH) {
102114 }
103115
104116 /**
105- * Thuật toán StackBlur đã sửa lỗi hổng chỉ số mảng & giữ nguyên 100% chất lượng Blur gốc
117+ * Thuật toán StackBlur (Multi-pass box blur) - Tối ưu cho hiệu năng CPU
118+ * Hỗ trợ hoàn hảo cho các thiết bị từ cũ đến mới.
106119 */
107120 private static Bitmap fastBlur (Bitmap sentBitmap , int radius ) {
108- if (sentBitmap == null || sentBitmap .isRecycled ()) return null ;
109-
110- Bitmap .Config config = sentBitmap .getConfig ();
111- if (config == null ) {
112- config = Bitmap .Config .ARGB_8888 ;
113- }
121+ if (sentBitmap == null || sentBitmap .isRecycled () || radius < 1 ) return null ;
114122
123+ // FIX: getConfig() có thể null với bitmap dạng HARDWARE -> copy() sẽ ném exception
124+ Bitmap .Config config = sentBitmap .getConfig () != null ? sentBitmap .getConfig () : Bitmap .Config .ARGB_8888 ;
115125 Bitmap bitmap = sentBitmap .copy (config , true );
116- if (bitmap == null ) return null ;
117-
118- if (radius < 1 ) return null ;
119126
120127 int w = bitmap .getWidth ();
121128 int h = bitmap .getHeight ();
@@ -130,14 +137,13 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
130137 int [] r = new int [wh ];
131138 int [] g = new int [wh ];
132139 int [] b = new int [wh ];
133- int rsum , gsum , bsum , x , y , i , p , yi , yw ;
140+ int rsum , gsum , bsum , x , y , i , p , yp , yi , yw ;
134141 int [] vmin = new int [Math .max (w , h )];
135142
136143 int divsum = (div + 1 ) >> 1 ;
137144 divsum *= divsum ;
138- int maxDv = 256 * divsum ;
139- int [] dv = new int [maxDv ];
140- for (i = 0 ; i < maxDv ; i ++) {
145+ int [] dv = new int [256 * divsum ];
146+ for (i = 0 ; i < 256 * divsum ; i ++) {
141147 dv [i ] = (i / divsum );
142148 }
143149
@@ -151,7 +157,6 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
151157 int routsum , goutsum , boutsum ;
152158 int rinsum , ginsum , binsum ;
153159
154- // --- LƯỢT 1: LÀM MỜ CHIỀU NGANG ---
155160 for (y = 0 ; y < h ; y ++) {
156161 rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
157162 for (i = -radius ; i <= radius ; i ++) {
@@ -177,69 +182,53 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
177182 stackpointer = radius ;
178183
179184 for (x = 0 ; x < w ; x ++) {
180- r [yi ] = dv [Math .min (Math .max (rsum , 0 ), maxDv - 1 )];
181- g [yi ] = dv [Math .min (Math .max (gsum , 0 ), maxDv - 1 )];
182- b [yi ] = dv [Math .min (Math .max (bsum , 0 ), maxDv - 1 )];
183-
185+ r [yi ] = dv [rsum ];
186+ g [yi ] = dv [gsum ];
187+ b [yi ] = dv [bsum ];
184188 rsum -= routsum ;
185189 gsum -= goutsum ;
186190 bsum -= boutsum ;
187-
188191 stackstart = stackpointer - radius + div ;
189192 sir = stack [stackstart % div ];
190-
191193 routsum -= sir [0 ];
192194 goutsum -= sir [1 ];
193195 boutsum -= sir [2 ];
194-
195196 if (y == 0 ) vmin [x ] = Math .min (x + radius + 1 , wm );
196197 p = pix [yw + vmin [x ]];
197-
198198 sir [0 ] = (p & 0xff0000 ) >> 16 ;
199199 sir [1 ] = (p & 0x00ff00 ) >> 8 ;
200200 sir [2 ] = (p & 0x0000ff );
201-
202201 rinsum += sir [0 ];
203202 ginsum += sir [1 ];
204203 binsum += sir [2 ];
205-
206204 rsum += rinsum ;
207205 gsum += ginsum ;
208206 bsum += binsum ;
209-
210207 stackpointer = (stackpointer + 1 ) % div ;
211208 sir = stack [(stackpointer ) % div ];
212-
213209 routsum += sir [0 ];
214210 goutsum += sir [1 ];
215211 boutsum += sir [2 ];
216-
217212 rinsum -= sir [0 ];
218213 ginsum -= sir [1 ];
219214 binsum -= sir [2 ];
220-
221215 yi ++;
222216 }
223217 yw += w ;
224218 }
225-
226- // --- LƯỢT 2: LÀM MỜ CHIỀU DỌC ---
227219 for (x = 0 ; x < w ; x ++) {
228220 rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
229-
230- // ĐÃ SỬA LỖI: Khởi tạo stack chuẩn xác bằng Math.min(hm, Math.max(i, 0)) * w + x
221+ yp = -radius * w ;
231222 for (i = -radius ; i <= radius ; i ++) {
232- yi = Math .min ( hm , Math . max (i , 0 )) * w + x ;
223+ yi = Math .max (0 , yp ) + x ;
233224 sir = stack [i + radius ];
234225 sir [0 ] = r [yi ];
235226 sir [1 ] = g [yi ];
236227 sir [2 ] = b [yi ];
237-
238228 rbs = r1 - Math .abs (i );
239229 rsum += r [yi ] * rbs ;
240230 gsum += g [yi ] * rbs ;
241231 bsum += b [yi ] * rbs ;
242-
243232 if (i > 0 ) {
244233 rinsum += sir [0 ];
245234 ginsum += sir [1 ];
@@ -249,57 +238,42 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
249238 goutsum += sir [1 ];
250239 boutsum += sir [2 ];
251240 }
241+ if (i < hm ) yp += w ;
252242 }
253-
254243 yi = x ;
255244 stackpointer = radius ;
256245 for (y = 0 ; y < h ; y ++) {
257- pix [yi ] = (0xff000000 & pix [yi ])
258- | (dv [Math .min (Math .max (rsum , 0 ), maxDv - 1 )] << 16 )
259- | (dv [Math .min (Math .max (gsum , 0 ), maxDv - 1 )] << 8 )
260- | dv [Math .min (Math .max (bsum , 0 ), maxDv - 1 )];
261-
246+ pix [yi ] = (0xff000000 & pix [yi ]) | (dv [rsum ] << 16 ) | (dv [gsum ] << 8 ) | dv [bsum ];
262247 rsum -= routsum ;
263248 gsum -= goutsum ;
264249 bsum -= boutsum ;
265-
266250 stackstart = stackpointer - radius + div ;
267251 sir = stack [stackstart % div ];
268-
269252 routsum -= sir [0 ];
270253 goutsum -= sir [1 ];
271254 boutsum -= sir [2 ];
272-
273255 if (x == 0 ) vmin [y ] = Math .min (y + r1 , hm ) * w ;
274256 p = x + vmin [y ];
275-
276257 sir [0 ] = r [p ];
277258 sir [1 ] = g [p ];
278259 sir [2 ] = b [p ];
279-
280260 rinsum += sir [0 ];
281261 ginsum += sir [1 ];
282262 binsum += sir [2 ];
283-
284263 rsum += rinsum ;
285264 gsum += ginsum ;
286265 bsum += binsum ;
287-
288266 stackpointer = (stackpointer + 1 ) % div ;
289267 sir = stack [stackpointer ];
290-
291- routsum -= sir [0 ];
292- goutsum -= sir [1 ];
293- boutsum -= sir [2 ];
294-
268+ routsum += sir [0 ];
269+ goutsum += sir [1 ];
270+ boutsum += sir [2 ];
295271 rinsum -= sir [0 ];
296272 ginsum -= sir [1 ];
297273 binsum -= sir [2 ];
298-
299274 yi += w ;
300275 }
301276 }
302-
303277 bitmap .setPixels (pix , 0 , w , 0 , 0 , w , h );
304278 return bitmap ;
305279 }
0 commit comments