Works
diff --git a/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt b/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
index abf6dc2..d1c5da9 100755
--- a/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
+++ b/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
@@ -3,13 +3,25 @@
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import com.airbnb.lottie.compose.LottieAnimation
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.LottiePainter
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
+import com.airbnb.lottie.compose.rememberLottiePainter
class ComposeIssueReproActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -23,6 +35,14 @@
fun Content() {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
val progress by animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
- LottieAnimation(composition, { progress })
+ val painter = rememberLottiePainter(composition, progress)
+ Image(
+ painter = painter,
+ contentDescription = "",
+ contentScale = ContentScale.Fit,
+ alignment = Alignment.BottomCenter,
+ modifier = Modifier
+ .size(500.dp, 700.dp),
+ )
}
}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt
new file mode 100644
index 0000000..efcfdc8
--- /dev/null
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt
@@ -0,0 +1,119 @@
+package com.airbnb.lottie.compose
+
+import android.graphics.Matrix
+import android.graphics.Typeface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ScaleFactor
+import androidx.compose.ui.unit.IntSize
+import com.airbnb.lottie.AsyncUpdates
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.RenderMode
+import com.airbnb.lottie.utils.Utils
+import kotlin.math.roundToInt
+
+@Composable
+fun rememberLottiePainter(
+ composition: LottieComposition? = null,
+ progress: Float = 0f,
+ outlineMasksAndMattes: Boolean = false,
+ applyOpacityToLayers: Boolean = false,
+ enableMergePaths: Boolean = false,
+ renderMode: RenderMode = RenderMode.AUTOMATIC,
+ maintainOriginalImageBounds: Boolean = false,
+ dynamicProperties: LottieDynamicProperties? = null,
+ clipToCompositionBounds: Boolean = true,
+ fontMap: Map<String, Typeface>? = null,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
+): LottiePainter {
+ val painter = remember { LottiePainter() }
+ painter.composition.value = composition
+ painter.progress.floatValue = progress
+ painter.outlineMasksAndMattes.value = outlineMasksAndMattes
+ painter.applyOpacityToLayers.value = applyOpacityToLayers
+ painter.enableMergePaths.value = enableMergePaths
+ painter.renderMode.value = renderMode
+ painter.maintainOriginalImageBounds.value = maintainOriginalImageBounds
+ painter.dynamicProperties.value = dynamicProperties
+ painter.clipToCompositionBounds.value = clipToCompositionBounds
+ painter.fontMap.value = fontMap
+ painter.asyncUpdates.value = asyncUpdates
+ return painter
+}
+
+class LottiePainter(
+ composition: LottieComposition? = null,
+ progress: Float = 0f,
+ outlineMasksAndMattes: Boolean = false,
+ applyOpacityToLayers: Boolean = false,
+ enableMergePaths: Boolean = false,
+ renderMode: RenderMode = RenderMode.AUTOMATIC,
+ maintainOriginalImageBounds: Boolean = false,
+ dynamicProperties: LottieDynamicProperties? = null,
+ clipToCompositionBounds: Boolean = true,
+ fontMap: Map<String, Typeface>? = null,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
+) : Painter() {
+ val composition = mutableStateOf(composition)
+ val progress = mutableFloatStateOf(progress)
+ val outlineMasksAndMattes = mutableStateOf(outlineMasksAndMattes)
+ val applyOpacityToLayers = mutableStateOf(applyOpacityToLayers)
+ val enableMergePaths = mutableStateOf(enableMergePaths)
+ val renderMode = mutableStateOf(renderMode)
+ val maintainOriginalImageBounds = mutableStateOf(maintainOriginalImageBounds)
+ val dynamicProperties = mutableStateOf(dynamicProperties)
+ private var setDynamicProperties: LottieDynamicProperties? = null
+ val clipToCompositionBounds = mutableStateOf(clipToCompositionBounds)
+ val fontMap = mutableStateOf(fontMap)
+ val asyncUpdates = mutableStateOf(asyncUpdates)
+
+ private val drawable = LottieDrawable()
+ private val matrix = Matrix()
+ override val intrinsicSize: Size
+ get() {
+ val composition = composition.value ?: return Size.Unspecified
+ return Size(composition.bounds.width().toFloat(), composition.bounds.height().toFloat())
+ }
+
+ override fun DrawScope.onDraw() {
+ val composition = composition.value ?: return
+ drawIntoCanvas { canvas ->
+ val compositionSize = Size(composition.bounds.width().toFloat(), composition.bounds.height().toFloat())
+ val intSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
+
+ matrix.reset()
+ matrix.preScale(intSize.width / compositionSize.width, intSize.height / compositionSize.height)
+
+ drawable.enableMergePathsForKitKatAndAbove(enableMergePaths.value)
+ drawable.renderMode = renderMode.value
+ drawable.asyncUpdates = asyncUpdates.value
+ drawable.composition = composition
+ drawable.setFontMap(fontMap.value)
+ if (dynamicProperties.value !== setDynamicProperties) {
+ setDynamicProperties?.removeFrom(drawable)
+ dynamicProperties.value?.addTo(drawable)
+ setDynamicProperties = dynamicProperties.value
+ }
+ drawable.setOutlineMasksAndMattes(outlineMasksAndMattes.value)
+ drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers.value
+ drawable.maintainOriginalImageBounds = maintainOriginalImageBounds.value
+ drawable.clipToCompositionBounds = clipToCompositionBounds.value
+ drawable.progress = progress.floatValue
+ drawable.setBounds(0, 0, composition.bounds.width(), composition.bounds.height())
+ drawable.draw(canvas.nativeCanvas, matrix)
+ }
+
+ }
+}
+
+private operator fun Size.times(scale: ScaleFactor): IntSize {
+ return IntSize((width * scale.scaleX).toInt(), (height * scale.scaleY).toInt())
+}