Skip to content

Commit df172e6

Browse files
committed
Update
1 parent 56adf84 commit df172e6

1 file changed

Lines changed: 98 additions & 33 deletions

File tree

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

Lines changed: 98 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,84 @@
1616
import android.media.Image;
1717
import android.media.ImageReader;
1818
import android.os.Build;
19+
import android.os.Handler;
20+
import android.os.Looper;
1921
import android.view.View;
2022

23+
import java.util.concurrent.ExecutorService;
24+
import java.util.concurrent.Executors;
25+
2126
public 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

Comments
 (0)