Skip to content

Commit

Permalink
Tweak formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsvanvelzen committed Jul 16, 2024
1 parent 644a4d5 commit 801c043
Showing 1 changed file with 31 additions and 30 deletions.
61 changes: 31 additions & 30 deletions app/src/main/java/org/jellyfin/androidtv/util/BlurHashDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,94 +9,95 @@ import kotlin.math.withSign

@Suppress("MagicNumber", "NestedBlockDepth")
object BlurHashDecoder {
private const val CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"

/**
* Decode a blur hash into a new bitmap.
*/
fun decode(blurHash: String?, width: Int, height: Int, punch: Float = 1f): Bitmap? {
if (blurHash == null || blurHash.length < 6) {
return null
}
if (blurHash == null || blurHash.length < 6) return null

val numCompEnc = decode83(blurHash, 0, 1)
val numCompX = (numCompEnc % 9) + 1
val numCompY = (numCompEnc / 9) + 1
val totalComp = numCompX * numCompY
if (blurHash.length != 4 + 2 * totalComp) {
return null
}
if (blurHash.length != 4 + 2 * totalComp) return null

val maxAcEnc = decode83(blurHash, 1, 2)
val maxAc = (maxAcEnc + 1) / 166f
val colors = FloatArray(totalComp * 3)
var colorEnc = decode83(blurHash, 2, 6)
decodeDc(colorEnc, colors)

for (i in 1 until totalComp) {
val from = 4 + i * 2
colorEnc = decode83(blurHash, from, from + 2)
decodeAc(colorEnc, maxAc * punch, colors, i * 3)
}

return composeBitmap(width, height, numCompX, numCompY, colors)
}

private fun decode83(str: String, from: Int, to: Int): Int {
var result = 0

for (i in from until to) {
val index = CHARS.indexOf(str[i])
if (index != -1) {
result = result * 83 + index
}
if (index != -1) result = result * 83 + index
}

return result
}

private fun decodeDc(colorEnc: Int, outArray: FloatArray) {
val r = (colorEnc shr 16) and 0xFF
val g = (colorEnc shr 8) and 0xFF
val b = colorEnc and 0xFF

outArray[0] = srgbToLinear(r)
outArray[1] = srgbToLinear(g)
outArray[2] = srgbToLinear(b)
}

private fun srgbToLinear(colorEnc: Int): Float {
val v = colorEnc / 255f
return if (v <= 0.04045f) {
(v / 12.92f)
} else {
((v + 0.055f) / 1.055f).pow(2.4f)

return when {
v <= 0.04045f -> (v / 12.92f)
else -> ((v + 0.055f) / 1.055f).pow(2.4f)
}
}

private fun decodeAc(value: Int, maxAc: Float, outArray: FloatArray, outIndex: Int) {
val r = value / (19 * 19)
val g = (value / 19) % 19
val b = value % 19

outArray[outIndex] = signedPow2((r - 9) / 9.0f) * maxAc
outArray[outIndex + 1] = signedPow2((g - 9) / 9.0f) * maxAc
outArray[outIndex + 2] = signedPow2((b - 9) / 9.0f) * maxAc
}

private fun signedPow2(value: Float) = (value * value).withSign(value)

private fun composeBitmap(
width: Int, height: Int,
numCompX: Int, numCompY: Int,
colors: FloatArray
): Bitmap {
private fun composeBitmap(width: Int, height: Int, numCompX: Int, numCompY: Int, colors: FloatArray): Bitmap {
// use an array for better performance when writing pixel colors
val imageArray = IntArray(width * height)
val cosinesX = createCosines(width, numCompX)
val cosinesY = if (width == height && numCompX == numCompY) {
cosinesX
} else {
createCosines(height, numCompY)
val cosinesY = when {
width == height && numCompX == numCompY -> cosinesX
else -> createCosines(height, numCompY)
}

for (y in 0 until height) {
for (x in 0 until width) {
var r = 0f
var g = 0f
var b = 0f

for (j in 0 until numCompY) {
val cosY = cosinesY[y * numCompY + j]

for (i in 0 until numCompX) {
val cosX = cosinesX[x * numCompX + i]
val basis = cosX * cosY
Expand All @@ -106,27 +107,27 @@ object BlurHashDecoder {
b += colors[colorIndex + 2] * basis
}
}
imageArray[x + width * y] =
Color.rgb(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b))

imageArray[x + width * y] = Color.rgb(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b))
}
}

return Bitmap.createBitmap(imageArray, width, height, Bitmap.Config.ARGB_8888)
}

private fun createCosines(size: Int, numComp: Int) = FloatArray(size * numComp) { index ->
val x = index / numComp
val i = index % numComp

cos(PI * x * i / size).toFloat()
}

private fun linearToSrgb(value: Float): Int {
val v = value.coerceIn(0f, 1f)
return if (v <= 0.0031308f) {
(v * 12.92f * 255f + 0.5f).toInt()
} else {
((1.055f * v.pow(1 / 2.4f) - 0.055f) * 255 + 0.5f).toInt()

return when {
v <= 0.0031308f -> (v * 12.92f * 255f + 0.5f).toInt()
else -> ((1.055f * v.pow(1 / 2.4f) - 0.055f) * 255 + 0.5f).toInt()
}
}

private const val CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"
}

0 comments on commit 801c043

Please sign in to comment.