feat: Add expect/actual for blurhash and base64Image in commons
- Create PlatformImage expect/actual abstracting Bitmap/BufferedImage - Move pure blurhash algorithms (Base83, SRGB, CosineCache, BlurHashEncoder) to commonMain - Rewrite BlurHashDecoder to return PlatformImage instead of Bitmap - Add PlatformImage.toBlurhash() extension in commonMain - Add platform-specific Bitmap.toBlurhash() and BufferedImage.toBlurhash() - Add base64Image toPlatformImage() for both Android and JVM 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+8
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.commons.base64Image
|
|||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import com.vitorpamplona.amethyst.commons.blurhash.PlatformImage
|
||||||
|
import com.vitorpamplona.amethyst.commons.blurhash.toPlatformImage
|
||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
|
|
||||||
fun Base64Image.toBitmap(content: String): Bitmap {
|
fun Base64Image.toBitmap(content: String): Bitmap {
|
||||||
@@ -35,3 +37,9 @@ fun Base64Image.toBitmap(content: String): Bitmap {
|
|||||||
|
|
||||||
throw Exception("Unable to convert base64 to image $content")
|
throw Exception("Unable to convert base64 to image $content")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a base64 image data URI to a PlatformImage.
|
||||||
|
* Delegates to toBitmap and wraps the result.
|
||||||
|
*/
|
||||||
|
fun Base64Image.toPlatformImage(content: String): PlatformImage = toBitmap(content).toPlatformImage()
|
||||||
|
|||||||
+5
-37
@@ -21,41 +21,9 @@
|
|||||||
package com.vitorpamplona.amethyst.commons.blurhash
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
fun Bitmap.toBlurhash(): String {
|
/**
|
||||||
val aspectRatio = this.width.toFloat() / this.height.toFloat()
|
* Encodes this Android Bitmap to a blurhash string.
|
||||||
|
* Delegates to PlatformImage.toBlurhash() for the actual encoding.
|
||||||
if (this.width > 100 && this.height > 100) {
|
*/
|
||||||
return Bitmap.createScaledBitmap(this, 100, (100 / aspectRatio).toInt(), false).toBlurhash()
|
fun Bitmap.toBlurhash(): String = this.toPlatformImage().toBlurhash()
|
||||||
}
|
|
||||||
|
|
||||||
val intArray = IntArray(width * height)
|
|
||||||
this.getPixels(intArray, 0, width, 0, 0, width, height)
|
|
||||||
|
|
||||||
val numX =
|
|
||||||
if (aspectRatio > 1) {
|
|
||||||
9
|
|
||||||
} else if (aspectRatio < 1) {
|
|
||||||
(9 * aspectRatio).roundToInt()
|
|
||||||
} else {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
val numY =
|
|
||||||
if (aspectRatio > 1) {
|
|
||||||
(9 * (1 / aspectRatio)).roundToInt()
|
|
||||||
} else if (aspectRatio < 1) {
|
|
||||||
9
|
|
||||||
} else {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
return BlurHashEncoder().encode(
|
|
||||||
intArray,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
numX,
|
|
||||||
numY,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
|
||||||
|
actual class PlatformImage(
|
||||||
|
val bitmap: Bitmap,
|
||||||
|
) {
|
||||||
|
actual val width: Int get() = bitmap.width
|
||||||
|
actual val height: Int get() = bitmap.height
|
||||||
|
|
||||||
|
actual fun getPixels(
|
||||||
|
pixels: IntArray,
|
||||||
|
offset: Int,
|
||||||
|
stride: Int,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
) {
|
||||||
|
bitmap.getPixels(pixels, offset, stride, x, y, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun scale(
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage = PlatformImage(Bitmap.createScaledBitmap(bitmap, width, height, false))
|
||||||
|
|
||||||
|
actual companion object {
|
||||||
|
actual fun create(
|
||||||
|
pixels: IntArray,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage = PlatformImage(Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to convert Android Bitmap to PlatformImage.
|
||||||
|
*/
|
||||||
|
fun Bitmap.toPlatformImage(): PlatformImage = PlatformImage(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to get the underlying Android Bitmap.
|
||||||
|
*/
|
||||||
|
fun PlatformImage.toAndroidBitmap(): Bitmap = this.bitmap
|
||||||
+3
-4
@@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.commons.blurhash
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.vitorpamplona.amethyst.commons.blurhash.SRGB.Companion.linearToSrgb
|
import com.vitorpamplona.amethyst.commons.blurhash.SRGB.Companion.linearToSrgb
|
||||||
import com.vitorpamplona.amethyst.commons.blurhash.SRGB.Companion.srgbToLinear
|
import com.vitorpamplona.amethyst.commons.blurhash.SRGB.Companion.srgbToLinear
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@@ -66,7 +65,7 @@ object BlurHashDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a blur hash into a new bitmap.
|
* Decode a blur hash into a new PlatformImage.
|
||||||
*
|
*
|
||||||
* @param useCache use in memory cache for the calculated math, reused by images with same size.
|
* @param useCache use in memory cache for the calculated math, reused by images with same size.
|
||||||
* if the cache does not exist yet it will be created and populated with new calculations. By
|
* if the cache does not exist yet it will be created and populated with new calculations. By
|
||||||
@@ -76,7 +75,7 @@ object BlurHashDecoder {
|
|||||||
blurHash: String?,
|
blurHash: String?,
|
||||||
width: Int,
|
width: Int,
|
||||||
useCache: Boolean = true,
|
useCache: Boolean = true,
|
||||||
): Bitmap? {
|
): PlatformImage? {
|
||||||
if (blurHash == null || blurHash.length < 6) {
|
if (blurHash == null || blurHash.length < 6) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,7 @@ object BlurHashDecoder {
|
|||||||
val height = (width * (1 / (numCompX.toFloat() / numCompY.toFloat()))).roundToInt()
|
val height = (width * (1 / (numCompX.toFloat() / numCompY.toFloat()))).roundToInt()
|
||||||
val colors = computeColors(numCompX, numCompY, blurHash)
|
val colors = computeColors(numCompX, numCompY, blurHash)
|
||||||
val imageArray = composeImageArray(width, height, numCompX, numCompY, colors, useCache)
|
val imageArray = composeImageArray(width, height, numCompX, numCompY, colors, useCache)
|
||||||
return Bitmap.createBitmap(imageArray, width, height, Bitmap.Config.ARGB_8888)
|
return PlatformImage.create(imageArray, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeDc(colorEnc: Int): FloatArray {
|
private fun decodeDc(colorEnc: Int): FloatArray {
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes this PlatformImage to a blurhash string.
|
||||||
|
* The image will be scaled down if larger than 100x100 for performance.
|
||||||
|
*/
|
||||||
|
fun PlatformImage.toBlurhash(): String {
|
||||||
|
val aspectRatio = this.width.toFloat() / this.height.toFloat()
|
||||||
|
|
||||||
|
if (this.width > 100 && this.height > 100) {
|
||||||
|
return this.scale(100, (100 / aspectRatio).toInt()).toBlurhash()
|
||||||
|
}
|
||||||
|
|
||||||
|
val intArray = IntArray(width * height)
|
||||||
|
this.getPixels(intArray, 0, width, 0, 0, width, height)
|
||||||
|
|
||||||
|
val numX =
|
||||||
|
if (aspectRatio > 1) {
|
||||||
|
9
|
||||||
|
} else if (aspectRatio < 1) {
|
||||||
|
(9 * aspectRatio).roundToInt()
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
|
val numY =
|
||||||
|
if (aspectRatio > 1) {
|
||||||
|
(9 * (1 / aspectRatio)).roundToInt()
|
||||||
|
} else if (aspectRatio < 1) {
|
||||||
|
9
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlurHashEncoder().encode(
|
||||||
|
intArray,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
numX,
|
||||||
|
numY,
|
||||||
|
)
|
||||||
|
}
|
||||||
+3
-2
@@ -21,6 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.commons.blurhash
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
import androidx.collection.LruCache
|
import androidx.collection.LruCache
|
||||||
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
|
||||||
object CosineCache {
|
object CosineCache {
|
||||||
@@ -54,7 +55,7 @@ object CosineCache {
|
|||||||
DoubleArray(height * numCompY) {
|
DoubleArray(height * numCompY) {
|
||||||
val y = it / numCompY
|
val y = it / numCompY
|
||||||
val j = it % numCompY
|
val j = it % numCompY
|
||||||
cos(Math.PI * y * j / height)
|
cos(PI * y * j / height)
|
||||||
}.also {
|
}.also {
|
||||||
cacheCosinesY.put(height * numCompY, it)
|
cacheCosinesY.put(height * numCompY, it)
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ object CosineCache {
|
|||||||
DoubleArray(width * numCompX) {
|
DoubleArray(width * numCompX) {
|
||||||
val x = it / numCompX
|
val x = it / numCompX
|
||||||
val i = it % numCompX
|
val i = it % numCompX
|
||||||
cos(Math.PI * x * i / width)
|
cos(PI * x * i / width)
|
||||||
}.also { cacheCosinesX.put(width * numCompX, it) }
|
}.also { cacheCosinesX.put(width * numCompX, it) }
|
||||||
}
|
}
|
||||||
else -> cacheCosinesX[width * numCompX]!!
|
else -> cacheCosinesX[width * numCompX]!!
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform-agnostic image wrapper for blurhash encoding/decoding.
|
||||||
|
*
|
||||||
|
* On Android: wraps android.graphics.Bitmap
|
||||||
|
* On Desktop JVM: wraps java.awt.image.BufferedImage
|
||||||
|
*/
|
||||||
|
expect class PlatformImage {
|
||||||
|
val width: Int
|
||||||
|
val height: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read ARGB pixels into the provided array.
|
||||||
|
* Pixels are in ARGB format (0xAARRGGBB).
|
||||||
|
*/
|
||||||
|
fun getPixels(
|
||||||
|
pixels: IntArray,
|
||||||
|
offset: Int,
|
||||||
|
stride: Int,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a scaled copy of this image.
|
||||||
|
*/
|
||||||
|
fun scale(
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new image from ARGB pixel array.
|
||||||
|
*/
|
||||||
|
fun create(
|
||||||
|
pixels: IntArray,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.base64Image
|
||||||
|
|
||||||
|
import com.vitorpamplona.amethyst.commons.blurhash.PlatformImage
|
||||||
|
import com.vitorpamplona.amethyst.commons.blurhash.toPlatformImage
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.util.Base64
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a base64 image data URI to a PlatformImage (BufferedImage wrapper).
|
||||||
|
*/
|
||||||
|
fun Base64Image.toPlatformImage(content: String): PlatformImage {
|
||||||
|
val matcher = pattern.matcher(content)
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
val base64String = matcher.group(2)
|
||||||
|
val byteArray = Base64.getDecoder().decode(base64String)
|
||||||
|
val bufferedImage =
|
||||||
|
ImageIO.read(ByteArrayInputStream(byteArray))
|
||||||
|
?: throw Exception("Unable to decode base64 image: $content")
|
||||||
|
return bufferedImage.toPlatformImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to convert base64 to image $content")
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes this BufferedImage to a blurhash string.
|
||||||
|
* Delegates to PlatformImage.toBlurhash() for the actual encoding.
|
||||||
|
*/
|
||||||
|
fun BufferedImage.toBlurhash(): String = this.toPlatformImage().toBlurhash()
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.blurhash
|
||||||
|
|
||||||
|
import java.awt.Image
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
|
||||||
|
actual class PlatformImage(
|
||||||
|
val image: BufferedImage,
|
||||||
|
) {
|
||||||
|
actual val width: Int get() = image.width
|
||||||
|
actual val height: Int get() = image.height
|
||||||
|
|
||||||
|
actual fun getPixels(
|
||||||
|
pixels: IntArray,
|
||||||
|
offset: Int,
|
||||||
|
stride: Int,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
) {
|
||||||
|
image.getRGB(x, y, width, height, pixels, offset, stride)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun scale(
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage {
|
||||||
|
val scaled = image.getScaledInstance(width, height, Image.SCALE_FAST)
|
||||||
|
val buffered = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
||||||
|
buffered.graphics.drawImage(scaled, 0, 0, null)
|
||||||
|
return PlatformImage(buffered)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual companion object {
|
||||||
|
actual fun create(
|
||||||
|
pixels: IntArray,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
): PlatformImage {
|
||||||
|
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
||||||
|
image.setRGB(0, 0, width, height, pixels, 0, width)
|
||||||
|
return PlatformImage(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to convert BufferedImage to PlatformImage.
|
||||||
|
*/
|
||||||
|
fun BufferedImage.toPlatformImage(): PlatformImage = PlatformImage(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to get the underlying BufferedImage.
|
||||||
|
*/
|
||||||
|
fun PlatformImage.toBufferedImage(): BufferedImage = this.image
|
||||||
Reference in New Issue
Block a user