Skip to content

Commit 0e1fe77

Browse files
committed
Update
1 parent 2aa8335 commit 0e1fe77

1 file changed

Lines changed: 53 additions & 79 deletions

File tree

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

Lines changed: 53 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
public class FastBlurUtility {
1313

14+
// Tỉ lệ thu nhỏ ảnh để xử lý nhanh (1/10 giúp giảm 100 lần số pixel cần tính toán)
1415
private static final float SCALE_FACTOR = 0.10f;
1516
private static final int BLUR_RADIUS = 8;
1617

@@ -19,44 +20,51 @@ public class FastBlurUtility {
1920
*/
2021
public static Bitmap getBlurBackgroundDrawer(Activity activity) {
2122
Bitmap bmp = takeScreenShot(activity);
22-
if (bmp == null) return null;
23-
24-
Bitmap blurredBmp = startBlurBackground(bmp);
25-
26-
if (bmp != blurredBmp && !bmp.isRecycled()) {
27-
bmp.recycle();
28-
}
29-
return blurredBmp;
23+
return startBlurBackground(bmp);
3024
}
3125

3226
/**
33-
* Quy trình xử lý: Thu nhỏ -> Làm mờ -> Phóng to & Dim
27+
* Quy trình xử lý: Thu nhỏ -> Làm mờ -> Phóng to & Nhuộm tối (Dim)
28+
* Đảm bảo mượt mà từ SDK 23 trở lên.
3429
*/
3530
public static Bitmap startBlurBackground(Bitmap bkg) {
3631
if (bkg == null || bkg.isRecycled()) return null;
3732

38-
// Đảm bảo kích thước tối thiểu 16px để tránh ảnh bị quá nhỏ gây mờ đục hoặc hỏng layout
39-
int width = Math.max(Math.round(bkg.getWidth() * SCALE_FACTOR), 16);
40-
int height = Math.max(Math.round(bkg.getHeight() * SCALE_FACTOR), 16);
33+
// 1. Tính toán kích thước thu nhỏ
34+
int width = Math.round(bkg.getWidth() * SCALE_FACTOR);
35+
int height = Math.round(bkg.getHeight() * SCALE_FACTOR);
4136

42-
Bitmap smallBitmap = Bitmap.createScaledBitmap(bkg, width, height, true);
37+
if (width <= 0 || height <= 0) return bkg;
4338

44-
Bitmap blurred = fastBlur(smallBitmap, BLUR_RADIUS);
39+
try {
40+
// 2. Thu nhỏ ảnh (Sử dụng bộ lọc Bilinear để ảnh mượt hơn)
41+
Bitmap smallBitmap = Bitmap.createScaledBitmap(bkg, width, height, true);
4542

46-
if (smallBitmap != null && smallBitmap != blurred && !smallBitmap.isRecycled()) {
47-
smallBitmap.recycle();
48-
}
43+
// 3. Làm mờ bằng thuật toán StackBlur (CPU-based, cực kỳ ổn định)
44+
Bitmap blurred = fastBlur(smallBitmap, BLUR_RADIUS);
45+
46+
// FIX: smallBitmap chỉ là ảnh trung gian, luôn giải phóng sau khi dùng xong
47+
// (trừ trường hợp createScaledBitmap trả về chính bkg do width/height không đổi)
48+
if (smallBitmap != bkg && !smallBitmap.isRecycled()) {
49+
smallBitmap.recycle();
50+
}
4951

50-
if (blurred == null) return null;
52+
// FIX: fastBlur() có thể trả về null (radius < 1), phải kiểm tra trước khi dùng
53+
if (blurred == null || blurred.isRecycled()) {
54+
return bkg;
55+
}
5156

52-
return scaleAndDim(blurred, bkg.getWidth(), bkg.getHeight());
57+
// 4. Phóng to về kích thước gốc và áp dụng bộ lọc màu tối
58+
return scaleAndDim(blurred, bkg.getWidth(), bkg.getHeight());
59+
} catch (OutOfMemoryError e) {
60+
return bkg;
61+
}
5362
}
5463

5564
/**
56-
* Chụp ảnh màn hình an toàn
65+
* Chụp ảnh màn hình an toàn trên SDK 23+
5766
*/
5867
private static Bitmap takeScreenShot(Activity activity) {
59-
if (activity == null || activity.isFinishing()) return null;
6068
try {
6169
View view = activity.getWindow().getDecorView();
6270
if (view.getWidth() <= 0 || view.getHeight() <= 0) return null;
@@ -71,29 +79,33 @@ private static Bitmap takeScreenShot(Activity activity) {
7179
}
7280

7381
/**
74-
* Phóng to ảnh và áp dụng ColorMatrix làm tối (Dim)
82+
* Phóng to ảnh và áp dụng ColorMatrix để làm tối nền (Dim)
7583
*/
7684
private static Bitmap scaleAndDim(Bitmap bitmap, int targetW, int targetH) {
7785
if (bitmap == null || bitmap.isRecycled()) return null;
7886

7987
Bitmap output = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
8088
Canvas canvas = new Canvas(output);
8189

90+
// Paint với bộ lọc chống răng cưa và lọc bitmap khi scale
8291
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
8392

93+
// Tạo bộ lọc màu để giảm độ sáng (contrast 0.85f ~ giảm 15% độ sáng)
8494
ColorMatrix cm = new ColorMatrix();
85-
float contrast = 0.80f;
95+
float contrast = 0.80f;
8696
cm.set(new float[]{
8797
contrast, 0, 0, 0, 0,
8898
0, contrast, 0, 0, 0,
8999
0, 0, contrast, 0, 0,
90100
0, 0, 0, 1, 0});
91101
paint.setColorFilter(new ColorMatrixColorFilter(cm));
92102

103+
// Vẽ ảnh từ vùng nguồn (nhỏ) ra vùng đích (toàn màn hình)
93104
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
94105
Rect dst = new Rect(0, 0, targetW, targetH);
95106
canvas.drawBitmap(bitmap, src, dst, paint);
96107

108+
// Giải phóng bitmap tạm sau khi đã vẽ xong
97109
if (!bitmap.isRecycled()) {
98110
bitmap.recycle();
99111
}
@@ -102,20 +114,15 @@ private static Bitmap scaleAndDim(Bitmap bitmap, int targetW, int targetH) {
102114
}
103115

104116
/**
105-
* Thuật toán StackBlur đã sửa lỗi hổng chỉ số mảng & giữ nguyên 100% chất lượng Blur gốc
117+
* Thuật toán StackBlur (Multi-pass box blur) - Tối ưu cho hiệu năng CPU
118+
* Hỗ trợ hoàn hảo cho các thiết bị từ cũ đến mới.
106119
*/
107120
private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
108-
if (sentBitmap == null || sentBitmap.isRecycled()) return null;
109-
110-
Bitmap.Config config = sentBitmap.getConfig();
111-
if (config == null) {
112-
config = Bitmap.Config.ARGB_8888;
113-
}
121+
if (sentBitmap == null || sentBitmap.isRecycled() || radius < 1) return null;
114122

123+
// FIX: getConfig() có thể null với bitmap dạng HARDWARE -> copy() sẽ ném exception
124+
Bitmap.Config config = sentBitmap.getConfig() != null ? sentBitmap.getConfig() : Bitmap.Config.ARGB_8888;
115125
Bitmap bitmap = sentBitmap.copy(config, true);
116-
if (bitmap == null) return null;
117-
118-
if (radius < 1) return null;
119126

120127
int w = bitmap.getWidth();
121128
int h = bitmap.getHeight();
@@ -130,14 +137,13 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
130137
int[] r = new int[wh];
131138
int[] g = new int[wh];
132139
int[] b = new int[wh];
133-
int rsum, gsum, bsum, x, y, i, p, yi, yw;
140+
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
134141
int[] vmin = new int[Math.max(w, h)];
135142

136143
int divsum = (div + 1) >> 1;
137144
divsum *= divsum;
138-
int maxDv = 256 * divsum;
139-
int[] dv = new int[maxDv];
140-
for (i = 0; i < maxDv; i++) {
145+
int[] dv = new int[256 * divsum];
146+
for (i = 0; i < 256 * divsum; i++) {
141147
dv[i] = (i / divsum);
142148
}
143149

@@ -151,7 +157,6 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
151157
int routsum, goutsum, boutsum;
152158
int rinsum, ginsum, binsum;
153159

154-
// --- LƯỢT 1: LÀM MỜ CHIỀU NGANG ---
155160
for (y = 0; y < h; y++) {
156161
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
157162
for (i = -radius; i <= radius; i++) {
@@ -177,69 +182,53 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
177182
stackpointer = radius;
178183

179184
for (x = 0; x < w; x++) {
180-
r[yi] = dv[Math.min(Math.max(rsum, 0), maxDv - 1)];
181-
g[yi] = dv[Math.min(Math.max(gsum, 0), maxDv - 1)];
182-
b[yi] = dv[Math.min(Math.max(bsum, 0), maxDv - 1)];
183-
185+
r[yi] = dv[rsum];
186+
g[yi] = dv[gsum];
187+
b[yi] = dv[bsum];
184188
rsum -= routsum;
185189
gsum -= goutsum;
186190
bsum -= boutsum;
187-
188191
stackstart = stackpointer - radius + div;
189192
sir = stack[stackstart % div];
190-
191193
routsum -= sir[0];
192194
goutsum -= sir[1];
193195
boutsum -= sir[2];
194-
195196
if (y == 0) vmin[x] = Math.min(x + radius + 1, wm);
196197
p = pix[yw + vmin[x]];
197-
198198
sir[0] = (p & 0xff0000) >> 16;
199199
sir[1] = (p & 0x00ff00) >> 8;
200200
sir[2] = (p & 0x0000ff);
201-
202201
rinsum += sir[0];
203202
ginsum += sir[1];
204203
binsum += sir[2];
205-
206204
rsum += rinsum;
207205
gsum += ginsum;
208206
bsum += binsum;
209-
210207
stackpointer = (stackpointer + 1) % div;
211208
sir = stack[(stackpointer) % div];
212-
213209
routsum += sir[0];
214210
goutsum += sir[1];
215211
boutsum += sir[2];
216-
217212
rinsum -= sir[0];
218213
ginsum -= sir[1];
219214
binsum -= sir[2];
220-
221215
yi++;
222216
}
223217
yw += w;
224218
}
225-
226-
// --- LƯỢT 2: LÀM MỜ CHIỀU DỌC ---
227219
for (x = 0; x < w; x++) {
228220
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
229-
230-
// ĐÃ SỬA LỖI: Khởi tạo stack chuẩn xác bằng Math.min(hm, Math.max(i, 0)) * w + x
221+
yp = -radius * w;
231222
for (i = -radius; i <= radius; i++) {
232-
yi = Math.min(hm, Math.max(i, 0)) * w + x;
223+
yi = Math.max(0, yp) + x;
233224
sir = stack[i + radius];
234225
sir[0] = r[yi];
235226
sir[1] = g[yi];
236227
sir[2] = b[yi];
237-
238228
rbs = r1 - Math.abs(i);
239229
rsum += r[yi] * rbs;
240230
gsum += g[yi] * rbs;
241231
bsum += b[yi] * rbs;
242-
243232
if (i > 0) {
244233
rinsum += sir[0];
245234
ginsum += sir[1];
@@ -249,57 +238,42 @@ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
249238
goutsum += sir[1];
250239
boutsum += sir[2];
251240
}
241+
if (i < hm) yp += w;
252242
}
253-
254243
yi = x;
255244
stackpointer = radius;
256245
for (y = 0; y < h; y++) {
257-
pix[yi] = (0xff000000 & pix[yi])
258-
| (dv[Math.min(Math.max(rsum, 0), maxDv - 1)] << 16)
259-
| (dv[Math.min(Math.max(gsum, 0), maxDv - 1)] << 8)
260-
| dv[Math.min(Math.max(bsum, 0), maxDv - 1)];
261-
246+
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
262247
rsum -= routsum;
263248
gsum -= goutsum;
264249
bsum -= boutsum;
265-
266250
stackstart = stackpointer - radius + div;
267251
sir = stack[stackstart % div];
268-
269252
routsum -= sir[0];
270253
goutsum -= sir[1];
271254
boutsum -= sir[2];
272-
273255
if (x == 0) vmin[y] = Math.min(y + r1, hm) * w;
274256
p = x + vmin[y];
275-
276257
sir[0] = r[p];
277258
sir[1] = g[p];
278259
sir[2] = b[p];
279-
280260
rinsum += sir[0];
281261
ginsum += sir[1];
282262
binsum += sir[2];
283-
284263
rsum += rinsum;
285264
gsum += ginsum;
286265
bsum += binsum;
287-
288266
stackpointer = (stackpointer + 1) % div;
289267
sir = stack[stackpointer];
290-
291-
routsum -= sir[0];
292-
goutsum -= sir[1];
293-
boutsum -= sir[2];
294-
268+
routsum += sir[0];
269+
goutsum += sir[1];
270+
boutsum += sir[2];
295271
rinsum -= sir[0];
296272
ginsum -= sir[1];
297273
binsum -= sir[2];
298-
299274
yi += w;
300275
}
301276
}
302-
303277
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
304278
return bitmap;
305279
}

0 commit comments

Comments
 (0)