diff --git a/apng_library/src/androidTest/java/oupson/apng/ApngDecoderInstrumentedTest.kt b/apng_library/src/androidTest/java/oupson/apng/ApngDecoderInstrumentedTest.kt index 0b333c1..0a5ce5c 100644 --- a/apng_library/src/androidTest/java/oupson/apng/ApngDecoderInstrumentedTest.kt +++ b/apng_library/src/androidTest/java/oupson/apng/ApngDecoderInstrumentedTest.kt @@ -5,6 +5,7 @@ import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.BitmapDrawable import androidx.test.platform.app.InstrumentationRegistry import junit.framework.TestCase +import kotlinx.coroutines.runBlocking import org.junit.Test import oupson.apng.Utils.Companion.areBitmapSimilar import oupson.apng.Utils.Companion.getFrame @@ -16,11 +17,13 @@ class ApngDecoderInstrumentedTest { fun testBtmConfigDecoding() { val context = InstrumentationRegistry.getInstrumentation().context val input = context.assets.open("sushi.png") - val anim = ApngDecoder.decodeApng( - context, - input, - ApngDecoder.Config(bitmapConfig = Bitmap.Config.RGB_565) - ) as AnimationDrawable + val anim = runBlocking { + ApngDecoder.decodeApng( + context, + input, + ApngDecoder.Config(bitmapConfig = Bitmap.Config.RGB_565) + ) as AnimationDrawable + } for (i in 0 until anim.numberOfFrames) { TestCase.assertTrue((anim.getFrame(i) as BitmapDrawable).bitmap.config == Bitmap.Config.RGB_565) @@ -33,11 +36,13 @@ class ApngDecoderInstrumentedTest { val list = context.assets.list("bunny")?.map { getFrame(context, "bunny/$it") }!! val input = context.assets.open("bugbuckbunny.png") - val anim = ApngDecoder.decodeApng( - context, - input, - ApngDecoder.Config(bitmapConfig = Bitmap.Config.ARGB_8888, decodeCoverFrame = true) - ) as ApngDrawable + val anim = runBlocking { + ApngDecoder.decodeApng( + context, + input, + ApngDecoder.Config(bitmapConfig = Bitmap.Config.ARGB_8888, decodeCoverFrame = true) + ) as ApngDrawable + } TestCase.assertTrue(areBitmapSimilar(list[0], anim.coverFrame!!)) for (i in 0 until anim.numberOfFrames) { diff --git a/apng_library/src/androidTest/java/oupson/apng/ApngEncoderInstrumentedTest.kt b/apng_library/src/androidTest/java/oupson/apng/ApngEncoderInstrumentedTest.kt index 5caa031..3e0fc6a 100644 --- a/apng_library/src/androidTest/java/oupson/apng/ApngEncoderInstrumentedTest.kt +++ b/apng_library/src/androidTest/java/oupson/apng/ApngEncoderInstrumentedTest.kt @@ -5,6 +5,7 @@ import android.graphics.drawable.BitmapDrawable import androidx.test.platform.app.InstrumentationRegistry import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue +import kotlinx.coroutines.runBlocking import org.junit.Test import oupson.apng.Utils.Companion.areBitmapSimilar import oupson.apng.Utils.Companion.getFrame @@ -57,8 +58,9 @@ class ApngEncoderInstrumentedTest { val list = context.assets.list("ball")?.map { getFrame(context, "ball/$it") }!! val optimisedOutputStream = ByteArrayOutputStream() - val optimisedEncoder = ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size) - .setOptimiseApng(true) + val optimisedEncoder = + ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size) + .setOptimiseApng(true) list.forEach { optimisedEncoder.writeFrame(it) @@ -70,8 +72,14 @@ class ApngEncoderInstrumentedTest { val bytes = optimisedOutputStream.toByteArray() val optimisedInputStream = ByteArrayInputStream(bytes) + val optimisedApng = - ApngDecoder.decodeApng(context, optimisedInputStream) as AnimationDrawable + runBlocking { + ApngDecoder.decodeApng( + context, + optimisedInputStream + ) as AnimationDrawable + } optimisedInputStream.close() @@ -86,8 +94,9 @@ class ApngEncoderInstrumentedTest { val nonOptimisedOutputStream = ByteArrayOutputStream() - val nonOptimisedEncoder = ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size) - .setOptimiseApng(false) + val nonOptimisedEncoder = + ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size) + .setOptimiseApng(false) list.forEach { nonOptimisedEncoder.writeFrame(it) @@ -101,7 +110,12 @@ class ApngEncoderInstrumentedTest { val nonOptimisedInputStream = ByteArrayInputStream(nonOptimisedBytes) val nonOptimisedApng = - ApngDecoder.decodeApng(context, nonOptimisedInputStream) as AnimationDrawable + runBlocking { + ApngDecoder.decodeApng( + context, + nonOptimisedInputStream + ) as AnimationDrawable + } nonOptimisedInputStream.close() for (i in 0 until optimisedApng.numberOfFrames) { @@ -120,8 +134,9 @@ class ApngEncoderInstrumentedTest { val list = context.assets.list("bunny")?.map { getFrame(context, "bunny/$it") }!! val optimisedOutputStream = ByteArrayOutputStream() - val optimisedEncoder = ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size) - .setOptimiseApng(true) + val optimisedEncoder = + ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size) + .setOptimiseApng(true) list.forEach { optimisedEncoder.writeFrame(it) @@ -134,7 +149,12 @@ class ApngEncoderInstrumentedTest { val optimisedInputStream = ByteArrayInputStream(bytes) val optimisedApng = - ApngDecoder.decodeApng(context, optimisedInputStream) as AnimationDrawable + runBlocking { + ApngDecoder.decodeApng( + context, + optimisedInputStream + ) as AnimationDrawable + } optimisedInputStream.close() @@ -149,8 +169,9 @@ class ApngEncoderInstrumentedTest { val nonOptimisedOutputStream = ByteArrayOutputStream() - val nonOptimisedEncoder = ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size) - .setOptimiseApng(false) + val nonOptimisedEncoder = + ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size) + .setOptimiseApng(false) list.forEach { nonOptimisedEncoder.writeFrame(it) @@ -164,7 +185,12 @@ class ApngEncoderInstrumentedTest { val nonOptimisedInputStream = ByteArrayInputStream(nonOptimisedBytes) val nonOptimisedApng = - ApngDecoder.decodeApng(context, nonOptimisedInputStream) as AnimationDrawable + runBlocking { + ApngDecoder.decodeApng( + context, + nonOptimisedInputStream + ) as AnimationDrawable + } nonOptimisedInputStream.close() for (i in 0 until optimisedApng.numberOfFrames) { diff --git a/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt b/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt index 17984a3..bf08a56 100644 --- a/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt +++ b/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt @@ -49,22 +49,23 @@ class ApngDecoder { internal var bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888, internal var decodeCoverFrame: Boolean = false ) { - fun getSpeed() : Float = this.speed - fun setSpeed(speed : Float) : Config { + fun getSpeed(): Float = this.speed + fun setSpeed(speed: Float): Config { this.speed = speed return this } - fun getBitmapConfig() : Bitmap.Config = this.bitmapConfig - fun setBitmapConfig(config : Bitmap.Config) : Config { + fun getBitmapConfig(): Bitmap.Config = this.bitmapConfig + fun setBitmapConfig(config: Bitmap.Config): Config { this.bitmapConfig = config return this } - fun isDecodingCoverFrame() : Boolean { + fun isDecodingCoverFrame(): Boolean { return this.decodeCoverFrame } - fun setIsDecodingCoverFrame(decodeCoverFrame : Boolean) : Config { + + fun setIsDecodingCoverFrame(decodeCoverFrame: Boolean): Config { this.decodeCoverFrame = decodeCoverFrame return this } @@ -91,7 +92,10 @@ class ApngDecoder { * @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. If it is not an animated image, it is a [Drawable]. */ // TODO DOCUMENT CONFIG - @Suppress("MemberVisibilityCanBePrivate") + @Suppress( + "MemberVisibilityCanBePrivate", + "BlockingMethodInNonBlockingContext" + ) // BlockingMethodInNonBlockingContext is a warning generated by java @Throws @JvmStatic @JvmOverloads suspend fun decodeApng( @@ -133,23 +137,23 @@ class ApngDecoder { var byteRead: Int val lengthChunk = ByteArray(4) do { - val length : Int - val chunk : ByteArray + val length: Int + val chunk: ByteArray if (withContext(Dispatchers.IO) { - byteRead = inputStream.read(lengthChunk) + byteRead = inputStream.read(lengthChunk) - if (byteRead != -1) { - length = Utils.uIntFromBytesBigEndian(lengthChunk) + if (byteRead != -1) { + length = Utils.uIntFromBytesBigEndian(lengthChunk) - chunk = ByteArray(length + 8) - byteRead = inputStream.read(chunk) - false - } else { - chunk = ByteArray(0) - true - } - }) { + chunk = ByteArray(length + 8) + byteRead = inputStream.read(chunk) + false + } else { + chunk = ByteArray(0) + true + } + }) { break } @@ -534,7 +538,7 @@ class ApngDecoder { inputStream.reset() return@withContext if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val bytesRead : ByteArray + val bytesRead: ByteArray withContext(Dispatchers.IO) { bytesRead = inputStream.readBytes() inputStream.close() @@ -564,7 +568,7 @@ class ApngDecoder { * @param config Decoder configuration * @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. If it is not an animated image, it is a [Drawable]. */ - @Suppress("unused") + @Suppress("unused", "BlockingMethodInNonBlockingContext") @JvmStatic // TODO DOCUMENT suspend fun decodeApng( @@ -574,7 +578,7 @@ class ApngDecoder { ): Drawable = decodeApng( context, - FileInputStream(file), config + withContext(Dispatchers.IO) { FileInputStream(file) }, config ) /** @@ -649,7 +653,7 @@ class ApngDecoder { * @param callback [ApngDecoder.Callback] to handle success and error. * @param config Decoder configuration */ - @Suppress("unused") + @Suppress("unused", "BlockingMethodInNonBlockingContext") @JvmStatic @JvmOverloads fun decodeApngAsyncInto( @@ -658,7 +662,7 @@ class ApngDecoder { imageView: ImageView, callback: Callback? = null, config: Config = Config(), - scope : CoroutineScope = GlobalScope + scope: CoroutineScope = GlobalScope ) { scope.launch(Dispatchers.Default) { try { @@ -666,8 +670,8 @@ class ApngDecoder { decodeApng( context, withContext(Dispatchers.IO) { - FileInputStream(file) - }, + FileInputStream(file) + }, config ) withContext(Dispatchers.Main) { @@ -703,7 +707,7 @@ class ApngDecoder { imageView: ImageView, callback: Callback? = null, config: Config = Config(), - scope : CoroutineScope = GlobalScope + scope: CoroutineScope = GlobalScope ) { val inputStream = context.contentResolver.openInputStream(uri)!! scope.launch(Dispatchers.Default) { @@ -746,7 +750,7 @@ class ApngDecoder { imageView: ImageView, callback: Callback? = null, config: Config = Config(), - scope : CoroutineScope = GlobalScope + scope: CoroutineScope = GlobalScope ) { scope.launch(Dispatchers.Default) { try { @@ -781,7 +785,7 @@ class ApngDecoder { * @param callback [ApngDecoder.Callback] to handle success and error. * @param config Decoder configuration */ - @Suppress("unused") + @Suppress("unused", "BlockingMethodInNonBlockingContext") @JvmStatic @JvmOverloads fun decodeApngAsyncInto( @@ -790,7 +794,7 @@ class ApngDecoder { imageView: ImageView, callback: Callback? = null, config: Config = Config(), - scope : CoroutineScope = GlobalScope + scope: CoroutineScope = GlobalScope ) { scope.launch(Dispatchers.Default) { try { @@ -836,9 +840,9 @@ class ApngDecoder { imageView: ImageView, callback: Callback? = null, config: Config = Config(), - scope : CoroutineScope = GlobalScope + scope: CoroutineScope = GlobalScope ) { - scope.launch(Dispatchers.Default) { + scope.launch(Dispatchers.Default) { try { if (string.startsWith("http://") || string.startsWith("https://")) { decodeApngAsyncInto( @@ -877,7 +881,7 @@ class ApngDecoder { } } else { withContext(Dispatchers.Main) { - callback?.onError(java.lang.Exception("Cannot open string")) + callback?.onError(Exception("Cannot open string")) } } } catch (e: Exception) {