是否可以获取新的ImageDecoder类以手动返回一帧又一帧的位图? [英] Is it possible to get the new ImageDecoder class to return Bitmaps, one frame after another, manually?

查看:98
本文介绍了是否可以获取新的ImageDecoder类以手动返回一帧又一帧的位图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试手动(逐帧)浏览GIF和WEBP动画文件的位图,以便它不仅适用于视图",而且还适用于其他情况(例如动态壁纸).

I'm trying to go over bitmaps of animated GIF&WEBP files manually (frame by frame), so that it would work not just for Views, but on other cases too (such as a live wallpaper).

只有使用ImageDecoder API的Android P支持动画GIF/WEBP文件(示例

Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .

对于GIF,我想尝试Glide来完成任务,但是失败了,所以我尝试通过使用允许加载它们的库来克服这一问题( 这里 ).我认为效果很好.

For GIF, I wanted to try Glide for the task, but I've failed, so I've tried overcoming this, by using a library that allows to load them (here, solution here). I think it works fine.

对于WebP,我认为我已经找到了另一个可以在较旧的Android版本上运行的库( 这里 ,将叉子做成> 这里 ),但在某些情况下似乎无法很好地处理WebP文件(报告 此处 ).我试图找出问题所在以及解决方法,但没有成功.

For WebP, I thought I've found another library that could work on older Android versions (here, made fork here), but it seems that it can't handle WebP files well in some cases (reported here). I tried to figure out what's the issue and how to solve it, but I didn't succeed.

因此,假设有一天Google将通过支持库支持较早的Android版本的GIF和WEBP动画(他们将其写为 此处 ),我决定尝试使用ImageDecoder来完成该任务.

So, assuming that some day Google will support GIF&WEBP animation for older Android versions via the support library (they wrote it here), I've decided to try to use ImageDecoder for the task.

在ImageDecoder的整个API中寻找东西,在如何使用它方面有很大的限制.我看不出如何克服其局限性.

Thing is, looking in the entire API of ImageDecoder , it's quite restricted in how we should use it. I don't see how I can overcome its limitations.

这是ImageDecoder可用于在ImageView上显示动画WebP的方式(当然,只是示例,

This is how ImageDecoder can be used to show an animated WebP on an ImageView (just a sample, of course, available here) :

class MainActivity : AppCompatActivity() {
    @SuppressLint("StaticFieldLeak")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val source = ImageDecoder.createSource(resources, R.raw.test)
        object : AsyncTask<Void, Void, Drawable?>() {
            override fun doInBackground(vararg params: Void?): Drawable? {
                return try {
                    ImageDecoder.decodeDrawable(source)
                } catch (e: Exception) {
                    null
                }
            }

            override fun onPostExecute(result: Drawable?) {
                super.onPostExecute(result)
                imageView.setImageDrawable(result)
                if (result is AnimatedImageDrawable) {
                    result.start()
                }
            }

        }.execute()

    }
}

我尝试阅读 ImageDecoder 的所有文档 strong> AnimatedImageDrawable ,并查看其代码,但我看不到如何手动遍历每个帧,并且在它们之间需要等待时间.

I've tried to read all of the documentations of ImageDecoder and AnimatedImageDrawable, and also look at its code, but I don't see how it's possible to manually go over each frame, and have the time that needs to be waited between them.

  1. 是否可以使用ImageDecoder API手动遍历每个帧,绘制位图并知道在帧之间等待需要多少时间?有任何解决方法?甚至甚至可以使用AnimatedImageDrawable吗?

  1. Is there a way to use ImageDecoder API to go over each frame manually, getting a Bitmap to draw and knowing how much time it's needed to wait between frames? Any workaround available? Maybe even using AnimatedImageDrawable ?

我想在较旧的Android版本上执行相同的操作.是否有可能?如果可以,怎么办?也许在不同的API/库上? Google编写了一种在较旧的Android版本上使用ImageDecoder的方式,但是我看不到它在任何地方都被提及(我提供的链接除外).可能尚未准备好... Android P甚至还没有达到0.1%的用户...也许 壁画 可以做到吗?我也尝试过检查它,但是我也看不到它有能力做到这一点,并且它是一个庞大的库,仅用于此任务,因此我宁愿使用其他库.我也知道libwebp可用,但是它在C/C ++中,并且不确定它是否适用于Android,以及不确定Java/Kotlin上是否有用于Android的端口.

I'd like to do the same on older Android versions. Is it possible? If so how? Maybe on a different API/library? Google wrote it works on a way to use ImageDecoder on older Android versions, but I don't see it being mentioned anywhere (except for the link I've provided). Probably not ready yet... Android P didn't even reach 0.1% of users yet... Maybe Fresco can do it? I've tried to check it there too, but I don't see that it's capable of such a thing either, and it's a huge library to use just for this task, so I'd prefer to use a different library instead... I also know that libwebp is available, but it's in C/C++ and not sure if it's suited for Android, and whether there is a port for it on Java/Kotlin for Android.



因为我想我已经有了想要的东西,无论是对于第三方库还是对于ImageDecoder,都能够从动画WebP中获取位图,所以我仍然想知道如何使用如果可能,请使用ImageDecoder.我尝试使用ImageDecoder.decodeDrawable(source, object : ImageDecoder.OnHeaderDecodedListener...,但是它不提供帧计数信息,并且在API中我无法看到我可以转到特定的帧索引并从那里开始,或者知道某个特定的帧下一帧需要多长时间.因此,我对这些 此处 提出了要求.

Since I think I got what I wanted, for both a third party library and for ImageDecoder, to be able to get bitmaps out of animated WebP, I'd still want to know how to get the frame count and current frame using ImageDecoder, if that's possible. I tried using ImageDecoder.decodeDrawable(source, object : ImageDecoder.OnHeaderDecodedListener... , but it doesn't provide frame count information, and there is no way in the API that I can see that I can go to a specific frame index and start from there, or to know for a specific frame how long it needs to go to the next frame. So I made a reuqest about those here.

可悲的是,我也没有发现Google的ImageDecoder也可用于旧版Android.

Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.

如果有某种方法可以像我对相对较新的HEIC动画文件所做的一样,这也很有趣.目前仅在Android P上受支持.

It's also interesting if there is some kind of way to do the same as I did for the relatively new animation file of HEIC. Currently it's supported only on Android P.

推荐答案

好的,我使用 Glide库 ,以及> GlideWebpDecoder库 .

OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .

我不确定这是否是最好的方法,但是我认为它应该可以正常工作.接下来的代码显示了如何针对动画需要显示的每个帧,将可绘制的绘制放入我创建的Bitmap实例中.这不完全是我的要求,但可能会对其他人有所帮助.

I'm not sure if that's the best way to do it, but I think it should work fine. The next code shows how it's possible to make the drawable draw into the Bitmap instance that I create, for each frame that the animation needs to show. It's not exactly what I asked, but it might help others.

这是代码(项目 此处 ):

Here's the code (project available here) :

CallbackEx.kt

abstract class CallbackEx : Drawable.Callback {
    override fun unscheduleDrawable(who: Drawable, what: Runnable) {}
    override fun invalidateDrawable(who: Drawable) {}
    override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {}
}

MyAppGlideModule.kt

@GlideModule
class MyAppGlideModule : AppGlideModule()

MainActivity.kt

class MainActivity : AppCompatActivity() {
    var webpDrawable: WebpDrawable? = null
    var gifDrawable: GifDrawable? = null
    var callback: Drawable.Callback? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        useFrameByFrameDecoding()
//        useNormalDecoding()
    }

    fun useNormalDecoding() {
        //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
        Glide.with(this)
                //                .load(R.raw.test)
                //                .load(R.raw.fast)
                .load(R.raw.example2)

                //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                .into(object : SimpleTarget<Drawable>() {
                    override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                        imageView.setImageDrawable(drawable)
                        when (drawable) {
                            is GifDrawable -> {
                                drawable.start()
                            }
                            is WebpDrawable -> {
                                drawable.start()
                            }
                        }
                    }
                })
    }

    fun useFrameByFrameDecoding() {
        //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
        Glide.with(this)
                .load(R.raw.test)
                //                .load(R.raw.fast)
                //                .load(R.raw.example2)
                //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                .into(object : SimpleTarget<Drawable>() {
                    override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                        //                        val callback
                        when (drawable) {
                            is GifDrawable -> {
                                gifDrawable = drawable
                                val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                val canvas = Canvas(bitmap)
                                drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                drawable.setLoopCount(GifDrawable.LOOP_FOREVER)
                                callback = object : CallbackEx() {
                                    override fun invalidateDrawable(who: Drawable) {
                                        who.draw(canvas)
                                        imageView.setImageBitmap(bitmap)
                                        Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                    }
                                }
                                drawable.callback = callback
                                drawable.start()
                            }
                            is WebpDrawable -> {
                                webpDrawable = drawable
                                val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                val canvas = Canvas(bitmap)
                                drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                drawable.setLoopCount(WebpDrawable.LOOP_FOREVER)
                                callback = object : CallbackEx() {
                                    override fun invalidateDrawable(who: Drawable) {
                                        who.draw(canvas)
                                        imageView.setImageBitmap(bitmap)
                                        Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                    }
                                }
                                drawable.callback = callback
                                drawable.start()
                            }
                        }
                    }
                })
    }

    override fun onStart() {
        super.onStart()
        gifDrawable?.start()
        gifDrawable?.start()
    }

    override fun onStop() {
        super.onStop()
        Log.d("AppLog", "onStop")
        webpDrawable?.stop()
        gifDrawable?.stop()
    }

}

