Working on ExperimentalApngEncoder.kt

This commit is contained in:
Oupson 2020-09-23 21:45:26 +02:00
parent dfc427a54f
commit 7b6fbfdf57
2 changed files with 45 additions and 56 deletions

View File

@ -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)

View File

@ -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