Rewrite Frame

Work on optimizer
This commit is contained in:
oupson 2019-01-12 15:34:57 +01:00
parent 6d1f6af7c5
commit ef95ec2e1f
7 changed files with 92 additions and 85 deletions

View File

@ -2,6 +2,7 @@ package oupson.apng
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Environment
import oupson.apng.ImageUtils.BitmapDiffCalculator import oupson.apng.ImageUtils.BitmapDiffCalculator
import oupson.apng.ImageUtils.PngEncoder import oupson.apng.ImageUtils.PngEncoder
import oupson.apng.ImageUtils.PnnQuantizer import oupson.apng.ImageUtils.PnnQuantizer
@ -13,6 +14,7 @@ import oupson.apng.utils.Utils.Companion.getDispose_op
import oupson.apng.utils.Utils.Companion.pngSignature import oupson.apng.utils.Utils.Companion.pngSignature
import oupson.apng.utils.Utils.Companion.to2Bytes import oupson.apng.utils.Utils.Companion.to2Bytes
import oupson.apng.utils.Utils.Companion.to4Bytes import oupson.apng.utils.Utils.Companion.to4Bytes
import java.io.File
import java.util.zip.CRC32 import java.util.zip.CRC32
/** /**
@ -517,6 +519,9 @@ class Apng {
it.maxWidth = maxWidth it.maxWidth = maxWidth
it.maxHeight = maxHeight it.maxHeight = maxHeight
} }
for (i in 0 until frames.size){
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(frames[i].byteArray)
}
val drawedFrame = ApngAnimator(null).draw(frames) val drawedFrame = ApngAnimator(null).draw(frames)
for (i in 1 until frames.size) { for (i in 1 until frames.size) {
val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i]) val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i])

View File

@ -79,7 +79,7 @@ class ApngAnimator(private val context: Context?) {
* @throws NotApngException * @throws NotApngException
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
fun load(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) { fun load(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync { doAsync {
val bytes = file.readBytes() val bytes = file.readBytes()
if (isApng(bytes)) { if (isApng(bytes)) {
@ -103,6 +103,7 @@ class ApngAnimator(private val context: Context?) {
} }
} }
} }
return this
} }
@ -113,7 +114,7 @@ class ApngAnimator(private val context: Context?) {
* @param speed The speed * @param speed The speed
* @throws NotApngException * @throws NotApngException
*/ */
fun load(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) { fun load(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync { doAsync {
context?.contentResolver?.openInputStream(uri)?.readBytes()?.let { context?.contentResolver?.openInputStream(uri)?.readBytes()?.let {
if (isApng(it)) { if (isApng(it)) {
@ -138,6 +139,7 @@ class ApngAnimator(private val context: Context?) {
} }
} }
} }
return this
} }
/** /**
@ -147,7 +149,7 @@ class ApngAnimator(private val context: Context?) {
* @throws NotApngException * @throws NotApngException
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
fun loadUrl(url: URL, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) { fun loadUrl(url: URL, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync(exceptionHandler = { e -> e.printStackTrace() }) { doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
this@ApngAnimator.speed = speed this@ApngAnimator.speed = speed
// Download PNG // Download PNG
@ -176,6 +178,7 @@ class ApngAnimator(private val context: Context?) {
} }
return this
} }
/** /**
@ -185,7 +188,7 @@ class ApngAnimator(private val context: Context?) {
* @throws NotApngException * @throws NotApngException
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
fun load(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) { fun load(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync { doAsync {
this@ApngAnimator.speed = speed this@ApngAnimator.speed = speed
if (isApng(byteArray)) { if (isApng(byteArray)) {
@ -209,6 +212,7 @@ class ApngAnimator(private val context: Context?) {
} }
} }
} }
return this
} }
/** /**
@ -217,7 +221,7 @@ class ApngAnimator(private val context: Context?) {
* @param speed The speed * @param speed The speed
* @throws NotApngException * @throws NotApngException
*/ */
fun load(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) { fun load(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync { doAsync {
this@ApngAnimator.speed = speed this@ApngAnimator.speed = speed
if (string.contains("http") || string.contains("https")) { if (string.contains("http") || string.contains("https")) {
@ -240,6 +244,7 @@ class ApngAnimator(private val context: Context?) {
} }
} }
} }
return this
} }
/** /**

View File

@ -1,10 +1,14 @@
package oupson.apng package oupson.apng
import android.util.Log
import oupson.apng.chunks.IDAT import oupson.apng.chunks.IDAT
import oupson.apng.chunks.IHDR import oupson.apng.chunks.IHDR
import oupson.apng.exceptions.NotPngException import oupson.apng.exceptions.NotPngException
import oupson.apng.utils.Utils import oupson.apng.utils.Utils
import oupson.apng.utils.Utils.Companion.IDAT
import oupson.apng.utils.Utils.Companion.IHDR
import oupson.apng.utils.Utils.Companion.isPng import oupson.apng.utils.Utils.Companion.isPng
import java.util.*
/** /**
* A frame for an animated png * A frame for an animated png
@ -18,12 +22,12 @@ class Frame {
var byteArray : ByteArray var byteArray : ByteArray
var width : Int var width : Int = -1
var height : Int var height : Int = -1
var ihdr : IHDR lateinit var ihdr : IHDR
var idat : IDAT lateinit var idat : IDAT
val delay : Float val delay : Float
@ -39,21 +43,17 @@ class Frame {
constructor(byteArray: ByteArray) { constructor(byteArray: ByteArray) {
if (isPng(byteArray)) { if (isPng(byteArray)) {
this.byteArray = byteArray this.byteArray = byteArray
Log.e("tag", byteArray.size.toString())
// Get width and height for image // Get width and height for image
ihdr = IHDR()
ihdr.parse(byteArray)
width = ihdr.pngWidth
height = ihdr.pngHeight
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
delay = 1000f delay = 1000f
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
var cursor = 8
while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
parseChunk(chunk)
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
}
} else { } else {
throw NotPngException() throw NotPngException()
} }
@ -62,15 +62,12 @@ class Frame {
if (isPng(byteArray)) { if (isPng(byteArray)) {
this.byteArray = byteArray this.byteArray = byteArray
// Get width and height for image // Get width and height for image
ihdr = IHDR() var cursor = 8
ihdr.parse(byteArray) while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
width = ihdr.pngWidth parseChunk(chunk)
height = ihdr.pngHeight cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
}
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
this.delay = delay this.delay = delay
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
@ -84,15 +81,12 @@ class Frame {
if (isPng(byteArray)) { if (isPng(byteArray)) {
this.byteArray = byteArray this.byteArray = byteArray
// Get width and height for image // Get width and height for image
ihdr = IHDR() var cursor = 8
ihdr.parse(byteArray) while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
width = ihdr.pngWidth parseChunk(chunk)
height = ihdr.pngHeight cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
}
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
this.delay = delay this.delay = delay
@ -110,15 +104,12 @@ class Frame {
if (isPng(byteArray)) { if (isPng(byteArray)) {
this.byteArray = byteArray this.byteArray = byteArray
// Get width and height for image // Get width and height for image
ihdr = IHDR() var cursor = 8
ihdr.parse(byteArray) while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
width = ihdr.pngWidth parseChunk(chunk)
height = ihdr.pngHeight cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
}
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
this.delay = delay this.delay = delay
@ -138,15 +129,12 @@ class Frame {
if (isPng(byteArray)) { if (isPng(byteArray)) {
this.byteArray = byteArray this.byteArray = byteArray
// Get width and height for image // Get width and height for image
ihdr = IHDR() var cursor = 8
ihdr.parse(byteArray) while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
width = ihdr.pngWidth parseChunk(chunk)
height = ihdr.pngHeight cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
}
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
this.delay = delay this.delay = delay
@ -161,4 +149,20 @@ class Frame {
throw NotPngException() throw NotPngException()
} }
} }
fun parseChunk(byteArray: ByteArray) {
when(Arrays.toString(byteArray.copyOfRange(4, 8))) {
IHDR -> {
ihdr = IHDR()
ihdr.parse(byteArray)
width = ihdr.pngWidth
height = ihdr.pngHeight
}
IDAT -> {
// Get IDAT Bytes
idat = IDAT()
idat.parse(byteArray)
}
}
}
} }

View File

@ -80,22 +80,22 @@ class PngEncoder {
* @param compressionLevel ! Don't use it : It's buggy * @param compressionLevel ! Don't use it : It's buggy
*/ */
fun encode(image: Bitmap, encodeAlpha: Boolean = false, filter: Int = 0, compressionLevel: Int = 0): ByteArray { fun encode(image: Bitmap, encodeAlpha: Boolean = false, filter: Int = 0, compressionLevel: Int = 0): ByteArray {
Companion.filter = FILTER_NONE this.filter = FILTER_NONE
if (filter <= FILTER_LAST) { if (filter <= FILTER_LAST) {
Companion.filter = filter this.filter = filter
} }
if (compressionLevel in 0..9) { if (compressionLevel in 0..9) {
Companion.compressionLevel = compressionLevel this.compressionLevel = compressionLevel
} }
Companion.encodeAlpha = encodeAlpha this.encodeAlpha = encodeAlpha
val pngIdBytes = byteArrayOf(-119, 80, 78, 71, 13, 10, 26, 10) val pngIdBytes = byteArrayOf(-119, 80, 78, 71, 13, 10, 26, 10)
width = image.width width = image.width
height = image.height height = image.height
Companion.image = image this.image = image
/* /*
* start with an array that is big enough to hold all the pixels * start with an array that is big enough to hold all the pixels
* (plus filter bytes), and an extra 200 bytes for header info * (plus filter bytes), and an extra 200 bytes for header info
@ -221,9 +221,8 @@ class PngEncoder {
* Write a PNG "IHDR" chunk into the pngBytes array. * Write a PNG "IHDR" chunk into the pngBytes array.
*/ */
private fun writeHeader() { private fun writeHeader() {
val startPos: Int = bytePos
bytePos = writeInt4(13, bytePos) bytePos = writeInt4(13, bytePos)
val startPos: Int = bytePos
bytePos = writeBytes(IHDR, bytePos) bytePos = writeBytes(IHDR, bytePos)
width = image!!.width width = image!!.width
height = image!!.height height = image!!.height

View File

@ -4,11 +4,9 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.util.Log
import kotlinx.android.synthetic.main.activity_creator.* import kotlinx.android.synthetic.main.activity_creator.*
import org.jetbrains.anko.alert import org.jetbrains.anko.alert
import org.jetbrains.anko.customView import org.jetbrains.anko.customView
@ -17,7 +15,6 @@ import org.jetbrains.anko.sdk27.coroutines.onClick
import oupson.apng.APNGDisassembler import oupson.apng.APNGDisassembler
import oupson.apng.Apng import oupson.apng.Apng
import oupson.apng.ApngAnimator import oupson.apng.ApngAnimator
import oupson.apng.ImageUtils.PngEncoder
import oupson.apngcreator.adapter.frameListViewAdapter import oupson.apngcreator.adapter.frameListViewAdapter
import java.io.File import java.io.File
@ -45,33 +42,30 @@ class CreatorActivity : AppCompatActivity() {
fab_create.onClick { fab_create.onClick {
var apngCreated = Apng() var apngCreated = Apng()
items.forEach { items.forEach { bitmap ->
apngCreated.addFrames(it) apngCreated.addFrames(bitmap)
} }
apngCreated = APNGDisassembler.disassemble(apngCreated.toByteArray()).apply {
Log.e("tag", apngCreated.frames.size.toString()) optimiseFrame()
apngCreated = APNGDisassembler.disassemble(apngCreated.toByteArray()) }
apngCreated.optimiseFrame()
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "vtm").writeBytes(apngCreated.toByteArray())
val a = ApngAnimator(applicationContext) val a = ApngAnimator(applicationContext)
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "apn.png").writeBytes(apngCreated.toByteArray())
a.load(apngCreated.toByteArray()) a.load(apngCreated.toByteArray())
a.onLoaded { a.onLoaded { anim ->
alert { alert {
customView { customView {
imageView { imageView {
Log.e("tag", "${it.anim?.numberOfFrames.toString()} : ${items.size}") /**anim.anim?.let {cu ->
it.anim?.let { for (i in 0 until cu.numberOfFrames) {
for (i in 0 until it.numberOfFrames) { val vt = Bitmap.createBitmap(cu.getFrame(i).intrinsicWidth, cu.getFrame(i).intrinsicHeight, Bitmap.Config.ARGB_8888)
val vt = Bitmap.createBitmap(it.getFrame(i).intrinsicWidth, it.getFrame(i).intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(vt) val canvas = Canvas(vt)
it.getFrame(i).draw(canvas) cu.getFrame(i).draw(canvas)
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame$i.png").writeBytes(PngEncoder.encode(vt)) File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(PngEncoder.encode(vt))
} }
} }
this.setImageDrawable(it.anim) this.setImageDrawable(anim.anim)
*/
} }
} }
}.show() }.show()

View File

@ -26,7 +26,7 @@
android:clickable="true" android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_play_arrow_white_24dp" /> app:srcCompat="@drawable/ic_add_white_24dp" />
<ListView <ListView
android:id="@+id/dragView" android:id="@+id/dragView"