diff --git a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt
index f3e720d..ccb8f48 100644
--- a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt
+++ b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt
@@ -1,9 +1,6 @@
package oupson.apng
-import android.graphics.Bitmap
import android.graphics.BitmapFactory
-import android.graphics.Canvas
-import android.util.Log
import oupson.apng.Utils.Companion.isApng
import oupson.apng.Utils.Companion.pngSignature
import oupson.apng.Utils.Companion.to4Bytes
@@ -12,240 +9,227 @@ import oupson.apng.chunks.fcTL
import oupson.apng.exceptions.NotApngException
import java.util.zip.CRC32
-class APNGDisassembler(val byteArray: ByteArray) {
- val pngList = ArrayList()
- var png : ArrayList? = null
- var cover : ArrayList? = null
- var delay = -1f
- var yOffset= -1
- var xOffset = -1
- var plte : ByteArray? = null
- var tnrs : ByteArray? = null
- var maxWidth = 0
- var maxHeight = 0
- var blend_op : Utils.Companion.blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
- var dispose_op : Utils.Companion.dispose_op= Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
+class APNGDisassembler() {
+ companion object {
- var apng : Apng
+ fun disassemble(byteArray: ByteArray) : Apng {
+ val pngList = ArrayList()
+ var png: ArrayList? = null
+ var cover: ArrayList? = null
+ var delay = -1f
+ var yOffset = -1
+ var xOffset = -1
+ var plte: ByteArray? = null
+ var tnrs: ByteArray? = null
+ var maxWidth = 0
+ var maxHeight = 0
+ var blend_op: Utils.Companion.blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
+ var dispose_op: Utils.Companion.dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
+ val apng: Apng
+ if (isApng(byteArray)) {
+ apng = Apng()
+ val ihdr = IHDR()
+ ihdr.parseIHDR(byteArray)
+ maxWidth = ihdr.pngWidth
+ maxHeight = ihdr.pngHeight
+ for (i in 0 until byteArray.size) {
+ // find new Frame with fcTL
+ if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x63.toByte() && byteArray[i + 2] == 0x54.toByte() && byteArray[i + 3] == 0x4C.toByte()) {
+ if (png == null) {
+ if (cover != null) {
+ cover.addAll(to4Bytes(0).toList())
+ // Add IEND
+ val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
+ // Generate crc for IEND
+ val crC32 = CRC32()
+ crC32.update(iend, 0, iend.size)
+ cover.addAll(iend.toList())
+ cover.addAll(to4Bytes(crC32.value.toInt()).toList())
- init {
- if (isApng(byteArray)) {
- apng = Apng()
- val ihdr = IHDR()
- ihdr.parseIHDR(byteArray)
- maxWidth = ihdr.pngWidth
- maxHeight = ihdr.pngHeight
- for(i in 0 until byteArray.size) {
- // find new Frame with fcTL
- if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x63.toByte() && byteArray[ i + 2 ] == 0x54.toByte() && byteArray[ i + 3 ] == 0x4C.toByte()) {
- if (png == null) {
- if (cover != null) {
- cover!!.addAll(to4Bytes(0).toList())
+ apng.cover = BitmapFactory.decodeByteArray(cover.toByteArray(), 0, cover.size)
+ }
+ png = ArrayList()
+ val fcTL = fcTL(byteArray.copyOfRange(i - 4, i + 36))
+ delay = fcTL.delay
+ yOffset = fcTL.y_offset
+ xOffset = fcTL.x_offset
+ blend_op = fcTL.blend_op
+ dispose_op = fcTL.dispose_op
+ val width = fcTL.pngWidth
+ val height = fcTL.pngHeight
+ png.addAll(pngSignature.toList())
+ png.addAll(generate_ihdr(ihdr, width, height).toList())
+
+ if (plte != null) {
+ png.addAll(plte.toList())
+ }
+
+ if (tnrs != null) {
+ png.addAll(tnrs.toList())
+ }
+ } else {
+ // Add IEND body length : 0
+ png.addAll(to4Bytes(0).toList())
// Add IEND
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
// Generate crc for IEND
val crC32 = CRC32()
crC32.update(iend, 0, iend.size)
- cover!!.addAll(iend.toList())
- cover!!.addAll(to4Bytes(crC32.value.toInt()).toList())
+ png.addAll(iend.toList())
+ png.addAll(to4Bytes(crC32.value.toInt()).toList())
+ pngList.add(Frame(png.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
- apng.cover = BitmapFactory.decodeByteArray(cover!!.toByteArray(), 0, cover!!.size)
- }
- png = ArrayList()
- val fcTL = fcTL(byteArray.copyOfRange(i - 4, i + 36))
- delay = fcTL.delay
- yOffset = fcTL.y_offset
- xOffset = fcTL.x_offset
- blend_op = fcTL.blend_op
- dispose_op = fcTL.dispose_op
- val width = fcTL.pngWidth
- val height = fcTL.pngHeight
- png!!.addAll(pngSignature.toList())
- png!!.addAll(generate_ihdr(ihdr, width, height).toList())
+ png = ArrayList()
- if (plte != null) {
- png!!.addAll(plte!!.toList())
- }
+ val bodySize = {
+ var lengthString = ""
+ byteArray.copyOfRange(i - 4, i).forEach {
+ lengthString += String.format("%02x", it)
+ }
+ lengthString.toLong(16).toInt()
+ }()
+ val newBytes = byteArray.copyOfRange(i - 4, i + 4 + bodySize)
+ val fcTL = fcTL(newBytes)
+ delay = fcTL.delay
- if (tnrs != null) {
- png!!.addAll(tnrs!!.toList())
+ yOffset = fcTL.y_offset
+ xOffset = fcTL.x_offset
+
+ blend_op = fcTL.blend_op
+ dispose_op = fcTL.dispose_op
+ val width = fcTL.pngWidth
+ val height = fcTL.pngHeight
+ png.addAll(pngSignature.toList())
+ png.addAll(generate_ihdr(ihdr, width, height).toList())
+ if (plte != null) {
+ png.addAll(plte.toList())
+ }
+
+ if (tnrs != null) {
+ png.addAll(tnrs.toList())
+ }
}
- } else {
- // Add IEND body length : 0
+ } else if (i == byteArray.size - 1) {
png!!.addAll(to4Bytes(0).toList())
// Add IEND
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
// Generate crc for IEND
val crC32 = CRC32()
crC32.update(iend, 0, iend.size)
- png!!.addAll(iend.toList())
- png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
- pngList.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
-
- png = ArrayList()
-
- val bodySize = {
+ png.addAll(iend.toList())
+ png.addAll(to4Bytes(crC32.value.toInt()).toList())
+ pngList.add(Frame(png.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
+ }
+ // Check if is IDAT
+ else if (byteArray[i] == 0x49.toByte() && byteArray[i + 1] == 0x44.toByte() && byteArray[i + 2] == 0x41.toByte() && byteArray[i + 3] == 0x54.toByte()) {
+ if (png == null) {
+ if (cover == null) {
+ cover = ArrayList()
+ cover.addAll(pngSignature.toList())
+ cover.addAll(generate_ihdr(ihdr, maxWidth, maxHeight).toList())
+ }
+ // Find the chunk length
var lengthString = ""
byteArray.copyOfRange(i - 4, i).forEach {
lengthString += String.format("%02x", it)
}
- lengthString.toLong(16).toInt()
- }()
- val newBytes = byteArray.copyOfRange(i - 4, i + 4 + bodySize)
- val fcTL = fcTL(newBytes)
- delay = fcTL.delay
-
- yOffset = fcTL.y_offset
- xOffset = fcTL.x_offset
-
- blend_op = fcTL.blend_op
- dispose_op = fcTL.dispose_op
- val width = fcTL.pngWidth
- val height = fcTL.pngHeight
- png!!.addAll(pngSignature.toList())
- png!!.addAll(generate_ihdr(ihdr, width, height).toList())
- if (plte != null) {
- png!!.addAll(plte!!.toList())
- }
-
- if (tnrs != null) {
- png!!.addAll(tnrs!!.toList())
+ val bodySize = lengthString.toLong(16).toInt()
+ cover.addAll(byteArray.copyOfRange(i - 4, i).toList())
+ val body = ArrayList()
+ body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
+ // Get image bytes
+ for (j in i + 4 until i + 4 + bodySize) {
+ body.add(byteArray[j])
+ }
+ val crC32 = CRC32()
+ crC32.update(body.toByteArray(), 0, body.size)
+ cover.addAll(body)
+ cover.addAll(to4Bytes(crC32.value.toInt()).toList())
+ } else {
+ // Find the chunk length
+ var lengthString = ""
+ byteArray.copyOfRange(i - 4, i).forEach {
+ lengthString += String.format("%02x", it)
+ }
+ val bodySize = lengthString.toLong(16).toInt()
+ png.addAll(byteArray.copyOfRange(i - 4, i).toList())
+ val body = ArrayList()
+ body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
+ // Get image bytes
+ for (j in i + 4 until i + 4 + bodySize) {
+ body.add(byteArray[j])
+ }
+ val crC32 = CRC32()
+ crC32.update(body.toByteArray(), 0, body.size)
+ png.addAll(body)
+ png.addAll(to4Bytes(crC32.value.toInt()).toList())
}
}
- }
- else if (i == byteArray.size - 1) {
- png!!.addAll(to4Bytes(0).toList())
- // Add IEND
- val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
- // Generate crc for IEND
- val crC32 = CRC32()
- crC32.update(iend, 0, iend.size)
- png!!.addAll(iend.toList())
- png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
- pngList.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
- }
- // Check if is IDAT
- else if (byteArray[i] == 0x49.toByte() && byteArray[i + 1] == 0x44.toByte() && byteArray[ i + 2 ] == 0x41.toByte() && byteArray[ i + 3 ] == 0x54.toByte()) {
- if (png == null) {
- if (cover == null) {
- cover = ArrayList()
- cover!!.addAll(pngSignature.toList())
- cover!!.addAll(generate_ihdr(ihdr, maxWidth, maxHeight).toList())
- }
+ // Check if is fdAT
+ else if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x64.toByte() && byteArray[i + 2] == 0x41.toByte() && byteArray[i + 3] == 0x54.toByte()) {
// Find the chunk length
var lengthString = ""
- byteArray.copyOfRange( i - 4, i).forEach {
+ byteArray.copyOfRange(i - 4, i).forEach {
lengthString += String.format("%02x", it)
}
val bodySize = lengthString.toLong(16).toInt()
- cover!!.addAll(byteArray.copyOfRange(i-4, i).toList())
+ png!!.addAll(to4Bytes(bodySize - 4).toList())
val body = ArrayList()
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
// Get image bytes
- for (j in i +4 until i + 4 + bodySize) {
+ for (j in i + 8 until i + 4 + bodySize) {
body.add(byteArray[j])
}
val crC32 = CRC32()
crC32.update(body.toByteArray(), 0, body.size)
- cover!!.addAll(body)
- cover!!.addAll(to4Bytes(crC32.value.toInt()).toList())
- } else {
- // Find the chunk length
+ png.addAll(body)
+ png.addAll(to4Bytes(crC32.value.toInt()).toList())
+ }
+ // Get plte chunks if exist. The PLTE chunk contains from 1 to 256 palette entries, each a three-byte series of the form:
+ else if (byteArray[i] == 0x50.toByte() && byteArray[i + 1] == 0x4C.toByte() && byteArray[i + 2] == 0x54.toByte() && byteArray[i + 3] == 0x45.toByte()) {
var lengthString = ""
- byteArray.copyOfRange( i - 4, i).forEach {
+ byteArray.copyOfRange(i - 4, i).forEach {
lengthString += String.format("%02x", it)
}
val bodySize = lengthString.toLong(16).toInt()
- png!!.addAll(byteArray.copyOfRange(i-4, i).toList())
- val body = ArrayList()
- body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
- // Get image bytes
- for (j in i +4 until i + 4 + bodySize) {
- body.add(byteArray[j])
+ plte = byteArray.copyOfRange(i - 4, i + 8 + bodySize)
+ }
+ // Get tnrs chunk if exist. Used for transparency
+ else if (byteArray[i] == 0x74.toByte() && byteArray[i + 1] == 0x52.toByte() && byteArray[i + 2] == 0x4E.toByte() && byteArray[i + 3] == 0x53.toByte()) {
+ var lengthString = ""
+ byteArray.copyOfRange(i - 4, i).forEach {
+ lengthString += String.format("%02x", it)
}
- val crC32 = CRC32()
- crC32.update(body.toByteArray(), 0, body.size)
- png!!.addAll(body)
- png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
+ val bodySize = lengthString.toLong(16).toInt()
+ tnrs = byteArray.copyOfRange(i - 4, i + 8 + bodySize)
}
}
- // Check if is fdAT
- else if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x64.toByte() && byteArray[ i + 2 ] == 0x41.toByte() && byteArray[ i + 3 ] == 0x54.toByte()) {
- // Find the chunk length
- var lengthString = ""
- byteArray.copyOfRange( i - 4, i).forEach {
- lengthString += String.format("%02x", it)
- }
- val bodySize = lengthString.toLong(16).toInt()
- png!!.addAll(to4Bytes(bodySize - 4).toList())
- val body = ArrayList()
- body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
- // Get image bytes
- for (j in i + 8 until i + 4 + bodySize) {
- body.add(byteArray[j])
- }
- val crC32 = CRC32()
- crC32.update(body.toByteArray(), 0, body.size)
- png!!.addAll(body)
- png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
- }
- // Get plte chunks if exist. The PLTE chunk contains from 1 to 256 palette entries, each a three-byte series of the form:
- else if (byteArray[i] == 0x50.toByte() && byteArray[i + 1] == 0x4C.toByte() && byteArray[ i + 2 ] == 0x54.toByte() && byteArray[ i + 3 ] == 0x45.toByte()) {
- var lengthString = ""
- byteArray.copyOfRange( i - 4, i).forEach {
- lengthString += String.format("%02x", it)
- }
- val bodySize = lengthString.toLong(16).toInt()
- plte = byteArray.copyOfRange( i -4, i + 8 + bodySize)
- }
- // Get tnrs chunk if exist. Used for transparency
- else if (byteArray[i] == 0x74.toByte() && byteArray[i + 1] == 0x52.toByte() && byteArray[ i + 2 ] == 0x4E.toByte() && byteArray[ i + 3 ] == 0x53.toByte()) {
- var lengthString = ""
- byteArray.copyOfRange( i - 4, i).forEach {
- lengthString += String.format("%02x", it)
- }
- val bodySize = lengthString.toLong(16).toInt()
- tnrs = byteArray.copyOfRange( i -4, i + 8 + bodySize)
- }
+ apng.frames = pngList
+ return apng
+ } else {
+ throw NotApngException()
}
- apng.frames = pngList
-
- } else {
- throw NotApngException()
}
- }
-
- private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
- val ihdr = ArrayList()
- // We need a body var to know body length and generate crc
- val ihdr_body = ArrayList()
- // Add chunk body length
- ihdr.addAll(to4Bytes(ihdrOfApng.ihdrCorps.size).toList())
- // Add IHDR
- ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
- // Add the max width and height
- ihdr_body.addAll(to4Bytes(width).toList())
- ihdr_body.addAll(to4Bytes(height).toList())
- // Add complicated stuff like depth color ...
- // If you want correct png you need same parameters. Good solution is to create new png.
- ihdr_body.addAll(ihdrOfApng.ihdrCorps.copyOfRange(8, 13).toList())
- // Generate CRC
- val crC32 = CRC32()
- crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
- ihdr.addAll(ihdr_body)
- ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
- return ihdr.toByteArray()
- }
-
-
- fun genBitmap() : ArrayList {
- val generatedFrame = ArrayList()
- pngList.forEach {
- val btm = Bitmap.createBitmap(it.maxWidth!!, it.maxHeight!!, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(btm)
- canvas.drawBitmap(BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size), it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null)
- generatedFrame.add(btm)
+ private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
+ val ihdr = ArrayList()
+ // We need a body var to know body length and generate crc
+ val ihdr_body = ArrayList()
+ // Add chunk body length
+ ihdr.addAll(to4Bytes(ihdrOfApng.ihdrCorps.size).toList())
+ // Add IHDR
+ ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
+ // Add the max width and height
+ ihdr_body.addAll(to4Bytes(width).toList())
+ ihdr_body.addAll(to4Bytes(height).toList())
+ // Add complicated stuff like depth color ...
+ // If you want correct png you need same parameters. Good solution is to create new png.
+ ihdr_body.addAll(ihdrOfApng.ihdrCorps.copyOfRange(8, 13).toList())
+ // Generate CRC
+ val crC32 = CRC32()
+ crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
+ ihdr.addAll(ihdr_body)
+ ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
+ return ihdr.toByteArray()
}
- return generatedFrame
}
}
\ No newline at end of file
diff --git a/apng_library/src/main/java/oupson/apng/Apng.kt b/apng_library/src/main/java/oupson/apng/Apng.kt
index dc70eb8..f3b7c52 100644
--- a/apng_library/src/main/java/oupson/apng/Apng.kt
+++ b/apng_library/src/main/java/oupson/apng/Apng.kt
@@ -3,7 +3,6 @@ package oupson.apng
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import oupson.apng.ImageUtils.PnnQuantizer
-import oupson.apng.Utils.Companion.convertImage
import oupson.apng.Utils.Companion.getBlend_op
import oupson.apng.Utils.Companion.getDispose_op
import oupson.apng.Utils.Companion.pngSignature
@@ -12,7 +11,6 @@ import oupson.apng.Utils.Companion.to4Bytes
import oupson.apng.Utils.Companion.toByteArray
import oupson.apng.chunks.IDAT
import oupson.apng.exceptions.NoFrameException
-import java.io.ByteArrayOutputStream
import java.util.zip.CRC32
diff --git a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt
index aa86780..d76c60b 100644
--- a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt
+++ b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt
@@ -2,7 +2,6 @@ package oupson.apng
import android.content.Context
import android.graphics.*
-import android.os.Handler
import android.widget.ImageView
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
@@ -13,7 +12,7 @@ import java.net.URL
/**
* Class to play APNG
*/
-class ApngAnimator(val context: Context) {
+class ApngAnimator(private val context: Context) {
var isPlaying = true
private set(value) {
field = value
@@ -54,7 +53,7 @@ class ApngAnimator(val context: Context) {
doAsync {
this@ApngAnimator.speed = speed
// Download PNG
- APNGDisassembler(file.readBytes()).pngList.apply {
+ APNGDisassembler.disassemble(file.readBytes()).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
@@ -71,7 +70,7 @@ class ApngAnimator(val context: Context) {
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
this@ApngAnimator.speed = speed
// Download PNG
- APNGDisassembler(Loader().load(context, url)).pngList.apply {
+ APNGDisassembler.disassemble(Loader.load(context, url)).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
@@ -87,7 +86,7 @@ class ApngAnimator(val context: Context) {
fun load(byteArray: ByteArray, speed: Float? = null) {
doAsync {
this@ApngAnimator.speed = speed
- APNGDisassembler(byteArray).pngList.apply {
+ APNGDisassembler.disassemble(byteArray).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
diff --git a/apng_library/src/main/java/oupson/apng/ApngFactory.kt b/apng_library/src/main/java/oupson/apng/ApngFactory.kt
deleted file mode 100644
index 1ddb1e0..0000000
--- a/apng_library/src/main/java/oupson/apng/ApngFactory.kt
+++ /dev/null
@@ -1,287 +0,0 @@
-package oupson.apng
-
-import oupson.apng.Utils.Companion.pngSignature
-import oupson.apng.exceptions.NoFrameException
-import java.util.zip.CRC32
-
-
-/**
- * APNG is a class for create apng
- *
- * Call .addFrame() to add a Frame
- * Call .create to get the generated file
- *
- * @author oupson
- *
- * @throws NotPngException
- * @throws NoFrameException
- *
- */
-class ApngFactory {
-
- private var seq = 0
-
- var frames = ArrayList()
-
- /**
- * @return a byte array of the generated png
- *
- * @throws NoFrameException
- */
- fun create(): ByteArray {
-
- if (frames.isNotEmpty()) {
- val res = ArrayList()
-
- // Add PNG signature
- res.addAll(pngSignature.toList())
-
- // Add Image Header
- res.addAll(generate_ihdr().toList())
-
- // Add Animation Controller
- res.addAll(generateACTL())
-
- // Get max height and max width
- val maxHeight = frames.sortedByDescending { it.height }[0].height
- val maxWitdh = frames.sortedByDescending { it.width }[0].width
-
- for (i in 0 until frames.size) {
-
- // If it's the first frame
- if (i == 0) {
- val framesByte = ArrayList()
- // region fcTL
- // Create the fcTL
- val fcTL = ArrayList()
- // Add the length of the chunk body
- framesByte.addAll(byteArrayOf(0x00, 0x00, 0x00, 0x1A).toList())
-
- // Add acTL
- fcTL.addAll(byteArrayOf(0x66, 0x63, 0x54, 0x4c).toList())
-
- // Add the frame number
- fcTL.addAll(to4Bytes(seq).toList())
- seq++
-
- // Add width and height
- fcTL.addAll(to4Bytes(frames[i].width).toList())
- fcTL.addAll(to4Bytes(frames[i].height).toList())
-
- // Calculate offset
- if (frames[i].width < maxWitdh) {
- val xOffset = (maxWitdh / 2) - (frames[i].width / 2)
- fcTL.addAll(to4Bytes(xOffset).toList())
- } else {
- fcTL.addAll(to4Bytes(0).toList())
- }
- if (frames[i].height < maxHeight) {
- val xOffset = (maxHeight / 2) - (frames[i].height / 2)
- fcTL.addAll(to4Bytes(xOffset).toList())
- } else {
- fcTL.addAll(to4Bytes(0).toList())
- }
-
- // Set frame delay
- fcTL.addAll(to2Bytes(frames[i].delay.toInt()).toList())
- fcTL.addAll(to2Bytes(1000).toList())
-
- fcTL.add(0x01)
- fcTL.add(0x00)
-
- val crc = CRC32()
- crc.update(fcTL.toByteArray(), 0, fcTL.size)
- framesByte.addAll(fcTL)
- framesByte.addAll(to4Bytes(crc.value.toInt()).toList())
- // endregion
-
- // region idat
- frames[i].idat.IDATBody.forEach {
- val fdat = ArrayList()
- // Add the chunk body length
- framesByte.addAll(to4Bytes(it.size).toList())
- // Add IDAT
- fdat.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
- // Add chunk body
- fdat.addAll(it.toList())
- // Generate CRC
- val crc1 = CRC32()
- crc1.update(fdat.toByteArray(), 0, fdat.size)
- framesByte.addAll(fdat)
- framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
- }
- // endregion
- res.addAll(framesByte)
- } else {
- val framesByte = ArrayList()
- val fcTL = ArrayList()
- // region fcTL
- framesByte.addAll(byteArrayOf(0x00, 0x00, 0x00, 0x1A).toList())
-
- fcTL.addAll(byteArrayOf(0x66, 0x63, 0x54, 0x4c).toList())
-
- // Frame number
- fcTL.addAll(to4Bytes(seq).toList())
- seq++
- // width and height
- fcTL.addAll(to4Bytes(frames[i].width).toList())
- fcTL.addAll(to4Bytes(frames[i].height).toList())
-
- if (frames[i].width < maxWitdh) {
- val xOffset = (maxWitdh / 2) - (frames[i].width / 2)
- fcTL.addAll(to4Bytes(xOffset).toList())
- } else {
- fcTL.addAll(to4Bytes(0).toList())
- }
- if (frames[i].height < maxHeight) {
- val xOffset = (maxHeight / 2) - (frames[i].height / 2)
- fcTL.addAll(to4Bytes(xOffset).toList())
- } else {
- fcTL.addAll(to4Bytes(0).toList())
- }
-
- // Set frame delay
- fcTL.addAll(to2Bytes(frames[i].delay.toInt()).toList())
- fcTL.addAll(to2Bytes(1000).toList())
-
- fcTL.add(0x01)
- fcTL.add(0x00)
-
- val crc = CRC32()
- crc.update(fcTL.toByteArray(), 0, fcTL.size)
- framesByte.addAll(fcTL)
- framesByte.addAll(to4Bytes(crc.value.toInt()).toList())
- // endregion
-
- // region fdAT
- // Write fdAT
- frames[i].idat.IDATBody.forEach {
- val fdat = ArrayList()
- // Add IDAT size of frame + 4 byte of the seq
- framesByte.addAll(to4Bytes(it.size + 4).toList())
- // Add fdAT
- fdat.addAll(byteArrayOf(0x66, 0x64, 0x41, 0x54).toList())
- // Add Sequence number
- // ! THIS IS NOT FRAME NUMBER
- fdat.addAll(to4Bytes(seq).toList())
- // Increase seq
- seq++
- fdat.addAll(it.toList())
- // Generate CRC
- val crc1 = CRC32()
- crc1.update(fdat.toByteArray(), 0, fdat.size)
- framesByte.addAll(fdat)
- framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
- }
- // endregion
- res.addAll(framesByte)
- }
- }
- // Add IEND body length : 0
- res.addAll(to4Bytes(0).toList())
- // Add IEND
- val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
- // Generate crc for IEND
- val crC32 = CRC32()
- crC32.update(iend, 0, iend.size)
- res.addAll(iend.toList())
- res.addAll(to4Bytes(crC32.value.toInt()).toList())
- return res.toByteArray()
- } else {
- throw NoFrameException()
- }
- }
-
- // Animation Control chunk
- private fun generateACTL(): ArrayList {
- val res = ArrayList()
- val actl = ArrayList()
-
- // Add length bytes
- res.addAll(to4Bytes(8).toList())
-
- // Add acTL
- actl.addAll(byteArrayOf(0x61, 0x63, 0x54, 0x4c).toList())
-
- // Add number of frames
- actl.addAll(to4Bytes(frames.size).toList())
-
- // Number of repeat, 0 to infinite
- actl.addAll(to4Bytes(0).toList())
- res.addAll(actl)
-
- // generate crc
- val crc = CRC32()
- crc.update(actl.toByteArray(), 0, actl.size)
- res.addAll(to4Bytes(crc.value.toInt()).toList())
- return res
- }
-
-
- /**
- * Generate a 4 bytes array from an Int
- * @param i The int
- */
- fun to4Bytes(i: Int): ByteArray {
- val result = ByteArray(4)
- result[0] = (i shr 24).toByte()
- result[1] = (i shr 16).toByte()
- result[2] = (i shr 8).toByte()
- result[3] = i /*>> 0*/.toByte()
- return result
- }
-
- /**
- * Generate a 2 bytes array from an Int
- * @param i The int
- */
- fun to2Bytes(i: Int): ByteArray {
- val result = ByteArray(2)
- result[0] = (i shr 8).toByte()
- result[1] = i /*>> 0*/.toByte()
- return result
- }
-
-
- /**
- * Add a frame to the Animated PNG
- *
- * @param byteArray It's the byteArray of a png
- * @param delay Delay in MS between this frame and the next
- */
- fun addFrame(byteArray: ByteArray, delay: Float = 1000f) {
- frames.add(Frame(byteArray, delay))
- }
-
- // Generate Image Header chunk
- private fun generate_ihdr(): ByteArray {
- val ihdr = ArrayList()
-
- // We need a body var to know body length and generate crc
- val ihdr_body = ArrayList()
-
- // Get max height and max width of all the frames
- val maxHeight = frames.sortedByDescending { it.height }[0].height
- val maxWitdh = frames.sortedByDescending { it.width }[0].width
-
- // Add chunk body length
- ihdr.addAll(to4Bytes(frames[0].ihdr.ihdrCorps.size).toList())
- // Add IHDR
- ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
-
- // Add the max width and height
- ihdr_body.addAll(to4Bytes(maxWitdh).toList())
- ihdr_body.addAll(to4Bytes(maxHeight).toList())
-
- // Add complicated stuff like depth color ...
- // If you want correct png you need same parameters. Good solution is to create new png.
- ihdr_body.addAll(frames[0].ihdr.ihdrCorps.copyOfRange(8, 13).toList())
-
- // Generate CRC
- val crC32 = CRC32()
- crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
- ihdr.addAll(ihdr_body)
- ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
- return ihdr.toByteArray()
- }
-}
\ No newline at end of file
diff --git a/apng_library/src/main/java/oupson/apng/Frame.kt b/apng_library/src/main/java/oupson/apng/Frame.kt
index 47991d2..d551c4b 100644
--- a/apng_library/src/main/java/oupson/apng/Frame.kt
+++ b/apng_library/src/main/java/oupson/apng/Frame.kt
@@ -1,7 +1,6 @@
package oupson.apng
import android.graphics.BitmapFactory
-import oupson.apng.Utils.Companion.convertImage
import oupson.apng.Utils.Companion.isPng
import oupson.apng.Utils.Companion.toByteArray
import oupson.apng.chunks.IDAT
diff --git a/apng_library/src/main/java/oupson/apng/ImageUtils/PnnQuantizer.java b/apng_library/src/main/java/oupson/apng/ImageUtils/PnnQuantizer.java
index 0e51e6f..5aff13e 100644
--- a/apng_library/src/main/java/oupson/apng/ImageUtils/PnnQuantizer.java
+++ b/apng_library/src/main/java/oupson/apng/ImageUtils/PnnQuantizer.java
@@ -20,13 +20,13 @@ import java.util.Map;
import java.util.Random;
public class PnnQuantizer {
- protected final short SHORT_MAX = Short.MAX_VALUE;
- protected final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
- protected boolean hasTransparency = false, hasSemiTransparency = false;
+ private final short SHORT_MAX = Short.MAX_VALUE;
+ private final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
+ private boolean hasTransparency = false, hasSemiTransparency = false;
protected int width, height;
protected int pixels[] = null;
- protected Integer m_transparentColor;
- protected Map closestMap = new HashMap<>();
+ private Integer m_transparentColor;
+ private Map closestMap = new HashMap();
public PnnQuantizer(String fname) throws IOException {
fromBitmap(fname);
@@ -54,7 +54,7 @@ public class PnnQuantizer {
int nn, fw, bk, tm, mtm;
}
- protected int getColorIndex(final int c)
+ private int getColorIndex(final int c)
{
if(hasSemiTransparency)
return (Color.alpha(c) & 0xF0) << 8 | (Color.red(c) & 0xF0) << 4 | (Color.green(c) & 0xF0) | (Color.blue(c) >> 4);
@@ -63,7 +63,7 @@ public class PnnQuantizer {
return (Color.red(c) & 0xF8) << 8 | (Color.green(c) & 0xFC) << 3 | (Color.blue(c) >> 3);
}
- protected double sqr(double value)
+ private double sqr(double value)
{
return value * value;
}
@@ -412,7 +412,7 @@ public class PnnQuantizer {
return true;
}
- protected Bitmap quantize_image(final int[] pixels, int[] qPixels)
+ private Bitmap quantize_image(final int[] pixels, int[] qPixels)
{
int pixelIndex = 0;
boolean odd_scanline = false;
diff --git a/apng_library/src/main/java/oupson/apng/Loader.kt b/apng_library/src/main/java/oupson/apng/Loader.kt
index a0ffb33..efc8a9b 100644
--- a/apng_library/src/main/java/oupson/apng/Loader.kt
+++ b/apng_library/src/main/java/oupson/apng/Loader.kt
@@ -8,31 +8,33 @@ import java.io.IOException
import java.net.URL
class Loader {
- fun load( context: Context, url : URL) : ByteArray {
- val currenDir = context.filesDir
- val fileTXT = File(currenDir, "apngLoader.txt")
- var filePNG = File(currenDir, "apngLoader.png")
- if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
- return filePNG.readBytes()
- } else {
- try {
- val connection = url.openConnection()
- connection.connect()
- val input = BufferedInputStream(connection.getInputStream())
- val output = ByteArrayOutputStream()
- val data = ByteArray(1024)
- var count = 0
- while ({ count = input.read(data); count }() != -1) {
- output.write(data, 0, count)
+ companion object {
+ fun load(context: Context, url: URL): ByteArray {
+ val currenDir = context.filesDir
+ val fileTXT = File(currenDir, "apngLoader.txt")
+ var filePNG = File(currenDir, "apngLoader.png")
+ if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
+ return filePNG.readBytes()
+ } else {
+ try {
+ val connection = url.openConnection()
+ connection.connect()
+ val input = BufferedInputStream(connection.getInputStream())
+ val output = ByteArrayOutputStream()
+ val data = ByteArray(1024)
+ var count = 0
+ while ({ count = input.read(data); count }() != -1) {
+ output.write(data, 0, count)
+ }
+ output.flush()
+ output.close()
+ input.close()
+ fileTXT.writeText(url.toString())
+ filePNG.writeBytes(output.toByteArray())
+ return output.toByteArray()
+ } catch (e: IOException) {
+ throw e
}
- output.flush()
- output.close()
- input.close()
- fileTXT.writeText(url.toString())
- filePNG.writeBytes(output.toByteArray())
- return output.toByteArray()
- } catch (e: IOException) {
- throw e
}
}
}
diff --git a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
index 873c39c..268d522 100644
--- a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
+++ b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
@@ -1,6 +1,5 @@
package oupson.apngcreator
-import android.graphics.BitmapFactory
import android.os.Bundle
import android.os.Environment
import android.support.v7.app.AppCompatActivity
@@ -8,14 +7,11 @@ import android.util.Log
import android.widget.SeekBar
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_main.*
-import oupson.apng.ApngAnimator
-import android.widget.Toast
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import oupson.apng.APNGDisassembler
-import oupson.apng.Apng
+import oupson.apng.ApngAnimator
import oupson.apng.Loader
-import oupson.apngcreator.R.id.imageView
import java.io.File
import java.net.URL
@@ -40,9 +36,9 @@ class MainActivity : AppCompatActivity() {
doAsync {
- Loader().load(applicationContext, URL(imageUrl)).apply {
- val a = APNGDisassembler(this).apng
- a.reduceSize(100, false, 75)
+ Loader.load(applicationContext, URL(imageUrl)).apply {
+ val a = APNGDisassembler.disassemble(this)
+ a.reduceSize(100, false, 60)
File(File(Environment.getExternalStorageDirectory(), "Documents"), "apng.png").writeBytes(a.toByteArray())
runOnUiThread {
toast("Converted ! ")