From 15b527c58d6d8cd51e6640011b94bf7efd36abbb Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 6 Dec 2024 18:13:40 -0500 Subject: [PATCH] Fixes blurhash rendering --- .../amethyst/ui/note/types/Video.kt | 1 + .../amethyst/commons/preview/BlurhashTest.kt | 15 ++++- .../commons/preview/BlurHashDecoder.kt | 58 ++++++++++++------- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Video.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Video.kt index 05861922c..55ebe7201 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Video.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Video.kt @@ -106,6 +106,7 @@ fun VideoDisplay( authorName = note.author?.toBestDisplayName(), artworkUri = imeta.image.firstOrNull(), mimeType = imeta.mimeType, + blurhash = imeta.blurhash, ) }, ) diff --git a/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/BlurhashTest.kt b/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/BlurhashTest.kt index ba2af780f..d2e14fdc8 100644 --- a/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/BlurhashTest.kt +++ b/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/BlurhashTest.kt @@ -20,18 +20,20 @@ */ package com.vitorpamplona.amethyst.commons.preview +import androidx.test.ext.junit.runners.AndroidJUnit4 import junit.framework.TestCase.assertTrue import org.junit.Assert import org.junit.Test +import org.junit.runner.RunWith import kotlin.math.roundToInt +@RunWith(AndroidJUnit4::class) class BlurhashTest { val warmHex = "[45#Y7_2^-xt%OSb%4S0-qt0xbotaRInV|M{RlD~M{M_IVIUNHM{M{M{M{RjNGRkoyj]o[t8tPt8" val testHex = "|NHL-]~pabocs+jDM{j?of4T9ZR+WBWZbdR-WCog04ITn\$t6t6t6t6oJoLZ}?bIUWBs:M{WCogRjs:s+o#R+WBoft7axWBx]IV%LogM{t5xaWBay%KRjxus.WCNGWWt7j[j]s+R-S5ofjYV@j[ofD%t8RPoJt7t7R*WCof" @Test fun testAspectRatioWarm() { - Assert.assertEquals(0.44444445f, BlurHashDecoderOld.aspectRatio(warmHex)!!, 0.001f) Assert.assertEquals(0.44444445f, BlurHashDecoder.aspectRatio(warmHex)!!, 0.001f) } @@ -45,9 +47,18 @@ class BlurhashTest { assertTrue(bmp1!!.sameAs(bmp2!!)) } + @Test + fun testDecoderWarm25Pixels() { + val aspectRatio = BlurHashDecoder.aspectRatio(warmHex) ?: 1.0f + + val bmp1 = BlurHashDecoderOld.decode(warmHex, 25, (25 * (1 / aspectRatio)).roundToInt()) + val bmp2 = BlurHashDecoder.decodeKeepAspectRatio(warmHex, 25) + + assertTrue(bmp1!!.sameAs(bmp2!!)) + } + @Test fun testAspectRatioTest() { - Assert.assertEquals(1.0f, BlurHashDecoderOld.aspectRatio(testHex)!!) Assert.assertEquals(1.0f, BlurHashDecoder.aspectRatio(testHex)!!) } diff --git a/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/BlurHashDecoder.kt b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/BlurHashDecoder.kt index a95e9894d..18ba0a61c 100644 --- a/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/BlurHashDecoder.kt +++ b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/BlurHashDecoder.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.commons.preview import android.graphics.Bitmap -import android.graphics.Color import kotlin.math.cos import kotlin.math.pow import kotlin.math.roundToInt @@ -60,6 +59,28 @@ object BlurHashDecoder { return numCompX.toFloat() / numCompY.toFloat() } + fun computeColors( + numCompX: Int, + numCompY: Int, + blurHash: String, + ): Array { + val maxAc = (decode83At(blurHash, 1) + 1) / 166f + return Array(numCompX * numCompY) { i -> + if (i == 0) { + decodeDc(decode83(blurHash, 2, 6)) + } else { + decodeAc(decode83Fixed2(blurHash, 4 + i * 2), maxAc) + } + } + } + + fun computeNumComponets(blurHash: String): Pair { + val numCompEnc = decode83At(blurHash, 0) + val numCompX = (numCompEnc % 9) + 1 + val numCompY = (numCompEnc / 9) + 1 + return Pair(numCompX, numCompY) + } + /** * Decode a blur hash into a new bitmap. * @@ -75,24 +96,14 @@ object BlurHashDecoder { if (blurHash == null || blurHash.length < 6) { return null } - val numCompEnc = decode83At(blurHash, 0) - val numCompX = (numCompEnc % 9) + 1 - val numCompY = (numCompEnc / 9) + 1 + val (numCompX, numCompY) = computeNumComponets(blurHash) if (blurHash.length != 4 + 2 * numCompX * numCompY) { return null } - val height = (100 * (1 / (numCompX.toFloat() / numCompY.toFloat()))).roundToInt() - val maxAc = (decode83At(blurHash, 1) + 1) / 166f - - val colors = - Array(numCompX * numCompY) { i -> - if (i == 0) { - decodeDc(decode83(blurHash, 2, 6)) - } else { - decodeAc(decode83Fixed2(blurHash, 4 + i * 2), maxAc) - } - } - return composeBitmap(width, height, numCompX, numCompY, colors, useCache) + val height = (width * (1 / (numCompX.toFloat() / numCompY.toFloat()))).roundToInt() + val colors = computeColors(numCompX, numCompY, blurHash) + val imageArray = composeImageArray(width, height, numCompX, numCompY, colors, useCache) + return Bitmap.createBitmap(imageArray, width, height, Bitmap.Config.ARGB_8888) } private fun decode83At( @@ -149,14 +160,14 @@ object BlurHashDecoder { private fun signedPow2(value: Float) = value.pow(2f).withSign(value) - private fun composeBitmap( + private fun composeImageArray( width: Int, height: Int, numCompX: Int, numCompY: Int, colors: Array, useCache: Boolean, - ): Bitmap { + ): IntArray { // use an array for better performance when writing pixel colors val imageArray = IntArray(width * height) val calculateCosX = !useCache || !cacheCosinesX.containsKey(width * numCompX) @@ -185,12 +196,19 @@ object BlurHashDecoder { } } - imageArray[x + width * y] = Color.rgb(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b)) + imageArray[x + width * y] = rgb(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b)) } } - return Bitmap.createBitmap(imageArray, width, height, Bitmap.Config.ARGB_8888) + + return imageArray } + fun rgb( + red: Int, + green: Int, + blue: Int, + ): Int = -0x1000000 or (red shl 16) or (green shl 8) or blue + private fun getArrayForCosinesY( calculate: Boolean, height: Int,