From a273e7da0915711aa16de18bc5057c434c557d7f Mon Sep 17 00:00:00 2001 From: oupson Date: Sat, 1 Feb 2020 15:09:35 +0100 Subject: [PATCH] Working on beta --- .idea/caches/build_file_checksums.ser | Bin 596 -> 596 bytes apng_library/build.gradle | 2 +- .../src/main/java/oupson/apng/ApngAnimator.kt | 38 ++- .../src/main/java/oupson/apng/Frame.kt | 2 +- .../src/main/java/oupson/apng/Loader.kt | 8 +- .../java/oupson/apng/decoder/ApngDecoder.kt | 306 +++++++++++++----- .../fragments/ApngDecoderFragment.kt | 2 +- .../apngcreator/fragments/JavaFragment.java | 10 +- .../apngcreator/fragments/KotlinFragment.kt | 13 +- .../src/main/res/layout/activity_viewer.xml | 2 +- .../src/main/res/layout/fragment_kotlin.xml | 20 +- app-test/src/main/res/values/styles.xml | 1 + 12 files changed, 283 insertions(+), 121 deletions(-) diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 7fd441e6e092c60b801152327db751c3ef40203f..26c51b1a93917304a41ce3bc7ba08e836a0f56a7 100644 GIT binary patch delta 36 ucmV+<0Nel61k?nOmj!SE6lC?0oYfHbz9vl$UdP?xacv!j!P{<=fdSJn4iQ-Z delta 36 ucmV+<0Nel61k?nOmj!R(cwXa?oYfG$prUuMj-Abe6j1V+wytfHfdSK8UlTz9 diff --git a/apng_library/build.gradle b/apng_library/build.gradle index 52173da..6d89a74 100644 --- a/apng_library/build.gradle +++ b/apng_library/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 21 targetSdkVersion 28 versionCode 1 - versionName "1.0.10" + versionName "1.0.10-beta1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt index 6efd17b..7f802c4 100644 --- a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt +++ b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.SharedPreferences import android.graphics.* import android.net.Uri +import android.util.Log import android.widget.ImageView import androidx.annotation.RawRes import kotlinx.coroutines.Dispatchers @@ -18,6 +19,8 @@ import oupson.apng.utils.Utils.Companion.isPng import java.io.File import java.net.URL +// TODO REWRITE WITH CALLBACKS + /** * @param file The APNG to load * @param speed The speed of the APNG @@ -270,20 +273,32 @@ class ApngAnimator(private val context: Context?) { GlobalScope.launch(Dispatchers.IO) { this@ApngAnimator.speed = speed // Download PNG - Loader.load(context!!, url).apply { - try { - this@ApngAnimator.load(this, speed, apngAnimatorOptions) - } catch (e : NotPngException) { - if (loadNotApng) { - val bytes = this.readBytes() - GlobalScope.launch(Dispatchers.Main) { - imageView?.scaleType = this@ApngAnimator.scaleType ?: ImageView.ScaleType.FIT_CENTER - imageView?.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.size)) + try { + Loader.load(context!!, url).apply { + try { + this@ApngAnimator.load(this, speed, apngAnimatorOptions) + } catch (e: NotPngException) { + if (loadNotApng) { + val bytes = this.readBytes() + GlobalScope.launch(Dispatchers.Main) { + imageView?.scaleType = + this@ApngAnimator.scaleType ?: ImageView.ScaleType.FIT_CENTER + imageView?.setImageBitmap( + BitmapFactory.decodeByteArray( + bytes, + 0, + bytes.size + ) + ) + } + } else { + throw NotApngException() } - } else { - throw NotApngException() } } + } catch (e : java.lang.Exception) { + if (BuildConfig.DEBUG) + Log.e("ApngAnimator", "Error : $e") } } return this @@ -535,6 +550,7 @@ class ApngAnimator(private val context: Context?) { private fun toAnimationDrawable( generatedFrame : ArrayList ): CustomAnimationDrawable { if (isApng) { return CustomAnimationDrawable().apply { + isOneShot = false for (i in 0 until generatedFrame.size) { addFrame(BitmapDrawable(generatedFrame[i]), ((duration!![i]) / (speed ?: 1f)).toInt()) } diff --git a/apng_library/src/main/java/oupson/apng/Frame.kt b/apng_library/src/main/java/oupson/apng/Frame.kt index bb11a5a..49fe895 100644 --- a/apng_library/src/main/java/oupson/apng/Frame.kt +++ b/apng_library/src/main/java/oupson/apng/Frame.kt @@ -19,7 +19,7 @@ import oupson.apng.utils.Utils.Companion.isPng * @param maxWidth The max width of the APNG * @param maxHeight The max height of the APNG */ -class Frame// Get width and height for image +class Frame // Get width and height for image ( byteArray: ByteArray, delay: Float = 1000f, diff --git a/apng_library/src/main/java/oupson/apng/Loader.kt b/apng_library/src/main/java/oupson/apng/Loader.kt index 870d98e..b07f834 100644 --- a/apng_library/src/main/java/oupson/apng/Loader.kt +++ b/apng_library/src/main/java/oupson/apng/Loader.kt @@ -12,10 +12,10 @@ import java.net.URL class Loader { companion object { /** - * Download file from given url - * @param context Context of app - * @param url Url of the file to download - * @return [ByteArray] of the file + * Download file from given url. + * @param context Context of app. + * @param url Url of the file to download. + * @return [ByteArray] of the file. */ @Throws(IOException::class, java.io.FileNotFoundException::class) suspend fun load(context: Context, url: URL) = 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 2c16be1..6c9f1ee 100644 --- a/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt +++ b/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.* import android.graphics.drawable.AnimatedImageDrawable import android.graphics.drawable.AnimationDrawable +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build @@ -15,7 +16,6 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import oupson.apng.APNGDisassembler -import oupson.apng.BitmapDrawable import oupson.apng.BuildConfig import oupson.apng.Loader import oupson.apng.chunks.IHDR @@ -34,14 +34,14 @@ import java.util.zip.CRC32 class ApngDecoder { interface Callback { - fun onSuccess(drawable : Drawable) - fun onError(error : java.lang.Exception) + fun onSuccess(drawable: Drawable) + fun onError(error: java.lang.Exception) } companion object { private const val TAG = "ApngDecoder" - private val clearPaint : Paint by lazy { + private val clearPaint: Paint by lazy { Paint().apply { xfermode = PorterDuffXfermode( PorterDuff.Mode.CLEAR @@ -50,13 +50,13 @@ 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] - * @param inStream Input Stream to decode. Will be close at the end + * Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable]. + * @param inStream Input Stream to decode. Will be close at the end. * @param speed Optional parameter. */ @Suppress("MemberVisibilityCanBePrivate") @JvmStatic - fun decodeApng(inStream: InputStream, speed : Float = 1f) : Drawable { + fun decodeApng(context: Context, inStream: InputStream, speed: Float = 1f): Drawable { val inputStream = BufferedInputStream(inStream) val bytes = ByteArray(8) inputStream.mark(0) @@ -72,7 +72,8 @@ class ApngDecoder { var maxWidth = 0 var maxHeight = 0 var blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE - var disposeOp: Utils.Companion.DisposeOp = Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE + var disposeOp: Utils.Companion.DisposeOp = + Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE val ihdr = IHDR() var isApng = false @@ -80,7 +81,7 @@ class ApngDecoder { isOneShot = false } - var buffer : Bitmap? = null + var buffer: Bitmap? = null var byteRead: Int val lengthChunk = ByteArray(4) @@ -114,7 +115,11 @@ class ApngDecoder { crC32.update(iend, 0, iend.size) it.addAll(iend.asList()) it.addAll(Utils.to4Bytes(crC32.value.toInt()).asList()) - APNGDisassembler.apng.cover = BitmapFactory.decodeByteArray(it.toByteArray(), 0, it.size) + APNGDisassembler.apng.cover = BitmapFactory.decodeByteArray( + it.toByteArray(), + 0, + it.size + ) } png = ArrayList() val fcTL = fcTL() @@ -139,7 +144,8 @@ class ApngDecoder { ihdr, width, height - ).asList()) + ).asList() + ) plte?.let { png?.addAll(it.asList()) } @@ -157,34 +163,63 @@ class ApngDecoder { png.addAll(iend.asList()) png.addAll(Utils.to4Bytes(crC32.value.toInt()).asList()) - val btm = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888) - val decoded = BitmapFactory.decodeByteArray(png.toByteArray(), 0, png.size) + val btm = Bitmap.createBitmap( + maxWidth, + maxHeight, + Bitmap.Config.ARGB_8888 + ) + val decoded = BitmapFactory.decodeByteArray( + png.toByteArray(), + 0, + png.size + ) val canvas = Canvas(btm) canvas.drawBitmap(buffer!!, 0f, 0f, null) if (blendOp == Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE) { - canvas.drawRect(xOffset.toFloat(), yOffset.toFloat(), xOffset+ decoded.width.toFloat(), yOffset + decoded.height.toFloat(), + canvas.drawRect( + xOffset.toFloat(), + yOffset.toFloat(), + xOffset + decoded.width.toFloat(), + yOffset + decoded.height.toFloat(), clearPaint ) } - canvas.drawBitmap(decoded, xOffset.toFloat(), yOffset.toFloat(), null) + canvas.drawBitmap( + decoded, + xOffset.toFloat(), + yOffset.toFloat(), + null + ) + drawable.addFrame( BitmapDrawable( + context.resources, btm - ), (delay / speed).toInt()) + ), + (delay / speed).toInt() + ) - when(disposeOp) { + when (disposeOp) { Utils.Companion.DisposeOp.APNG_DISPOSE_OP_PREVIOUS -> { //Do nothings } // Add current frame to bitmap buffer // APNG_DISPOSE_OP_BACKGROUND: the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame. Utils.Companion.DisposeOp.APNG_DISPOSE_OP_BACKGROUND -> { - val res = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888) + val res = Bitmap.createBitmap( + maxWidth, + maxHeight, + Bitmap.Config.ARGB_8888 + ) val can = Canvas(res) can.drawBitmap(btm, 0f, 0f, null) - can.drawRect(xOffset.toFloat(), yOffset.toFloat(), xOffset + decoded.width.toFloat(), yOffset + decoded.height.toFloat(), + can.drawRect( + xOffset.toFloat(), + yOffset.toFloat(), + xOffset + decoded.width.toFloat(), + yOffset + decoded.height.toFloat(), clearPaint ) buffer = res @@ -209,7 +244,8 @@ class ApngDecoder { ihdr, width, height - ).asList()) + ).asList() + ) plte?.let { png.addAll(it.asList()) } @@ -230,34 +266,62 @@ class ApngDecoder { png.addAll(Utils.to4Bytes(crC32.value.toInt()).asList()) - val btm = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888) - val decoded = BitmapFactory.decodeByteArray(png.toByteArray(), 0, png.size) + val btm = Bitmap.createBitmap( + maxWidth, + maxHeight, + Bitmap.Config.ARGB_8888 + ) + val decoded = BitmapFactory.decodeByteArray( + png.toByteArray(), + 0, + png.size + ) val canvas = Canvas(btm) canvas.drawBitmap(buffer!!, 0f, 0f, null) if (blendOp == Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE) { - canvas.drawRect(xOffset.toFloat(), yOffset.toFloat(), xOffset+ decoded.width.toFloat(), yOffset + decoded.height.toFloat(), + canvas.drawRect( + xOffset.toFloat(), + yOffset.toFloat(), + xOffset + decoded.width.toFloat(), + yOffset + decoded.height.toFloat(), clearPaint ) } - canvas.drawBitmap(decoded, xOffset.toFloat(), yOffset.toFloat(), null) + canvas.drawBitmap( + decoded, + xOffset.toFloat(), + yOffset.toFloat(), + null + ) drawable.addFrame( BitmapDrawable( + context.resources, btm - ), (delay / speed).toInt()) + ), + (delay / speed).toInt() + ) - when(disposeOp) { + when (disposeOp) { Utils.Companion.DisposeOp.APNG_DISPOSE_OP_PREVIOUS -> { //Do nothings } // Add current frame to bitmap buffer // APNG_DISPOSE_OP_BACKGROUND: the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame. Utils.Companion.DisposeOp.APNG_DISPOSE_OP_BACKGROUND -> { - val res = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888) + val res = Bitmap.createBitmap( + maxWidth, + maxHeight, + Bitmap.Config.ARGB_8888 + ) val can = Canvas(res) can.drawBitmap(btm, 0f, 0f, null) - can.drawRect(xOffset.toFloat(), yOffset.toFloat(), xOffset + decoded.width.toFloat(), yOffset + decoded.height.toFloat(), + can.drawRect( + xOffset.toFloat(), + yOffset.toFloat(), + xOffset + decoded.width.toFloat(), + yOffset + decoded.height.toFloat(), clearPaint ) buffer = res @@ -277,6 +341,7 @@ class ApngDecoder { it.addAll(Utils.to4Bytes(crC32.value.toInt()).asList()) inputStream.close() return BitmapDrawable( + context.resources, BitmapFactory.decodeByteArray( it.toByteArray(), 0, @@ -296,27 +361,40 @@ class ApngDecoder { ihdr, maxWidth, maxHeight - ).asList()) + ).asList() + ) } // Find the chunk length - val bodySize = Utils.parseLength(byteArray.copyOfRange(i - 4, i)) + val bodySize = + Utils.parseLength(byteArray.copyOfRange(i - 4, i)) cover.addAll(byteArray.copyOfRange(i - 4, i).asList()) val body = ArrayList() body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).asList()) // Get image bytes - body.addAll(byteArray.copyOfRange(i + 4, i + 4 + bodySize).asList()) + body.addAll( + byteArray.copyOfRange( + i + 4, + i + 4 + bodySize + ).asList() + ) val crC32 = CRC32() crC32.update(body.toByteArray(), 0, body.size) cover.addAll(body) cover.addAll(Utils.to4Bytes(crC32.value.toInt()).asList()) } else { // Find the chunk length - val bodySize = Utils.parseLength(byteArray.copyOfRange(i - 4, i)) + val bodySize = + Utils.parseLength(byteArray.copyOfRange(i - 4, i)) png.addAll(byteArray.copyOfRange(i - 4, i).asList()) val body = ArrayList() body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).asList()) // Get image bytes - body.addAll(byteArray.copyOfRange(i + 4, i + 4 + bodySize).asList()) + body.addAll( + byteArray.copyOfRange( + i + 4, + i + 4 + bodySize + ).asList() + ) val crC32 = CRC32() crC32.update(body.toByteArray(), 0, body.size) png.addAll(body) @@ -345,8 +423,12 @@ class ApngDecoder { name.contentEquals(Utils.IHDR) -> { ihdr.parse(byteArray) maxWidth = ihdr.pngWidth - maxHeight =ihdr.pngHeight - buffer = Bitmap.createBitmap(maxWidth, maxHeight, Bitmap.Config.ARGB_8888) + maxHeight = ihdr.pngHeight + buffer = Bitmap.createBitmap( + maxWidth, + maxHeight, + Bitmap.Config.ARGB_8888 + ) } name.contentEquals(Utils.acTL) -> { isApng = true @@ -378,28 +460,31 @@ 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]. + * @param context Context. * @param file File to decode. * @param speed Optional parameter. */ @Suppress("unused") @JvmStatic - fun decodeApng(file : File, speed: Float = 1f) : Drawable = + fun decodeApng(context: Context, file: File, speed: Float = 1f): Drawable = decodeApng( + context, FileInputStream(file), speed ) /** * 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. * @param uri Uri to open. * @param speed Optional parameter. */ @Suppress("unused") @JvmStatic - fun decodeApng(context : Context, uri : Uri, speed: Float = 1f) : Drawable { + fun decodeApng(context: Context, uri: Uri, speed: Float = 1f): Drawable { val inputStream = context.contentResolver.openInputStream(uri) ?: throw Exception("Failed to open InputStream, InputStream is null") return decodeApng( + context, inputStream, speed ) @@ -407,48 +492,61 @@ 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]. - * @param context Context is needed for contentResolver + * @param context Context is needed for contentResolver. * @param res Resource to decode. * @param speed Optional parameter. */ @Suppress("unused") @JvmStatic - fun decodeApng(context : Context, @RawRes res : Int, speed: Float = 1f) : Drawable = + fun decodeApng(context: Context, @RawRes res: Int, speed: Float = 1f): Drawable = decodeApng( - context.resources.openRawResource(res), speed + context, + context.resources.openRawResource(res), + speed ) /** * 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. * @param url URL to decode. * @param speed Optional parameter. */ @Suppress("unused") @JvmStatic - suspend fun decodeApng(context : Context, url : URL, speed: Float = 1f) = withContext(Dispatchers.IO) { - decodeApng( - FileInputStream( - Loader.load(context, url) - ), speed - ) - } + suspend fun decodeApng(context: Context, url: URL, speed: Float = 1f) = + withContext(Dispatchers.IO) { + decodeApng( + context, + FileInputStream( + Loader.load(context, url) + ), + speed + ) + } /** - * Load Apng into an imageView, asynchronously + * Load Apng into an imageView, asynchronously. + * @param context Context. * @param file File to decode. * @param imageView Image View. * @param speed Optional parameter. - * @param callback [ApngDecoder.Callback] to handle success and error + * @param callback [ApngDecoder.Callback] to handle success and error. */ @Suppress("unused") @JvmStatic @JvmOverloads - fun decodeApngAsyncInto(file : File, imageView : ImageView, speed: Float = 1f, callback: Callback? = null) { + fun decodeApngAsyncInto( + context: Context, + file: File, + imageView: ImageView, + speed: Float = 1f, + callback: Callback? = null + ) { GlobalScope.launch(Dispatchers.IO) { try { val drawable = decodeApng( + context, FileInputStream(file), speed ) @@ -460,7 +558,7 @@ class ApngDecoder { } callback?.onSuccess(drawable) } - } catch (e : java.lang.Exception) { + } catch (e: Exception) { withContext(Dispatchers.Main) { callback?.onError(e) } @@ -469,22 +567,30 @@ class ApngDecoder { } /** - * Load Apng into an imageView, asynchronously - * @param context Context - * @param uri Uri to load + * Load Apng into an imageView, asynchronously. + * @param context Context. + * @param uri Uri to load. * @param imageView Image View. * @param speed Optional parameter. - * @param callback [ApngDecoder.Callback] to handle success and error + * @param callback [ApngDecoder.Callback] to handle success and error. */ @Suppress("unused") @JvmStatic @JvmOverloads - fun decodeApngAsyncInto(context : Context, uri : Uri, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) { - val inputStream = context.contentResolver.openInputStream(uri) ?: throw Exception("Failed to open InputStream, InputStream is null") + fun decodeApngAsyncInto( + context: Context, + uri: Uri, + imageView: ImageView, + speed: Float = 1f, + callback: Callback? = null + ) { + val inputStream = context.contentResolver.openInputStream(uri) + ?: throw Exception("Failed to open InputStream, InputStream is null") GlobalScope.launch(Dispatchers.IO) { try { val drawable = decodeApng( + context, inputStream, speed ) @@ -496,7 +602,7 @@ class ApngDecoder { } callback?.onSuccess(drawable) } - } catch (e : java.lang.Exception) { + } catch (e: Exception) { withContext(Dispatchers.Main) { callback?.onError(e) } @@ -505,21 +611,27 @@ class ApngDecoder { } /** - * Load Apng into an imageView, asynchronously - * @param context Context - * @param res Raw resource to load + * Load Apng into an imageView, asynchronously. + * @param context Context. + * @param res Raw resource to load. * @param imageView Image View. * @param speed Optional parameter. - * @param callback [ApngDecoder.Callback] to handle success and error + * @param callback [ApngDecoder.Callback] to handle success and error. */ @Suppress("unused") @JvmStatic @JvmOverloads - fun decodeApngAsyncInto(context : Context, @RawRes res : Int, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) { + fun decodeApngAsyncInto( + context: Context, @RawRes res: Int, + imageView: ImageView, + speed: Float = 1f, + callback: Callback? = null + ) { GlobalScope.launch(Dispatchers.IO) { try { val drawable = decodeApng( + context, context.resources.openRawResource(res), speed ) @@ -531,7 +643,7 @@ class ApngDecoder { } callback?.onSuccess(drawable) } - } catch (e : java.lang.Exception) { + } catch (e: Exception) { withContext(Dispatchers.Main) { callback?.onError(e) } @@ -541,21 +653,28 @@ class ApngDecoder { } /** - * Load Apng into an imageView, asynchronously - * @param context Context - * @param url URL to load + * Load Apng into an imageView, asynchronously. + * @param context Context. + * @param url URL to load. * @param imageView Image View. * @param speed Optional parameter. - * @param callback [ApngDecoder.Callback] to handle success and error + * @param callback [ApngDecoder.Callback] to handle success and error. */ @Suppress("unused") @JvmStatic @JvmOverloads - fun decodeApngAsyncInto(context : Context, url : URL, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) { + fun decodeApngAsyncInto( + context: Context, + url: URL, + imageView: ImageView, + speed: Float = 1f, + callback: Callback? = null + ) { GlobalScope.launch(Dispatchers.IO) { try { val drawable = decodeApng( + context, FileInputStream( Loader.load( context, @@ -572,7 +691,7 @@ class ApngDecoder { } callback?.onSuccess(drawable) } - } catch (e : java.lang.Exception) { + } catch (e: Exception) { withContext(Dispatchers.Main) { callback?.onError(e) } @@ -581,17 +700,23 @@ class ApngDecoder { } /** - * Load Apng into an imageView, asynchronously - * @param context Context + * Load Apng into an imageView, asynchronously. + * @param context Context. * @param string URL to load * @param imageView Image View. * @param speed Optional parameter. - * @param callback [ApngDecoder.Callback] to handle success and error + * @param callback [ApngDecoder.Callback] to handle success and error. */ @Suppress("unused") @JvmStatic @JvmOverloads - fun decodeApngAsyncInto(context : Context, string: String, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) { + fun decodeApngAsyncInto( + context: Context, + string: String, + imageView: ImageView, + speed: Float = 1f, + callback: Callback? = null + ) { GlobalScope.launch(Dispatchers.IO) { try { if (string.startsWith("http://") || string.startsWith("https://")) { @@ -602,8 +727,9 @@ class ApngDecoder { speed, callback ) - } else if(File(string).exists()) { - var pathToLoad = if (string.startsWith("content://")) string else "file://$string" + } else if (File(string).exists()) { + var pathToLoad = + if (string.startsWith("content://")) string else "file://$string" pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23") decodeApngAsyncInto( context, @@ -615,6 +741,7 @@ class ApngDecoder { } else if (string.startsWith("file://android_asset/")) { val drawable = decodeApng( + context, context.assets.open(string.replace("file:///android_asset/", "")), speed ) @@ -631,7 +758,7 @@ class ApngDecoder { callback?.onError(java.lang.Exception("Cannot open string")) } } - } catch (e : java.lang.Exception) { + } catch (e: Exception) { withContext(Dispatchers.Main) { callback?.onError(e) } @@ -640,20 +767,27 @@ class ApngDecoder { } /** - * Generate a correct IHDR from the IHDR chunk of the APNG - * @param ihdrOfApng The IHDR of the APNG - * @param width The width of the frame - * @param height The height of the frame - * @return [ByteArray] The generated IHDR + * Generate a correct IHDR from the IHDR chunk of the APNG. + * @param ihdrOfApng The IHDR of the APNG. + * @param width The width of the frame. + * @param height The height of the frame. + * @return [ByteArray] The generated IHDR. */ - private fun generateIhdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray { + private fun generateIhdr(ihdrOfApng: IHDR, width: Int, height: Int): ByteArray { val ihdr = ArrayList() // We need a body var to know body length and generate crc val ihdrBody = ArrayList() // Add chunk body length ihdr.addAll(Utils.to4Bytes(ihdrOfApng.body.size).asList()) // Add IHDR - ihdrBody.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).asList()) + ihdrBody.addAll( + byteArrayOf( + 0x49.toByte(), + 0x48.toByte(), + 0x44.toByte(), + 0x52.toByte() + ).asList() + ) // Add the max width and height ihdrBody.addAll(Utils.to4Bytes(width).asList()) ihdrBody.addAll(Utils.to4Bytes(height).asList()) diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt b/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt index b1864e4..82cb297 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt +++ b/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt @@ -44,7 +44,7 @@ class ApngDecoderFragment : Fragment() { override fun onError(error: Exception) { if (BuildConfig.DEBUG) - Log.e(TAG, "onError", error) + Log.e(TAG, "onError : $error") } }) } diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java b/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java index 9e29711..2ace096 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java +++ b/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java @@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment; import org.jetbrains.annotations.NotNull; import oupson.apng.decoder.ApngDecoder; +import oupson.apngcreator.BuildConfig; import oupson.apngcreator.R; @@ -24,7 +25,8 @@ public class JavaFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Log.i(TAG, "onCreateView()"); + if (BuildConfig.DEBUG) + Log.i(TAG, "onCreateView()"); View v = inflater.inflate(R.layout.fragment_java, container, false); @@ -49,12 +51,14 @@ public class JavaFragment extends Fragment { ApngDecoder.decodeApngAsyncInto(context, imageUrl, imageView, 1f, new ApngDecoder.Callback() { @Override public void onSuccess(@NotNull Drawable drawable) { - Log.i(TAG, "Success"); + if (BuildConfig.DEBUG) + Log.i(TAG, "Success"); } @Override public void onError(@NotNull Exception error) { - Log.e(TAG, "Error", error); + if (BuildConfig.DEBUG) + Log.e(TAG, "Error : " + error.toString()); } }); diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt b/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt index c15a5e6..514e019 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt +++ b/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt @@ -87,12 +87,17 @@ class KotlinFragment : Fragment() { }) if (animator == null) { - animator = apngImageView?.loadApng(imageUrls[selected])?.apply { - onLoaded { - setOnFrameChangeLister { - // Log.e("app-test", "onLoop") + try { + animator = apngImageView?.loadApng(imageUrls[selected])?.apply { + onLoaded { + setOnFrameChangeLister { + // Log.e("app-test", "onLoop") + } } } + } catch (e : Exception) { + if (BuildConfig.DEBUG) + Log.e(TAG, "Error : $e") } } diff --git a/app-test/src/main/res/layout/activity_viewer.xml b/app-test/src/main/res/layout/activity_viewer.xml index 04f0486..9acc182 100644 --- a/app-test/src/main/res/layout/activity_viewer.xml +++ b/app-test/src/main/res/layout/activity_viewer.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - -