88import android .content .Context ;
99
1010public final class BlurEngine {
11- public static BlurController controller = new BlurController ();
12- public static volatile Bitmap blurBitmap ;
11+ // Tỉ lệ thu nhỏ và bán kính mờ để cân bằng hiệu năng
12+ private static final float SCALE_FACTOR = 0.15f ;
13+ private static final int BLUR_RADIUS = 8 ;
14+
1315 public static boolean isPaused = false ;
14-
15- public static float DEFAULT_CORNER_RADIUS = 30.0f ;
16- public float cornerRadius = DEFAULT_CORNER_RADIUS ;
16+ public float cornerRadius = 30.0f ;
1717
1818 private View targetView ;
1919 private int [] location = new int [2 ];
20- private int [] parentLocation = new int [2 ];
2120 private Rect srcRect = new Rect ();
2221 private Bitmap cachedBitmap ;
2322 private Canvas cachedCanvas ;
@@ -38,47 +37,54 @@ public void setup() {
3837 targetView .getViewTreeObserver ().addOnPreDrawListener (new BlurPreDrawListener (this , targetView ));
3938 }
4039
40+ /**
41+ * CẬP NHẬT: Tự động chụp nội dung phía sau View hiện tại
42+ */
4143 public Bitmap getUpdatedBlurBitmap () {
42- if (isPaused || blurBitmap == null || blurBitmap .isRecycled () ||
43- targetView .getWidth () <= 0 || targetView .getHeight () <= 0 ) {
44+ if (isPaused || targetView .getWidth () <= 0 || targetView .getHeight () <= 0 ) {
4445 return null ;
4546 }
4647
47- // Lấy RootView thực sự (thường là DecorView của Window)
4848 View rootView = targetView .getRootView ();
4949 if (rootView == null ) return null ;
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- }
70-
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 ;
50+
51+ // 1. Tính toán kích thước thu nhỏ
52+ int w = Math .round (targetView .getWidth () * SCALE_FACTOR );
53+ int h = Math .round (targetView .getHeight () * SCALE_FACTOR );
54+
55+ if (w <= 0 || h <= 0 ) return null ;
56+
57+ try {
58+ // 2. Khởi tạo hoặc tái sử dụng Bitmap đệm
59+ if (cachedBitmap == null || cachedBitmap .getWidth () != w || cachedBitmap .getHeight () != h ) {
60+ if (cachedBitmap != null ) cachedBitmap .recycle ();
61+ cachedBitmap = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
62+ cachedCanvas = new Canvas (cachedBitmap );
7963 }
64+
65+ // 3. Xác định vị trí của View trên màn hình
66+ targetView .getLocationOnScreen (location );
67+
68+ // 4. CHỤP NỘI DUNG PHÍA SAU
69+ cachedCanvas .save ();
70+ cachedCanvas .drawColor (Color .TRANSPARENT , PorterDuff .Mode .CLEAR );
71+ cachedCanvas .scale (SCALE_FACTOR , SCALE_FACTOR );
72+ cachedCanvas .translate (-location [0 ], -location [1 ]);
73+
74+ // Tạm ẩn chính nó để không bị hiệu ứng gương (chụp đè chính mình)
75+ targetView .setVisibility (View .INVISIBLE );
76+ rootView .draw (cachedCanvas );
77+ targetView .setVisibility (View .VISIBLE );
78+ cachedCanvas .restore ();
79+
80+ // 5. LÀM MỜ & NHUỘM MÀU
81+ fastBlur (cachedBitmap , BLUR_RADIUS );
82+ cachedCanvas .drawColor (getBlurTintColor ());
83+
84+ return cachedBitmap ;
85+ } catch (Exception e ) {
86+ return null ;
8087 }
81- return null ;
8288 }
8389
8490 private int getBlurTintColor () {
@@ -99,6 +105,111 @@ public static Paint getStrokePaint(Context context) {
99105 return strokePaint ;
100106 }
101107
108+ /**
109+ * Thuật toán StackBlur tích hợp trực tiếp để xử lý tại chỗ
110+ */
111+ private void fastBlur (Bitmap bitmap , int radius ) {
112+ if (radius < 1 ) return ;
113+ int w = bitmap .getWidth ();
114+ int h = bitmap .getHeight ();
115+ int [] pix = new int [w * h ];
116+ bitmap .getPixels (pix , 0 , w , 0 , 0 , w , h );
117+
118+ int wm = w - 1 ;
119+ int hm = h - 1 ;
120+ int wh = w * h ;
121+ int div = radius + radius + 1 ;
122+
123+ int [] r = new int [wh ];
124+ int [] g = new int [wh ];
125+ int [] b = new int [wh ];
126+ int rsum , gsum , bsum , x , y , i , p , yp , yi , yw ;
127+ int [] vmin = new int [Math .max (w , h )];
128+
129+ int divsum = (div + 1 ) >> 1 ;
130+ divsum *= divsum ;
131+ int [] dv = new int [256 * divsum ];
132+ for (i = 0 ; i < 256 * divsum ; i ++) dv [i ] = (i / divsum );
133+
134+ yw = yi = 0 ;
135+ int [][] stack = new int [div ][3 ];
136+ int stackpointer ;
137+ int stackstart ;
138+ int [] sir ;
139+ int rbs ;
140+ int r1 = radius + 1 ;
141+ int routsum , goutsum , boutsum ;
142+ int rinsum , ginsum , binsum ;
143+
144+ for (y = 0 ; y < h ; y ++) {
145+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
146+ for (i = -radius ; i <= radius ; i ++) {
147+ p = pix [yi + Math .min (wm , Math .max (i , 0 ))];
148+ sir = stack [i + radius ];
149+ sir [0 ] = (p & 0xff0000 ) >> 16 ;
150+ sir [1 ] = (p & 0x00ff00 ) >> 8 ;
151+ sir [2 ] = (p & 0x0000ff );
152+ rbs = r1 - Math .abs (i );
153+ rsum += sir [0 ] * rbs ; gsum += sir [1 ] * rbs ; bsum += sir [2 ] * rbs ;
154+ if (i > 0 ) { rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ]; }
155+ else { routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ]; }
156+ }
157+ stackpointer = radius ;
158+ for (x = 0 ; x < w ; x ++) {
159+ r [yi ] = dv [rsum ]; g [yi ] = dv [gsum ]; b [yi ] = dv [bsum ];
160+ rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
161+ stackstart = stackpointer - radius + div ;
162+ sir = stack [stackstart % div ];
163+ routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
164+ if (y == 0 ) vmin [x ] = Math .min (x + radius + 1 , wm );
165+ p = pix [yw + vmin [x ]];
166+ sir [0 ] = (p & 0xff0000 ) >> 16 ; sir [1 ] = (p & 0x00ff00 ) >> 8 ; sir [2 ] = (p & 0x0000ff );
167+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
168+ rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
169+ stackpointer = (stackpointer + 1 ) % div ;
170+ sir = stack [stackpointer % div ];
171+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
172+ rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
173+ yi ++;
174+ }
175+ yw += w ;
176+ }
177+ for (x = 0 ; x < w ; x ++) {
178+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0 ;
179+ yp = -radius * w ;
180+ for (i = -radius ; i <= radius ; i ++) {
181+ yi = Math .max (0 , yp ) + x ;
182+ sir = stack [i + radius ];
183+ sir [0 ] = r [yi ]; sir [1 ] = g [yi ]; sir [2 ] = b [yi ];
184+ rbs = r1 - Math .abs (i );
185+ rsum += r [yi ] * rbs ; gsum += g [yi ] * rbs ; bsum += b [yi ] * rbs ;
186+ if (i > 0 ) { rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ]; }
187+ else { routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ]; }
188+ if (i < hm ) yp += w ;
189+ }
190+ yi = x ;
191+ stackpointer = radius ;
192+ for (y = 0 ; y < h ; y ++) {
193+ pix [yi ] = (0xff000000 & pix [yi ]) | (dv [rsum ] << 16 ) | (dv [gsum ] << 8 ) | dv [bsum ];
194+ rsum -= routsum ; gsum -= goutsum ; bsum -= boutsum ;
195+ stackstart = stackpointer - radius + div ;
196+ sir = stack [stackstart % div ];
197+ routsum -= sir [0 ]; goutsum -= sir [1 ]; boutsum -= sir [2 ];
198+ if (x == 0 ) vmin [y ] = Math .min (y + r1 , hm ) * w ;
199+ p = x + vmin [y ];
200+ sir [0 ] = r [p ]; sir [1 ] = g [p ]; sir [2 ] = b [p ];
201+ rinsum += sir [0 ]; ginsum += sir [1 ]; binsum += sir [2 ];
202+ rsum += rinsum ; gsum += ginsum ; bsum += binsum ;
203+ stackpointer = (stackpointer + 1 ) % div ;
204+ sir = stack [stackpointer ];
205+ routsum += sir [0 ]; goutsum += sir [1 ]; boutsum += sir [2 ];
206+ rinsum -= sir [0 ]; ginsum -= sir [1 ]; binsum -= sir [2 ];
207+ yi += w ;
208+ }
209+ }
210+ bitmap .setPixels (pix , 0 , w , 0 , 0 , w , h );
211+ }
212+
102213 public void destroy () {
103214 if (cachedBitmap != null && !cachedBitmap .isRecycled ()) {
104215 cachedBitmap .recycle ();
0 commit comments