Browse Source

Fix AnimatedImage component (#2174)

* Fix and optimize image animation for 0 and 1 frames

* Change default frame duration to 10fps to match those of a browser

* Fix zero-size image with ImageBitmap.Blank
pull/2183/head
Ilya Ryzhenkov 2 years ago committed by GitHub
parent
commit
7fbbdd8eca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 66
      components/AnimatedImage/library/src/desktopMain/kotlin/org/jetbrains/compose/animatedimage/DesktopAnimatedImage.kt

66
components/AnimatedImage/library/src/desktopMain/kotlin/org/jetbrains/compose/animatedimage/DesktopAnimatedImage.kt

@ -10,7 +10,7 @@ import org.jetbrains.skia.Codec
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
private const val DEFAULT_FRAME_DURATION = 50 private const val DEFAULT_FRAME_DURATION = 100
actual class AnimatedImage(val codec: Codec) actual class AnimatedImage(val codec: Codec)
@ -26,42 +26,50 @@ actual suspend fun loadResourceAnimatedImage(path: String): AnimatedImage {
@Composable @Composable
actual fun AnimatedImage.animate(): ImageBitmap { actual fun AnimatedImage.animate(): ImageBitmap {
val transition = rememberInfiniteTransition() when (codec.frameCount) {
val frameIndex by transition.animateValue( 0 -> return ImageBitmap.Blank // No frames at all
initialValue = 0, 1 -> {
targetValue = codec.frameCount - 1, // Just one frame, no animation
Int.VectorConverter, val bitmap = remember(codec) { Bitmap().apply { allocPixels(codec.imageInfo) } }
animationSpec = infiniteRepeatable( remember(bitmap) {
animation = keyframes { codec.readPixels(bitmap, 0)
durationMillis = 0
for ((index, frame) in codec.framesInfo.withIndex()) {
index at durationMillis
val frameDuration = calcFrameDuration(frame)
durationMillis += frameDuration
}
} }
) return bitmap.asComposeImageBitmap()
) }
else -> {
val transition = rememberInfiniteTransition()
val frameIndex by transition.animateValue(
initialValue = 0,
targetValue = codec.frameCount - 1,
Int.VectorConverter,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 0
for ((index, frame) in codec.framesInfo.withIndex()) {
index at durationMillis
val frameDuration = calcFrameDuration(frame)
val bitmap = remember(codec) { Bitmap().apply { allocPixels(codec.imageInfo) } } durationMillis += frameDuration
}
}
)
)
remember(bitmap, frameIndex) { val bitmap = remember(codec) { Bitmap().apply { allocPixels(codec.imageInfo) } }
codec.readPixels(bitmap, frameIndex)
} remember(bitmap, frameIndex) {
codec.readPixels(bitmap, frameIndex)
}
return bitmap.asComposeImageBitmap() return bitmap.asComposeImageBitmap()
}
}
} }
private fun calcFrameDuration(frame: AnimationFrameInfo): Int { private fun calcFrameDuration(frame: AnimationFrameInfo): Int {
var frameDuration = frame.duration
// If the frame does not contain information about a duration, set a reasonable constant duration // If the frame does not contain information about a duration, set a reasonable constant duration
if (frameDuration == 0) { val frameDuration = frame.duration
frameDuration = DEFAULT_FRAME_DURATION return if (frameDuration == 0) DEFAULT_FRAME_DURATION else frameDuration
}
return frameDuration
} }
/** /**

Loading…
Cancel
Save