99
1010public final class BlurEngine {
1111 public static BlurController controller = new BlurController ();
12-
13- // volatile để đảm bảo tính nhất quán dữ liệu giữa Thread của Controller và UI Thread
1412 public static volatile Bitmap blurBitmap ;
1513 public static boolean isPaused = true ;
1614
@@ -41,32 +39,41 @@ public void setup() {
4139 }
4240
4341 public Bitmap getUpdatedBlurBitmap () {
44- // Kiểm tra an toàn Bitmap trước khi xử lý
42+ // 1. Kiểm tra an toàn: Bitmap phải tồn tại và View phải có kích thước
4543 if (isPaused || blurBitmap == null || blurBitmap .isRecycled () ||
4644 targetView .getWidth () <= 0 || targetView .getHeight () <= 0 ) {
4745 return null ;
4846 }
4947
50- targetView .getLocationInWindow (location );
48+ // 2. Lấy vị trí CHÍNH XÁC của View trên màn hình ngay tại thời điểm vẽ
49+ targetView .getLocationOnScreen (location );
5150
51+ // 3. Lấy kích thước toàn màn hình (hoặc root view) để tính tỷ lệ
5252 View rootView = targetView .getRootView ();
53- // Tính toán tỷ lệ dựa trên kích thước thực tế của bitmap (đã được scale nhỏ ở Controller)
54- float scaleX = (float ) blurBitmap .getWidth () / rootView .getWidth ();
55- float scaleY = (float ) blurBitmap .getHeight () / rootView .getHeight ();
53+ int rootWidth = rootView .getWidth ();
54+ int rootHeight = rootView .getHeight ();
55+
56+ if (rootWidth <= 0 || rootHeight <= 0 ) return null ;
5657
57- int x = (int ) (location [0 ] * scaleX );
58- int y = (int ) (location [1 ] * scaleY );
59- int w = (int ) (targetView .getWidth () * scaleX );
60- int h = (int ) (targetView .getHeight () * scaleY );
58+ // 4. Tính toán tỷ lệ giữa ảnh blur đã scale và màn hình thực tế
59+ float scaleX = (float ) blurBitmap .getWidth () / rootWidth ;
60+ float scaleY = (float ) blurBitmap .getHeight () / rootHeight ;
6161
62- // Giới hạn vùng cắt bên trong phạm vi Bitmap
62+ // 5. Tính toán tọa độ vùng cắt trên ảnh blur
63+ // Cần dùng float để tránh sai số tích lũy khi vuốt nhanh, sau đó mới ép kiểu int
64+ int x = Math .round (location [0 ] * scaleX );
65+ int y = Math .round (location [1 ] * scaleY );
66+ int w = Math .round (targetView .getWidth () * scaleX );
67+ int h = Math .round (targetView .getHeight () * scaleY );
68+
69+ // 6. Ràng buộc tọa độ để không cắt ra ngoài phạm vi bitmap
6370 x = Math .max (0 , Math .min (x , blurBitmap .getWidth () - w ));
6471 y = Math .max (0 , Math .min (y , blurBitmap .getHeight () - h ));
6572
6673 if (w > 0 && h > 0 ) {
6774 try {
75+ // Tối ưu bộ nhớ: Chỉ tạo lại bitmap đệm khi kích thước view thay đổi
6876 if (cachedBitmap == null || cachedBitmap .getWidth () != w || cachedBitmap .getHeight () != h ) {
69- // Giải phóng cached cũ nếu kích thước thay đổi
7077 if (cachedBitmap != null ) cachedBitmap .recycle ();
7178 cachedBitmap = Bitmap .createBitmap (w , h , Bitmap .Config .ARGB_8888 );
7279 cachedCanvas = new Canvas (cachedBitmap );
@@ -75,8 +82,12 @@ public Bitmap getUpdatedBlurBitmap() {
7582 srcRect .set (x , y , x + w , y + h );
7683 cachedCanvas .drawColor (0 , PorterDuff .Mode .CLEAR );
7784
85+ // Vẽ vùng ảnh tương ứng từ ảnh nền vào cache
7886 cachedCanvas .drawBitmap (blurBitmap , srcRect , new Rect (0 , 0 , w , h ), null );
87+
88+ // Nhuộm màu (Tint) để tạo hiệu ứng kính mờ (Frost Glass)
7989 cachedCanvas .drawColor (getBlurTintColor ());
90+
8091 return cachedBitmap ;
8192 } catch (Exception e ) {
8293 return null ;
@@ -97,14 +108,11 @@ public static Paint getStrokePaint(Context context) {
97108 strokePaint .setStyle (Paint .Style .STROKE );
98109 strokePaint .setStrokeWidth (3.0f );
99110 }
100-
101111 int colorRes = ThemeModeState .isDarkMode () ? R .color .colorPirmLight : R .color .colorPirmDark ;
102112 int color = ContextCompat .getColor (context , colorRes );
103-
104113 if (strokePaint .getColor () != color ) {
105114 strokePaint .setColor (color );
106115 }
107-
108116 return strokePaint ;
109117 }
110118
0 commit comments