diff --git a/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/UseResources.kt b/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/UseResources.kt
index bc5f514132..b1fa10c534 100644
--- a/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/UseResources.kt
+++ b/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/UseResources.kt
@@ -5,7 +5,9 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.*
@OptIn(ExperimentalResourceApi::class)
@@ -23,6 +25,12 @@ internal fun UseResources() {
)
Icon(
imageVector = resource("vector.xml").rememberImageVector(LocalDensity.current).orEmpty(),
+ modifier = Modifier.size(150.dp),
+ contentDescription = null
+ )
+ Icon(
+ painter = painterResource("dir/vector.xml"),
+ modifier = Modifier.size(150.dp),
contentDescription = null
)
}
diff --git a/components/resources/demo/shared/src/commonMain/resources/dir/vector.xml b/components/resources/demo/shared/src/commonMain/resources/dir/vector.xml
new file mode 100644
index 0000000000..abd196bb92
--- /dev/null
+++ b/components/resources/demo/shared/src/commonMain/resources/dir/vector.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/components/resources/library/build.gradle.kts b/components/resources/library/build.gradle.kts
index bd26802d18..7387edf05b 100644
--- a/components/resources/library/build.gradle.kts
+++ b/components/resources/library/build.gradle.kts
@@ -35,6 +35,9 @@ kotlin {
implementation(kotlin("test"))
}
}
+ val commonButJSMain by creating {
+ dependsOn(commonMain)
+ }
val skikoMain by creating {
dependsOn(commonMain)
}
@@ -44,6 +47,7 @@ kotlin {
val desktopMain by getting {
dependsOn(skikoMain)
dependsOn(jvmAndAndroidMain)
+ dependsOn(commonButJSMain)
}
val desktopTest by getting {
dependencies {
@@ -54,6 +58,7 @@ kotlin {
}
val androidMain by getting {
dependsOn(jvmAndAndroidMain)
+ dependsOn(commonButJSMain)
}
val androidTest by getting {
dependencies {
@@ -62,6 +67,7 @@ kotlin {
}
val iosMain by getting {
dependsOn(skikoMain)
+ dependsOn(commonButJSMain)
}
val iosTest by getting
val iosSimulatorArm64Main by getting
@@ -73,6 +79,7 @@ kotlin {
}
val macosMain by creating {
dependsOn(skikoMain)
+ dependsOn(commonButJSMain)
}
val macosX64Main by getting {
dependsOn(macosMain)
diff --git a/components/resources/library/src/commonButJSMain/kotlin/org/jetbrains/compose/resources/Resource.commonbutjs.kt b/components/resources/library/src/commonButJSMain/kotlin/org/jetbrains/compose/resources/Resource.commonbutjs.kt
new file mode 100644
index 0000000000..ce0cba09cd
--- /dev/null
+++ b/components/resources/library/src/commonButJSMain/kotlin/org/jetbrains/compose/resources/Resource.commonbutjs.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
+ */
+
+package org.jetbrains.compose.resources
+
+import kotlinx.coroutines.runBlocking
+
+internal actual fun isSyncResourceLoadingSupported() = true
+
+@OptIn(ExperimentalResourceApi::class)
+internal actual fun Resource.readBytesSync(): ByteArray = runBlocking { readBytes() }
+
diff --git a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ComposeResource.common.kt b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ComposeResource.common.kt
index c09f0f2b6a..bb63b7c6c6 100644
--- a/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ComposeResource.common.kt
+++ b/components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ComposeResource.common.kt
@@ -7,7 +7,11 @@ package org.jetbrains.compose.resources
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import org.jetbrains.compose.resources.vector.xmldom.Element
import org.jetbrains.compose.resources.vector.parseVectorRoot
@@ -19,13 +23,16 @@ private val emptyImageVector: ImageVector by lazy {
ImageVector.Builder(defaultWidth = 1.dp, defaultHeight = 1.dp, viewportWidth = 1f, viewportHeight = 1f).build()
}
-@OptIn(ExperimentalResourceApi::class)
+/**
+ * Get and remember resource. While loading and if resource not exists result will be null.
+ */
+@ExperimentalResourceApi
@Composable
-private fun Resource.rememberLoadingResource(fromByteArrayConverter: ByteArray.()->T): LoadState {
- val state: MutableState> = remember(this) { mutableStateOf(LoadState.Loading()) }
+fun Resource.rememberImageBitmap(): LoadState {
+ val state: MutableState> = remember(this) { mutableStateOf(LoadState.Loading()) }
LaunchedEffect(this) {
state.value = try {
- LoadState.Success(readBytes().fromByteArrayConverter())
+ LoadState.Success(readBytes().toImageBitmap())
} catch (e: Exception) {
LoadState.Error(e)
}
@@ -38,16 +45,17 @@ private fun Resource.rememberLoadingResource(fromByteArrayConverter: ByteArr
*/
@ExperimentalResourceApi
@Composable
-fun Resource.rememberImageBitmap(): LoadState =
- rememberLoadingResource { toImageBitmap() }
-
-/**
- * Get and remember resource. While loading and if resource not exists result will be null.
- */
-@ExperimentalResourceApi
-@Composable
-fun Resource.rememberImageVector(density: Density): LoadState =
- rememberLoadingResource { toImageVector(density) }
+fun Resource.rememberImageVector(density: Density): LoadState {
+ val state: MutableState> = remember(this, density) { mutableStateOf(LoadState.Loading()) }
+ LaunchedEffect(this, density) {
+ state.value = try {
+ LoadState.Success(readBytes().toImageVector(density))
+ } catch (e: Exception) {
+ LoadState.Error(e)
+ }
+ }
+ return state.value
+}
private fun LoadState.orEmpty(emptyValue: T): T = when (this) {
is LoadState.Loading -> emptyValue
@@ -56,17 +64,71 @@ private fun LoadState.orEmpty(emptyValue: T): T = when (this) {
}
/**
- * return current ImageBitmap or return empty while loading
+ * Return current ImageBitmap or return empty while loading.
*/
@ExperimentalResourceApi
fun LoadState.orEmpty(): ImageBitmap = orEmpty(emptyImageBitmap)
/**
- * return current ImageVector or return empty while loading
+ * Return current ImageVector or return empty while loading.
*/
@ExperimentalResourceApi
fun LoadState.orEmpty(): ImageVector = orEmpty(emptyImageVector)
+
+@OptIn(ExperimentalResourceApi::class)
+@Composable
+private fun Resource.rememberImageBitmapSync(): ImageBitmap = remember(this) {
+ readBytesSync().toImageBitmap()
+}
+
+@OptIn(ExperimentalResourceApi::class)
+@Composable
+private fun Resource.rememberImageVectorSync(density: Density): ImageVector = remember(this, density) {
+ readBytesSync().toImageVector(density)
+}
+
+
+@OptIn(ExperimentalResourceApi::class)
+@Composable
+private fun painterResource(
+ res: String,
+ rememberImageBitmap: @Composable Resource.() -> ImageBitmap,
+ rememberImageVector: @Composable Resource.(Density) -> ImageVector
+): Painter =
+ if (res.endsWith(".xml")) {
+ rememberVectorPainter(resource(res).rememberImageVector(LocalDensity.current))
+ } else {
+ BitmapPainter(resource(res).rememberImageBitmap())
+ }
+
+/**
+ * Return a Painter from the given resource path.
+ * Can load either a BitmapPainter for rasterized images (.png, .jpg) or
+ * a VectorPainter for XML Vector Drawables (.xml).
+ *
+ * XML Vector Drawables have the same format as for Android
+ * (https://developer.android.com/reference/android/graphics/drawable/VectorDrawable)
+ * except that external references to Android resources are not supported.
+ *
+ * Note that XML Vector Drawables are not supported for Web and native MacOS targets currently.
+ *
+ */
+@ExperimentalResourceApi
+@Composable
+fun painterResource(res: String): Painter =
+ if (isSyncResourceLoadingSupported()) {
+ painterResource(res, {rememberImageBitmapSync()}, {density->rememberImageVectorSync(density)})
+ } else {
+ painterResource(res, {rememberImageBitmap().orEmpty()}, {density->rememberImageVector(density).orEmpty()})
+ }
+
+
+internal expect fun isSyncResourceLoadingSupported(): Boolean
+
+@OptIn(ExperimentalResourceApi::class)
+internal expect fun Resource.readBytesSync(): ByteArray
+
internal expect fun ByteArray.toImageBitmap(): ImageBitmap
internal expect fun parseXML(byteArray: ByteArray): Element
diff --git a/components/resources/library/src/jsMain/kotlin/org/jetbrains/compose/resources/Resource.js.kt b/components/resources/library/src/jsMain/kotlin/org/jetbrains/compose/resources/Resource.js.kt
index b05f8a49ad..b071ef8e04 100644
--- a/components/resources/library/src/jsMain/kotlin/org/jetbrains/compose/resources/Resource.js.kt
+++ b/components/resources/library/src/jsMain/kotlin/org/jetbrains/compose/resources/Resource.js.kt
@@ -46,4 +46,9 @@ internal actual class MissingResourceException actual constructor(path: String)
internal actual fun parseXML(byteArray: ByteArray): Element {
throw UnsupportedOperationException("XML Vector Drawables are not supported for Web target")
-}
\ No newline at end of file
+}
+
+internal actual fun isSyncResourceLoadingSupported() = false
+
+@OptIn(ExperimentalResourceApi::class)
+internal actual fun Resource.readBytesSync(): ByteArray = throw UnsupportedOperationException()
\ No newline at end of file