Проигрывание GIF в KMP Desktop

от автора

Я — Денис, Middle Android-разработчик в «Black Bricks». Недавно в нашем KMP проекте возникла необходимость добавить рекламный баннер с GIF. В этой статье я расскажу, с какими трудностями мы столкнулись и как удалось реализовать этот функционал.

Исходно, стандартных решений для корректного воспроизведения GIF в Jetpack Compose мы не нашли. Основная сложность заключалась в том, что решение должно было работать одинаково стабильно как на Windows, так и на macOS. Сперва остановились на этом решении. Но уже на первых тестах стало понятно, что GIF больше 4 мегабайт эта реализация не тянет. Загрузка ЦП была под 80%.

Ещё немного пошерстив интернет, наткнулись на реализацию с загрузкой GIF из сети. Нам в целом было не принципиально откуда грузить. Но из-за специфики расположения баннера, и требований отобразить его как можно быстрее, хотелось всё же захардкодить его в сам проект.

Мы немного модифицировали решение для проигрывания из локальных ресурсов. Но так как в реализации используется библиотека javax.imageio.ImageIO, то нормально она под Windows не завелась. Долго разбираться не стали, тем более этот вариант немного лагал.

По итогу пришли к такому:

@Composable fun GifImage() {     val codec = remember {         val path = "desktopApp/src/main/resources/images/sample.gif"         val file = File(path)         val bytes = file.readBytes()         Codec.makeFromData(Data.makeFromBytes(bytes))     }      AnimatedGif(         modifier = Modifier             .fillMaxHeight()             .aspectRatio(ratio = 1.5f, matchHeightConstraintsFirst = true),         codec = codec,     ) }  @Composable fun AnimatedGif(codec: Codec, modifier: Modifier) {     val transition = rememberInfiniteTransition()     val frameIndex by transition.animateValue(         initialValue = 0,         targetValue = codec.frameCount - 1,         typeConverter = Int.VectorConverter,         animationSpec = infiniteRepeatable(             animation = keyframes {                 durationMillis = 0                 for ((index, frame) in codec.framesInfo.withIndex()) {                     index at durationMillis                     durationMillis += frame.duration                 }             }         )     )      val bitmap = remember { Bitmap().apply { allocPixels(codec.imageInfo) } }     Canvas(modifier) {         codec.readPixels(bitmap, frameIndex)         val imageBitmap = bitmap.asComposeImageBitmap()         drawImage(             image = imageBitmap,             dstSize = IntSize(size.width.toInt(), size.height.toInt())         )     } }

Эта реализация неплохо работает с GIF до 12 мегабайт, и без лагов при скролле списков.

Спасибо за чтение!

Денис Попков

Middle Android разработчик в «Black Bricks»

Если вы нашли неточности/ошибки в статье или просто хотите дополнить её своим мнением — то прошу в комментарии! Или можете написать мне в Telegram.


ссылка на оригинал статьи https://habr.com/ru/articles/841634/