Skip to content

Commit

Permalink
small refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhirkevich committed Jun 10, 2024
1 parent 38f7628 commit 5d7139d
Show file tree
Hide file tree
Showing 27 changed files with 443 additions and 243 deletions.
2 changes: 1 addition & 1 deletion compottie-dot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ kotlin {
implementation(libs.serialization)
implementation(libs.okio)
implementation(libs.okio.fakefilesystem)
implementation(libs.coroutines.core)
implementation(project(":compottie"))

}

val desktopMain by getting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ import kotlinx.serialization.json.Json
import okio.Path.Companion.toPath
import okio.fakefilesystem.FakeFileSystem



private var _useStableWasmMemoryManagement : Boolean = false

/**
* Stable memory management will be much slower but more compatible with Kotlin compiler
* versions.
*
* It is disabled by default. Turn this on if you have problems with dotLottie decompression on wasm
* */
@ExperimentalCompottieApi
var L.useStableWasmMemoryManagement by ::_useStableWasmMemoryManagement

/**
* [LottieComposition] from a dotLottie zip archive.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.github.alexzhirkevich.compottie

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.withContext
import okio.FileSystem
import okio.Path
import okio.openZip
Expand All @@ -13,8 +16,10 @@ internal actual class ZipFileSystem actual constructor(
private val zipFileSystem = parent.openZip(path)

actual suspend fun read(path: Path): ByteArray {
return zipFileSystem.read(path) {
readByteArray()
return withContext(Dispatchers.IO) {
zipFileSystem.read(path) {
readByteArray()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.github.alexzhirkevich.compottie

import org.khronos.webgl.Int8Array
import org.khronos.webgl.get
import org.khronos.webgl.set
import kotlin.wasm.unsafe.UnsafeWasmMemoryApi
import kotlin.wasm.unsafe.withScopedMemoryAllocator

internal fun createSourceStable(data : Int8Array) : JsAny = js("""
({
start(c){
console.log(data)
c.enqueue(data)
c.close()
}
})
""")

internal fun createSourceUnstable(data : JsReference<ByteArray>) : JsAny = js("""
({
start(c){
const size = wasmExports.kotlinArraySize(data);
const result = new Int8Array(size);
for (let i = 0; i < size; i++) {
result[i] = wasmExports.kotlinArrayGet(data, i);
}
c.enqueue(result)
c.close()
}
})
""")

@OptIn(ExperimentalCompottieApi::class)
internal fun Int8Array.toByteArray(): Array<Byte> {
if (L.useStableWasmMemoryManagement) {
return Array(byteLength) {
this[it]
}
}
return jsInt8ArrayToKotlinByteArray(this)
}

@OptIn(ExperimentalCompottieApi::class)
internal fun ByteArray.toInt8Array() : Int8Array {
if (L.useStableWasmMemoryManagement) {
val result = Int8Array(size)
for (i in indices) {
result[i] = this[i]
}
return result
}
return byteArrayToInt8ArrayImpl(toJsReference())
}

@OptIn(ExperimentalJsExport::class)
@JsExport
private fun kotlinArrayGet(a: JsReference<ByteArray>, i: Int): Byte = a.get()[i]

@OptIn(ExperimentalJsExport::class)
@JsExport
private fun kotlinArraySize(a: JsReference<ByteArray>): Int = a.get().size


private fun byteArrayToInt8ArrayImpl(a: JsReference<ByteArray>): Int8Array = js("""{
const size = wasmExports.kotlinArraySize(a);
const result = new Int8Array(size);
for (let i = 0; i < size; i++) {
result[i] = wasmExports.kotlinArrayGet(a, i);
}
return result;
}""")



@JsFun(
""" (src, size, dstAddr) => {
const mem8 = new Int8Array(wasmExports.memory.buffer, dstAddr, size);
mem8.set(src);
}
"""
)
private external fun jsExportInt8ArrayToWasm(src: Int8Array, size: Int, dstAddr: Int)

private fun jsInt8ArrayToKotlinByteArray(x: Int8Array): Array<Byte> {
val size = x.length

@OptIn(UnsafeWasmMemoryApi::class)
return withScopedMemoryAllocator { allocator ->
val memBuffer = allocator.allocate(size)
val dstAddress = memBuffer.address.toInt()
jsExportInt8ArrayToWasm(x, size, dstAddress)
Array(size) { i -> (memBuffer + i).loadByte() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,13 @@ package io.github.alexzhirkevich.compottie

import org.khronos.webgl.ArrayBufferView
import org.khronos.webgl.Int8Array
import org.khronos.webgl.set
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import kotlin.js.Promise

external class DecompressionStream(alg : String)

//external interface UnderlyingSource : JsAny {
// fun start(controller: SourceController)
//}
//
//interface SourceController {
// fun enqueue(data : ByteArray)
//
// fun close()
//}

fun createSource(data : Int8Array) : JsAny = js("""
({
start(c){
console.log(data)
c.enqueue(data)
c.close()
}
})
""")



external class ReadableStream(
source: JsAny
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,34 @@ package io.github.alexzhirkevich.compottie


import org.khronos.webgl.Int8Array
import org.khronos.webgl.get
import org.khronos.webgl.set
import kotlin.time.measureTime

@OptIn(ExperimentalCompottieApi::class)
internal actual suspend fun decompress(array: ByteArray, inflatedSize : Int) : ByteArray {

val ds = DecompressionStream("deflate-raw")

val stream = ReadableStream(createSource(copyToInt8Array(array)))
val source = if (L.useStableWasmMemoryManagement) {
createSourceStable(array.toInt8Array())
} else {
createSourceUnstable(array.toJsReference())
}
val stream = ReadableStream(source)

val reader = stream
.pipeThrough(ds)
.getReader()

val inflatedResult = ArrayList<Byte>(inflatedSize)


while (true) {
val result = reader.read().await()
if (result.done) {
break
}

inflatedResult.addAll(
copyToByteArray(Int8Array(result.value.buffer))
)
inflatedResult += Int8Array(result.value.buffer).toByteArray()
}

return inflatedResult.toByteArray()
}

internal fun copyToByteArray(array: Int8Array): List<Byte> {
var res : List<Byte>
measureTime {
res = List(array.byteLength) {
array[it]
}
}.also {
println("Copy to js array. Size: ${array.byteLength}. Time: $it")
}

return res
}

private fun copyToInt8Array(array: ByteArray): Int8Array {
val result = Int8Array(array.size)
measureTime {
for (i in array.indices) {
result[i] = array[i]
}
}.also {
println("Copy from js array. Size: ${array.size}. Time:$it")
}
return result
}
18 changes: 10 additions & 8 deletions compottie-network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,14 @@ if (System.getenv("GPG_KEY") != null) {
}


configurations.all {
resolutionStrategy.eachDependency {
if (requested.group.startsWith("io.ktor") &&
requested.name.startsWith("ktor-client-")
) {
useVersion("3.0.0-wasm2")
configurations
.filter { it.name.contains("wasmJs") }
.onEach {
it.resolutionStrategy.eachDependency {
if (requested.group.startsWith("io.ktor") &&
requested.name.startsWith("ktor-client-")
) {
useVersion("3.0.0-wasm2")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.alexzhirkevich.compottie

import androidx.compose.runtime.Stable
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ class DiskCacheStrategy : LottieCacheStrategy {
override suspend fun load(url: String): ByteArray? {
return null
}

override fun equals(other: Any?): Boolean {
return true
}

override fun hashCode(): Int {
return 1
}
}

//internal expect fun FileSystem.Companion.System : FileSystem
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.alexzhirkevich.compottie

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
Expand All @@ -12,25 +13,36 @@ import io.ktor.http.URLParserException
import io.ktor.http.Url
import io.ktor.util.toByteArray

@Composable
fun rememberNetworkAssetsManager(
//@Composable
//@Stable
//fun rememberNetworkAssetsManager(
// client: HttpClient = DefaultHttpClient,
// cacheStrategy: LottieCacheStrategy = rememberDiskCacheStrategy(),
// request : NetworkRequest = GetRequest,
//) : NetworkAssetsManager {
// val updatedRequest by rememberUpdatedState(request)
//
// return remember(client, cacheStrategy) {
// NetworkAssetsManager(client, cacheStrategy) { c, u ->
// updatedRequest.invoke(c, u)
// }
// }
//}

fun NetworkAssetsManager(
client: HttpClient = DefaultHttpClient,
cacheStrategy: LottieCacheStrategy = rememberDiskCacheStrategy(),
cacheStrategy: LottieCacheStrategy = DiskCacheStrategy(),
request : NetworkRequest = GetRequest,
) {
val updatedRequest by rememberUpdatedState(request)

return remember(client, cacheStrategy) {
NetworkAssetsManager(client, cacheStrategy) { c, u ->
updatedRequest.invoke(c, u)
}
}
}
) : LottieAssetsManager = NetworkAssetsManagerImpl(
client = client,
cacheStrategy = cacheStrategy,
request = request
)

class NetworkAssetsManager(
private val client: HttpClient,
private val cacheStrategy: LottieCacheStrategy,
private val request : NetworkRequest,
private class NetworkAssetsManagerImpl(
private val client: HttpClient = DefaultHttpClient,
private val cacheStrategy: LottieCacheStrategy = DiskCacheStrategy(),
private val request : NetworkRequest = GetRequest,
) : LottieAssetsManager {

override suspend fun fetch(asset: LottieAsset): ByteArray? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import io.github.alexzhirkevich.compottie.LottieCompositionSpec
import io.github.alexzhirkevich.compottie.NetworkAssetsManager
import io.github.alexzhirkevich.compottie.NetworkRequest
import io.github.alexzhirkevich.compottie.assets.LottieAssetsManager
import io.github.alexzhirkevich.compottie.rememberNetworkAssetsManager
import io.ktor.client.HttpClient
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.statement.bodyAsChannel
Expand All @@ -25,19 +24,17 @@ import io.ktor.util.toByteArray
* [LottieComposition] from web [url]
*
* @param client Ktor http client to use
* @param assetsManager lottie assets manager. By default no-op manager is used.
* Use [NetworkAssetsManager] if assets use web URLs too
*
* @see rememberNetworkAssetsManager
* @param assetsManager manager for assets that not embedded to the animation JSON or dotLottie archive.
* [NetworkAssetsManager] is used by default
* */
@Stable
fun LottieCompositionSpec.Companion.Url(
url : String,
format: LottieAnimationFormat = LottieAnimationFormat.Unknown,
format: LottieAnimationFormat = LottieAnimationFormat.Undefined,
client: HttpClient = DefaultHttpClient,
assetsManager: LottieAssetsManager = LottieAssetsManager,
cacheStrategy: LottieCacheStrategy = DiskCacheStrategy(),
request : NetworkRequest = GetRequest,
cacheStrategy: LottieCacheStrategy = DiskCacheStrategy(),
assetsManager: LottieAssetsManager = NetworkAssetsManager(client,cacheStrategy, request),
) : LottieCompositionSpec = NetworkCompositionSpec(
url = url,
format = format,
Expand Down
Loading

0 comments on commit 5d7139d

Please sign in to comment.