88import android .content .Context ;
99
1010public final class BlurEngine {
11- // --- Các thành phần cũ để giữ tương thích hệ thống ---
1211 public static BlurController controller = new BlurController ();
13- public static volatile Bitmap blurBitmap ;
12+ public static volatile Bitmap blurBitmap ;
1413 public static boolean isPaused = false ;
15- public static float DEFAULT_CORNER_RADIUS = 30.0f ;
1614
17- // --- Cấu hình cho tính năng Blur thời gian thực (Lựa chọn 2) ---
18- private static final float SCALE_FACTOR = 0.15f ;
19- private static final int BLUR_RADIUS = 8 ;
20-
15+ public static float DEFAULT_CORNER_RADIUS = 30.0f ;
2116 public float cornerRadius = DEFAULT_CORNER_RADIUS ;
2217
2318 private View targetView ;
2419 private int [] location = new int [2 ];
20+ private int [] parentLocation = new int [2 ];
2521 private Rect srcRect = new Rect ();
2622 private Bitmap cachedBitmap ;
2723 private Canvas cachedCanvas ;
@@ -42,59 +38,47 @@ public void setup() {
4238 targetView .getViewTreeObserver ().addOnPreDrawListener (new BlurPreDrawListener (this , targetView ));
4339 }
4440
45- /**
46- * Logic mới: Chụp nội dung phía sau trực tiếp để không bị mất blur khi cuộn dài
47- */
4841 public Bitmap getUpdatedBlurBitmap () {
49- if (isPaused || targetView .getWidth () <= 0 || targetView .getHeight () <= 0 ) {
42+ if (isPaused || blurBitmap == null || blurBitmap .isRecycled () ||
43+ targetView .getWidth () <= 0 || targetView .getHeight () <= 0 ) {
5044 return null ;
5145 }
5246
47+ // Lấy RootView thực sự (thường là DecorView của Window)
5348 View rootView = targetView .getRootView ();
5449 if (rootView == null ) return null ;
55-
56- // 1. Tính toán kích thước thu nhỏ để tối ưu hiệu suất
57- int w = Math .round (targetView .getWidth () * SCALE_FACTOR );
58- int h = Math .round (targetView .getHeight () * SCALE_FACTOR );
59-
60- if (w <= 0 || h <= 0 ) return null ;
61-
62- try {
63- // 2. Quản lý bộ nhớ đệm Bitmap
64- if (cachedBitmap == null || cachedBitmap .getWidth () != w || cachedBitmap .getHeight () != h ) {
65- if (cachedBitmap != null ) cachedBitmap .recycle ();
66- cachedBitmap = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
67- cachedCanvas = new Canvas (cachedBitmap );
68- }
69-
70- // 3. Xác định tọa độ thực tế trên màn hình
71- targetView .getLocationOnScreen (location );
72-
73- // 4. CHỤP NỘI DUNG NỀN (Lựa chọn 2)
74- cachedCanvas .save ();
75- cachedCanvas .drawColor (Color .TRANSPARENT , PorterDuff .Mode .CLEAR );
76-
77- // Di chuyển và scale canvas để "soi" đúng phần diện tích phía sau targetView
78- cachedCanvas .scale (SCALE_FACTOR , SCALE_FACTOR );
79- cachedCanvas .translate (-location [0 ], -location [1 ]);
50+ targetView .getLocationOnScreen (location );
51+ float scaleX = (float ) blurBitmap .getWidth () / rootView .getWidth ();
52+ float scaleY = (float ) blurBitmap .getHeight () / rootView .getHeight ();
53+
54+ int w = (int ) (targetView .getWidth () * scaleX );
55+ int h = (int ) (targetView .getHeight () * scaleY );
56+ int x = (int ) (location [0 ] * scaleX );
57+ int y = (int ) (location [1 ] * scaleY );
58+
59+ // Chống tràn biên Bitmap
60+ x = Math .max (0 , Math .min (x , blurBitmap .getWidth () - w ));
61+ y = Math .max (0 , Math .min (y , blurBitmap .getHeight () - h ));
62+
63+ if (w > 0 && h > 0 ) {
64+ try {
65+ if (cachedBitmap == null || cachedBitmap .getWidth () != w || cachedBitmap .getHeight () != h ) {
66+ if (cachedBitmap != null ) cachedBitmap .recycle ();
67+ cachedBitmap = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
68+ cachedCanvas = new Canvas (cachedBitmap );
69+ }
8070
81- // Tạm ẩn chính nó để tránh vòng lặp hiển thị (Recursive drawing)
82- targetView .setVisibility (View .INVISIBLE );
83- rootView .draw (cachedCanvas );
84- targetView .setVisibility (View .VISIBLE );
85-
86- cachedCanvas .restore ();
87-
88- // 5. Làm mờ trực tiếp trên ảnh đã thu nhỏ
89- fastBlur (cachedBitmap , BLUR_RADIUS );
90-
91- // 6. Phủ màu Tint theo theme (Dark/Light)
92- cachedCanvas .drawColor (getBlurTintColor ());
93-
94- return cachedBitmap ;
95- } catch (Exception e ) {
96- return null ;
71+ srcRect .set (x , y , x + w , y + h );
72+ cachedCanvas .drawColor (0 , PorterDuff .Mode .CLEAR );
73+
74+ cachedCanvas .drawBitmap (blurBitmap , srcRect , new Rect (0 , 0 , w , h ), null );
75+ cachedCanvas .drawColor (getBlurTintColor ());
76+ return cachedBitmap ;
77+ } catch (Exception e ) {
78+ return null ;
79+ }
9780 }
81+ return null ;
9882 }
9983
10084 private int getBlurTintColor () {
@@ -107,119 +91,14 @@ public static Paint getStrokePaint(Context context) {
10791 if (strokePaint == null ) {
10892 strokePaint = new Paint (Paint .ANTI_ALIAS_FLAG );
10993 strokePaint .setStyle (Paint .Style .STROKE );
110- strokePaint .setStrokeWidth (3.0f );
94+ strokePaint .setStrokeWidth (3.0f );
11195 }
11296 int colorRes = ThemeModeState .isDarkMode () ? R .color .colorPirmLight : R .color .colorPirmDark ;
11397 int color = ContextCompat .getColor (context , colorRes );
11498 if (strokePaint .getColor () != color ) strokePaint .setColor (color );
11599 return strokePaint ;
116100 }
117101
118- /**
119- * Thuật toán StackBlur tích hợp (Dùng xử lý ảnh nhỏ cực nhanh)
120- */
121- private void fastBlur (Bitmap bitmap , int radius ) {
122- if (radius < 1 ) return ;
123- int w = bitmap .getWidth ();
124- int h = bitmap .getHeight ();
125- int [] pix = new int [w * h ];
126- bitmap .getPixels (pix , 0 , w , 0 , 0 , w , h );
127-
128- int wm = w - 1 ;
129- int hm = h - 1 ;
130- int wh = w * h ;
131- int div = radius + radius + 1 ;
132-
133- int [] r = new int [wh ];
134- int [] g = new int [wh ];
135- int [] b = new int [wh ];
136- int rsum , gsum , bsum , x , y , i , p , yp , yi , yw ;
137- int [] vmin = new int [Math .max (w , h )];
138-
139- int divsum = (div + 1 ) >> 1 ;
140- divsum *= divsum ;
141- int [] dv = new int [256 * divsum ];
142- for (i = 0 ; i < 256 * divsum ; i ++) dv [i ] = (i / divsum );
143-
144- yw = yi = 0 ;
145- int [][] stack = new int [div ][3 ];
146- int stackpointer ;
147- int stackstart ;
148- int [] sir ;
149- int rbs ;
150- int r1 = radius + 1 ;
151- int routsum , goutsum , boutsum ;
152- int rinsum , ginsum , binsum ;
153-
154- for (y = 0 ; y < h ; y ++) {
155- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
156- for (i = -radius ; i <= radius ; i ++) {
157- p = pix [yi + Math .min (wm , Math .max (i , 0 ))];
158- sir = stack [i + radius ];
159- sir [0 ] = (p & 0xff0000 ) >> 16 ;
160- sir [1 ] = (p & 0x00ff00 ) >> 8 ;
161- sir [2 ] = (p & 0x0000ff );
162- rbs = r1 - Math .abs (i );
163- rsum += sir [0 ] * rbs ; gsum += sir [1 ] * rbs ; bsum += sir [2 ] * rbs ;
164- if (i > 0 ) { rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ]; }
165- else { routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ]; }
166- }
167- stackpointer = radius ;
168- for (x = 0 ; x < w ; x ++) {
169- r [yi ] = dv [rsum ]; g [yi ] = dv [gsum ]; b [yi ] = dv [bsum ];
170- rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
171- stackstart = stackpointer - radius + div ;
172- sir = stack [stackstart % div ];
173- routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
174- if (y == 0 ) vmin [x ] = Math .min (x + radius + 1 , wm );
175- p = pix [yw + vmin [x ]];
176- sir [0 ] = (p & 0xff0000 ) >> 16 ; sir [1 ] = (p & 0x00ff00 ) >> 8 ; sir [2 ] = (p & 0x0000ff );
177- rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
178- rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
179- stackpointer = (stackpointer + 1 ) % div ;
180- sir = stack [stackpointer % div ];
181- routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
182- rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
183- yi ++;
184- }
185- yw += w ;
186- }
187- for (x = 0 ; x < w ; x ++) {
188- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
189- yp = -radius * w ;
190- for (i = -radius ; i <= radius ; i ++) {
191- yi = Math .max (0 , yp ) + x ;
192- sir = stack [i + radius ];
193- sir [0 ] = r [yi ]; sir [1 ] = g [yi ]; sir [2 ] = b [yi ];
194- rbs = r1 - Math .abs (i );
195- rsum += r [yi ] * rbs ; gsum += g [yi ] * rbs ; bsum += b [yi ] * rbs ;
196- if (i > 0 ) { rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ]; }
197- else { routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ]; }
198- if (i < hm ) yp += w ;
199- }
200- yi = x ;
201- stackpointer = radius ;
202- for (y = 0 ; y < h ; y ++) {
203- pix [yi ] = (0xff000000 & pix [yi ]) | (dv [rsum ] << 16 ) | (dv [gsum ] << 8 ) | dv [bsum ];
204- rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
205- stackstart = stackpointer - radius + div ;
206- sir = stack [stackstart % div ];
207- routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
208- if (x == 0 ) vmin [y ] = Math .min (y + r1 , hm ) * w ;
209- p = x + vmin [y ];
210- sir [0 ] = r [p ]; sir [1 ] = g [p ]; sir [2 ] = b [p ];
211- rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
212- rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
213- stackpointer = (stackpointer + 1 ) % div ;
214- sir = stack [stackpointer ];
215- routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
216- rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
217- yi += w ;
218- }
219- }
220- bitmap .setPixels (pix , 0 , w , 0 , 0 , w , h );
221- }
222-
223102 public void destroy () {
224103 if (cachedBitmap != null && !cachedBitmap .isRecycled ()) {
225104 cachedBitmap .recycle ();
0 commit comments