Working on ExperimentalApngEncoder.kt
This commit is contained in:
parent
dfc427a54f
commit
7b6fbfdf57
|
@ -246,9 +246,9 @@ class ExperimentalApngEncoder(
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun writeEnd() {
|
fun writeEnd() {
|
||||||
// Add IEND body length : 0
|
// Add IEND body length : 0
|
||||||
outputStream.write(Utils.to4BytesArray(0))
|
outputStream.write(byteArrayOf(0, 0, 0, 0))
|
||||||
// Add IEND
|
// Add IEND
|
||||||
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
val iend = Utils.IEND
|
||||||
// Generate crc for IEND
|
// Generate crc for IEND
|
||||||
crc.reset()
|
crc.reset()
|
||||||
crc.update(iend, 0, iend.size)
|
crc.update(iend, 0, iend.size)
|
||||||
|
@ -263,20 +263,19 @@ class ExperimentalApngEncoder(
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun writeHeader() {
|
private fun writeHeader() {
|
||||||
writeInt4(13)
|
writeInt4(13)
|
||||||
val header = arrayListOf<Byte>()
|
val header = Utils.IHDR
|
||||||
header.addAll(Utils.IHDR.asList())
|
.plus(Utils.to4BytesArray(width))
|
||||||
header.addAll(Utils.to4Bytes(width))
|
.plus(Utils.to4BytesArray(height))
|
||||||
header.addAll(Utils.to4Bytes(height))
|
.plus(8) // bit depth
|
||||||
header.add(8) // bit depth
|
.plus(if (encodeAlpha) 6 else 2) // direct model
|
||||||
header.add(if (encodeAlpha) 6 else 2) // direct model
|
.plus(0) // compression method
|
||||||
header.add(0) // compression method
|
.plus(0) // filter method
|
||||||
header.add(0) // filter method
|
.plus(0) // no interlace
|
||||||
header.add(0) // no interlace
|
|
||||||
outputStream.write(
|
outputStream.write(
|
||||||
header.toByteArray()
|
header
|
||||||
)
|
)
|
||||||
crc.reset()
|
crc.reset()
|
||||||
crc.update(header.toByteArray())
|
crc.update(header)
|
||||||
crcValue = crc.value
|
crcValue = crc.value
|
||||||
writeInt4(crcValue.toInt())
|
writeInt4(crcValue.toInt())
|
||||||
}
|
}
|
||||||
|
@ -304,24 +303,20 @@ class ExperimentalApngEncoder(
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun writeACTL(num: Int) {
|
private fun writeACTL(num: Int) {
|
||||||
val actl = ArrayList<Byte>()
|
|
||||||
|
|
||||||
// Add length bytes
|
// Add length bytes
|
||||||
outputStream.write(byteArrayOf(0, 0, 0, 0x08))
|
outputStream.write(byteArrayOf(0, 0, 0, 0x08))
|
||||||
|
|
||||||
// Add acTL
|
// Add acTL
|
||||||
actl.addAll(byteArrayOf(0x61, 0x63, 0x54, 0x4c).asList())
|
val acTL = byteArrayOf(0x61, 0x63, 0x54, 0x4c)
|
||||||
|
// Add number of frames
|
||||||
// Add number of frames
|
.plus(Utils.to4BytesArray(num))
|
||||||
actl.addAll(Utils.to4Bytes(num).asList())
|
// Number of repeat, 0 to infinite
|
||||||
|
.plus(Utils.to4BytesArray(repetitionCount))
|
||||||
// Number of repeat, 0 to infinite
|
outputStream.write(acTL)
|
||||||
actl.addAll(Utils.to4Bytes(repetitionCount).asList())
|
|
||||||
outputStream.write(actl.toByteArray())
|
|
||||||
|
|
||||||
// generate crc
|
// generate crc
|
||||||
crc.reset()
|
crc.reset()
|
||||||
crc.update(actl.toByteArray(), 0, actl.size)
|
crc.update(acTL, 0, acTL.size)
|
||||||
outputStream.write(Utils.to4BytesArray(crc.value.toInt()))
|
outputStream.write(Utils.to4BytesArray(crc.value.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,39 +333,37 @@ class ExperimentalApngEncoder(
|
||||||
xOffsets: Int,
|
xOffsets: Int,
|
||||||
yOffsets: Int
|
yOffsets: Int
|
||||||
) {
|
) {
|
||||||
val fcTL = ArrayList<Byte>()
|
|
||||||
|
|
||||||
// Add the length of the chunk body
|
// Add the length of the chunk body
|
||||||
outputStream.write(byteArrayOf(0x00, 0x00, 0x00, 0x1A))
|
outputStream.write(byteArrayOf(0x00, 0x00, 0x00, 0x1A))
|
||||||
|
|
||||||
// Add fcTL
|
// Add fcTL
|
||||||
fcTL.addAll(Utils.fcTL.asList())
|
val fcTL = Utils.fcTL
|
||||||
|
// Add the frame number
|
||||||
|
.plus(Utils.to4BytesArray(currentSeq++))
|
||||||
|
|
||||||
// Add the frame number
|
// Add width and height
|
||||||
fcTL.addAll(Utils.to4Bytes(currentSeq++))
|
.plus(Utils.to4BytesArray(btm.width))
|
||||||
|
.plus(Utils.to4BytesArray(btm.height))
|
||||||
|
|
||||||
// Add width and height
|
// Add offsets
|
||||||
fcTL.addAll(Utils.to4Bytes(btm.width))
|
.plus(Utils.to4BytesArray(xOffsets))
|
||||||
fcTL.addAll(Utils.to4Bytes(btm.height))
|
.plus(Utils.to4BytesArray(yOffsets))
|
||||||
|
|
||||||
// Add offsets
|
// Set frame delay
|
||||||
fcTL.addAll(Utils.to4Bytes(xOffsets))
|
// TODO BETTER FRACTION
|
||||||
fcTL.addAll(Utils.to4Bytes(yOffsets))
|
.plus(Utils.to2Bytes(delay.toInt()))
|
||||||
|
.plus(Utils.to2Bytes(1000))
|
||||||
|
|
||||||
// Set frame delay
|
// Add DisposeOp and BlendOp
|
||||||
fcTL.addAll(Utils.to2Bytes(delay.toInt()).asList())
|
.plus(Utils.getDisposeOp(disposeOp))
|
||||||
fcTL.addAll(Utils.to2Bytes(1000).asList())
|
.plus(Utils.getBlendOp(blendOp))
|
||||||
|
|
||||||
// Add DisposeOp and BlendOp
|
|
||||||
fcTL.add(Utils.getDisposeOp(disposeOp).toByte())
|
|
||||||
fcTL.add(Utils.getBlendOp(blendOp).toByte())
|
|
||||||
|
|
||||||
// Create CRC
|
// Create CRC
|
||||||
crc.reset()
|
crc.reset()
|
||||||
crc.update(fcTL.toByteArray(), 0, fcTL.size)
|
crc.update(fcTL, 0, fcTL.size)
|
||||||
|
|
||||||
// Write all
|
// Write all
|
||||||
outputStream.write(fcTL.toByteArray())
|
outputStream.write(fcTL)
|
||||||
outputStream.write(Utils.to4BytesArray(crc.value.toInt()))
|
outputStream.write(Utils.to4BytesArray(crc.value.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,24 +459,20 @@ class ExperimentalApngEncoder(
|
||||||
nCompressed = compressedLines.size
|
nCompressed = compressedLines.size
|
||||||
|
|
||||||
crc.reset()
|
crc.reset()
|
||||||
// Add 4 bytes to the length, for the sequence number
|
// Add 4 bytes to the length, for the sequence number, if the current frame is not the first frame (and not an IDAT).
|
||||||
writeInt4(nCompressed + if (currentFrame == 0) 0 else 4)
|
writeInt4(nCompressed + if (currentFrame == 0) 0 else 4)
|
||||||
|
|
||||||
|
// If the current frame is the first frame, write idat for backward compatibility
|
||||||
if (currentFrame == 0) {
|
if (currentFrame == 0) {
|
||||||
outputStream.write(Utils.IDAT)
|
outputStream.write(Utils.IDAT)
|
||||||
crc.update(Utils.IDAT)
|
crc.update(Utils.IDAT)
|
||||||
} else {
|
} else { // Write a fdAT chunk, containing the current sequence number
|
||||||
val fdat = ArrayList<Byte>().also { fdat ->
|
// Add fdat and sequence number
|
||||||
// Add fdat
|
val fdat = Utils.fdAT+ Utils.to4BytesArray(currentSeq++)
|
||||||
fdat.addAll(byteArrayOf(0x66, 0x64, 0x41, 0x54).asList())
|
|
||||||
// Add the sequence number
|
|
||||||
fdat.addAll(Utils.to4Bytes(currentSeq++).asList())
|
|
||||||
}.toByteArray()
|
|
||||||
|
|
||||||
outputStream.write(fdat)
|
outputStream.write(fdat)
|
||||||
crc.update(fdat)
|
crc.update(fdat)
|
||||||
}
|
}
|
||||||
//bytePos = writeBytes(compressedLines, nCompressed, bytePos)
|
|
||||||
outputStream.write(compressedLines)
|
outputStream.write(compressedLines)
|
||||||
crc.update(compressedLines, 0, nCompressed)
|
crc.update(compressedLines, 0, nCompressed)
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Utils {
|
||||||
* @param disposeOp The DisposeOp
|
* @param disposeOp The DisposeOp
|
||||||
* @return [Int] An int equivalent to the DisposeOp
|
* @return [Int] An int equivalent to the DisposeOp
|
||||||
*/
|
*/
|
||||||
fun getDisposeOp(disposeOp: DisposeOp): Int {
|
fun getDisposeOp(disposeOp: DisposeOp): Byte {
|
||||||
return when (disposeOp) {
|
return when (disposeOp) {
|
||||||
Companion.DisposeOp.APNG_DISPOSE_OP_NONE -> 0
|
Companion.DisposeOp.APNG_DISPOSE_OP_NONE -> 0
|
||||||
Companion.DisposeOp.APNG_DISPOSE_OP_BACKGROUND -> 1
|
Companion.DisposeOp.APNG_DISPOSE_OP_BACKGROUND -> 1
|
||||||
|
@ -109,9 +109,9 @@ class Utils {
|
||||||
/**
|
/**
|
||||||
* Get the int equivalent to the BlendOp
|
* Get the int equivalent to the BlendOp
|
||||||
* @param blendOp The BlendOp
|
* @param blendOp The BlendOp
|
||||||
* @return [Int] An int equivalent to the BlendOp
|
* @return [Byte] An int equivalent to the BlendOp
|
||||||
*/
|
*/
|
||||||
fun getBlendOp(blendOp: BlendOp): Int {
|
fun getBlendOp(blendOp: BlendOp): Byte {
|
||||||
return when (blendOp) {
|
return when (blendOp) {
|
||||||
Companion.BlendOp.APNG_BLEND_OP_SOURCE -> 0
|
Companion.BlendOp.APNG_BLEND_OP_SOURCE -> 0
|
||||||
Companion.BlendOp.APNG_BLEND_OP_OVER -> 1
|
Companion.BlendOp.APNG_BLEND_OP_OVER -> 1
|
||||||
|
|
Loading…
Reference in New Issue