Adding support for first frame not in animation
Fixing a bug
This commit is contained in:
parent
39fdc31aaa
commit
dfc427a54f
|
@ -15,7 +15,6 @@ import java.util.zip.DeflaterOutputStream
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
// TODO ADD SUPPORT FOR FIRST FRAME NOT IN ANIM
|
|
||||||
// TODO OPTIMISE APNG
|
// TODO OPTIMISE APNG
|
||||||
/**
|
/**
|
||||||
* A class to write APNG.
|
* A class to write APNG.
|
||||||
|
@ -90,6 +89,9 @@ class ExperimentalApngEncoder(
|
||||||
/** Number of loop of the animation, zero to infinite **/
|
/** Number of loop of the animation, zero to infinite **/
|
||||||
private var repetitionCount: Int = 0
|
private var repetitionCount: Int = 0
|
||||||
|
|
||||||
|
/** If the first frame should be included in the animation **/
|
||||||
|
private var firstFrameInAnim: Boolean = true
|
||||||
|
|
||||||
init {
|
init {
|
||||||
outputStream.write(Utils.pngSignature)
|
outputStream.write(Utils.pngSignature)
|
||||||
writeHeader()
|
writeHeader()
|
||||||
|
@ -139,9 +141,9 @@ class ExperimentalApngEncoder(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the compression level
|
* Set the compression level.
|
||||||
* @param compressionLevel A integer between 0 and 9 (not include)
|
* @param compressionLevel A integer between 0 and 9 (not include).
|
||||||
* @return [ExperimentalApngEncoder] for chaining
|
* @return [ExperimentalApngEncoder] for chaining.
|
||||||
*/
|
*/
|
||||||
fun compressionLevel(compressionLevel: Int): ExperimentalApngEncoder {
|
fun compressionLevel(compressionLevel: Int): ExperimentalApngEncoder {
|
||||||
if (compressionLevel in 0..9) {
|
if (compressionLevel in 0..9) {
|
||||||
|
@ -156,6 +158,16 @@ class ExperimentalApngEncoder(
|
||||||
return this
|
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.
|
* 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.
|
* @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)
|
else if (btm.height > height)
|
||||||
throw InvalidFrameSizeException("Frame height must be inferior or equal at the animation 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)
|
writeImageData(btm)
|
||||||
currentSeq++
|
currentFrame++
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,6 +39,7 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private var items: ArrayList<Pair<Uri, Int>> = ArrayList()
|
private var items: ArrayList<Pair<Uri, Int>> = ArrayList()
|
||||||
private var adapter: ImageAdapter? = null
|
private var adapter: ImageAdapter? = null
|
||||||
|
private var firstFrameInAnim = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -84,6 +85,7 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
menuInflater.inflate(R.menu.creator_menu, menu)
|
menuInflater.inflate(R.menu.creator_menu, menu)
|
||||||
|
menu?.findItem(R.id.menu_first_frame_in_anim)?.isChecked = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +130,7 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
maxHeight,
|
maxHeight,
|
||||||
items.size
|
items.size
|
||||||
).compressionLevel(9)
|
).compressionLevel(9)
|
||||||
|
.firstFrameInAnim(firstFrameInAnim)
|
||||||
items.forEachIndexed { i, uri ->
|
items.forEachIndexed { i, uri ->
|
||||||
if (BuildConfig.DEBUG)
|
if (BuildConfig.DEBUG)
|
||||||
Log.v(TAG, "Encoding frame $i")
|
Log.v(TAG, "Encoding frame $i")
|
||||||
|
@ -213,6 +216,8 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
maxHeight,
|
maxHeight,
|
||||||
items.size
|
items.size
|
||||||
).compressionLevel(9)
|
).compressionLevel(9)
|
||||||
|
.firstFrameInAnim(firstFrameInAnim)
|
||||||
|
|
||||||
items.forEach { uri ->
|
items.forEach { uri ->
|
||||||
println("delay : ${uri.second.toFloat()}ms")
|
println("delay : ${uri.second.toFloat()}ms")
|
||||||
val str = contentResolver.openInputStream(uri.first) ?: return@forEach
|
val str = contentResolver.openInputStream(uri.first) ?: return@forEach
|
||||||
|
@ -236,6 +241,7 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
type = "image/png"
|
type = "image/png"
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent.createChooser(
|
Intent.createChooser(
|
||||||
|
@ -285,6 +291,11 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
adapter?.notifyDataSetChanged()
|
adapter?.notifyDataSetChanged()
|
||||||
true
|
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
|
else -> if (item != null) super.onOptionsItemSelected(item) else true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,6 +344,8 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
maxHeight,
|
maxHeight,
|
||||||
items.size
|
items.size
|
||||||
).compressionLevel(9)
|
).compressionLevel(9)
|
||||||
|
.firstFrameInAnim(firstFrameInAnim)
|
||||||
|
|
||||||
items.forEach { uri ->
|
items.forEach { uri ->
|
||||||
// println("delay : ${adapter?.delay?.get(i)?.toFloat() ?: 1000f}ms")
|
// println("delay : ${adapter?.delay?.get(i)?.toFloat() ?: 1000f}ms")
|
||||||
val str =
|
val str =
|
||||||
|
|
|
@ -1,38 +1,43 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_save_apng"
|
android:id="@+id/menu_save_apng"
|
||||||
android:icon="@drawable/ic_save_white_24dp"
|
android:icon="@drawable/ic_save_white_24dp"
|
||||||
android:title="@string/save"
|
android:title="@string/save"
|
||||||
app:showAsAction="ifRoom"
|
app:iconTint="@color/control"
|
||||||
app:iconTint="@color/control"/>
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_create_apng"
|
android:id="@+id/menu_create_apng"
|
||||||
android:icon="@drawable/ic_play_arrow_white_24dp"
|
android:icon="@drawable/ic_play_arrow_white_24dp"
|
||||||
android:title="@string/create"
|
android:title="@string/create"
|
||||||
app:showAsAction="ifRoom"
|
app:iconTint="@color/control"
|
||||||
app:iconTint="@color/control"/>
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_share_apng"
|
android:id="@+id/menu_share_apng"
|
||||||
android:icon="@drawable/ic_share_share_24dp"
|
android:icon="@drawable/ic_share_share_24dp"
|
||||||
android:title="@string/share"
|
android:title="@string/share"
|
||||||
app:showAsAction="ifRoom"
|
app:iconTint="@color/control"
|
||||||
app:iconTint="@color/control"/>
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_set_all_duration"
|
android:id="@+id/menu_set_all_duration"
|
||||||
android:icon="@drawable/ic_access_time_white_24dp"
|
android:icon="@drawable/ic_access_time_white_24dp"
|
||||||
android:title="@string/set_all_duration"
|
android:title="@string/set_all_duration"
|
||||||
app:showAsAction="ifRoom"
|
app:iconTint="@color/control"
|
||||||
app:iconTint="@color/control"/>
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_clear"
|
android:id="@+id/menu_clear"
|
||||||
android:title="@string/clear"
|
android:title="@string/clear"
|
||||||
app:showAsAction="never"
|
app:iconTint="@color/control"
|
||||||
app:iconTint="@color/control" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_first_frame_in_anim"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/menu_first_frame_in_anim"
|
||||||
|
app:showAsAction="never|withText" />
|
||||||
</menu>
|
</menu>
|
|
@ -16,6 +16,7 @@
|
||||||
<string name="set_all_duration">Set duration of all frames</string>
|
<string name="set_all_duration">Set duration of all frames</string>
|
||||||
<string name="done">Done</string>
|
<string name="done">Done</string>
|
||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
|
<string name="menu_first_frame_in_anim">First frame in animation</string>
|
||||||
|
|
||||||
<string name="open">open</string>
|
<string name="open">open</string>
|
||||||
<string name="close">close</string>
|
<string name="close">close</string>
|
||||||
|
|
Loading…
Reference in New Issue