Working on optimiser functionality to reduce APNG size.
Loader now static Disassembler now static Optimize imports
This commit is contained in:
parent
0150810541
commit
66055221f1
|
@ -1,9 +1,6 @@
|
||||||
package oupson.apng
|
package oupson.apng
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.util.Log
|
|
||||||
import oupson.apng.Utils.Companion.isApng
|
import oupson.apng.Utils.Companion.isApng
|
||||||
import oupson.apng.Utils.Companion.pngSignature
|
import oupson.apng.Utils.Companion.pngSignature
|
||||||
import oupson.apng.Utils.Companion.to4Bytes
|
import oupson.apng.Utils.Companion.to4Bytes
|
||||||
|
@ -12,240 +9,227 @@ import oupson.apng.chunks.fcTL
|
||||||
import oupson.apng.exceptions.NotApngException
|
import oupson.apng.exceptions.NotApngException
|
||||||
import java.util.zip.CRC32
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
class APNGDisassembler(val byteArray: ByteArray) {
|
class APNGDisassembler() {
|
||||||
val pngList = ArrayList<Frame>()
|
companion object {
|
||||||
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
|
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())
|
||||||
|
|
||||||
init {
|
apng.cover = BitmapFactory.decodeByteArray(cover.toByteArray(), 0, cover.size)
|
||||||
if (isApng(byteArray)) {
|
}
|
||||||
apng = Apng()
|
png = ArrayList()
|
||||||
val ihdr = IHDR()
|
val fcTL = fcTL(byteArray.copyOfRange(i - 4, i + 36))
|
||||||
ihdr.parseIHDR(byteArray)
|
delay = fcTL.delay
|
||||||
maxWidth = ihdr.pngWidth
|
yOffset = fcTL.y_offset
|
||||||
maxHeight = ihdr.pngHeight
|
xOffset = fcTL.x_offset
|
||||||
for(i in 0 until byteArray.size) {
|
blend_op = fcTL.blend_op
|
||||||
// find new Frame with fcTL
|
dispose_op = fcTL.dispose_op
|
||||||
if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x63.toByte() && byteArray[ i + 2 ] == 0x54.toByte() && byteArray[ i + 3 ] == 0x4C.toByte()) {
|
val width = fcTL.pngWidth
|
||||||
if (png == null) {
|
val height = fcTL.pngHeight
|
||||||
if (cover != null) {
|
png.addAll(pngSignature.toList())
|
||||||
cover!!.addAll(to4Bytes(0).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
|
// Add IEND
|
||||||
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
||||||
// Generate crc for IEND
|
// Generate crc for IEND
|
||||||
val crC32 = CRC32()
|
val crC32 = CRC32()
|
||||||
crC32.update(iend, 0, iend.size)
|
crC32.update(iend, 0, iend.size)
|
||||||
cover!!.addAll(iend.toList())
|
png.addAll(iend.toList())
|
||||||
cover!!.addAll(to4Bytes(crC32.value.toInt()).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()
|
||||||
}
|
|
||||||
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) {
|
val bodySize = {
|
||||||
png!!.addAll(plte!!.toList())
|
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) {
|
yOffset = fcTL.y_offset
|
||||||
png!!.addAll(tnrs!!.toList())
|
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 {
|
} else if (i == byteArray.size - 1) {
|
||||||
// Add IEND body length : 0
|
|
||||||
png!!.addAll(to4Bytes(0).toList())
|
png!!.addAll(to4Bytes(0).toList())
|
||||||
// Add IEND
|
// Add IEND
|
||||||
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
||||||
// Generate crc for IEND
|
// Generate crc for IEND
|
||||||
val crC32 = CRC32()
|
val crC32 = CRC32()
|
||||||
crC32.update(iend, 0, iend.size)
|
crC32.update(iend, 0, iend.size)
|
||||||
png!!.addAll(iend.toList())
|
png.addAll(iend.toList())
|
||||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
png.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||||
pngList.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
|
pngList.add(Frame(png.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight, blend_op, dispose_op))
|
||||||
|
}
|
||||||
png = ArrayList()
|
// 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()) {
|
||||||
val bodySize = {
|
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 = ""
|
var lengthString = ""
|
||||||
byteArray.copyOfRange(i - 4, i).forEach {
|
byteArray.copyOfRange(i - 4, i).forEach {
|
||||||
lengthString += String.format("%02x", it)
|
lengthString += String.format("%02x", it)
|
||||||
}
|
}
|
||||||
lengthString.toLong(16).toInt()
|
val bodySize = lengthString.toLong(16).toInt()
|
||||||
}()
|
cover.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||||
val newBytes = byteArray.copyOfRange(i - 4, i + 4 + bodySize)
|
val body = ArrayList<Byte>()
|
||||||
val fcTL = fcTL(newBytes)
|
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||||
delay = fcTL.delay
|
// Get image bytes
|
||||||
|
for (j in i + 4 until i + 4 + bodySize) {
|
||||||
yOffset = fcTL.y_offset
|
body.add(byteArray[j])
|
||||||
xOffset = fcTL.x_offset
|
}
|
||||||
|
val crC32 = CRC32()
|
||||||
blend_op = fcTL.blend_op
|
crC32.update(body.toByteArray(), 0, body.size)
|
||||||
dispose_op = fcTL.dispose_op
|
cover.addAll(body)
|
||||||
val width = fcTL.pngWidth
|
cover.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||||
val height = fcTL.pngHeight
|
} else {
|
||||||
png!!.addAll(pngSignature.toList())
|
// Find the chunk length
|
||||||
png!!.addAll(generate_ihdr(ihdr, width, height).toList())
|
var lengthString = ""
|
||||||
if (plte != null) {
|
byteArray.copyOfRange(i - 4, i).forEach {
|
||||||
png!!.addAll(plte!!.toList())
|
lengthString += String.format("%02x", it)
|
||||||
}
|
}
|
||||||
|
val bodySize = lengthString.toLong(16).toInt()
|
||||||
if (tnrs != null) {
|
png.addAll(byteArray.copyOfRange(i - 4, i).toList())
|
||||||
png!!.addAll(tnrs!!.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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Check if is fdAT
|
||||||
else if (i == byteArray.size - 1) {
|
else if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x64.toByte() && byteArray[i + 2] == 0x41.toByte() && byteArray[i + 3] == 0x54.toByte()) {
|
||||||
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
|
// Find the chunk length
|
||||||
var lengthString = ""
|
var lengthString = ""
|
||||||
byteArray.copyOfRange( i - 4, i).forEach {
|
byteArray.copyOfRange(i - 4, i).forEach {
|
||||||
lengthString += String.format("%02x", it)
|
lengthString += String.format("%02x", it)
|
||||||
}
|
}
|
||||||
val bodySize = lengthString.toLong(16).toInt()
|
val bodySize = lengthString.toLong(16).toInt()
|
||||||
cover!!.addAll(byteArray.copyOfRange(i-4, i).toList())
|
png!!.addAll(to4Bytes(bodySize - 4).toList())
|
||||||
val body = ArrayList<Byte>()
|
val body = ArrayList<Byte>()
|
||||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||||
// Get image bytes
|
// 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])
|
body.add(byteArray[j])
|
||||||
}
|
}
|
||||||
val crC32 = CRC32()
|
val crC32 = CRC32()
|
||||||
crC32.update(body.toByteArray(), 0, body.size)
|
crC32.update(body.toByteArray(), 0, body.size)
|
||||||
cover!!.addAll(body)
|
png.addAll(body)
|
||||||
cover!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
png.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||||
} else {
|
}
|
||||||
// Find the chunk length
|
// 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 = ""
|
var lengthString = ""
|
||||||
byteArray.copyOfRange( i - 4, i).forEach {
|
byteArray.copyOfRange(i - 4, i).forEach {
|
||||||
lengthString += String.format("%02x", it)
|
lengthString += String.format("%02x", it)
|
||||||
}
|
}
|
||||||
val bodySize = lengthString.toLong(16).toInt()
|
val bodySize = lengthString.toLong(16).toInt()
|
||||||
png!!.addAll(byteArray.copyOfRange(i-4, i).toList())
|
plte = byteArray.copyOfRange(i - 4, i + 8 + bodySize)
|
||||||
val body = ArrayList<Byte>()
|
}
|
||||||
body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
// Get tnrs chunk if exist. Used for transparency
|
||||||
// Get image bytes
|
else if (byteArray[i] == 0x74.toByte() && byteArray[i + 1] == 0x52.toByte() && byteArray[i + 2] == 0x4E.toByte() && byteArray[i + 3] == 0x53.toByte()) {
|
||||||
for (j in i +4 until i + 4 + bodySize) {
|
var lengthString = ""
|
||||||
body.add(byteArray[j])
|
byteArray.copyOfRange(i - 4, i).forEach {
|
||||||
|
lengthString += String.format("%02x", it)
|
||||||
}
|
}
|
||||||
val crC32 = CRC32()
|
val bodySize = lengthString.toLong(16).toInt()
|
||||||
crC32.update(body.toByteArray(), 0, body.size)
|
tnrs = byteArray.copyOfRange(i - 4, i + 8 + bodySize)
|
||||||
png!!.addAll(body)
|
|
||||||
png!!.addAll(to4Bytes(crC32.value.toInt()).toList())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if is fdAT
|
apng.frames = pngList
|
||||||
else if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x64.toByte() && byteArray[ i + 2 ] == 0x41.toByte() && byteArray[ i + 3 ] == 0x54.toByte()) {
|
return apng
|
||||||
// Find the chunk length
|
} else {
|
||||||
var lengthString = ""
|
throw NotApngException()
|
||||||
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<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])
|
|
||||||
}
|
|
||||||
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
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw NotApngException()
|
|
||||||
}
|
}
|
||||||
}
|
private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
|
||||||
|
val ihdr = ArrayList<Byte>()
|
||||||
private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray {
|
// We need a body var to know body length and generate crc
|
||||||
val ihdr = ArrayList<Byte>()
|
val ihdr_body = ArrayList<Byte>()
|
||||||
// We need a body var to know body length and generate crc
|
// Add chunk body length
|
||||||
val ihdr_body = ArrayList<Byte>()
|
ihdr.addAll(to4Bytes(ihdrOfApng.ihdrCorps.size).toList())
|
||||||
// Add chunk body length
|
// Add IHDR
|
||||||
ihdr.addAll(to4Bytes(ihdrOfApng.ihdrCorps.size).toList())
|
ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
|
||||||
// Add IHDR
|
// Add the max width and height
|
||||||
ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
|
ihdr_body.addAll(to4Bytes(width).toList())
|
||||||
// Add the max width and height
|
ihdr_body.addAll(to4Bytes(height).toList())
|
||||||
ihdr_body.addAll(to4Bytes(width).toList())
|
// Add complicated stuff like depth color ...
|
||||||
ihdr_body.addAll(to4Bytes(height).toList())
|
// If you want correct png you need same parameters. Good solution is to create new png.
|
||||||
// Add complicated stuff like depth color ...
|
ihdr_body.addAll(ihdrOfApng.ihdrCorps.copyOfRange(8, 13).toList())
|
||||||
// If you want correct png you need same parameters. Good solution is to create new png.
|
// Generate CRC
|
||||||
ihdr_body.addAll(ihdrOfApng.ihdrCorps.copyOfRange(8, 13).toList())
|
val crC32 = CRC32()
|
||||||
// Generate CRC
|
crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
|
||||||
val crC32 = CRC32()
|
ihdr.addAll(ihdr_body)
|
||||||
crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
|
ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||||
ihdr.addAll(ihdr_body)
|
return ihdr.toByteArray()
|
||||||
ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
|
|
||||||
return ihdr.toByteArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun genBitmap() : ArrayList<Bitmap> {
|
|
||||||
val generatedFrame = ArrayList<Bitmap>()
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
return generatedFrame
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ package oupson.apng
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import oupson.apng.ImageUtils.PnnQuantizer
|
import oupson.apng.ImageUtils.PnnQuantizer
|
||||||
import oupson.apng.Utils.Companion.convertImage
|
|
||||||
import oupson.apng.Utils.Companion.getBlend_op
|
import oupson.apng.Utils.Companion.getBlend_op
|
||||||
import oupson.apng.Utils.Companion.getDispose_op
|
import oupson.apng.Utils.Companion.getDispose_op
|
||||||
import oupson.apng.Utils.Companion.pngSignature
|
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.Utils.Companion.toByteArray
|
||||||
import oupson.apng.chunks.IDAT
|
import oupson.apng.chunks.IDAT
|
||||||
import oupson.apng.exceptions.NoFrameException
|
import oupson.apng.exceptions.NoFrameException
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.util.zip.CRC32
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package oupson.apng
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.os.Handler
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.doAsync
|
||||||
import org.jetbrains.anko.uiThread
|
import org.jetbrains.anko.uiThread
|
||||||
|
@ -13,7 +12,7 @@ import java.net.URL
|
||||||
/**
|
/**
|
||||||
* Class to play APNG
|
* Class to play APNG
|
||||||
*/
|
*/
|
||||||
class ApngAnimator(val context: Context) {
|
class ApngAnimator(private val context: Context) {
|
||||||
var isPlaying = true
|
var isPlaying = true
|
||||||
private set(value) {
|
private set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
@ -54,7 +53,7 @@ class ApngAnimator(val context: Context) {
|
||||||
doAsync {
|
doAsync {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
// Download PNG
|
// Download PNG
|
||||||
APNGDisassembler(file.readBytes()).pngList.apply {
|
APNGDisassembler.disassemble(file.readBytes()).frames.apply {
|
||||||
draw(this)
|
draw(this)
|
||||||
}
|
}
|
||||||
setupAnimationDrawableAndStart()
|
setupAnimationDrawableAndStart()
|
||||||
|
@ -71,7 +70,7 @@ class ApngAnimator(val context: Context) {
|
||||||
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
// Download PNG
|
// Download PNG
|
||||||
APNGDisassembler(Loader().load(context, url)).pngList.apply {
|
APNGDisassembler.disassemble(Loader.load(context, url)).frames.apply {
|
||||||
draw(this)
|
draw(this)
|
||||||
}
|
}
|
||||||
setupAnimationDrawableAndStart()
|
setupAnimationDrawableAndStart()
|
||||||
|
@ -87,7 +86,7 @@ class ApngAnimator(val context: Context) {
|
||||||
fun load(byteArray: ByteArray, speed: Float? = null) {
|
fun load(byteArray: ByteArray, speed: Float? = null) {
|
||||||
doAsync {
|
doAsync {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
APNGDisassembler(byteArray).pngList.apply {
|
APNGDisassembler.disassemble(byteArray).frames.apply {
|
||||||
draw(this)
|
draw(this)
|
||||||
}
|
}
|
||||||
setupAnimationDrawableAndStart()
|
setupAnimationDrawableAndStart()
|
||||||
|
|
|
@ -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<Frame>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a byte array of the generated png
|
|
||||||
*
|
|
||||||
* @throws NoFrameException
|
|
||||||
*/
|
|
||||||
fun create(): ByteArray {
|
|
||||||
|
|
||||||
if (frames.isNotEmpty()) {
|
|
||||||
val res = ArrayList<Byte>()
|
|
||||||
|
|
||||||
// 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<Byte>()
|
|
||||||
// region fcTL
|
|
||||||
// Create the fcTL
|
|
||||||
val fcTL = ArrayList<Byte>()
|
|
||||||
// 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<Byte>()
|
|
||||||
// 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<Byte>()
|
|
||||||
val fcTL = ArrayList<Byte>()
|
|
||||||
// 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<Byte>()
|
|
||||||
// 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<Byte> {
|
|
||||||
val res = ArrayList<Byte>()
|
|
||||||
val actl = ArrayList<Byte>()
|
|
||||||
|
|
||||||
// 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<Byte>()
|
|
||||||
|
|
||||||
// We need a body var to know body length and generate crc
|
|
||||||
val ihdr_body = ArrayList<Byte>()
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package oupson.apng
|
package oupson.apng
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import oupson.apng.Utils.Companion.convertImage
|
|
||||||
import oupson.apng.Utils.Companion.isPng
|
import oupson.apng.Utils.Companion.isPng
|
||||||
import oupson.apng.Utils.Companion.toByteArray
|
import oupson.apng.Utils.Companion.toByteArray
|
||||||
import oupson.apng.chunks.IDAT
|
import oupson.apng.chunks.IDAT
|
||||||
|
|
|
@ -20,13 +20,13 @@ import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class PnnQuantizer {
|
public class PnnQuantizer {
|
||||||
protected final short SHORT_MAX = Short.MAX_VALUE;
|
private final short SHORT_MAX = Short.MAX_VALUE;
|
||||||
protected final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
private final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
||||||
protected boolean hasTransparency = false, hasSemiTransparency = false;
|
private boolean hasTransparency = false, hasSemiTransparency = false;
|
||||||
protected int width, height;
|
protected int width, height;
|
||||||
protected int pixels[] = null;
|
protected int pixels[] = null;
|
||||||
protected Integer m_transparentColor;
|
private Integer m_transparentColor;
|
||||||
protected Map<Integer, short[]> closestMap = new HashMap<>();
|
private Map<Integer, short[]> closestMap = new HashMap();
|
||||||
|
|
||||||
public PnnQuantizer(String fname) throws IOException {
|
public PnnQuantizer(String fname) throws IOException {
|
||||||
fromBitmap(fname);
|
fromBitmap(fname);
|
||||||
|
@ -54,7 +54,7 @@ public class PnnQuantizer {
|
||||||
int nn, fw, bk, tm, mtm;
|
int nn, fw, bk, tm, mtm;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getColorIndex(final int c)
|
private int getColorIndex(final int c)
|
||||||
{
|
{
|
||||||
if(hasSemiTransparency)
|
if(hasSemiTransparency)
|
||||||
return (Color.alpha(c) & 0xF0) << 8 | (Color.red(c) & 0xF0) << 4 | (Color.green(c) & 0xF0) | (Color.blue(c) >> 4);
|
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);
|
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;
|
return value * value;
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ public class PnnQuantizer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Bitmap quantize_image(final int[] pixels, int[] qPixels)
|
private Bitmap quantize_image(final int[] pixels, int[] qPixels)
|
||||||
{
|
{
|
||||||
int pixelIndex = 0;
|
int pixelIndex = 0;
|
||||||
boolean odd_scanline = false;
|
boolean odd_scanline = false;
|
||||||
|
|
|
@ -8,31 +8,33 @@ import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
class Loader {
|
class Loader {
|
||||||
fun load( context: Context, url : URL) : ByteArray {
|
companion object {
|
||||||
val currenDir = context.filesDir
|
fun load(context: Context, url: URL): ByteArray {
|
||||||
val fileTXT = File(currenDir, "apngLoader.txt")
|
val currenDir = context.filesDir
|
||||||
var filePNG = File(currenDir, "apngLoader.png")
|
val fileTXT = File(currenDir, "apngLoader.txt")
|
||||||
if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
|
var filePNG = File(currenDir, "apngLoader.png")
|
||||||
return filePNG.readBytes()
|
if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
|
||||||
} else {
|
return filePNG.readBytes()
|
||||||
try {
|
} else {
|
||||||
val connection = url.openConnection()
|
try {
|
||||||
connection.connect()
|
val connection = url.openConnection()
|
||||||
val input = BufferedInputStream(connection.getInputStream())
|
connection.connect()
|
||||||
val output = ByteArrayOutputStream()
|
val input = BufferedInputStream(connection.getInputStream())
|
||||||
val data = ByteArray(1024)
|
val output = ByteArrayOutputStream()
|
||||||
var count = 0
|
val data = ByteArray(1024)
|
||||||
while ({ count = input.read(data); count }() != -1) {
|
var count = 0
|
||||||
output.write(data, 0, count)
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package oupson.apngcreator
|
package oupson.apngcreator
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
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
|
||||||
|
@ -8,14 +7,11 @@ import android.util.Log
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import oupson.apng.ApngAnimator
|
|
||||||
import android.widget.Toast
|
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.doAsync
|
||||||
import org.jetbrains.anko.toast
|
import org.jetbrains.anko.toast
|
||||||
import oupson.apng.APNGDisassembler
|
import oupson.apng.APNGDisassembler
|
||||||
import oupson.apng.Apng
|
import oupson.apng.ApngAnimator
|
||||||
import oupson.apng.Loader
|
import oupson.apng.Loader
|
||||||
import oupson.apngcreator.R.id.imageView
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
@ -40,9 +36,9 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
||||||
doAsync {
|
doAsync {
|
||||||
Loader().load(applicationContext, URL(imageUrl)).apply {
|
Loader.load(applicationContext, URL(imageUrl)).apply {
|
||||||
val a = APNGDisassembler(this).apng
|
val a = APNGDisassembler.disassemble(this)
|
||||||
a.reduceSize(100, false, 75)
|
a.reduceSize(100, false, 60)
|
||||||
File(File(Environment.getExternalStorageDirectory(), "Documents"), "apng.png").writeBytes(a.toByteArray())
|
File(File(Environment.getExternalStorageDirectory(), "Documents"), "apng.png").writeBytes(a.toByteArray())
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
toast("Converted ! ")
|
toast("Converted ! ")
|
||||||
|
|
Loading…
Reference in New Issue