Parse IHDR and fcTL inside of ApngDecoder.kt instead of using dedicated class

Deprecation of Chunk.kt, fcTL.kt and IHDR.kt
This commit is contained in:
Oupson 2021-02-21 15:44:11 +01:00
parent b4bd0dd17e
commit 5d44b35882
4 changed files with 75 additions and 34 deletions

View File

@ -3,6 +3,7 @@ package oupson.apng.chunks
/** /**
* An interface for the png chunks * An interface for the png chunks
*/ */
@Deprecated("Deprecated", level = DeprecationLevel.WARNING)
interface Chunk { interface Chunk {
var body : ByteArray var body : ByteArray

View File

@ -3,6 +3,7 @@ package oupson.apng.chunks
import oupson.apng.utils.Utils import oupson.apng.utils.Utils
// TODO REMOVE // TODO REMOVE
@Deprecated("Deprecated", level = DeprecationLevel.WARNING)
class IHDR : Chunk { class IHDR : Chunk {
override var body = byteArrayOf() override var body = byteArrayOf()
var pngWidth = -1 var pngWidth = -1

View File

@ -5,7 +5,7 @@ import oupson.apng.utils.Utils.Companion.decodeBlendOp
import oupson.apng.utils.Utils.Companion.decodeDisposeOp import oupson.apng.utils.Utils.Companion.decodeDisposeOp
@Suppress("ClassName") @Suppress("ClassName")
// TODO REMOVE @Deprecated("Deprecated, Use ApngEncoder and ApngDecoder instead", level = DeprecationLevel.WARNING)
class fcTL : Chunk { class fcTL : Chunk {
override var body : ByteArray = byteArrayOf() override var body : ByteArray = byteArrayOf()

View File

@ -17,8 +17,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import oupson.apng.BuildConfig import oupson.apng.BuildConfig
import oupson.apng.Loader import oupson.apng.Loader
import oupson.apng.chunks.IHDR
import oupson.apng.chunks.fcTL
import oupson.apng.decoder.ApngDecoder.Companion.decodeApng import oupson.apng.decoder.ApngDecoder.Companion.decodeApng
import oupson.apng.exceptions.BadApngException import oupson.apng.exceptions.BadApngException
import oupson.apng.exceptions.BadCRCException import oupson.apng.exceptions.BadCRCException
@ -95,8 +93,9 @@ class ApngDecoder {
var blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE var blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE
var disposeOp: Utils.Companion.DisposeOp = var disposeOp: Utils.Companion.DisposeOp =
Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE
// TODO REMOVE
val ihdr = IHDR() var ihdrOfApng = ByteArray(0)
var isApng = false var isApng = false
val drawable = AnimationDrawable().apply { val drawable = AnimationDrawable().apply {
@ -142,17 +141,33 @@ class ApngDecoder {
)*/ // TODO )*/ // TODO
} }
png = ArrayList() png = ArrayList()
val fcTL = fcTL()
// TODO REMOVE FCTL CLASS // PARSE FCTL
fcTL.parse(byteArray) // Get the width of the png
delay = fcTL.delay val width = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 8, i + 12).map(Byte::toInt))
yOffset = fcTL.yOffset // Get the height of the png
xOffset = fcTL.xOffset val height = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 12, i + 16).map(Byte::toInt))
blendOp = fcTL.blendOp /*
disposeOp = fcTL.disposeOp * The `delay_num` and `delay_den` parameters together specify a fraction indicating the time to display the current frame, in seconds.
val width = fcTL.pngWidth * If the the value of the numerator is 0 the decoder should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound.
val height = fcTL.pngHeight */
// Get delay numerator
val delayNum = Utils.uShortFromBytesBigEndian(byteArray.copyOfRange(i + 24, i + 26).map(Byte::toInt)).toFloat()
// Get delay denominator
var delayDen = Utils.uShortFromBytesBigEndian(byteArray.copyOfRange(i + 26, i + 28).map(Byte::toInt)).toFloat()
// If the denominator is 0, it is to be treated as if it were 100 (that is, `delay_num` then specifies 1/100ths of a second).
if (delayDen == 0f) {
delayDen = 100f
}
delay = (delayNum / delayDen * 1000)
// Get x and y offsets
xOffset = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 16, i + 20).map(Byte::toInt))
yOffset = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 20, i + 24).map(Byte::toInt))
blendOp = Utils.decodeBlendOp(byteArray[33].toInt())
disposeOp = Utils.decodeDisposeOp(byteArray[32].toInt())
if (xOffset + width > maxWidth) { if (xOffset + width > maxWidth) {
throw BadApngException("`xOffset` + `width` must be <= `IHDR` width") throw BadApngException("`xOffset` + `width` must be <= `IHDR` width")
@ -163,7 +178,7 @@ class ApngDecoder {
png.addAll(Utils.pngSignature.asList()) png.addAll(Utils.pngSignature.asList())
png.addAll( png.addAll(
generateIhdr( generateIhdr(
ihdr, ihdrOfApng,
width, width,
height height
).asList() ).asList()
@ -259,19 +274,39 @@ class ApngDecoder {
png = ArrayList() png = ArrayList()
val fcTL = fcTL()
fcTL.parse(byteArray) // PARSE FCTL
delay = fcTL.delay // Get the width of the png
yOffset = fcTL.yOffset val width = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 8, i + 12).map(Byte::toInt))
xOffset = fcTL.xOffset // Get the height of the png
blendOp = fcTL.blendOp val height = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 12, i + 16).map(Byte::toInt))
disposeOp = fcTL.disposeOp /*
val width = fcTL.pngWidth * The `delay_num` and `delay_den` parameters together specify a fraction indicating the time to display the current frame, in seconds.
val height = fcTL.pngHeight * If the the value of the numerator is 0 the decoder should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound.
*/
// Get delay numerator
val delayNum = Utils.uShortFromBytesBigEndian(byteArray.copyOfRange(i + 24, i + 26).map(Byte::toInt)).toFloat()
// Get delay denominator
var delayDen = Utils.uShortFromBytesBigEndian(byteArray.copyOfRange(i + 26, i + 28).map(Byte::toInt)).toFloat()
// If the denominator is 0, it is to be treated as if it were 100 (that is, `delay_num` then specifies 1/100ths of a second).
if (delayDen == 0f) {
delayDen = 100f
}
delay = (delayNum / delayDen * 1000)
// Get x and y offsets
xOffset = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 16, i + 20).map(Byte::toInt))
yOffset = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i + 20, i + 24).map(Byte::toInt))
blendOp = Utils.decodeBlendOp(byteArray[33].toInt())
disposeOp = Utils.decodeDisposeOp(byteArray[32].toInt())
png.addAll(Utils.pngSignature.asList()) png.addAll(Utils.pngSignature.asList())
png.addAll( png.addAll(
generateIhdr( generateIhdr(
ihdr, ihdrOfApng,
width, width,
height height
).asList() ).asList()
@ -395,7 +430,7 @@ class ApngDecoder {
cover.addAll(Utils.pngSignature.asList()) cover.addAll(Utils.pngSignature.asList())
cover.addAll( cover.addAll(
generateIhdr( generateIhdr(
ihdr, ihdrOfApng,
maxWidth, maxWidth,
maxHeight maxHeight
).asList() ).asList()
@ -458,9 +493,14 @@ class ApngDecoder {
tnrs = byteArray tnrs = byteArray
} }
name.contentEquals(Utils.IHDR) -> { name.contentEquals(Utils.IHDR) -> {
ihdr.parse(byteArray) // Get length of the body of the chunk
maxWidth = ihdr.pngWidth val bodySize = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i - 4, i).map(Byte::toInt))
maxHeight = ihdr.pngHeight // Get the width of the png
maxWidth = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i +4, i + 8).map(Byte::toInt))
// Get the height of the png
maxHeight = Utils.uIntFromBytesBigEndian(byteArray.copyOfRange(i +8, i +12).map(Byte::toInt))
ihdrOfApng = byteArray.copyOfRange(i + 4, i + bodySize + 4)
buffer = Bitmap.createBitmap( buffer = Bitmap.createBitmap(
maxWidth, maxWidth,
maxHeight, maxHeight,
@ -854,13 +894,12 @@ class ApngDecoder {
* @param height The height of the frame. * @param height The height of the frame.
* @return [ByteArray] The generated IHDR. * @return [ByteArray] The generated IHDR.
*/ */
// TODO REMOVE IHDR private fun generateIhdr(ihdrOfApng: ByteArray, width: Int, height: Int): ByteArray {
private fun generateIhdr(ihdrOfApng: IHDR, width: Int, height: Int): ByteArray {
val ihdr = ArrayList<Byte>() val ihdr = ArrayList<Byte>()
// We need a body var to know body length and generate crc // We need a body var to know body length and generate crc
val ihdrBody = ArrayList<Byte>() val ihdrBody = ArrayList<Byte>()
// Add chunk body length // Add chunk body length
ihdr.addAll(Utils.uIntToByteArray(ihdrOfApng.body.size).asList()) ihdr.addAll(Utils.uIntToByteArray(ihdrOfApng.size).asList())
// Add IHDR // Add IHDR
ihdrBody.addAll( ihdrBody.addAll(
arrayOf( arrayOf(
@ -875,7 +914,7 @@ class ApngDecoder {
ihdrBody.addAll(Utils.uIntToByteArray(height).asList()) ihdrBody.addAll(Utils.uIntToByteArray(height).asList())
// Add complicated stuff like depth color ... // Add complicated stuff like depth color ...
// If you want correct png you need same parameters. Good solution is to create new png. // If you want correct png you need same parameters. Good solution is to create new png.
ihdrBody.addAll(ihdrOfApng.body.copyOfRange(8, 13).asList()) ihdrBody.addAll(ihdrOfApng.copyOfRange(8, 13).asList())
// Generate CRC // Generate CRC
val crC32 = CRC32() val crC32 = CRC32()
crC32.update(ihdrBody.toByteArray(), 0, ihdrBody.size) crC32.update(ihdrBody.toByteArray(), 0, ihdrBody.size)