不确定为什么SimpleTarget被标记为已弃用,不过我应该使用什么代替.

Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.

使用类似的技术,我还发现了如何使用ImageDecoder进行此操作,但是由于某种原因,它们没有相同的功能. 此处 .

Using a similar technique, I've also found out how to do it using ImageDecoder, but not with the same functionality for some reason. A sample project available here.

代码如下:

MainActivity.kt

class MainActivity : AppCompatActivity() {
    var webpDrawable: AnimatedImageDrawable? = null

    @SuppressLint("StaticFieldLeak")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val source = ImageDecoder.createSource(resources, R.raw.test)
        object : AsyncTask<Void, Void, Drawable?>() {
            override fun doInBackground(vararg params: Void?): Drawable? {
                return try {
                    ImageDecoder.decodeDrawable(source)
                } catch (e: Exception) {
                    null
                }
            }

            override fun onPostExecute(drawable: Drawable?) {
                super.onPostExecute(drawable)
//                imageView.setImageDrawable(result)
                if (drawable is AnimatedImageDrawable) {
                    webpDrawable = drawable
                    val bitmap =
                        Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                    val canvas = Canvas(bitmap)
                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                    drawable.repeatCount = AnimatedImageDrawable.REPEAT_INFINITE
                    drawable.callback = object : Drawable.Callback {
                        val handler = Handler()
                        override fun unscheduleDrawable(who: Drawable, what: Runnable) {
                            Log.d("AppLog", "unscheduleDrawable")
                        }

                        override fun invalidateDrawable(who: Drawable) {
                            who.draw(canvas)
                            imageView.setImageBitmap(bitmap)
                            Log.d("AppLog", "invalidateDrawable")
                        }

                        override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
                            Log.d("AppLog", "scheduleDrawable next frame in ${`when` - SystemClock.uptimeMillis()} ms")
                            handler.postAtTime(what, `when`)
                        }
                    }
                    drawable.start()
                }
            }
        }.execute()
    }

    override fun onStart() {
        super.onStart()
        webpDrawable?.start()
    }

    override fun onStop() {
        super.onStop()
        webpDrawable?.stop()
    }

}

这篇关于是否可以获取新的ImageDecoder类以手动返回一帧又一帧的位图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