Is it possible to get the new ImageDecoder class to return Bitmaps, one frame after another, manually?
up vote
12
down vote
favorite
Background
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).
The problem
Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .
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.
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.
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.
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.
What I've found
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()
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.
The questions
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 ?
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.
EDIT:
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.
Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.
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.
This question has an open bounty worth +400
reputation from android developer ending ending at 2018-11-14 19:28:12Z">in 4 days.
Looking for an answer drawing from credible and/or official sources.
add a comment |
up vote
12
down vote
favorite
Background
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).
The problem
Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .
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.
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.
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.
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.
What I've found
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()
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.
The questions
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 ?
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.
EDIT:
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.
Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.
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.
This question has an open bounty worth +400
reputation from android developer ending ending at 2018-11-14 19:28:12Z">in 4 days.
Looking for an answer drawing from credible and/or official sources.
1
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
1
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
2
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
1
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31
add a comment |
up vote
12
down vote
favorite
up vote
12
down vote
favorite
Background
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).
The problem
Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .
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.
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.
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.
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.
What I've found
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()
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.
The questions
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 ?
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.
EDIT:
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.
Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.
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.
Background
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).
The problem
Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .
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.
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.
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.
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.
What I've found
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()
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.
The questions
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 ?
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.
EDIT:
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.
Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.
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.
edited 13 hours ago
asked Oct 27 at 17:02
android developer
57k95452829
57k95452829
This question has an open bounty worth +400
reputation from android developer ending ending at 2018-11-14 19:28:12Z">in 4 days.
Looking for an answer drawing from credible and/or official sources.
This question has an open bounty worth +400
reputation from android developer ending ending at 2018-11-14 19:28:12Z">in 4 days.
Looking for an answer drawing from credible and/or official sources.
1
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
1
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
2
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
1
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31
add a comment |
1
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
1
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
2
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
1
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31
1
1
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
1
1
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
2
2
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
1
1
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
accepted
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
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) :
MyAppGlideModule.kt
@GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
Glide.with(this)
.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>?)
if (drawable 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)
val originalCallback = drawable.callback
drawable.setLoopCount(-1)
drawable.callback = object : Drawable.Callback
override fun unscheduleDrawable(who: Drawable, what: Runnable)
originalCallback?.unscheduleDrawable(who, what)
Log.d("AppLog", "unscheduleDrawable")
override fun invalidateDrawable(who: Drawable)
originalCallback?.invalidateDrawable(who)
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable $drawable.frameIndex/$drawable.frameCount")
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long)
originalCallback?.scheduleDrawable(who, what, `when`)
Log.d("AppLog", "scheduleDrawable $`when`")
drawable.start()
)
override fun onStart()
super.onStart()
webpDrawable?.start()
override fun onStop()
super.onStop()
webpDrawable?.stop()
override fun onDestroy()
super.onDestroy()
webpDrawable?.recycle()
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
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.
Here's the code:
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()
add a comment |
up vote
1
down vote
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
the resulting AnimatedImageDrawable has two methods: getNumberOfFrames() and getFrame(int).
eg.
if (drawable is AnimatedImageDrawable)
for (i in 1..drawable.getNumberOfFrames())
val preview = drawable.getFrame(i)
val bitmap = ...
else
/* regular Drawable */
caching the previews might save on image processing - because repeated processing of many images may only drain the battery unnecessarily and affects performance.
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
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) :
MyAppGlideModule.kt
@GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
Glide.with(this)
.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>?)
if (drawable 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)
val originalCallback = drawable.callback
drawable.setLoopCount(-1)
drawable.callback = object : Drawable.Callback
override fun unscheduleDrawable(who: Drawable, what: Runnable)
originalCallback?.unscheduleDrawable(who, what)
Log.d("AppLog", "unscheduleDrawable")
override fun invalidateDrawable(who: Drawable)
originalCallback?.invalidateDrawable(who)
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable $drawable.frameIndex/$drawable.frameCount")
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long)
originalCallback?.scheduleDrawable(who, what, `when`)
Log.d("AppLog", "scheduleDrawable $`when`")
drawable.start()
)
override fun onStart()
super.onStart()
webpDrawable?.start()
override fun onStop()
super.onStop()
webpDrawable?.stop()
override fun onDestroy()
super.onDestroy()
webpDrawable?.recycle()
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
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.
Here's the code:
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()
add a comment |
up vote
1
down vote
accepted
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
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) :
MyAppGlideModule.kt
@GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
Glide.with(this)
.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>?)
if (drawable 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)
val originalCallback = drawable.callback
drawable.setLoopCount(-1)
drawable.callback = object : Drawable.Callback
override fun unscheduleDrawable(who: Drawable, what: Runnable)
originalCallback?.unscheduleDrawable(who, what)
Log.d("AppLog", "unscheduleDrawable")
override fun invalidateDrawable(who: Drawable)
originalCallback?.invalidateDrawable(who)
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable $drawable.frameIndex/$drawable.frameCount")
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long)
originalCallback?.scheduleDrawable(who, what, `when`)
Log.d("AppLog", "scheduleDrawable $`when`")
drawable.start()
)
override fun onStart()
super.onStart()
webpDrawable?.start()
override fun onStop()
super.onStop()
webpDrawable?.stop()
override fun onDestroy()
super.onDestroy()
webpDrawable?.recycle()
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
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.
Here's the code:
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()
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
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) :
MyAppGlideModule.kt
@GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
Glide.with(this)
.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>?)
if (drawable 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)
val originalCallback = drawable.callback
drawable.setLoopCount(-1)
drawable.callback = object : Drawable.Callback
override fun unscheduleDrawable(who: Drawable, what: Runnable)
originalCallback?.unscheduleDrawable(who, what)
Log.d("AppLog", "unscheduleDrawable")
override fun invalidateDrawable(who: Drawable)
originalCallback?.invalidateDrawable(who)
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable $drawable.frameIndex/$drawable.frameCount")
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long)
originalCallback?.scheduleDrawable(who, what, `when`)
Log.d("AppLog", "scheduleDrawable $`when`")
drawable.start()
)
override fun onStart()
super.onStart()
webpDrawable?.start()
override fun onStop()
super.onStop()
webpDrawable?.stop()
override fun onDestroy()
super.onDestroy()
webpDrawable?.recycle()
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
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.
Here's the code:
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()
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
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) :
MyAppGlideModule.kt
@GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.imageView)
Glide.with(this)
.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>?)
if (drawable 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)
val originalCallback = drawable.callback
drawable.setLoopCount(-1)
drawable.callback = object : Drawable.Callback
override fun unscheduleDrawable(who: Drawable, what: Runnable)
originalCallback?.unscheduleDrawable(who, what)
Log.d("AppLog", "unscheduleDrawable")
override fun invalidateDrawable(who: Drawable)
originalCallback?.invalidateDrawable(who)
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable $drawable.frameIndex/$drawable.frameCount")
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long)
originalCallback?.scheduleDrawable(who, what, `when`)
Log.d("AppLog", "scheduleDrawable $`when`")
drawable.start()
)
override fun onStart()
super.onStart()
webpDrawable?.start()
override fun onStop()
super.onStop()
webpDrawable?.stop()
override fun onDestroy()
super.onDestroy()
webpDrawable?.recycle()
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
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.
Here's the code:
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()
edited 17 hours ago
answered yesterday
android developer
57k95452829
57k95452829
add a comment |
add a comment |
up vote
1
down vote
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
the resulting AnimatedImageDrawable has two methods: getNumberOfFrames() and getFrame(int).
eg.
if (drawable is AnimatedImageDrawable)
for (i in 1..drawable.getNumberOfFrames())
val preview = drawable.getFrame(i)
val bitmap = ...
else
/* regular Drawable */
caching the previews might save on image processing - because repeated processing of many images may only drain the battery unnecessarily and affects performance.
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
add a comment |
up vote
1
down vote
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
the resulting AnimatedImageDrawable has two methods: getNumberOfFrames() and getFrame(int).
eg.
if (drawable is AnimatedImageDrawable)
for (i in 1..drawable.getNumberOfFrames())
val preview = drawable.getFrame(i)
val bitmap = ...
else
/* regular Drawable */
caching the previews might save on image processing - because repeated processing of many images may only drain the battery unnecessarily and affects performance.
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
add a comment |
up vote
1
down vote
up vote
1
down vote
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
the resulting AnimatedImageDrawable has two methods: getNumberOfFrames() and getFrame(int).
eg.
if (drawable is AnimatedImageDrawable)
for (i in 1..drawable.getNumberOfFrames())
val preview = drawable.getFrame(i)
val bitmap = ...
else
/* regular Drawable */
caching the previews might save on image processing - because repeated processing of many images may only drain the battery unnecessarily and affects performance.
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
the resulting AnimatedImageDrawable has two methods: getNumberOfFrames() and getFrame(int).
eg.
if (drawable is AnimatedImageDrawable)
for (i in 1..drawable.getNumberOfFrames())
val preview = drawable.getFrame(i)
val bitmap = ...
else
/* regular Drawable */
caching the previews might save on image processing - because repeated processing of many images may only drain the battery unnecessarily and affects performance.
edited 12 hours ago
answered 12 hours ago
Martin Zeitler
11k33559
11k33559
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
add a comment |
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
I see. I thought this information is available before full decoding. Since I've already found the main solutions, yet I still have a bounty to grant, I'd like to ask if you know some other things: How to skip to specific frame and start animation from it? How to know for how long will a frame be needed to be shown and then get the next? Bonus: How to check if the animation has transparency or not (they have color space, but not sure if this helps) ? For now you get +1 for what you've found.
– android developer
10 hours ago
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53024304%2fis-it-possible-to-get-the-new-imagedecoder-class-to-return-bitmaps-one-frame-af%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
I`ve got a solution for GIFs, but unfortunately it`s implemented as a C header. Don`t know how hard it would be to port to Java.
– hidefromkgb
Oct 30 at 18:58
1
@hidefromkgb If you don't like Java, you can use Kotlin instead :) . Google has shown that it's now easier to use C/C++ from Java/Kotlin world. I suggest you to try...
– android developer
Oct 30 at 19:00
2
Well, OK. This is what I was talking about. Low-level low-levelness all along.
– hidefromkgb
Oct 30 at 19:02
1
@hidefromkgb Nice. Maybe you could look at the links I've provided then. They have implementation for both animated GIF and animated WEBP.
– android developer
Oct 30 at 19:31