78 lines
3.1 KiB
Kotlin
78 lines
3.1 KiB
Kotlin
package com.example.livingai.utils
|
|
|
|
import android.content.Context
|
|
import android.graphics.Bitmap
|
|
import android.graphics.BitmapFactory
|
|
import android.graphics.Canvas
|
|
import android.graphics.Color
|
|
import android.graphics.Paint
|
|
import androidx.annotation.DrawableRes
|
|
import androidx.core.graphics.createBitmap
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
|
|
object SilhouetteManager {
|
|
private val originals = ConcurrentHashMap<String, Bitmap>()
|
|
private val inverted = ConcurrentHashMap<String, Bitmap>()
|
|
private val bitmasks = ConcurrentHashMap<String, BooleanArray>()
|
|
|
|
fun initialize(context: Context, names: List<String>) {
|
|
names.forEach { name ->
|
|
val resId = context.resources.getIdentifier(name, "drawable", context.packageName)
|
|
if (resId != 0) {
|
|
val bmp = BitmapFactory.decodeResource(context.resources, resId)
|
|
originals[name] = bmp
|
|
val inv = invertBitmap(bmp)
|
|
inverted[name] = inv
|
|
bitmasks[name] = createBitmask(inv)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun getOriginal(name: String): Bitmap? = originals[name]
|
|
fun getInverted(name: String): Bitmap? = inverted[name]
|
|
fun getBitmask(name: String): BooleanArray? = bitmasks[name]
|
|
|
|
private fun invertBitmap(src: Bitmap): Bitmap {
|
|
val bmOut = Bitmap.createBitmap(src.width, src.height, src.config ?: Bitmap.Config.ARGB_8888)
|
|
val canvas = Canvas(bmOut)
|
|
val paint = Paint()
|
|
val colorMatrix = android.graphics.ColorMatrix(
|
|
floatArrayOf(
|
|
-1f, 0f, 0f, 0f, 255f,
|
|
0f, -1f, 0f, 0f, 255f,
|
|
0f, 0f, -1f, 0f, 255f,
|
|
0f, 0f, 0f, 1f, 0f
|
|
)
|
|
)
|
|
paint.colorFilter = android.graphics.ColorMatrixColorFilter(colorMatrix)
|
|
canvas.drawBitmap(src, 0f, 0f, paint)
|
|
return bmOut
|
|
}
|
|
|
|
private fun createBitmask(bitmap: Bitmap): BooleanArray {
|
|
val w = bitmap.width
|
|
val h = bitmap.height
|
|
val mask = BooleanArray(w * h)
|
|
val pixels = IntArray(w * h)
|
|
bitmap.getPixels(pixels, 0, w, 0, 0, w, h)
|
|
for (i in pixels.indices) {
|
|
val c = pixels[i]
|
|
val alpha = Color.alpha(c)
|
|
// simple threshold: non-transparent and not near-white (assuming inverted is black on transparent/white)
|
|
// The inverted logic makes black -> white, white -> black.
|
|
// Wait, if original is black silhouette on transparent:
|
|
// Inverted: black becomes white, transparent becomes... white?
|
|
// Let's check invertBitmap logic.
|
|
// Matrix: R' = 255 - R, G' = 255 - G, B' = 255 - B, A' = A.
|
|
// If original is Black (0,0,0,255) -> White (255,255,255,255).
|
|
// If original is Transparent (0,0,0,0) -> Transparent (0,0,0,0).
|
|
|
|
// So "Inverted" means we have White silhouette on Transparent.
|
|
// We want bitmask where the silhouette is.
|
|
// So we look for non-transparent pixels.
|
|
mask[i] = alpha > 16
|
|
}
|
|
return mask
|
|
}
|
|
}
|