diff --git a/31_FIX_UXUI.md b/31_FIX_UXUI.md
new file mode 100644
index 0000000..c6c56d8
--- /dev/null
+++ b/31_FIX_UXUI.md
@@ -0,0 +1,172 @@
+Dưới đây là kế hoạch Markdown chi tiết giúp bạn tách biệt rạch ròi giữa **Hiệu ứng xóa phông AI** và **Lấy nét vật lý phần cứng**, giải quyết tận gốc lỗi lag ngầm, đồng thời tái cấu trúc lại hệ thống quản lý thuộc tính **Presets** để bổ sung đầy đủ các thông số ánh sáng và sắc độ.
+
+---
+
+# 📊 KẾ HOẠCH TOÀN DIỆN: ĐỒNG BỘ FOCUS VẬT LÝ, TỐI ƯU LUỒNG BLUR AI VÀ CẤU TRÚC LẠI PRESETS
+
+Mục tiêu cốt lõi:
+
+1. **Triệt tiêu lag ngầm khi tắt Blur AI:** Sửa lỗi hệ thống vẫn chạy ngầm vòng lặp pixel đồ họa của `multiclass_segmenter.tflite` gây lag mặc dù giao diện không hiển thị hiệu ứng.
+2. **Đồng bộ Focus vật lý:** Giữ nguyên cơ chế lấy nét quang học qua Camera2 API, loại bỏ hoàn toàn sự can thiệp của thuật toán mờ phần mềm lên vật thể.
+3. **Mở rộng thuộc tính Presets:** Định nghĩa lại cấu trúc dữ liệu cấu hình màu sắc, bổ sung các thông số chuyên sâu: *Highlight, Shadow, Saturation, Contrast, Brightness*.
+
+---
+
+## 🛠️ 1. Khắc Phục Lỗi Lag Ngầm Của Bộ Xử Lý Blur AI (`MainActivity.kt`)
+
+**Nguyên nhân gây lag:** Khi bạn tắt chế độ xóa nền AI nhưng tăng thước trượt Blur, ứng dụng hiển thị như thể không dùng AI, nhưng ở tầng mã nguồn Native, điều kiện kiểm tra (`if/else`) chưa chuẩn khiến hàm `processMediaPipeOnlyBokeh()` vẫn bị gọi liên tục dưới nền, ép CPU xử lý ma trận `ByteBuffer` vô ích.
+
+### 🟢 Giải pháp: Thiết lập cờ trạng thái hiệu ứng (Feature Flags) để ngắt luồng AI tuyệt đối
+
+```kotlin
+// Định nghĩa các chế độ hoạt động của ứng dụng Photobooth
+enum class BackgroundMode {
+ DEFAULT, // Giữ nguyên ảnh gốc thô của Camera
+ HARDWARE_DOF, // Chỉ dùng Focus vật lý của thấu kính
+ AI_BLUR // Dùng AI tách nền và làm mờ phần mềm
+}
+
+var currentBackgroundMode = BackgroundMode.DEFAULT
+
+fun handleImageStreamPipeline(capturedBitmap: Bitmap, rawMaskBuffer: ByteBuffer, rulerValue: Int): Bitmap {
+ // SỬA LỖI LAG NGẦM: Nếu không bật chế độ AI_BLUR hoặc thước trượt bằng 0,
+ // trả về ngay lập tức ảnh thô, chặn đứng hoàn toàn vòng lặp CPU.
+ if (currentBackgroundMode != BackgroundMode.AI_BLUR || rulerValue == 0) {
+ return capturedBitmap
+ }
+
+ // Chỉ khi bật AI_BLUR và rulerValue > 0 thì mới gánh luồng xử lý nặng này
+ return processMediaPipeOnlyBokeh(capturedBitmap, rawMaskBuffer, rulerValue)
+}
+
+```
+
+---
+
+## 🎯 2. Quy Hoạch Luồng Focus Vật Lý Khi Chạm Khung Ngắm
+
+Khi chuyển sang tab **"Focus Vật lý"**, chúng ta chuyển trạng thái hệ thống về `BackgroundMode.HARDWARE_DOF`. Lúc này, hệ thống bỏ qua toàn bộ mặt nạ phân lớp của Google MediaPipe, giao phó 100% việc tạo độ sâu trường ảnh (DoF) tự nhiên cho mô-tơ thấu kính của thiết bị di động.
+
+* **Viewfinder:** Gọi lệnh `applyHardwareTouchToFocus()` qua Camera2 API gửi tọa độ vùng đo tiêu cự `MeteringRectangle` xuống phần cứng thấu kính.
+* **Ảnh xuất xưởng:** Nhận trực tiếp Bitmap thô từ cảm biến camera để lưu, đảm bảo ảnh trung thực như máy ảnh cơ, không bị lỗi lẹm viền hay tạo bóng ma (Ghost Shadow) quanh cơ thể.
+
+---
+
+## 🎨 3. Cấu Trúc Lại Hệ Thống Thuộc Tính Presets Màu Sắc
+
+Để các Presets (bộ lọc màu) chỉnh sửa đúng các thuộc tính ánh sáng và sắc độ, chúng ta cần chuyển đổi cấu trúc dữ liệu từ dạng chuỗi (String) đơn giản sang một đối tượng cấu hình có cấu trúc dữ liệu cụ thể (Structured Configuration Class).
+
+### 📐 3.1. Thiết kế Model Dữ Liệu Bộ Màu (`PresetModel.kt`)
+
+```kotlin
+data class ColorPreset(
+ val id: String,
+ val name: String,
+ // --- Thuộc tính ánh sáng ---
+ val brightness: Float, // -1.0f đến 1.0f (Mặc định 0.0f)
+ val contrast: Float, // 0.5f đến 2.0f (Mặc định 1.0f)
+ val highlight: Float, // -1.0f đến 1.0f (Mặc định 0.0f) -> Mới bổ sung
+ val shadow: Float, // -1.0f đến 1.0f (Mặc định 0.0f) -> Mới bổ sung
+ // --- Thuộc tính sắc độ & màu sắc ---
+ val saturation: Float, // 0.0f đến 2.0f (Mặc định 1.0f)
+ val temperature: Float // -0.5f đến 0.5f (Mặc định 0.0f) (Ấm/Lạnh)
+)
+
+```
+
+### 💻 3.2. Thuật Toán Áp Dụng Thông Số Preset Lên Điểm Ảnh (Color Toning Engine)
+
+Thuật toán xử lý ma trận màu sắc dưới đây nhận cấu hình từ các thuộc tính trên để biến đổi hệ màu RGB của bức ảnh:
+
+```kotlin
+fun applyAdvancedPreset(inputBitmap: Bitmap, preset: ColorPreset): Bitmap {
+ val w = inputBitmap.width
+ val h = inputBitmap.height
+ val outputBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
+
+ for (y in 0 until h) {
+ for (x in 0 until w) {
+ val pixel = inputBitmap.getPixel(x, y)
+
+ // Trích xuất kênh màu gốc
+ var r = (pixel shr 16 and 0xFF) / 255.0f
+ var g = (pixel shr 8 and 0xFF) / 255.0f
+ var b = (pixel and 0xFF) / 255.0f
+
+ // 1. XỬ LÝ ÁNH SÁNG (Brightness & Contrast)
+ r = (r - 0.5f) * preset.contrast + 0.5f + preset.brightness
+ g = (g - 0.5f) * preset.contrast + 0.5f + preset.brightness
+ b = (b - 0.5f) * preset.contrast + 0.5f + preset.brightness
+
+ // Tính toán độ sáng tổng thể của pixel (Luminance) để định vị vùng Highlight/Shadow
+ val luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b
+
+ // 2. BỔ SUNG: XỬ LÝ HIGHLIGHT & SHADOW
+ if (luminance > 0.6f) {
+ // Vùng sáng (Highlight): Tăng/Giảm độ sáng dựa trên thông số highlight của Preset
+ val weight = (luminance - 0.6f) / 0.4f
+ r += preset.highlight * weight
+ g += preset.highlight * weight
+ b += preset.highlight * weight
+ } else if (luminance < 0.4f) {
+ // Vùng tối (Shadow): Bù sáng hoặc dìm tối dựa trên thông số shadow của Preset
+ val weight = (0.4f - luminance) / 0.4f
+ r += preset.shadow * weight
+ g += preset.shadow * weight
+ b += preset.shadow * weight
+ }
+
+ // 3. XỬ LÝ SẮC ĐỘ (Saturation)
+ val gray = luminance
+ r = gray + (r - gray) * preset.saturation
+ g = gray + (g - gray) * preset.saturation
+ b = gray + (b - gray) * preset.saturation
+
+ // Ép dải màu về giới hạn an toàn [0.0, 1.0] trước khi đóng gói lại pixel
+ val finalR = (r.coerceIn(0.0f, 1.0f) * 255).toInt()
+ val finalG = (g.coerceIn(0.0f, 1.0f) * 255).toInt()
+ val finalB = (b.coerceIn(0.0f, 1.0f) * 255).toInt()
+
+ outputBitmap.setPixel(x, y, (0xFF shl 24) or (finalR shl 16) or (finalG shl 8) or finalB)
+ }
+ }
+ return outputBitmap
+}
+
+```
+
+---
+
+## 🌐 4. Quản Lý Đồng Bộ File Định Cấu Hình Mẫu (`PresetsStore`)
+
+Đối với tính năng **Tải thêm từ Server trực tuyến (Online Store)** qua nút `...` đã lên kế hoạch từ trước, file cấu hình mẫu `manifest.json` trên Server của bạn sẽ được bổ sung các thuộc tính mới đồng bộ như sau:
+
+```json
+{
+ "presets": [
+ {
+ "id": "preset_kodak_gold",
+ "name": "Kodak Gold 200",
+ "url": "https://yourserver.com/assets/presets/kodak_gold.json",
+ "version": 1,
+ "properties": {
+ "brightness": 0.05,
+ "contrast": 1.15,
+ "highlight": -0.10,
+ "shadow": 0.15,
+ "saturation": 1.20,
+ "temperature": 0.08
+ }
+ }
+ ]
+}
+
+```
+
+---
+
+## 📅 5. Kịch Bản Kiểm Thử Đảm Bảo Chất Lượng (QA Checklist)
+
+* [ ] **Kiểm thử triệt tiêu lag ngầm (Lag Isolation Test):** Tắt tính năng Xóa nền AI (chuyển sang chế độ Mặc định/Focus vật lý). Kéo thanh trượt Ruler lên tối đa `+100` và di chuyển máy liên tục. (Yêu cầu: Kính ngắm phải mượt mà tuyệt đối 60 FPS, không được khựng giật vì hàm xử lý AI lúc này đã bị khóa chặn hoàn toàn bằng cờ trạng thái).
+* [ ] **Kiểm thử Focus vật lý (Hardware Lens Shift):** Chạm vào các vùng khoảng cách khác nhau (Gần/Xa) trên Viewfinder xem ống kính camera có dịch chuyển mô-tơ lấy nét vật lý chính xác không, ảnh thu được không được phép có vết cắt giả tạo hay răng cưa phần mềm xung quanh tóc.
+* [ ] **Kiểm thử thông số Highlight/Shadow trên Preset:** Áp dụng một bộ màu có thông số `highlight: -0.3` và `shadow: +0.2` lên một bức ảnh có vùng trời quá sáng và vùng tối khuất bóng. (Yêu cầu: Chi tiết vùng mây trên trời được dìm sáng rõ nét lại, vùng tối khuất sau lưng được cứu sáng mượt mà).
\ No newline at end of file
diff --git a/android-project/app/src/main/java/com/photobooth/app/AttributeAdapter.kt b/android-project/app/src/main/java/com/photobooth/app/AttributeAdapter.kt
index fa006d1..11fc872 100644
--- a/android-project/app/src/main/java/com/photobooth/app/AttributeAdapter.kt
+++ b/android-project/app/src/main/java/com/photobooth/app/AttributeAdapter.kt
@@ -8,7 +8,7 @@ import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
enum class EditAttribute {
- BRIGHTNESS, CONTRAST, SATURATION, VIBRANCE, TEMPERATURE, TINT, // Nhóm 1: Cơ bản
+ BRIGHTNESS, CONTRAST, HIGHLIGHT, SHADOW, SATURATION, VIBRANCE, TEMPERATURE, TINT, // Nhóm 1: Cơ bản
CLARITY, DEHAZE, VIGNETTE_AMOUNT, // Nhóm 2: Nâng cao
FADED_BLACK_LEVEL, SHADOWS_TINT_R, SHADOWS_TINT_G, SHADOWS_TINT_B, // Nhóm 3: Tone Curve
GRAIN_AMOUNT, GRAIN_SIZE, // Nhóm 4: Nhiễu hạt
diff --git a/android-project/app/src/main/java/com/photobooth/app/ColorPreset.kt b/android-project/app/src/main/java/com/photobooth/app/ColorPreset.kt
index 904b150..2f67541 100644
--- a/android-project/app/src/main/java/com/photobooth/app/ColorPreset.kt
+++ b/android-project/app/src/main/java/com/photobooth/app/ColorPreset.kt
@@ -17,7 +17,9 @@ data class BasicAdjustments(
var saturation: Float, // -1.0f đến 1.0f
var vibrance: Float, // -1.0f đến 1.0f -> Bảo vệ màu da người
var temperature: Float, // -1.0f đến 1.0f -> Ấm (vàng) / Lạnh (xanh)
- var tint: Float // -1.0f đến 1.0f -> Xanh lá / Hồng dâu
+ var tint: Float, // -1.0f đến 1.0f -> Xanh lá / Hồng dâu
+ var highlight: Float = 0f, // -1.0f đến 1.0f -> Mới bổ sung
+ var shadow: Float = 0f // -1.0f đến 1.0f -> Mới bổ sung
)
data class AdvancedEffects(
diff --git a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
index 17eaeb6..d47fd87 100644
--- a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
+++ b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
@@ -78,6 +78,23 @@ class MainActivity : AppCompatActivity() {
private var cachedMaskWidth = 0
private var cachedMaskHeight = 0
+ // Định nghĩa các chế độ hoạt động của ứng dụng Photobooth
+ enum class BackgroundMode {
+ DEFAULT, // Giữ nguyên ảnh gốc thô của Camera
+ HARDWARE_DOF, // Chỉ dùng Focus vật lý của thấu kính
+ AI_BLUR // Dùng AI tách nền và làm mờ phần mềm
+ }
+
+ private var currentBackgroundMode = BackgroundMode.DEFAULT
+
+ private fun updateBackgroundMode() {
+ currentBackgroundMode = when {
+ selectedBgAssetPath != null -> BackgroundMode.AI_BLUR
+ blurIntensity > 0f || isPortraitBlurModeOpen -> BackgroundMode.HARDWARE_DOF
+ else -> BackgroundMode.DEFAULT
+ }
+ }
+
private fun getImageSegmenter(): ImageSegmenter? {
synchronized(aiInferenceLock) {
if (!isAiSupported) return null
@@ -207,6 +224,8 @@ class MainActivity : AppCompatActivity() {
val brightness = extractJsonDoubleField(content, "brightness").toFloat()
val contrast = extractJsonDoubleField(content, "contrast").toFloat()
+ val highlight = extractJsonDoubleField(content, "highlight").toFloat()
+ val shadow = extractJsonDoubleField(content, "shadow").toFloat()
val saturation = extractJsonDoubleField(content, "saturation").toFloat()
val vibrance = extractJsonDoubleField(content, "vibrance").toFloat()
val temperature = extractJsonDoubleField(content, "temperature").toFloat()
@@ -229,7 +248,7 @@ class MainActivity : AppCompatActivity() {
name = name,
themeCategory = category,
isEditable = true,
- basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint),
+ basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint, highlight, shadow),
advanced = AdvancedEffects(clarity, dehaze, vignetteAmount),
toneCurve = ToneCurveEmulation(fadedBlackLevel, shadowsTintR, shadowsTintG, shadowsTintB),
grain = FilmGrain(grainAmount, grainSize)
@@ -272,6 +291,8 @@ class MainActivity : AppCompatActivity() {
val attributeItems = listOf(
AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness),
AttributeNode(EditAttribute.CONTRAST, "Contrast (Tương phản)", R.drawable.ic_attr_contrast),
+ AttributeNode(EditAttribute.HIGHLIGHT, "Highlight (Vùng sáng)", R.drawable.ic_attr_highlight),
+ AttributeNode(EditAttribute.SHADOW, "Shadow (Vùng tối)", R.drawable.ic_attr_shadow),
AttributeNode(EditAttribute.SATURATION, "Saturation (Bão hòa màu)", R.drawable.ic_attr_saturation),
AttributeNode(EditAttribute.VIBRANCE, "Vibrance (Bão hòa thông minh)", R.drawable.ic_attr_vibrance),
AttributeNode(EditAttribute.TEMPERATURE, "Temperature (Nhiệt màu)", R.drawable.ic_attr_temperature),
@@ -305,6 +326,7 @@ class MainActivity : AppCompatActivity() {
)
backgroundOptionAdapter = BackgroundOptionAdapter(backgroundOptions) { option ->
selectedBgAssetPath = option.assetPath
+ updateBackgroundMode()
}
binding.rvBackgroundOptions.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
binding.rvBackgroundOptions.adapter = backgroundOptionAdapter
@@ -320,6 +342,7 @@ class MainActivity : AppCompatActivity() {
binding.panelRulerView.onValueChangeListener = { valInt ->
binding.tvPanelRulerValue.text = "$valInt"
blurIntensity = valInt / 100f
+ updateBackgroundMode()
}
}
@@ -454,8 +477,8 @@ class MainActivity : AppCompatActivity() {
))
finalMatrix.postConcat(contrastMatrix)
- // 3. Brightness (Độ sáng Exposure thực tế)
- val brightnessOffset = preset.basic.brightness * 60f
+ // 3. Brightness (Độ sáng Exposure thực tế, tích hợp nhè nhẹ Highlight/Shadow để người dùng nhận diện thay đổi trên Viewfinder)
+ val brightnessOffset = (preset.basic.brightness + (preset.basic.highlight + preset.basic.shadow) * 0.15f) * 60f
val brightnessMatrix = android.graphics.ColorMatrix(floatArrayOf(
1f, 0f, 0f, 0f, brightnessOffset,
0f, 1f, 0f, 0f, brightnessOffset,
@@ -917,11 +940,13 @@ class MainActivity : AppCompatActivity() {
// Sync ruler value
binding.panelRulerView.value = (blurIntensity * 100f).toInt()
binding.tvPanelRulerValue.text = "${(blurIntensity * 100f).toInt()}"
+ updateBackgroundMode()
} else {
isPortraitBlurModeOpen = false
restoreMainMenuButtons()
binding.viewLeftDockBackground.visibility = android.view.View.GONE
binding.panelRulerContainer.visibility = android.view.View.GONE
+ updateBackgroundMode()
}
}
@@ -948,11 +973,13 @@ class MainActivity : AppCompatActivity() {
binding.rvBackgroundOptions.visibility = android.view.View.VISIBLE
binding.rvHorizontalTimeline.visibility = android.view.View.GONE
binding.panelRulerContainer.visibility = android.view.View.GONE
+ updateBackgroundMode()
} else {
isBgReplaceModeOpen = false
restoreMainMenuButtons()
binding.viewLeftDockBackground.visibility = android.view.View.GONE
binding.rvBackgroundOptions.visibility = android.view.View.GONE
+ updateBackgroundMode()
}
}
@@ -1039,6 +1066,8 @@ class MainActivity : AppCompatActivity() {
return when (currentAttribute) {
EditAttribute.BRIGHTNESS -> preset.basic.brightness
EditAttribute.CONTRAST -> preset.basic.contrast
+ EditAttribute.HIGHLIGHT -> preset.basic.highlight
+ EditAttribute.SHADOW -> preset.basic.shadow
EditAttribute.SATURATION -> preset.basic.saturation
EditAttribute.VIBRANCE -> preset.basic.vibrance
EditAttribute.TEMPERATURE -> preset.basic.temperature
@@ -1066,6 +1095,8 @@ class MainActivity : AppCompatActivity() {
when (currentAttribute) {
EditAttribute.BRIGHTNESS -> preset.basic.brightness = value
EditAttribute.CONTRAST -> preset.basic.contrast = value
+ EditAttribute.HIGHLIGHT -> preset.basic.highlight = value
+ EditAttribute.SHADOW -> preset.basic.shadow = value
EditAttribute.SATURATION -> preset.basic.saturation = value
EditAttribute.VIBRANCE -> preset.basic.vibrance = value
EditAttribute.TEMPERATURE -> preset.basic.temperature = value
@@ -1121,7 +1152,9 @@ class MainActivity : AppCompatActivity() {
tempPreset.basic.saturation,
tempPreset.basic.vibrance,
tempPreset.basic.temperature,
- tempPreset.basic.tint
+ tempPreset.basic.tint,
+ tempPreset.basic.highlight,
+ tempPreset.basic.shadow
),
advanced = AdvancedEffects(
tempPreset.advanced.clarity,
@@ -1150,6 +1183,8 @@ class MainActivity : AppCompatActivity() {
"basic_adjustments": {
"brightness": ${customPreset.basic.brightness},
"contrast": ${customPreset.basic.contrast},
+ "highlight": ${customPreset.basic.highlight},
+ "shadow": ${customPreset.basic.shadow},
"saturation": ${customPreset.basic.saturation},
"vibrance": ${customPreset.basic.vibrance},
"temperature": ${customPreset.basic.temperature},
@@ -1211,7 +1246,8 @@ class MainActivity : AppCompatActivity() {
.build()
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
- val isBgActive = (selectedBgAssetPath != null) || (blurIntensity > 0f)
+ // SỬA LỖI LAG NGẦM: Chỉ chạy MediaPipe phân đoạn AI khi thực sự cần dùng chế độ AI_BLUR
+ val isBgActive = currentBackgroundMode == BackgroundMode.AI_BLUR
if (isBgActive) {
val rawProxyBitmap = imageProxy.toBitmap()
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
@@ -1523,6 +1559,47 @@ class MainActivity : AppCompatActivity() {
)
}
+ // Apply non-linear Highlights and Shadows correction
+ if (preset.basic.highlight != 0f || preset.basic.shadow != 0f) {
+ val width = filteredBitmap.width
+ val height = filteredBitmap.height
+ val pixels = IntArray(width * height)
+ filteredBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
+
+ val highlightVal = preset.basic.highlight
+ val shadowVal = preset.basic.shadow
+
+ for (i in 0 until (width * height)) {
+ val color = pixels[i]
+ var r = ((color shr 16) and 0xFF) / 255.0f
+ var g = ((color shr 8) and 0xFF) / 255.0f
+ var b = (color and 0xFF) / 255.0f
+
+ val luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b
+
+ if (luminance > 0.6f) {
+ // Vùng sáng (Highlight): Tăng/Giảm độ sáng dựa trên thông số highlight của Preset
+ val weight = (luminance - 0.6f) / 0.4f
+ r += highlightVal * weight
+ g += highlightVal * weight
+ b += highlightVal * weight
+ } else if (luminance < 0.4f) {
+ // Vùng tối (Shadow): Bù sáng hoặc dìm tối dựa trên thông số shadow của Preset
+ val weight = (0.4f - luminance) / 0.4f
+ r += shadowVal * weight
+ g += shadowVal * weight
+ b += shadowVal * weight
+ }
+
+ val finalR = (r.coerceIn(0.0f, 1.0f) * 255f).toInt()
+ val finalG = (g.coerceIn(0.0f, 1.0f) * 255f).toInt()
+ val finalB = (b.coerceIn(0.0f, 1.0f) * 255f).toInt()
+
+ pixels[i] = (0xFF shl 24) or (finalR shl 16) or (finalG shl 8) or finalB
+ }
+ filteredBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
+ }
+
// 4. Film Grain effect
if (preset.grain.grainAmount > 0f) {
val grainAmount = preset.grain.grainAmount
@@ -1718,20 +1795,11 @@ class MainActivity : AppCompatActivity() {
capturedImage: android.graphics.Bitmap,
isStreamMode: Boolean = false
): android.graphics.Bitmap {
- val isBgActive = (selectedBgAssetPath != null) || (blurIntensity > 0f)
- if (!isBgActive) {
+ // Chỉ chạy các thuật toán xử lý phân đoạn AI hoặc mờ phần mềm khi bật chế độ AI_BLUR
+ if (currentBackgroundMode != BackgroundMode.AI_BLUR) {
return capturedImage
}
- // Tắt hoàn toàn thuật toán xử lý mờ/trộn nhân tạo cho Portrait Blur (khi selectedBgAssetPath == null)
- // và trả về ảnh thô 100% để sử dụng tiêu cự thấu kính vật lý của camera.
- if (selectedBgAssetPath == null && blurIntensity > 0f) {
- if (!isStreamMode) {
- // Trong chế độ chụp ảnh: Trả về ảnh thô ngay lập tức, không chạy phân đoạn AI
- return capturedImage
- }
- }
-
if (!isAiSupported) {
// Khi không hỗ trợ AI và chỉ cần Portrait Blur, trả về ảnh thô để dùng thấu kính vật lý
if (selectedBgAssetPath == null && blurIntensity > 0f) {
diff --git a/android-project/app/src/main/res/drawable/ic_attr_highlight.xml b/android-project/app/src/main/res/drawable/ic_attr_highlight.xml
new file mode 100644
index 0000000..a007ac5
--- /dev/null
+++ b/android-project/app/src/main/res/drawable/ic_attr_highlight.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android-project/app/src/main/res/drawable/ic_attr_shadow.xml b/android-project/app/src/main/res/drawable/ic_attr_shadow.xml
new file mode 100644
index 0000000..f407074
--- /dev/null
+++ b/android-project/app/src/main/res/drawable/ic_attr_shadow.xml
@@ -0,0 +1,9 @@
+
+
+