Add option to choose animation drawable bitmap format.

Add a forgotten inputstream.close()
This commit is contained in:
oupson 2020-05-06 16:58:31 +02:00
parent 7657c0e505
commit 4cb330d522
1 changed files with 69 additions and 33 deletions

View File

@ -51,13 +51,15 @@ class ApngDecoder {
/** /**
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context needed for the animation drawable
* @param inStream Input Stream to decode. Will be close at the end. * @param inStream Input Stream to decode. Will be close at the end.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun decodeApng(context: Context, inStream: InputStream, speed: Float = 1f): Drawable { fun decodeApng(context: Context, inStream: InputStream, speed: Float = 1f, config : Bitmap.Config = Bitmap.Config.ARGB_8888): Drawable {
val inputStream = BufferedInputStream(inStream) val inputStream = BufferedInputStream(inStream)
val bytes = ByteArray(8) val bytes = ByteArray(8)
inputStream.mark(0) inputStream.mark(0)
@ -197,7 +199,11 @@ class ApngDecoder {
drawable.addFrame( drawable.addFrame(
BitmapDrawable( BitmapDrawable(
context.resources, context.resources,
btm if (btm.config != config) {
btm.copy(config, btm.isMutable)
} else {
btm
}
), ),
(delay / speed).toInt() (delay / speed).toInt()
) )
@ -299,7 +305,11 @@ class ApngDecoder {
drawable.addFrame( drawable.addFrame(
BitmapDrawable( BitmapDrawable(
context.resources, context.resources,
btm if (btm.config != config) {
btm.copy(config, btm.isMutable)
} else {
btm
}
), ),
(delay / speed).toInt() (delay / speed).toInt()
) )
@ -451,87 +461,97 @@ class ApngDecoder {
val source = ImageDecoder.createSource(buf) val source = ImageDecoder.createSource(buf)
ImageDecoder.decodeDrawable(source) ImageDecoder.decodeDrawable(source)
} else { } else {
Drawable.createFromStream( val drawable = Drawable.createFromStream(
inputStream, inputStream,
null null
) )
inputStream.close()
drawable
} }
} }
} }
/** /**
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context. * @param context Context needed for animation drawable.
* @param file File to decode. * @param file File to decode.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
fun decodeApng(context: Context, file: File, speed: Float = 1f): Drawable = fun decodeApng(context: Context, file: File, speed: Float = 1f, config : Bitmap.Config = Bitmap.Config.ARGB_8888): Drawable =
decodeApng( decodeApng(
context, context,
FileInputStream(file), speed FileInputStream(file), speed, config
) )
/** /**
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver. * @param context Context is needed for contentResolver and animation drawable.
* @param uri Uri to open. * @param uri Uri to open.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
fun decodeApng(context: Context, uri: Uri, speed: Float = 1f): Drawable { fun decodeApng(context: Context, uri: Uri, speed: Float = 1f, config : Bitmap.Config = Bitmap.Config.ARGB_8888): Drawable {
val inputStream = context.contentResolver.openInputStream(uri) val inputStream = context.contentResolver.openInputStream(uri)
?: throw Exception("Failed to open InputStream, InputStream is null") ?: throw Exception("Failed to open InputStream, InputStream is null")
return decodeApng( return decodeApng(
context, context,
inputStream, inputStream,
speed speed,
config
) )
} }
/** /**
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver. * @param context Context is needed for contentResolver and animation drawable.
* @param res Resource to decode. * @param res Resource to decode.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
fun decodeApng(context: Context, @RawRes res: Int, speed: Float = 1f): Drawable = fun decodeApng(context: Context, @RawRes res: Int, speed: Float = 1f, config : Bitmap.Config = Bitmap.Config.ARGB_8888): Drawable =
decodeApng( decodeApng(
context, context,
context.resources.openRawResource(res), context.resources.openRawResource(res),
speed speed,
config
) )
/** /**
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver. * @param context Context is needed for contentResolver and animation drawable.
* @param url URL to decode. * @param url URL to decode.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
suspend fun decodeApng(context: Context, url: URL, speed: Float = 1f) = suspend fun decodeApng(context: Context, url: URL, speed: Float = 1f, config : Bitmap.Config = Bitmap.Config.ARGB_8888) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
decodeApng( decodeApng(
context, context,
FileInputStream( FileInputStream(
Loader.load(context, url) Loader.load(context, url)
), ),
speed speed,
config
) )
} }
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context. * @param context Context needed for animation drawable.
* @param file File to decode. * @param file File to decode.
* @param imageView Image View. * @param imageView Image View.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error. * @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
@ -541,7 +561,8 @@ class ApngDecoder {
file: File, file: File,
imageView: ImageView, imageView: ImageView,
speed: Float = 1f, speed: Float = 1f,
callback: Callback? = null callback: Callback? = null,
config : Bitmap.Config = Bitmap.Config.ARGB_8888
) { ) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
try { try {
@ -549,7 +570,8 @@ class ApngDecoder {
decodeApng( decodeApng(
context, context,
FileInputStream(file), FileInputStream(file),
speed speed,
config
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)
@ -569,11 +591,12 @@ class ApngDecoder {
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context. * @param context Context needed for animation drawable and content resolver.
* @param uri Uri to load. * @param uri Uri to load.
* @param imageView Image View. * @param imageView Image View.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error. * @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
@ -583,7 +606,8 @@ class ApngDecoder {
uri: Uri, uri: Uri,
imageView: ImageView, imageView: ImageView,
speed: Float = 1f, speed: Float = 1f,
callback: Callback? = null callback: Callback? = null,
config : Bitmap.Config = Bitmap.Config.ARGB_8888
) { ) {
val inputStream = context.contentResolver.openInputStream(uri) val inputStream = context.contentResolver.openInputStream(uri)
?: throw Exception("Failed to open InputStream, InputStream is null") ?: throw Exception("Failed to open InputStream, InputStream is null")
@ -593,7 +617,8 @@ class ApngDecoder {
decodeApng( decodeApng(
context, context,
inputStream, inputStream,
speed speed,
config
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)
@ -613,11 +638,12 @@ class ApngDecoder {
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context. * @param context Context needed to decode the resource and for the animation drawable.
* @param res Raw resource to load. * @param res Raw resource to load.
* @param imageView Image View. * @param imageView Image View.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error. * @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
@ -626,7 +652,8 @@ class ApngDecoder {
context: Context, @RawRes res: Int, context: Context, @RawRes res: Int,
imageView: ImageView, imageView: ImageView,
speed: Float = 1f, speed: Float = 1f,
callback: Callback? = null callback: Callback? = null,
config : Bitmap.Config = Bitmap.Config.ARGB_8888
) { ) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
try { try {
@ -634,7 +661,8 @@ class ApngDecoder {
decodeApng( decodeApng(
context, context,
context.resources.openRawResource(res), context.resources.openRawResource(res),
speed speed,
config
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)
@ -655,11 +683,12 @@ class ApngDecoder {
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context. * @param context Context needed for the animation drawable.
* @param url URL to load. * @param url URL to load.
* @param imageView Image View. * @param imageView Image View.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error. * @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
@ -669,7 +698,8 @@ class ApngDecoder {
url: URL, url: URL,
imageView: ImageView, imageView: ImageView,
speed: Float = 1f, speed: Float = 1f,
callback: Callback? = null callback: Callback? = null,
config : Bitmap.Config = Bitmap.Config.ARGB_8888
) { ) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
try { try {
@ -682,7 +712,8 @@ class ApngDecoder {
url url
) )
), ),
speed speed,
config
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)
@ -702,11 +733,12 @@ class ApngDecoder {
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context. * @param context Context needed for decoding the image and creating the animation drawable.
* @param string URL to load * @param string URL to load
* @param imageView Image View. * @param imageView Image View.
* @param speed Optional parameter. * @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error. * @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
*/ */
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
@ -716,7 +748,8 @@ class ApngDecoder {
string: String, string: String,
imageView: ImageView, imageView: ImageView,
speed: Float = 1f, speed: Float = 1f,
callback: Callback? = null callback: Callback? = null,
config : Bitmap.Config = Bitmap.Config.ARGB_8888
) { ) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
try { try {
@ -726,7 +759,8 @@ class ApngDecoder {
URL(string), URL(string),
imageView, imageView,
speed, speed,
callback callback,
config
) )
} else if (File(string).exists()) { } else if (File(string).exists()) {
var pathToLoad = var pathToLoad =
@ -737,14 +771,16 @@ class ApngDecoder {
Uri.parse(pathToLoad), Uri.parse(pathToLoad),
imageView, imageView,
speed, speed,
callback callback,
config
) )
} else if (string.startsWith("file://android_asset/")) { } else if (string.startsWith("file://android_asset/")) {
val drawable = val drawable =
decodeApng( decodeApng(
context, context,
context.assets.open(string.replace("file:///android_asset/", "")), context.assets.open(string.replace("file:///android_asset/", "")),
speed speed,
config
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)