Test if optimised and non-optimised APNG are the same

This commit is contained in:
Oupson 2021-02-06 18:22:17 +01:00
parent f826afff86
commit be9e5426e0
2 changed files with 150 additions and 15 deletions

View File

@ -4,11 +4,17 @@ import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.BitmapDrawable
import androidx.test.platform.app.InstrumentationRegistry
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertTrue
import org.junit.Test
import oupson.apng.decoder.ApngDecoder
import oupson.apng.encoder.ApngEncoder
import oupson.apng.utils.Utils
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
class ApngEncoderInstrumentedTest {
@ -38,7 +44,7 @@ class ApngEncoderInstrumentedTest {
}
@Test
fun containTransparency() {
fun testContainTransparency() {
val context = InstrumentationRegistry.getInstrumentation().context
val bunnyFrame1 = getFrame(context, "bunny/frame_apngframe01.png")
@ -48,6 +54,118 @@ class ApngEncoderInstrumentedTest {
assertTrue(Utils.containTransparency(ballFrame1))
}
@Test
fun testOptimiseBall() {
val context = InstrumentationRegistry.getInstrumentation().context
val list = context.assets.list("ball")?.map { getFrame(context, "ball/$it") }!!
val optimisedOutputStream = ByteArrayOutputStream()
val optimisedEncoder = ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size)
.setOptimiseApng(true)
list.forEach {
optimisedEncoder.writeFrame(it)
}
optimisedEncoder.writeEnd()
optimisedOutputStream.close()
val bytes = optimisedOutputStream.toByteArray()
val optimisedInputStream = ByteArrayInputStream(bytes)
val optimisedApng =
ApngDecoder.decodeApng(context, optimisedInputStream) as AnimationDrawable
optimisedInputStream.close()
val nonOptimisedOutputStream = ByteArrayOutputStream()
val nonOptimisedEncoder = ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size)
.setOptimiseApng(false)
list.forEach {
nonOptimisedEncoder.writeFrame(it)
}
nonOptimisedEncoder.writeEnd()
nonOptimisedOutputStream.close()
val nonOptimisedBytes = nonOptimisedOutputStream.toByteArray()
val nonOptimisedInputStream = ByteArrayInputStream(nonOptimisedBytes)
val nonOptimisedApng =
ApngDecoder.decodeApng(context, nonOptimisedInputStream) as AnimationDrawable
nonOptimisedInputStream.close()
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
(optimisedApng.getFrame(i) as BitmapDrawable).bitmap,
(nonOptimisedApng.getFrame(i) as BitmapDrawable).bitmap
)
)
}
}
@Test
fun testOptimiseBunny() {
val context = InstrumentationRegistry.getInstrumentation().context
val list = context.assets.list("bunny")?.map { getFrame(context, "bunny/$it") }!!
val optimisedOutputStream = ByteArrayOutputStream()
val optimisedEncoder = ApngEncoder(optimisedOutputStream, list[0].width, list[0].height, list.size)
.setOptimiseApng(true)
list.forEach {
optimisedEncoder.writeFrame(it)
}
optimisedEncoder.writeEnd()
optimisedOutputStream.close()
val bytes = optimisedOutputStream.toByteArray()
val optimisedInputStream = ByteArrayInputStream(bytes)
val optimisedApng =
ApngDecoder.decodeApng(context, optimisedInputStream) as AnimationDrawable
optimisedInputStream.close()
val nonOptimisedOutputStream = ByteArrayOutputStream()
val nonOptimisedEncoder = ApngEncoder(nonOptimisedOutputStream, list[0].width, list[0].height, list.size)
.setOptimiseApng(false)
list.forEach {
nonOptimisedEncoder.writeFrame(it)
}
nonOptimisedEncoder.writeEnd()
nonOptimisedOutputStream.close()
val nonOptimisedBytes = nonOptimisedOutputStream.toByteArray()
val nonOptimisedInputStream = ByteArrayInputStream(nonOptimisedBytes)
val nonOptimisedApng =
ApngDecoder.decodeApng(context, nonOptimisedInputStream) as AnimationDrawable
nonOptimisedInputStream.close()
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
(optimisedApng.getFrame(i) as BitmapDrawable).bitmap,
(nonOptimisedApng.getFrame(i) as BitmapDrawable).bitmap
)
)
}
}
private fun isSimilar(buffer : Bitmap, frame : Bitmap, diff : Utils.Companion.DiffResult) : Boolean {
val btm = buffer.copy(Bitmap.Config.ARGB_8888, true)
@ -69,6 +187,17 @@ class ApngEncoderInstrumentedTest {
return true
}
fun isBitmapSimilar(btm1 : Bitmap, btm2 : Bitmap) : Boolean {
for (y in 0 until btm1.height) {
for (x in 0 until btm1.width) {
if (btm1.getPixel(x, y) != btm2.getPixel(x, y)) {
return false
}
}
}
return true
}
private fun getFrame(context: Context, name: String) : Bitmap {
val inputStream = context.assets.open(name)

View File

@ -284,7 +284,13 @@ class ApngEncoder(
) {
if (currentFrame == 0) {
if (btm.width != width || btm.height != height)
throw InvalidFrameSizeException(btm.width, btm.height, width, height, currentFrame == 0)
throw InvalidFrameSizeException(
btm.width,
btm.height,
width,
height,
currentFrame == 0
)
}
var frameBtm = btm
@ -292,21 +298,21 @@ class ApngEncoder(
var frameYOffsets = yOffsets
var frameBlendOp = blendOp
if (currentFrame != 0 || (currentFrame == 0 && firstFrameInAnim)) {
if (bitmapBuffer == null && optimise) {
bitmapBuffer = btm.copy(btm.config, false)
} else if (optimise) {
val diff = Utils.getDiffBitmap(bitmapBuffer!!, btm)
frameBtm = diff.bitmap
frameXOffsets = diff.offsetX
frameYOffsets = diff.offsetY
frameBlendOp = diff.blendOp
bitmapBuffer = btm.copy(btm.config, false)
}
if (optimise && currentFrame != 0 || (currentFrame == 0 && firstFrameInAnim)) {
if (bitmapBuffer == null) {
bitmapBuffer = btm.copy(btm.config, false)
} else {
val diff = Utils.getDiffBitmap(bitmapBuffer!!, btm)
frameBtm = diff.bitmap
frameXOffsets = diff.offsetX
frameYOffsets = diff.offsetY
frameBlendOp = diff.blendOp
bitmapBuffer = btm.copy(btm.config, false)
}
}
if (btm.width > width || btm.height > height)
throw InvalidFrameSizeException(btm.width, btm.height, width, height, currentFrame == 0)
if (frameBtm.width > width || frameBtm.height > height)
throw InvalidFrameSizeException(frameBtm.width, frameBtm.height, width, height, currentFrame == 0)
if (firstFrameInAnim || currentFrame != 0)
writeFCTL(frameBtm, delay, disposeOp, frameBlendOp, frameXOffsets, frameYOffsets)