From 7fbbdd8ecae660e92f1ef62c8e8a6ec89f90adff Mon Sep 17 00:00:00 2001 From: Ilya Ryzhenkov Date: Thu, 7 Jul 2022 19:53:33 +0300 Subject: [PATCH] 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 --- .../animatedimage/DesktopAnimatedImage.kt | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/components/AnimatedImage/library/src/desktopMain/kotlin/org/jetbrains/compose/animatedimage/DesktopAnimatedImage.kt b/components/AnimatedImage/library/src/desktopMain/kotlin/org/jetbrains/compose/animatedimage/DesktopAnimatedImage.kt index 41fb76e24b..cb9ef653d6 100644 --- a/components/AnimatedImage/library/src/desktopMain/kotlin/org/jetbrains/compose/animatedimage/DesktopAnimatedImage.kt +++ b/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.URL -private const val DEFAULT_FRAME_DURATION = 50 +private const val DEFAULT_FRAME_DURATION = 100 actual class AnimatedImage(val codec: Codec) @@ -26,42 +26,50 @@ actual suspend fun loadResourceAnimatedImage(path: String): AnimatedImage { @Composable actual fun AnimatedImage.animate(): ImageBitmap { - 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) - - durationMillis += frameDuration - } + when (codec.frameCount) { + 0 -> return ImageBitmap.Blank // No frames at all + 1 -> { + // Just one frame, no animation + val bitmap = remember(codec) { Bitmap().apply { allocPixels(codec.imageInfo) } } + remember(bitmap) { + codec.readPixels(bitmap, 0) } - ) - ) + 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) { - codec.readPixels(bitmap, frameIndex) - } + val bitmap = remember(codec) { Bitmap().apply { allocPixels(codec.imageInfo) } } + + remember(bitmap, frameIndex) { + codec.readPixels(bitmap, frameIndex) + } - return bitmap.asComposeImageBitmap() + return bitmap.asComposeImageBitmap() + } + } } 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 (frameDuration == 0) { - frameDuration = DEFAULT_FRAME_DURATION - } - - return frameDuration + val frameDuration = frame.duration + return if (frameDuration == 0) DEFAULT_FRAME_DURATION else frameDuration } /**