More "thread-safe" Png Encoder

Now PngEncoder and his variables should be freed after using PngEncoder().encode(...), as wanted in issue #7
This commit is contained in:
oupson 2020-05-14 09:06:35 +02:00
parent d32f5f3ebf
commit 9777569c4b
5 changed files with 371 additions and 377 deletions

View File

@ -21,6 +21,7 @@ import java.util.zip.CRC32
// TODO CREATE A BETTER CLASS
/**
* Create an APNG file
* If you want to create an APNG, use ApngEncoder instead
*/
@Suppress("unused")
class Apng {
@ -54,9 +55,9 @@ class Apng {
@Suppress("MemberVisibilityCanBePrivate")
fun addFrames(bitmap : Bitmap, index : Int? = null, delay : Float = 1000f, xOffset : Int = 0, yOffset : Int = 0, disposeOp: Utils.Companion.DisposeOp = Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE, blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE) {
if (index == null)
frames.add(Frame(PngEncoder.encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
frames.add(Frame(PngEncoder().encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
else
frames.add(index, Frame(PngEncoder.encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
frames.add(index, Frame(PngEncoder().encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
}
/**
@ -154,7 +155,7 @@ class Apng {
// Add cover image : Not part of animation
// region IDAT
val idat = IDAT()
idat.parse(PngEncoder.encode(cover!!, true, 1))
idat.parse(PngEncoder().encode(cover!!, true, 1))
idat.IDATBody.forEach {
val idatByteArray = ArrayList<Byte>()
framesByte.addAll(to4Bytes(it.size).asList())
@ -422,11 +423,11 @@ class Apng {
it.maxHeight = maxHeight
}
val drawedFrame = ApngAnimator(null).draw(frames)
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame0.png").writeBytes(PngEncoder.encode(drawedFrame[0]))
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame0.png").writeBytes(PngEncoder().encode(drawedFrame[0]))
for (i in 1 until frames.size) {
val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i])
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame$i.png").writeBytes(PngEncoder.encode(diffCalculator.res, true))
frames[i].byteArray = PngEncoder.encode(diffCalculator.res, true)
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame$i.png").writeBytes(PngEncoder().encode(diffCalculator.res, true))
frames[i].byteArray = PngEncoder().encode(diffCalculator.res, true)
frames[i].xOffsets = diffCalculator.xOffset
frames[i].yOffsets = diffCalculator.yOffset
frames[i].blendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_OVER

View File

@ -62,7 +62,7 @@ class ApngEncoder(
val idat = IDAT().apply {
val byteArray = if (usePngEncoder) {
PngEncoder.encode(btm, true)
PngEncoder().encode(btm, true)
} else {
val outputStream = ByteArrayOutputStream()
btm.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
@ -114,9 +114,9 @@ class ApngEncoder(
}
}
frameIndex++
if (usePngEncoder) {
/**if (usePngEncoder) {
PngEncoder.release()
}
}*/
}
fun writeEnd() {

View File

@ -65,7 +65,7 @@ class ExperimentalApngEncoder(
val idat = IDAT().apply {
val byteArray = if (usePngEncoder) {
PngEncoder.encode(btm, true)
PngEncoder().encode(btm, true)
} else {
val outputStream = ByteArrayOutputStream()
btm.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
@ -117,9 +117,9 @@ class ExperimentalApngEncoder(
}
}
frameIndex++
if (usePngEncoder) {
/**if (usePngEncoder) {
PngEncoder.release()
}
}*/
}
fun writeEnd() {

View File

@ -10,15 +10,11 @@ import kotlin.math.max
import kotlin.math.min
// TODO FIND A BETTER SOLUTION
// TODO ABSOLUTELY NOT THREAD SAFE, FIX THAT
/**
* Taken from http://catcode.com/pngencoder/com/keypoint/PngEncoder.java
*/
class PngEncoder {
companion object {
/** Encode alpha ? */
private var encodeAlpha = true
/** Constants for filter (NONE) */
private const val FILTER_NONE = 0
@ -30,15 +26,18 @@ class PngEncoder {
/** Constants for filter (LAST) */
private const val FILTER_LAST = 2
}
/** Encode alpha ? */
private var encodeAlpha = true
/** IHDR tag. */
private val IHDR = byteArrayOf(73, 72, 68, 82)
private val ihdr = byteArrayOf(73, 72, 68, 82)
/** IDAT tag. */
private val IDAT = byteArrayOf(73, 68, 65, 84)
private val idat = byteArrayOf(73, 68, 65, 84)
/** IEND tag. */
private val IEND = byteArrayOf(73, 69, 78, 68)
private val iend = byteArrayOf(73, 69, 78, 68)
/** The image. */
private var image: Bitmap? = null
@ -234,7 +233,7 @@ class PngEncoder {
private fun writeHeader() {
bytePos = writeInt4(13, bytePos)
val startPos: Int = bytePos
bytePos = writeBytes(IHDR, bytePos)
bytePos = writeBytes(ihdr, bytePos)
width = image!!.width
height = image!!.height
bytePos = writeInt4(width, bytePos)
@ -389,8 +388,8 @@ class PngEncoder {
crc.reset()
bytePos = writeInt4(nCompressed, bytePos)
bytePos = writeBytes(IDAT, bytePos)
crc.update(IDAT)
bytePos = writeBytes(idat, bytePos)
crc.update(idat)
bytePos = writeBytes(compressedLines, nCompressed, bytePos)
crc.update(compressedLines, 0, nCompressed)
@ -410,11 +409,10 @@ class PngEncoder {
*/
private fun writeEnd() {
bytePos = writeInt4(0, bytePos)
bytePos = writeBytes(IEND, bytePos)
bytePos = writeBytes(iend, bytePos)
crc.reset()
crc.update(IEND)
crc.update(iend)
crcValue = crc.value
bytePos = writeInt4(crcValue.toInt(), bytePos)
}
}
}

View File

@ -5,7 +5,6 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
@ -319,11 +318,7 @@ class CreatorActivity : AppCompatActivity() {
if (BuildConfig.DEBUG)
Log.i(TAG, "MaxWidth : $maxWidth; MaxHeight : $maxHeight")
val encoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ExperimentalApngEncoder(out, maxWidth, maxHeight, items.size, Bitmap.Config.RGBA_F16)
} else {
TODO("VERSION.SDK_INT < O")
}
val encoder = ExperimentalApngEncoder(out, maxWidth, maxHeight, items.size, Bitmap.Config.ARGB_8888)
items.forEach { uri ->
// println("delay : ${adapter?.delay?.get(i)?.toFloat() ?: 1000f}ms")
val str =