From dfc427a54f1e9c4fb690954dfa145c679fa9103e Mon Sep 17 00:00:00 2001 From: Oupson Date: Wed, 23 Sep 2020 21:09:58 +0200 Subject: [PATCH] Adding support for first frame not in animation Fixing a bug --- .../apng/encoder/ExperimentalApngEncoder.kt | 25 ++++++++++---- .../apngcreator/activities/CreatorActivity.kt | 13 ++++++++ app-test/src/main/res/menu/creator_menu.xml | 33 +++++++++++-------- app-test/src/main/res/values/strings.xml | 1 + 4 files changed, 52 insertions(+), 20 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 b5361f0..7beb240 100644 --- a/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt +++ b/apng_library/src/main/java/oupson/apng/encoder/ExperimentalApngEncoder.kt @@ -15,7 +15,6 @@ import java.util.zip.DeflaterOutputStream import kotlin.math.max import kotlin.math.min -// TODO ADD SUPPORT FOR FIRST FRAME NOT IN ANIM // TODO OPTIMISE APNG /** * A class to write APNG. @@ -90,6 +89,9 @@ class ExperimentalApngEncoder( /** Number of loop of the animation, zero to infinite **/ private var repetitionCount: Int = 0 + /** If the first frame should be included in the animation **/ + private var firstFrameInAnim: Boolean = true + init { outputStream.write(Utils.pngSignature) writeHeader() @@ -139,9 +141,9 @@ class ExperimentalApngEncoder( } /** - * Set the compression level - * @param compressionLevel A integer between 0 and 9 (not include) - * @return [ExperimentalApngEncoder] for chaining + * 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) { @@ -156,6 +158,16 @@ class ExperimentalApngEncoder( return this } + /** + * Set if the first frame should be included in the animation. + * @param firstFrameInAnim A boolean. + * @return [ExperimentalApngEncoder] for chaining. + */ + fun firstFrameInAnim(firstFrameInAnim: Boolean): ExperimentalApngEncoder { + this.firstFrameInAnim = firstFrameInAnim + return this + } + /** * Write a frame into the output stream. * @param inputStream An input stream that will be decoded in order to be written in the animation. Not freed. @@ -221,9 +233,10 @@ class ExperimentalApngEncoder( else if (btm.height > height) throw InvalidFrameSizeException("Frame height must be inferior or equal at the animation height") - writeFCTL(btm, delay, disposeOp, blendOp, xOffsets, yOffsets) + if (firstFrameInAnim || currentFrame != 0) + writeFCTL(btm, delay, disposeOp, blendOp, xOffsets, yOffsets) writeImageData(btm) - currentSeq++ + currentFrame++ } /** 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 19e243e..dd16a7d 100644 --- a/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt +++ b/app-test/src/main/java/oupson/apngcreator/activities/CreatorActivity.kt @@ -39,6 +39,7 @@ class CreatorActivity : AppCompatActivity() { private var items: ArrayList> = ArrayList() private var adapter: ImageAdapter? = null + private var firstFrameInAnim = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -84,6 +85,7 @@ class CreatorActivity : AppCompatActivity() { override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.creator_menu, menu) + menu?.findItem(R.id.menu_first_frame_in_anim)?.isChecked = true return true } @@ -128,6 +130,7 @@ class CreatorActivity : AppCompatActivity() { maxHeight, items.size ).compressionLevel(9) + .firstFrameInAnim(firstFrameInAnim) items.forEachIndexed { i, uri -> if (BuildConfig.DEBUG) Log.v(TAG, "Encoding frame $i") @@ -213,6 +216,8 @@ class CreatorActivity : AppCompatActivity() { maxHeight, items.size ).compressionLevel(9) + .firstFrameInAnim(firstFrameInAnim) + items.forEach { uri -> println("delay : ${uri.second.toFloat()}ms") val str = contentResolver.openInputStream(uri.first) ?: return@forEach @@ -236,6 +241,7 @@ class CreatorActivity : AppCompatActivity() { ) ) type = "image/png" + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity( Intent.createChooser( @@ -285,6 +291,11 @@ class CreatorActivity : AppCompatActivity() { adapter?.notifyDataSetChanged() true } + R.id.menu_first_frame_in_anim -> { + item.isChecked = !item.isChecked + firstFrameInAnim = item.isChecked + true + } else -> if (item != null) super.onOptionsItemSelected(item) else true } } @@ -333,6 +344,8 @@ class CreatorActivity : AppCompatActivity() { maxHeight, items.size ).compressionLevel(9) + .firstFrameInAnim(firstFrameInAnim) + items.forEach { uri -> // println("delay : ${adapter?.delay?.get(i)?.toFloat() ?: 1000f}ms") val str = diff --git a/app-test/src/main/res/menu/creator_menu.xml b/app-test/src/main/res/menu/creator_menu.xml index 107fc48..fdec24e 100644 --- a/app-test/src/main/res/menu/creator_menu.xml +++ b/app-test/src/main/res/menu/creator_menu.xml @@ -1,38 +1,43 @@ - + + app:iconTint="@color/control" + app:showAsAction="ifRoom" /> + app:iconTint="@color/control" + app:showAsAction="ifRoom" /> + app:iconTint="@color/control" + app:showAsAction="ifRoom" /> + app:iconTint="@color/control" + app:showAsAction="ifRoom" /> + android:id="@+id/menu_clear" + android:title="@string/clear" + app:iconTint="@color/control" + app:showAsAction="never" /> + \ No newline at end of file diff --git a/app-test/src/main/res/values/strings.xml b/app-test/src/main/res/values/strings.xml index 42f94ce..ce2957f 100644 --- a/app-test/src/main/res/values/strings.xml +++ b/app-test/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Set duration of all frames Done Clear + First frame in animation open close