New apng disassembler
This commit is contained in:
parent
21668d50c7
commit
cefb4ca9f4
|
@ -8,228 +8,207 @@ import oupson.apng.utils.Utils
|
|||
import oupson.apng.utils.Utils.Companion.isApng
|
||||
import oupson.apng.utils.Utils.Companion.pngSignature
|
||||
import oupson.apng.utils.Utils.Companion.to4Bytes
|
||||
import java.util.*
|
||||
import java.util.zip.CRC32
|
||||
|
||||
class APNGDisassembler {
|
||||
companion object {
|
||||
private var png: ArrayList<Byte>? = null
|
||||
private var cover: ArrayList<Byte>? = null
|
||||
private var delay = -1f
|
||||
private var yOffset = -1
|
||||
private var xOffset = -1
|
||||
private var plte: ByteArray? = null
|
||||
private var tnrs: ByteArray? = null
|
||||
private var maxWidth = 0
|
||||
private var maxHeight = 0
|
||||
private var blend_op: Utils.Companion.blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
|
||||
private var dispose_op: Utils.Companion.dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
|
||||
var apng: Apng = Apng()
|
||||
private val ihdr = IHDR()
|
||||
fun disassemble(byteArray: ByteArray) : Apng {
|
||||
val pngList = ArrayList<Frame>()
|
||||
var png: ArrayList<Byte>? = null
|
||||
var cover: ArrayList<Byte>? = 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())
|
||||
apng.cover = BitmapFactory.decodeByteArray(cover.toByteArray(), 0, cover.size)
|
||||
}
|
||||
var cursor = 8
|
||||
while (cursor < byteArray.size) {
|
||||
val chunk = byteArray.copyOfRange(cursor, cursor + parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||
parseChunk(chunk)
|
||||
cursor += parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||
}
|
||||
return apng
|
||||
} else {
|
||||
throw NotApngException()
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
private fun parseLength(byteArray: ByteArray) : Int {
|
||||
var lengthString = ""
|
||||
byteArray.forEach {
|
||||
lengthString += String.format("%02x", it)
|
||||
}
|
||||
return lengthString.toLong(16).toInt()
|
||||
}
|
||||
|
||||
if (plte != null) {
|
||||
png.addAll(plte.toList())
|
||||
}
|
||||
private fun generateIhdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
|
||||
val ihdr = ArrayList<Byte>()
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdrBody = ArrayList<Byte>()
|
||||
// Add chunk body length
|
||||
ihdr.addAll(to4Bytes(ihdrOfApng.ihdrCorps.size).toList())
|
||||
// Add IHDR
|
||||
ihdrBody.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
|
||||
// Add the max width and height
|
||||
ihdrBody.addAll(to4Bytes(width).toList())
|
||||
ihdrBody.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.
|
||||
ihdrBody.addAll(ihdrOfApng.ihdrCorps.copyOfRange(8, 13).toList())
|
||||
// Generate CRC
|
||||
val crC32 = CRC32()
|
||||
crC32.update(ihdrBody.toByteArray(), 0, ihdrBody.size)
|
||||
ihdr.addAll(ihdrBody)
|
||||
ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
return ihdr.toByteArray()
|
||||
}
|
||||
|
||||
if (tnrs != null) {
|
||||
png.addAll(tnrs.toList())
|
||||
}
|
||||
} else {
|
||||
// Add IEND body length : 0
|
||||
png.addAll(to4Bytes(0).toList())
|
||||
private fun parseChunk(byteArray: ByteArray) {
|
||||
val i = 4
|
||||
val name = Arrays.toString(byteArray.copyOfRange(i, i +4))
|
||||
when (name) {
|
||||
Utils.fcTL -> {
|
||||
if (png == null) {
|
||||
cover?.let {
|
||||
it.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 = {
|
||||
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())
|
||||
}
|
||||
it.addAll(iend.toList())
|
||||
it.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
apng.cover = BitmapFactory.decodeByteArray(it.toByteArray(), 0, it.size)
|
||||
}
|
||||
} else if (i == byteArray.size - 1) {
|
||||
|
||||
png = ArrayList()
|
||||
val fcTL = fcTL(byteArray)
|
||||
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(generateIhdr(ihdr, width, height).toList())
|
||||
|
||||
plte?.let {
|
||||
png!!.addAll(it.toList())
|
||||
}
|
||||
|
||||
tnrs?.let {
|
||||
png!!.addAll(it.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)
|
||||
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)
|
||||
}
|
||||
val bodySize = lengthString.toLong(16).toInt()
|
||||
cover.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
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<Byte>()
|
||||
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())
|
||||
png!!.addAll(iend.toList())
|
||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
apng.frames.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
|
||||
|
||||
png = ArrayList()
|
||||
|
||||
val fcTL = fcTL(byteArray)
|
||||
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(generateIhdr(ihdr, width, height).toList())
|
||||
plte?.let {
|
||||
png!!.addAll(it.toList())
|
||||
}
|
||||
|
||||
tnrs?.let {
|
||||
png!!.addAll(it.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()) {
|
||||
}
|
||||
Utils.IEND -> {
|
||||
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())
|
||||
apng.frames.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
|
||||
}
|
||||
Utils.IDAT -> {
|
||||
if (png == null) {
|
||||
if (cover == null) {
|
||||
cover = ArrayList()
|
||||
cover!!.addAll(pngSignature.toList())
|
||||
cover!!.addAll(generateIhdr(ihdr, maxWidth, maxHeight).toList())
|
||||
}
|
||||
// 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 bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
cover!!.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
for (j in i + 8 until i + 4 + bodySize) {
|
||||
body.add(byteArray[j])
|
||||
}
|
||||
body.addAll(byteArray.copyOfRange(i + 4,i + 4 + bodySize).toList())
|
||||
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)
|
||||
cover!!.addAll(body)
|
||||
cover!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
} else {
|
||||
// Find the chunk length
|
||||
val bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
png!!.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
body.addAll(byteArray.copyOfRange(i + 4,i + 4 + bodySize).toList())
|
||||
val crC32 = CRC32()
|
||||
crC32.update(body.toByteArray(), 0, body.size)
|
||||
png!!.addAll(body)
|
||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
}
|
||||
}
|
||||
apng.frames = pngList
|
||||
return apng
|
||||
} else {
|
||||
throw NotApngException()
|
||||
Utils.fdAT -> {
|
||||
// Find the chunk length
|
||||
val bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
png!!.addAll(to4Bytes(bodySize - 4).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
body.addAll(byteArray.copyOfRange(i + 8,i + 4 + bodySize).toList())
|
||||
val crC32 = CRC32()
|
||||
crC32.update(body.toByteArray(), 0, body.size)
|
||||
png!!.addAll(body)
|
||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
}
|
||||
Utils.plte -> {
|
||||
plte = byteArray
|
||||
}
|
||||
Utils.tnrs -> {
|
||||
tnrs = byteArray
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
|
||||
val ihdr = ArrayList<Byte>()
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdr_body = ArrayList<Byte>()
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -150,7 +150,7 @@ class ApngAnimator(private val context: Context) {
|
|||
this@ApngAnimator.speed = speed
|
||||
scaleType = apngAnimatorOptions?.scaleType
|
||||
// Download PNG
|
||||
snapshotAPNGDisassembler.disassemble(this).frames.apply {
|
||||
APNGDisassembler.disassemble(this).frames.apply {
|
||||
draw(this).apply {
|
||||
setupAnimationDrawableAndStart(this)
|
||||
}
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
package oupson.apng
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Debug
|
||||
import android.util.Log
|
||||
import oupson.apng.chunks.IHDR
|
||||
import oupson.apng.chunks.fcTL
|
||||
import oupson.apng.exceptions.NotApngException
|
||||
import oupson.apng.utils.Utils
|
||||
import oupson.apng.utils.Utils.Companion.fcTL
|
||||
import oupson.apng.utils.Utils.Companion.isApng
|
||||
import oupson.apng.utils.Utils.Companion.pngSignature
|
||||
import oupson.apng.utils.Utils.Companion.to4Bytes
|
||||
import java.util.*
|
||||
import java.util.zip.CRC32
|
||||
|
||||
class snapshotAPNGDisassembler {
|
||||
companion object {
|
||||
val pngList = ArrayList<Frame>()
|
||||
var png: ArrayList<Byte>? = null
|
||||
var cover: ArrayList<Byte>? = 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
|
||||
var apng: Apng = Apng()
|
||||
val ihdr = IHDR()
|
||||
fun disassemble(byteArray: ByteArray) : Apng {
|
||||
if (isApng(byteArray)) {
|
||||
apng = Apng()
|
||||
ihdr.parseIHDR(byteArray)
|
||||
maxWidth = ihdr.pngWidth
|
||||
maxHeight = ihdr.pngHeight
|
||||
var cursor = 8
|
||||
while (cursor < byteArray.size) {
|
||||
var chunk = byteArray.copyOfRange(cursor, cursor + parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||
parseChunk(chunk)
|
||||
cursor += parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||
}
|
||||
apng.frames = pngList
|
||||
return apng
|
||||
} else {
|
||||
throw NotApngException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseLength(byteArray: ByteArray) : Int {
|
||||
var lengthString = ""
|
||||
byteArray.forEach {
|
||||
lengthString += String.format("%02x", it)
|
||||
}
|
||||
return lengthString.toLong(16).toInt()
|
||||
}
|
||||
|
||||
private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
|
||||
val ihdr = ArrayList<Byte>()
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdr_body = ArrayList<Byte>()
|
||||
// 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()
|
||||
}
|
||||
|
||||
private fun parseChunk(byteArray: ByteArray) {
|
||||
val i = 4
|
||||
val name = Arrays.toString(byteArray.copyOfRange(i, i +4))
|
||||
when (name) {
|
||||
Utils.fcTL -> {
|
||||
if (png == null) {
|
||||
cover?.let {
|
||||
it.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)
|
||||
it.addAll(iend.toList())
|
||||
it.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
apng.cover = BitmapFactory.decodeByteArray(it.toByteArray(), 0, it.size)
|
||||
}
|
||||
|
||||
png = ArrayList()
|
||||
val fcTL = fcTL(byteArray)
|
||||
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())
|
||||
|
||||
plte?.let {
|
||||
png!!.addAll(it.toList())
|
||||
}
|
||||
|
||||
tnrs?.let {
|
||||
png!!.addAll(it.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)
|
||||
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 fcTL = fcTL(byteArray)
|
||||
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())
|
||||
plte?.let {
|
||||
png!!.addAll(it.toList())
|
||||
}
|
||||
|
||||
tnrs?.let {
|
||||
png!!.addAll(it.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
Utils.IEND -> {
|
||||
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))
|
||||
}
|
||||
Utils.IDAT -> {
|
||||
if (png == null) {
|
||||
if (cover == null) {
|
||||
cover = ArrayList()
|
||||
cover!!.addAll(pngSignature.toList())
|
||||
cover!!.addAll(generate_ihdr(ihdr, maxWidth, maxHeight).toList())
|
||||
}
|
||||
// Find the chunk length
|
||||
val bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
cover!!.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
body.addAll(byteArray.copyOfRange(i + 4,i + 4 + bodySize).toList())
|
||||
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
|
||||
val bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
png!!.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
body.addAll(byteArray.copyOfRange(i + 4,i + 4 + bodySize).toList())
|
||||
val crC32 = CRC32()
|
||||
crC32.update(body.toByteArray(), 0, body.size)
|
||||
png!!.addAll(body)
|
||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
}
|
||||
}
|
||||
Utils.fdAT -> {
|
||||
// Find the chunk length
|
||||
val bodySize = parseLength(byteArray.copyOfRange(i - 4, i))
|
||||
png!!.addAll(to4Bytes(bodySize - 4).toList())
|
||||
val body = ArrayList<Byte>()
|
||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Get image bytes
|
||||
body.addAll(byteArray.copyOfRange(i + 8,i + 4 + bodySize).toList())
|
||||
val crC32 = CRC32()
|
||||
crC32.update(body.toByteArray(), 0, body.size)
|
||||
png!!.addAll(body)
|
||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
}
|
||||
Utils.plte -> {
|
||||
plte = byteArray
|
||||
}
|
||||
Utils.tnrs -> {
|
||||
tnrs = byteArray
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,9 +13,9 @@ import java.util.*
|
|||
class MainActivity : AppCompatActivity() {
|
||||
lateinit var animator: ApngAnimator
|
||||
|
||||
val imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"
|
||||
// val imageUrl = "http://orig06.deviantart.net/7812/f/2012/233/7/5/twilight_rapidash_shaded_and_animated_by_tamalesyatole-d5bz7hd.png"
|
||||
//val imageUrl = "https://raw.githubusercontent.com/tinify/iMessage-Panda-sticker/master/StickerPackExtension/Stickers.xcstickers/Sticker%20Pack.stickerpack/panda.sticker/panda.png"
|
||||
//val imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"
|
||||
//val imageUrl = "http://orig06.deviantart.net/7812/f/2012/233/7/5/twilight_rapidash_shaded_and_animated_by_tamalesyatole-d5bz7hd.png"
|
||||
val imageUrl = "https://raw.githubusercontent.com/tinify/iMessage-Panda-sticker/master/StickerPackExtension/Stickers.xcstickers/Sticker%20Pack.stickerpack/panda.sticker/panda.png"
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
|
Loading…
Reference in New Issue