Revert
This commit is contained in:
parent
a086e06811
commit
7d7b02f2b9
|
@ -13,6 +13,7 @@ import android.widget.ImageView
|
|||
import androidx.annotation.RawRes
|
||||
import kotlinx.coroutines.*
|
||||
import oupson.apng.BuildConfig
|
||||
import oupson.apng.decoder.ApngDecoder.Companion.decodeApng
|
||||
import oupson.apng.drawable.ApngDrawable
|
||||
import oupson.apng.exceptions.BadApngException
|
||||
import oupson.apng.exceptions.BadCRCException
|
||||
|
@ -48,20 +49,22 @@ class ApngDecoder {
|
|||
internal var bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888,
|
||||
internal var decodeCoverFrame: Boolean = false
|
||||
) {
|
||||
fun getSpeed(): Float = this.speed
|
||||
fun setSpeed(speed: Float): Config {
|
||||
fun getSpeed() : Float = this.speed
|
||||
fun setSpeed(speed : Float) : Config {
|
||||
this.speed = speed
|
||||
return this
|
||||
}
|
||||
|
||||
fun getBitmapConfig(): Bitmap.Config = this.bitmapConfig
|
||||
fun setBitmapConfig(config: Bitmap.Config): Config {
|
||||
fun getBitmapConfig() : Bitmap.Config = this.bitmapConfig
|
||||
fun setBitmapConfig(config : Bitmap.Config) : Config {
|
||||
this.bitmapConfig = config
|
||||
return this
|
||||
}
|
||||
|
||||
fun isDecodingCoverFrame(): Boolean = this.decodeCoverFrame
|
||||
fun setIsDecodingCoverFrame(decodeCoverFrame: Boolean): Config {
|
||||
fun isDecodingCoverFrame() : Boolean {
|
||||
return this.decodeCoverFrame
|
||||
}
|
||||
fun setIsDecodingCoverFrame(decodeCoverFrame : Boolean) : Config {
|
||||
this.decodeCoverFrame = decodeCoverFrame
|
||||
return this
|
||||
}
|
||||
|
@ -80,289 +83,6 @@ class ApngDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for animation drawable.
|
||||
* @param file File to decode.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
file: File,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
ApngDecoder().decodeApng(
|
||||
context,
|
||||
FileInputStream(file),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for animation drawable and content resolver.
|
||||
* @param uri Uri to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)!!
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
ApngDecoder().decodeApng(
|
||||
context,
|
||||
inputStream,
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed to decode the resource and for the animation drawable.
|
||||
* @param res Raw resource to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context, @RawRes res: Int,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
ApngDecoder().decodeApng(
|
||||
context,
|
||||
context.resources.openRawResource(res),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for the animation drawable.
|
||||
* @param url URL to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
url: URL,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable = ApngDecoder().decodeApng(
|
||||
context,
|
||||
ByteArrayInputStream(
|
||||
Loader.load(
|
||||
url
|
||||
)
|
||||
),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for decoding the image and creating the animation drawable.
|
||||
* @param string URL to load
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
string: String,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (string.startsWith("http://") || string.startsWith("https://")) {
|
||||
decodeApngAsyncInto(
|
||||
context,
|
||||
URL(string),
|
||||
imageView,
|
||||
callback,
|
||||
config
|
||||
)
|
||||
} else if (File(string).exists()) {
|
||||
var pathToLoad =
|
||||
if (string.startsWith("content://")) string else "file://$string"
|
||||
pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
|
||||
decodeApngAsyncInto(
|
||||
context,
|
||||
Uri.parse(pathToLoad),
|
||||
imageView,
|
||||
callback,
|
||||
config
|
||||
)
|
||||
} else if (string.startsWith("file://android_asset/")) {
|
||||
val drawable =
|
||||
ApngDecoder().decodeApng(
|
||||
context,
|
||||
context.assets.open(string.replace("file:///android_asset/", "")),
|
||||
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(java.lang.Exception("Cannot open string"))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a correct IHDR from the IHDR chunk of the APNG.
|
||||
* @param ihdrOfApng The IHDR of the APNG.
|
||||
* @param width The width of the frame.
|
||||
* @param height The height of the frame.
|
||||
* @return [ByteArray] The generated IHDR.
|
||||
*/
|
||||
private fun generateIhdr(ihdrOfApng: ByteArray, width: Int, height: Int): ByteArray {
|
||||
val ihdr =
|
||||
ByteArray(0xD + 4 + 4 + 4) // 0xD (IHDR body length) + 4 (0x0, 0x0, 0x0, 0xD : the chunk length) + 4 : IHDR + 4 : CRC
|
||||
|
||||
// Add chunk body length
|
||||
System.arraycopy(Utils.uIntToByteArray(0xD), 0, ihdr, 0, 4)
|
||||
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdrBody = ByteArray(0xD + 4) // 0xD (IHDR body length) + 4 : IHDR
|
||||
|
||||
// Add IHDR
|
||||
System.arraycopy(Utils.IHDR, 0, ihdrBody, 0, 4)
|
||||
|
||||
// Add the max width and height
|
||||
System.arraycopy(Utils.uIntToByteArray(width), 0, ihdrBody, 4, 4)
|
||||
System.arraycopy(Utils.uIntToByteArray(height), 0, ihdrBody, 8, 4)
|
||||
|
||||
// Add complicated stuff like depth color ...
|
||||
// If you want correct png you need same parameters.
|
||||
System.arraycopy(ihdrOfApng, 8, ihdrBody, 12, 5)
|
||||
|
||||
// Generate CRC
|
||||
val crC32 = CRC32()
|
||||
crC32.update(ihdrBody, 0, 0xD + 4)
|
||||
|
||||
System.arraycopy(ihdrBody, 0, ihdr, 4, 0xD + 4)
|
||||
System.arraycopy(Utils.uIntToByteArray(crC32.value.toInt()), 0, ihdr, 0xD + 4 + 4, 4)
|
||||
return ihdr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode Apng and return a Drawable who can be an [ApngDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
|
||||
* @param context Context needed for the animation drawable
|
||||
|
@ -372,6 +92,7 @@ class ApngDecoder {
|
|||
*/
|
||||
// TODO DOCUMENT CONFIG
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApng(
|
||||
context: Context,
|
||||
|
@ -821,6 +542,7 @@ class ApngDecoder {
|
|||
* @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. If it is not an animated image, it is a [Drawable].
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
// TODO DOCUMENT
|
||||
fun decodeApng(
|
||||
context: Context,
|
||||
|
@ -840,6 +562,7 @@ class ApngDecoder {
|
|||
* @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
fun decodeApng(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
|
@ -861,6 +584,7 @@ class ApngDecoder {
|
|||
* @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
fun decodeApng(
|
||||
context: Context,
|
||||
@RawRes res: Int,
|
||||
|
@ -880,6 +604,7 @@ class ApngDecoder {
|
|||
* @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
|
||||
*/
|
||||
@Suppress("unused", "BlockingMethodInNonBlockingContext")
|
||||
@JvmStatic
|
||||
suspend fun decodeApng(
|
||||
context: Context,
|
||||
url: URL,
|
||||
|
@ -892,4 +617,287 @@ class ApngDecoder {
|
|||
config
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for animation drawable.
|
||||
* @param file File to decode.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
file: File,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope : CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
decodeApng(
|
||||
context,
|
||||
FileInputStream(file),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for animation drawable and content resolver.
|
||||
* @param uri Uri to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope : CoroutineScope = GlobalScope
|
||||
) {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)!!
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
decodeApng(
|
||||
context,
|
||||
inputStream,
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed to decode the resource and for the animation drawable.
|
||||
* @param res Raw resource to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context, @RawRes res: Int,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope : CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable =
|
||||
decodeApng(
|
||||
context,
|
||||
context.resources.openRawResource(res),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for the animation drawable.
|
||||
* @param url URL to load.
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
url: URL,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope : CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val drawable = decodeApng(
|
||||
context,
|
||||
ByteArrayInputStream(
|
||||
Loader.load(
|
||||
url
|
||||
)
|
||||
),
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Apng into an imageView, asynchronously.
|
||||
* @param context Context needed for decoding the image and creating the animation drawable.
|
||||
* @param string URL to load
|
||||
* @param imageView Image View.
|
||||
* @param callback [ApngDecoder.Callback] to handle success and error.
|
||||
* @param config Decoder configuration
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun decodeApngAsyncInto(
|
||||
context: Context,
|
||||
string: String,
|
||||
imageView: ImageView,
|
||||
callback: Callback? = null,
|
||||
config: Config = Config(),
|
||||
scope : CoroutineScope = GlobalScope
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (string.startsWith("http://") || string.startsWith("https://")) {
|
||||
decodeApngAsyncInto(
|
||||
context,
|
||||
URL(string),
|
||||
imageView,
|
||||
callback,
|
||||
config
|
||||
)
|
||||
} else if (File(string).exists()) {
|
||||
var pathToLoad =
|
||||
if (string.startsWith("content://")) string else "file://$string"
|
||||
pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
|
||||
decodeApngAsyncInto(
|
||||
context,
|
||||
Uri.parse(pathToLoad),
|
||||
imageView,
|
||||
callback,
|
||||
config
|
||||
)
|
||||
} else if (string.startsWith("file://android_asset/")) {
|
||||
val drawable =
|
||||
decodeApng(
|
||||
context,
|
||||
context.assets.open(string.replace("file:///android_asset/", "")),
|
||||
|
||||
config
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
imageView.setImageDrawable(drawable)
|
||||
(drawable as? AnimationDrawable)?.start()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
(drawable as? AnimatedImageDrawable)?.start()
|
||||
}
|
||||
callback?.onSuccess(drawable)
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(java.lang.Exception("Cannot open string"))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a correct IHDR from the IHDR chunk of the APNG.
|
||||
* @param ihdrOfApng The IHDR of the APNG.
|
||||
* @param width The width of the frame.
|
||||
* @param height The height of the frame.
|
||||
* @return [ByteArray] The generated IHDR.
|
||||
*/
|
||||
private fun generateIhdr(ihdrOfApng: ByteArray, width: Int, height: Int): ByteArray {
|
||||
val ihdr =
|
||||
ByteArray(0xD + 4 + 4 + 4) // 0xD (IHDR body length) + 4 (0x0, 0x0, 0x0, 0xD : the chunk length) + 4 : IHDR + 4 : CRC
|
||||
|
||||
// Add chunk body length
|
||||
System.arraycopy(Utils.uIntToByteArray(0xD), 0, ihdr, 0, 4)
|
||||
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdrBody = ByteArray(0xD + 4) // 0xD (IHDR body length) + 4 : IHDR
|
||||
|
||||
// Add IHDR
|
||||
System.arraycopy(Utils.IHDR, 0, ihdrBody, 0, 4)
|
||||
|
||||
// Add the max width and height
|
||||
System.arraycopy(Utils.uIntToByteArray(width), 0, ihdrBody, 4, 4)
|
||||
System.arraycopy(Utils.uIntToByteArray(height), 0, ihdrBody, 8, 4)
|
||||
|
||||
// Add complicated stuff like depth color ...
|
||||
// If you want correct png you need same parameters.
|
||||
System.arraycopy(ihdrOfApng, 8, ihdrBody, 12, 5)
|
||||
|
||||
// Generate CRC
|
||||
val crC32 = CRC32()
|
||||
crC32.update(ihdrBody, 0, 0xD + 4)
|
||||
|
||||
System.arraycopy(ihdrBody, 0, ihdr, 4, 0xD + 4)
|
||||
System.arraycopy(Utils.uIntToByteArray(crC32.value.toInt()), 0, ihdr, 0xD + 4 + 4, 4)
|
||||
return ihdr
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue