1616import android .media .Image ;
1717import android .media .ImageReader ;
1818import android .os .Build ;
19+ import android .os .Handler ;
20+ import android .os .Looper ;
1921import android .view .View ;
2022
23+ import java .util .concurrent .ExecutorService ;
24+ import java .util .concurrent .Executors ;
25+
2126public class FastBlurUtility {
2227
2328 private static final float SCALE_FACTOR = 0.10f ;
2429 private static final int BLUR_RADIUS = 8 ;
2530
31+ // ĐÃ SỬA: bảng chia dv[] chỉ phụ thuộc vào BLUR_RADIUS (hằng số),
32+ // nên tính 1 lần duy nhất khi load class thay vì cấp phát lại mỗi lần gọi fastBlur().
33+ private static final int DIV = BLUR_RADIUS + BLUR_RADIUS + 1 ;
34+ private static final int DIVSUM ;
35+ private static final int [] DV ;
36+
37+ static {
38+ int divsum = (DIV + 1 ) >> 1 ;
39+ divsum *= divsum ;
40+ DIVSUM = divsum ;
41+ DV = new int [256 * DIVSUM ];
42+ for (int i = 0 ; i < DV .length ; i ++) {
43+ DV [i ] = i / DIVSUM ;
44+ }
45+ }
46+
47+ // ĐÃ SỬA: dùng 1 background thread riêng để làm việc nặng (blur),
48+ // tránh block main thread gây giật lag khi mở màn hình.
49+ private static final ExecutorService BLUR_EXECUTOR = Executors .newSingleThreadExecutor ();
50+
51+ public interface OnBlurResultListener {
52+ void onBlurResult (Bitmap bitmap );
53+ }
54+
55+ /**
56+ * ĐÃ THÊM: API bất đồng bộ - nên dùng thay cho getBlurBackgroundDrawer() ở nơi có thể,
57+ * vì chỉ có bước chụp màn hình (rất nhanh) chạy trên main thread,
58+ * còn toàn bộ việc blur (chậm) được đẩy sang thread khác.
59+ */
60+ public static void getBlurBackgroundAsync (Activity activity , OnBlurResultListener listener ) {
61+ if (activity == null || activity .isFinishing ()) {
62+ listener .onBlurResult (null );
63+ return ;
64+ }
65+
66+ // Bước bắt buộc phải chạy trên main thread vì cần truy cập View,
67+ // nhưng bước này rất rẻ (chỉ vẽ 1 lần vào bitmap).
68+ Bitmap bmp = takeScreenShot (activity );
69+ if (bmp == null ) {
70+ listener .onBlurResult (null );
71+ return ;
72+ }
73+
74+ Handler mainHandler = new Handler (Looper .getMainLooper ());
75+ BLUR_EXECUTOR .execute (() -> {
76+ Bitmap blurred ;
77+ try {
78+ blurred = startBlurBackground (bmp );
79+ } catch (Throwable t ) {
80+ blurred = bmp ;
81+ }
82+
83+ final Bitmap result = blurred ;
84+ if (bmp != result && !bmp .isRecycled ()) {
85+ bmp .recycle ();
86+ }
87+
88+ mainHandler .post (() -> listener .onBlurResult (result ));
89+ });
90+ }
91+
92+ /**
93+ * Giữ lại API đồng bộ cũ để tương thích ngược, nhưng khuyến nghị dùng
94+ * getBlurBackgroundAsync() thay thế vì hàm này chạy blur ngay trên thread gọi nó
95+ * (nếu gọi từ main thread thì vẫn có thể gây giật).
96+ */
2697 public static Bitmap getBlurBackgroundDrawer (Activity activity ) {
2798 Bitmap bmp = takeScreenShot (activity );
2899 if (bmp == null ) return null ;
@@ -42,9 +113,12 @@ public static Bitmap startBlurBackground(Bitmap bkg) {
42113 try {
43114 // 1. Android 12+ (API 31+): Dùng GPU RenderEffect
44115 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
116+ // ĐÃ SỬA: gộp blur + dim (contrast) làm 1 RenderEffect duy nhất,
117+ // GPU tính cả 2 hiệu ứng trong 1 lần render, không cần thêm 1 bước
118+ // Canvas.drawBitmap + cấp phát bitmap phụ như bản cũ (applyDimFilter).
45119 Bitmap gpuBlurred = blurWithRenderEffect (bkg , 20f );
46120 if (gpuBlurred != null ) {
47- return applyDimFilter ( gpuBlurred ) ;
121+ return gpuBlurred ;
48122 }
49123 }
50124
@@ -79,7 +153,22 @@ private static Bitmap blurWithRenderEffect(Bitmap input, float radius) {
79153
80154 RenderNode node = new RenderNode ("BlurEffectNode" );
81155 node .setPosition (0 , 0 , width , height );
82- node .setRenderEffect (RenderEffect .createBlurEffect (radius , radius , Shader .TileMode .CLAMP ));
156+
157+ // ĐÃ SỬA: chain ColorFilterEffect (contrast/dim) lồng vào BlurEffect,
158+ // để GPU thực hiện cả 2 bước cùng lúc thay vì phải render riêng rồi
159+ // vẽ lại qua Canvas như bản gốc (applyDimFilter cũ).
160+ ColorMatrix cm = new ColorMatrix ();
161+ float contrast = 0.80f ;
162+ cm .set (new float []{
163+ contrast , 0 , 0 , 0 , 0 ,
164+ 0 , contrast , 0 , 0 , 0 ,
165+ 0 , 0 , contrast , 0 , 0 ,
166+ 0 , 0 , 0 , 1 , 0 });
167+
168+ RenderEffect blurEffect = RenderEffect .createBlurEffect (radius , radius , Shader .TileMode .CLAMP );
169+ RenderEffect combinedEffect = RenderEffect .createColorFilterEffect (
170+ new ColorMatrixColorFilter (cm ), blurEffect );
171+ node .setRenderEffect (combinedEffect );
83172
84173 Canvas canvas = node .beginRecording (width , height );
85174 canvas .drawBitmap (input , 0 , 0 , null );
@@ -123,27 +212,6 @@ private static Bitmap blurWithRenderEffect(Bitmap input, float radius) {
123212 return null ;
124213 }
125214
126- private static Bitmap applyDimFilter (Bitmap src ) {
127- Bitmap output = Bitmap .createBitmap (src .getWidth (), src .getHeight (), Bitmap .Config .ARGB_8888 );
128- Canvas canvas = new Canvas (output );
129-
130- Paint paint = new Paint (Paint .FILTER_BITMAP_FLAG | Paint .ANTI_ALIAS_FLAG );
131- ColorMatrix cm = new ColorMatrix ();
132- float contrast = 0.80f ;
133- cm .set (new float []{
134- contrast , 0 , 0 , 0 , 0 ,
135- 0 , contrast , 0 , 0 , 0 ,
136- 0 , 0 , contrast , 0 , 0 ,
137- 0 , 0 , 0 , 1 , 0 });
138- paint .setColorFilter (new ColorMatrixColorFilter (cm ));
139-
140- canvas .drawBitmap (src , 0 , 0 , paint );
141- if (!src .isRecycled ()) {
142- src .recycle ();
143- }
144- return output ;
145- }
146-
147215 public static Bitmap takeScreenShot (Activity activity ) {
148216 if (activity == null || activity .isFinishing ()) return null ;
149217 try {
@@ -216,12 +284,9 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
216284 int rsum , gsum , bsum , x , y , i , p , yp , yi , yw ;
217285 int [] vmin = new int [Math .max (w , h )];
218286
219- int divsum = (div + 1 ) >> 1 ;
220- divsum *= divsum ;
221- int [] dv = new int [256 * divsum ];
222- for (i = 0 ; i < 256 * divsum ; i ++) {
223- dv [i ] = (i / divsum );
224- }
287+ // ĐÃ SỬA: dùng bảng DV[] tĩnh (static) tính sẵn 1 lần thay vì cấp phát
288+ // mảng 256*divsum phần tử mỗi lần fastBlur() được gọi.
289+ int [] dv = DV ;
225290
226291 yw = yi = 0 ;
227292 int [][] stack = new int [div ][3 ];
@@ -334,9 +399,9 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
334399 yi = x ;
335400 stackpointer = radius ;
336401 for (y = 0 ; y < h ; y ++) {
337- pix [yi ] = (0xff000000 & pix [yi ])
338- | (dv [clampIndex (rsum , dv .length )] << 16 )
339- | (dv [clampIndex (gsum , dv .length )] << 8 )
402+ pix [yi ] = (0xff000000 & pix [yi ])
403+ | (dv [clampIndex (rsum , dv .length )] << 16 )
404+ | (dv [clampIndex (gsum , dv .length )] << 8 )
340405 | dv [clampIndex (bsum , dv .length )];
341406
342407 rsum -= routsum ;
@@ -382,4 +447,4 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
382447 bitmap .setPixels (pix , 0 , w , 0 , 0 , w , h );
383448 return bitmap ;
384449 }
385- }
450+ }
0 commit comments