From d8d58259e1beb195ecd006652d081223e86ea2cc Mon Sep 17 00:00:00 2001 From: Oupson Date: Sun, 20 Sep 2020 10:06:49 +0200 Subject: [PATCH] Working on ExperimentalApngEncoder.kt --- .../apng/encoder/ExperimentalApngEncoder.kt | 87 +++++++++++++++---- .../apngcreator/activities/CreatorActivity.kt | 25 ++++-- 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt b/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt index 24c09ec..2bd6764 100644 --- a/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt +++ b/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt @@ -23,10 +23,7 @@ class ExperimentalApngEncoder( private val outputStream: OutputStream, private val width: Int, private val height: Int, - numberOfFrames: Int, - private val encodeAlpha: Boolean = true, - filter: Int = 0, - compressionLevel: Int = 0 + numberOfFrames: Int ) { companion object { private const val TAG = "ExperimentalApngEncoder" @@ -66,7 +63,10 @@ class ExperimentalApngEncoder( private var compressionLevel: Int = 0 /** The filter type. */ - private var filter: Int = 0 + private var filter: Int = FILTER_NONE + + /** If the alpha channel must be encoded */ + private var encodeAlpha : Boolean = true /** The prior row. */ private var priorRow: ByteArray? = null @@ -74,21 +74,72 @@ class ExperimentalApngEncoder( /** The left bytes. */ private var leftBytes: ByteArray? = null + /** Number of loop of the animation, zero to infinite **/ + private var repetitionCount : Int = 0 + init { - this.filter = FILTER_NONE - if (filter <= FILTER_LAST) { - this.filter = filter - } - - if (compressionLevel in 0..9) { - this.compressionLevel = compressionLevel - } - outputStream.write(Utils.pngSignature) writeHeader() writeACTL(numberOfFrames) } + /** + * Set if the alpha channel must be encoded. + * @param encodeAlpha If the alpha channel must be encoded. + * @return [ExperimentalApngEncoder] for chaining. + */ + @Suppress("unused") + fun encodeAlpha(encodeAlpha : Boolean) : ExperimentalApngEncoder { + this.encodeAlpha = encodeAlpha + return this + } + + /** + * Set the filter. + * @param filter The filter. + * Values : + * - [FILTER_NONE] + * - [FILTER_SUB] + * - [FILTER_UP] + * - [FILTER_LAST] + * @return [ExperimentalApngEncoder] for chaining. + */ + @Suppress("unused") + fun filter(filter : Int) : ExperimentalApngEncoder { + if (filter <= FILTER_LAST) { + this.filter = filter + } else { + Log.e(TAG, "Invalid filter") + } + return this + } + + /** + * Set the repetition count. + * @param repetitionCount The number of repetition, zero for infinite. + * @return [ExperimentalApngEncoder] for chaining. + */ + @Suppress("unused") + fun repetitionCount(repetitionCount : Int) : ExperimentalApngEncoder{ + this.repetitionCount = repetitionCount + return this + } + + /** + * Set the compression level + * @param compressionLevel A integer between 0 and 9 (not include) + * @return [ExperimentalApngEncoder] for chaining + */ + fun compressionLevel(compressionLevel : Int) : ExperimentalApngEncoder { + if (compressionLevel in 0..9) { + this.compressionLevel = compressionLevel + } else { + Log.e(TAG, "Invalid compression level : $compressionLevel, expected a number in range 0..9") + } + + return this + } + @JvmOverloads fun writeFrame( inputStream: InputStream, @@ -120,7 +171,11 @@ class ExperimentalApngEncoder( throw Exception("Height of first frame must be equal to height of APNG. (${btm.height} != $height)") } - // TODO CHECK IF btm IS BIGGER THANT THE APNG + // TODO PROPER EXCEPTION + if (btm.width > width) + throw Exception("Frame width must be inferior or equal at the animation width") + else if (btm.height > height) + throw Exception("Frame height must be inferior or equal at the animation height") writeFCTL(btm, delay, disposeOp, blendOp, xOffsets, yOffsets) writeImageData(btm) @@ -193,7 +248,7 @@ class ExperimentalApngEncoder( actl.addAll(Utils.to4Bytes(num).asList()) // Number of repeat, 0 to infinite - actl.addAll(Utils.to4Bytes(0).asList()) + actl.addAll(Utils.to4Bytes(repetitionCount).asList()) outputStream.write(actl.toByteArray()) // generate crc diff --git a/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt b/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt index 5db946b..19e243e 100644 --- a/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt +++ b/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt @@ -122,7 +122,12 @@ class CreatorActivity : AppCompatActivity() { if (BuildConfig.DEBUG) Log.i(TAG, "MaxWidth : $maxWidth; MaxHeight : $maxHeight") - val encoder = ExperimentalApngEncoder(out, maxWidth, maxHeight, items.size, compressionLevel = 9) + val encoder = ExperimentalApngEncoder( + out, + maxWidth, + maxHeight, + items.size + ).compressionLevel(9) items.forEachIndexed { i, uri -> if (BuildConfig.DEBUG) Log.v(TAG, "Encoding frame $i") @@ -155,7 +160,7 @@ class CreatorActivity : AppCompatActivity() { ) } str.close() - } catch (e : Exception) { + } catch (e: Exception) { Log.e(TAG, "Error when creating apng", e) } } @@ -164,7 +169,7 @@ class CreatorActivity : AppCompatActivity() { out.close() if (BuildConfig.DEBUG) - Log.v(TAG, "${f.length() / 1000}ko") + Log.v(TAG, "Animation size is ${f.length() / 1000}ko") withContext(Dispatchers.Main) { val intent = Intent(Intent.ACTION_VIEW) @@ -202,7 +207,12 @@ class CreatorActivity : AppCompatActivity() { str?.close() } - val encoder = ExperimentalApngEncoder(out, maxWidth, maxHeight, items.size, compressionLevel = 9) + val encoder = ExperimentalApngEncoder( + out, + maxWidth, + maxHeight, + items.size + ).compressionLevel(9) items.forEach { uri -> println("delay : ${uri.second.toFloat()}ms") val str = contentResolver.openInputStream(uri.first) ?: return@forEach @@ -317,7 +327,12 @@ class CreatorActivity : AppCompatActivity() { if (BuildConfig.DEBUG) Log.i(TAG, "MaxWidth : $maxWidth; MaxHeight : $maxHeight") - val encoder = ExperimentalApngEncoder(out, maxWidth, maxHeight, items.size, compressionLevel = 9) + val encoder = ExperimentalApngEncoder( + out, + maxWidth, + maxHeight, + items.size + ).compressionLevel(9) items.forEach { uri -> // println("delay : ${adapter?.delay?.get(i)?.toFloat() ?: 1000f}ms") val str =