Skip to content

Commit 56adf84

Browse files
committed
Update
1 parent e113a55 commit 56adf84

3 files changed

Lines changed: 95 additions & 211 deletions

File tree

app/src/main/java/com/omarea/common/ui/DialogHelper.kt

Lines changed: 26 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import androidx.core.graphics.drawable.toDrawable
2121
import com.tool.tree.ThemeModeState
2222
import android.graphics.Bitmap
2323
import android.graphics.drawable.BitmapDrawable
24-
import java.util.concurrent.ExecutorService
25-
import java.util.concurrent.Executors
2624

2725
class DialogHelper {
2826
class DialogButton(val text: String, val onClick: Runnable? = null, val dismiss: Boolean = true)
@@ -77,11 +75,6 @@ class DialogHelper {
7775
// 是否禁用模糊背景
7876
var disableBlurBg = false
7977

80-
// Dùng chung 1 thread nền cho việc xử lý blur ảnh nền dialog, tránh tạo/hủy thread
81-
// mới mỗi lần mở dialog. Việc blur (GPU RenderEffect hoặc CPU StackBlur) không đụng
82-
// tới View hierarchy nên chạy an toàn ở đây, tách khỏi main thread.
83-
private val blurExecutor: ExecutorService = Executors.newSingleThreadExecutor()
84-
8578
fun animDialog(dialog: AlertDialog?): DialogWrap? {
8679
if (dialog != null && !dialog.isShowing) {
8780
dialog.window?.run {
@@ -409,78 +402,38 @@ class DialogHelper {
409402
return ThemeModeState.isDarkMode()
410403
}
411404

412-
// Nền fallback (màu đặc theo theme, hoặc dim nếu là dialog dạng floating).
413-
// Tách riêng để có thể hiện ngay lập tức, không phải đợi bước blur ảnh nền phía sau.
414-
private fun applyFallbackWindowBg(window: Window, activity: Activity, wallpaperMode: Boolean) {
415-
window.run {
416-
try {
417-
val bg = getWindowBackground(activity)
418-
if (bg == Color.TRANSPARENT) {
419-
if (isFloating) {
420-
setBackgroundDrawable(bg.toDrawable())
421-
setDimAmount(0.8f)
422-
return
423-
} else {
424-
val d = if (wallpaperMode || isNightMode(context)) {
425-
Color.argb(255, 18, 18, 18).toDrawable()
426-
} else {
427-
Color.argb(255, 245, 245, 245).toDrawable()
428-
}
429-
setBackgroundDrawable(d)
430-
}
431-
} else {
432-
setBackgroundDrawable(bg.toDrawable())
433-
}
434-
} catch (_: Exception) {
435-
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
436-
}
437-
}
438-
}
439-
440405
// Trong setWindowBlurBg
441406
fun setWindowBlurBg(window: Window, activity: Activity) {
442407
val wallpaperMode = activity.window.attributes.flags and WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER != 0
443-
444-
// Hiện nền fallback NGAY LẬP TỨC để dialog xuất hiện tức thì. Nếu blur bật và
445-
// thành công, nền này sẽ được thay bằng ảnh mờ ngay khi xử lý xong ở bước dưới -
446-
// đánh đổi một nhịp hiện nền tạm để đổi lấy việc không giật main thread khi mở
447-
// dialog như cách làm đồng bộ trước đây.
448-
applyFallbackWindowBg(window, activity, wallpaperMode)
449-
450-
if (disableBlurBg) return
451-
452-
// Bước chụp BẮT BUỘC chạy trên main thread vì cần view.draw() với view hierarchy
453-
// hiện tại. Nhờ chụp trực tiếp ở CAPTURE_SCALE (10%) thay vì full-res nên bước
454-
// này rất nhẹ, không đáng kể tới hiệu năng main thread.
455-
val smallBmp = FastBlurUtility.takeScreenShot(activity, FastBlurUtility.CAPTURE_SCALE) ?: return
456-
457-
val decorView = window.decorView
458-
val targetW = decorView.width
459-
val targetH = decorView.height
460-
if (targetW <= 0 || targetH <= 0) {
461-
if (!smallBmp.isRecycled) smallBmp.recycle()
462-
return
463-
}
464-
465-
// Phần xử lý blur (GPU RenderEffect / CPU StackBlur + phóng to) là phần nặng thực
466-
// sự và không đụng tới View hierarchy, nên chạy an toàn trên background thread -
467-
// đây chính là phần trước đây làm main thread bị giật mỗi khi mở dialog.
468-
blurExecutor.execute {
469-
val blurred = try {
470-
FastBlurUtility.blurCapturedBitmap(smallBmp, targetW, targetH)
471-
} catch (e: Throwable) {
408+
window.run {
409+
val blurBitmap = if (disableBlurBg) {
472410
null
411+
} else {
412+
FastBlurUtility.getBlurBackgroundDrawer(activity)
473413
}
474-
if (blurred != null) {
475-
decorView.post {
476-
// Dialog có thể đã bị dismiss / activity đã finish trong lúc blur chạy
477-
// nền -> luôn kiểm tra lại trước khi set background để tránh crash
478-
// hoặc rò rỉ view/bitmap.
479-
if (!activity.isFinishing && decorView.isAttachedToWindow) {
480-
window.setBackgroundDrawable(blurred.toDrawable(activity.resources))
481-
} else if (!blurred.isRecycled) {
482-
blurred.recycle()
414+
if (blurBitmap != null) {
415+
setBackgroundDrawable(blurBitmap.toDrawable(activity.resources))
416+
} else {
417+
try {
418+
val bg = getWindowBackground(activity)
419+
if (bg == Color.TRANSPARENT) {
420+
if (isFloating) {
421+
setBackgroundDrawable(bg.toDrawable())
422+
setDimAmount(0.8f)
423+
return
424+
} else {
425+
val d = if (wallpaperMode || isNightMode(context)) {
426+
Color.argb(255, 18, 18, 18).toDrawable()
427+
} else {
428+
Color.argb(255, 245, 245, 245).toDrawable()
429+
}
430+
setBackgroundDrawable(d)
431+
}
432+
} else {
433+
setBackgroundDrawable(bg.toDrawable())
483434
}
435+
} catch (_: Exception) {
436+
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
484437
}
485438
}
486439
}

app/src/main/java/com/omarea/common/ui/FastBlurUtility.java

Lines changed: 65 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -21,123 +21,53 @@
2121
public class FastBlurUtility {
2222

2323
private static final float SCALE_FACTOR = 0.10f;
24-
// Alias public để nơi khác (VD: DialogHelper khi tự chụp screenshot trên main thread rồi
25-
// đẩy phần blur sang background thread) biết chụp ở đúng tỉ lệ mà blurCapturedBitmap() cần.
26-
public static final float CAPTURE_SCALE = SCALE_FACTOR;
2724
private static final int BLUR_RADIUS = 8;
28-
// Bán kính blur GPU tương ứng với ảnh full-res gốc là 20f; vì giờ blur được thực hiện
29-
// trên ảnh đã thu nhỏ theo SCALE_FACTOR nên bán kính cũng phải thu nhỏ theo tỉ lệ tương ứng
30-
// để giữ hiệu ứng mờ tương đương sau khi phóng to lại (kỹ thuật downsample-blur-upsample).
31-
private static final float GPU_BLUR_RADIUS = 20f * SCALE_FACTOR;
32-
33-
// Bảng tra cứu (lookup table) dùng trong StackBlur chỉ phụ thuộc vào bán kính blur.
34-
// Vì BLUR_RADIUS luôn là hằng số, bảng này được tính đúng MỘT LẦN khi class được load,
35-
// thay vì cấp phát mảng mới (256 * divsum phần tử, ~80KB) và tính lại mỗi lần fastBlur()
36-
// chạy - đây từng là một nguồn gây lag/rác GC không cần thiết.
37-
private static final int[] DV_LOOKUP = buildLookupTable(BLUR_RADIUS);
38-
39-
private static int[] buildLookupTable(int radius) {
40-
int div = radius + radius + 1;
41-
int divsum = (div + 1) >> 1;
42-
divsum *= divsum;
43-
int[] dv = new int[256 * divsum];
44-
for (int i = 0; i < dv.length; i++) {
45-
dv[i] = i / divsum;
46-
}
47-
return dv;
48-
}
4925

5026
public static Bitmap getBlurBackgroundDrawer(Activity activity) {
51-
if (activity == null || activity.isFinishing()) return null;
27+
Bitmap bmp = takeScreenShot(activity);
28+
if (bmp == null) return null;
5229

53-
View decorView;
54-
int targetW, targetH;
55-
try {
56-
decorView = activity.getWindow().getDecorView();
57-
targetW = decorView.getWidth();
58-
targetH = decorView.getHeight();
59-
} catch (Throwable e) {
60-
return null;
61-
}
62-
if (targetW <= 0 || targetH <= 0) return null;
63-
64-
// Chụp trực tiếp ở độ phân giải đã thu nhỏ (SCALE_FACTOR) thay vì chụp screenshot
65-
// full-res rồi mới scale xuống như trước. Đây là nguồn gây lag lớn nhất trước đây:
66-
// - view.draw() vào một Bitmap full màn hình rất tốn bộ nhớ + thời gian rasterize.
67-
// - Với path GPU (Android 12+), phải copy toàn bộ hardware buffer kích thước màn
68-
// hình gốc từ GPU sang software bitmap (result.copy(ARGB_8888, true)) - rất chậm.
69-
// Chụp ảnh nhỏ ngay từ đầu giúp mọi bước xử lý phía sau (vẽ, copy, blur) đều nhanh hơn
70-
// nhiều lần vì chỉ phải xử lý ~1% số pixel so với trước.
71-
Bitmap smallBmp = takeScreenShot(activity, SCALE_FACTOR);
72-
if (smallBmp == null) return null;
30+
Bitmap blurredBmp = startBlurBackground(bmp);
7331

74-
try {
75-
return blurCapturedBitmap(smallBmp, targetW, targetH);
76-
} catch (Throwable e) {
77-
// An toàn tuyệt đối: nếu có lỗi toán học hay OOM, không có ảnh để trả về
78-
if (!smallBmp.isRecycled()) smallBmp.recycle();
79-
return null;
32+
if (bmp != blurredBmp && !bmp.isRecycled()) {
33+
bmp.recycle();
8034
}
35+
36+
return blurredBmp;
8137
}
8238

83-
/**
84-
* Giữ lại cho tương thích ngược: xử lý blur từ một bitmap full-res có sẵn (ví dụ do nơi
85-
* khác tự chụp screenshot rồi truyền vào). Nếu có thể, nên dùng getBlurBackgroundDrawer()
86-
* để tận dụng việc chụp ảnh trực tiếp ở kích thước nhỏ.
87-
*/
8839
public static Bitmap startBlurBackground(Bitmap bkg) {
8940
if (bkg == null || bkg.isRecycled()) return null;
9041

9142
try {
92-
int targetW = bkg.getWidth();
93-
int targetH = bkg.getHeight();
43+
// 1. Android 12+ (API 31+): Dùng GPU RenderEffect
44+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
45+
Bitmap gpuBlurred = blurWithRenderEffect(bkg, 20f);
46+
if (gpuBlurred != null) {
47+
return applyDimFilter(gpuBlurred);
48+
}
49+
}
9450

95-
int width = Math.max(1, Math.round(targetW * SCALE_FACTOR));
96-
int height = Math.max(1, Math.round(targetH * SCALE_FACTOR));
51+
// 2. Android 11 trở xuống (hoặc nếu GPU fail): Dùng StackBlur CPU
52+
int width = Math.round(bkg.getWidth() * SCALE_FACTOR);
53+
int height = Math.round(bkg.getHeight() * SCALE_FACTOR);
9754

98-
Bitmap smallBitmap = Bitmap.createScaledBitmap(bkg, width, height, true);
99-
if (smallBitmap == null) return bkg;
55+
if (width <= 0 || height <= 0) return bkg;
10056

101-
return blurCapturedBitmap(smallBitmap, targetW, targetH);
102-
} catch (Throwable e) {
103-
// An toàn tuyệt đối: Nếu có bất kỳ lỗi toán học hay OOM nào, trả lại ảnh gốc
104-
return bkg;
105-
}
106-
}
57+
Bitmap smallBitmap = Bitmap.createScaledBitmap(bkg, width, height, true);
58+
Bitmap blurred = fastBlur(smallBitmap, BLUR_RADIUS);
10759

108-
/**
109-
* Thực hiện blur trên một bitmap ĐÃ được thu nhỏ sẵn (theo CAPTURE_SCALE), rồi phóng to +
110-
* làm tối (dim) về đúng kích thước targetW x targetH. Bitmap đầu vào sẽ bị recycle bởi
111-
* hàm này (hoặc bởi các hàm con của nó) sau khi dùng xong.
112-
*
113-
* Hàm này KHÔNG đụng tới View hierarchy nên an toàn để gọi trên background thread - khác
114-
* với takeScreenShot()/takeScreenShot(activity, scale), vốn bắt buộc phải chạy trên main
115-
* thread vì cần view.draw(). Cách dùng khuyến khích cho nơi cần tránh block main thread:
116-
* 1. Trên main thread: val small = FastBlurUtility.takeScreenShot(activity, FastBlurUtility.CAPTURE_SCALE)
117-
* 2. Trên background thread: val blurred = FastBlurUtility.blurCapturedBitmap(small, targetW, targetH)
118-
* 3. Post kết quả về main thread để set background.
119-
*/
120-
public static Bitmap blurCapturedBitmap(Bitmap smallBmp, int targetW, int targetH) {
121-
// 1. Android 12+ (API 31+): Dùng GPU RenderEffect trên ảnh nhỏ (rất nhanh)
122-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
123-
Bitmap gpuBlurred = blurWithRenderEffect(smallBmp, GPU_BLUR_RADIUS);
124-
if (gpuBlurred != null) {
125-
if (!smallBmp.isRecycled()) {
126-
smallBmp.recycle();
127-
}
128-
return scaleAndDim(gpuBlurred, targetW, targetH);
60+
if (smallBitmap != null && smallBitmap != blurred && !smallBitmap.isRecycled()) {
61+
smallBitmap.recycle();
12962
}
130-
}
13163

132-
// 2. Android 11 trở xuống (hoặc nếu GPU fail): Dùng StackBlur CPU
133-
Bitmap blurred = fastBlur(smallBmp, BLUR_RADIUS);
64+
if (blurred == null) return bkg;
13465

135-
if (blurred == null) {
136-
// fastBlur thất bại: vẫn cần trả về đúng kích thước, dùng luôn ảnh nhỏ chưa blur
137-
return scaleAndDim(smallBmp, targetW, targetH);
66+
return scaleAndDim(blurred, bkg.getWidth(), bkg.getHeight());
67+
} catch (Throwable e) {
68+
// An toàn tuyệt đối: Nếu có bất kỳ lỗi toán học hay OOM nào, trả lại ảnh gốc
69+
return bkg;
13870
}
139-
140-
return scaleAndDim(blurred, targetW, targetH);
14171
}
14272

14373
private static Bitmap blurWithRenderEffect(Bitmap input, float radius) {
@@ -193,38 +123,48 @@ private static Bitmap blurWithRenderEffect(Bitmap input, float radius) {
193123
return null;
194124
}
195125

196-
public static Bitmap takeScreenShot(Activity activity) {
197-
return takeScreenShot(activity, 1f);
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;
198145
}
199146

200-
/**
201-
* Chụp screenshot của decor view, có thể vẽ trực tiếp ở tỉ lệ thu nhỏ (scale < 1f) để
202-
* tránh phải cấp phát + rasterize một Bitmap full màn hình rồi mới scale xuống sau đó.
203-
*/
204-
public static Bitmap takeScreenShot(Activity activity, float scale) {
147+
public static Bitmap takeScreenShot(Activity activity) {
205148
if (activity == null || activity.isFinishing()) return null;
206149
try {
207150
View view = activity.getWindow().getDecorView();
208-
int viewWidth = view.getWidth();
209-
int viewHeight = view.getHeight();
210-
if (viewWidth <= 0 || viewHeight <= 0) return null;
211-
212-
int width = Math.max(1, Math.round(viewWidth * scale));
213-
int height = Math.max(1, Math.round(viewHeight * scale));
151+
if (view.getWidth() <= 0 || view.getHeight() <= 0) return null;
214152

215-
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
153+
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
216154
Canvas canvas = new Canvas(bitmap);
217-
if (scale != 1f) {
218-
canvas.scale(scale, scale);
219-
}
220155
view.draw(canvas);
221156
return bitmap;
222157
} catch (Exception e) {
223158
return null;
224159
}
225160
}
226161

227-
private static void applyDimPaint(Paint paint) {
162+
private static Bitmap scaleAndDim(Bitmap bitmap, int targetW, int targetH) {
163+
Bitmap output = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
164+
Canvas canvas = new Canvas(output);
165+
166+
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
167+
228168
ColorMatrix cm = new ColorMatrix();
229169
float contrast = 0.80f;
230170
cm.set(new float[]{
@@ -233,14 +173,6 @@ private static void applyDimPaint(Paint paint) {
233173
0, 0, contrast, 0, 0,
234174
0, 0, 0, 1, 0});
235175
paint.setColorFilter(new ColorMatrixColorFilter(cm));
236-
}
237-
238-
private static Bitmap scaleAndDim(Bitmap bitmap, int targetW, int targetH) {
239-
Bitmap output = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
240-
Canvas canvas = new Canvas(output);
241-
242-
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
243-
applyDimPaint(paint);
244176

245177
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
246178
Rect dst = new Rect(0, 0, targetW, targetH);
@@ -284,9 +216,12 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
284216
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
285217
int[] vmin = new int[Math.max(w, h)];
286218

287-
// Dùng bảng tra cứu đã cache sẵn khi radius trùng với BLUR_RADIUS (trường hợp luôn
288-
// xảy ra trong thực tế); chỉ build lại nếu có ai đó gọi fastBlur với radius khác.
289-
int[] dv = (radius == BLUR_RADIUS) ? DV_LOOKUP : buildLookupTable(radius);
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+
}
290225

291226
yw = yi = 0;
292227
int[][] stack = new int[div][3];
@@ -391,17 +326,17 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
391326
goutsum += sir[1];
392327
boutsum += sir[2];
393328
}
394-
// Kiểm tra giới hạn hàng dựa trên kích thước thực hm * w
329+
// ĐÃ SỬA: Kiểm tra giới hạn hàng dựa trên kích thước thực hm * w
395330
if (yp < (hm * w)) {
396331
yp += w;
397332
}
398333
}
399334
yi = x;
400335
stackpointer = radius;
401336
for (y = 0; y < h; y++) {
402-
pix[yi] = (0xff000000 & pix[yi])
403-
| (dv[clampIndex(rsum, dv.length)] << 16)
404-
| (dv[clampIndex(gsum, dv.length)] << 8)
337+
pix[yi] = (0xff000000 & pix[yi])
338+
| (dv[clampIndex(rsum, dv.length)] << 16)
339+
| (dv[clampIndex(gsum, dv.length)] << 8)
405340
| dv[clampIndex(bsum, dv.length)];
406341

407342
rsum -= routsum;

0 commit comments

Comments
 (0)