New instrumented tests

This commit is contained in:
Oupson 2021-02-24 15:38:38 +01:00
parent 6b90282056
commit d5011f3ee8
8 changed files with 153 additions and 81 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@ -0,0 +1,52 @@
package oupson.apng
import android.graphics.Bitmap
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.BitmapDrawable
import androidx.test.platform.app.InstrumentationRegistry
import junit.framework.TestCase
import org.junit.Test
import oupson.apng.Utils.Companion.areBitmapSimilar
import oupson.apng.Utils.Companion.getFrame
import oupson.apng.decoder.ApngDecoder
import oupson.apng.drawable.ApngDrawable
class ApngDecoderInstrumentedTest {
@Test
fun testBtmConfigDecoding() {
val context = InstrumentationRegistry.getInstrumentation().context
val input = context.assets.open("sushi.png")
val anim = ApngDecoder.decodeApng(
context,
input,
ApngDecoder.Config(bitmapConfig = Bitmap.Config.RGB_565)
) as AnimationDrawable
for (i in 0 until anim.numberOfFrames) {
TestCase.assertTrue((anim.getFrame(i) as BitmapDrawable).bitmap.config == Bitmap.Config.RGB_565)
}
}
@Test
fun testBigBuckBunny() {
val context = InstrumentationRegistry.getInstrumentation().context
val list = context.assets.list("bunny")?.map { getFrame(context, "bunny/$it") }!!
val input = context.assets.open("bugbuckbunny.png")
val anim = ApngDecoder.decodeApng(
context,
input,
ApngDecoder.Config(bitmapConfig = Bitmap.Config.ARGB_8888, decodeCoverFrame = true)
) as ApngDrawable
TestCase.assertTrue(areBitmapSimilar(list[0], anim.coverFrame!!))
for (i in 0 until anim.numberOfFrames) {
TestCase.assertTrue(
areBitmapSimilar(
(anim.getFrame(i) as BitmapDrawable).bitmap,
list[i + 1]
)
)
}
}
}

View File

@ -1,15 +1,14 @@
package oupson.apng
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.Utils.Companion.areBitmapSimilar
import oupson.apng.Utils.Companion.getFrame
import oupson.apng.Utils.Companion.isSimilar
import oupson.apng.decoder.ApngDecoder
import oupson.apng.encoder.ApngEncoder
import oupson.apng.utils.Utils
@ -78,7 +77,7 @@ class ApngEncoderInstrumentedTest {
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
areBitmapSimilar(
list[i],
(optimisedApng.getFrame(i) as BitmapDrawable).bitmap
)
@ -107,7 +106,7 @@ class ApngEncoderInstrumentedTest {
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
areBitmapSimilar(
(optimisedApng.getFrame(i) as BitmapDrawable).bitmap,
(nonOptimisedApng.getFrame(i) as BitmapDrawable).bitmap
)
@ -141,7 +140,7 @@ class ApngEncoderInstrumentedTest {
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
areBitmapSimilar(
list[i],
(optimisedApng.getFrame(i) as BitmapDrawable).bitmap
)
@ -170,54 +169,12 @@ class ApngEncoderInstrumentedTest {
for (i in 0 until optimisedApng.numberOfFrames) {
assertTrue(
isBitmapSimilar(
areBitmapSimilar(
(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)
for (y in 0 until diff.bitmap.height) {
for (x in 0 until diff.bitmap.width) {
val p = diff.bitmap.getPixel(x, y)
if (p != Color.TRANSPARENT || p == Color.TRANSPARENT && diff.blendOp == Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE)
btm.setPixel(diff.offsetX + x, diff.offsetY + y, p)
}
}
for (y in 0 until buffer.height) {
for (x in 0 until buffer.width) {
if (frame.getPixel(x, y) != btm.getPixel(x, y)) {
return false
}
}
}
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)
val bitmap = BitmapFactory.decodeStream(inputStream, null, BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_8888
})
inputStream.close()
return bitmap!!
}
}

View File

@ -0,0 +1,54 @@
package oupson.apng
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import oupson.apng.utils.Utils
class Utils {
companion object {
fun getFrame(context: Context, name: String, btmConfig : Bitmap.Config = Bitmap.Config.ARGB_8888) : Bitmap {
val inputStream = context.assets.open(name)
val bitmap = BitmapFactory.decodeStream(inputStream, null, BitmapFactory.Options().apply {
inPreferredConfig = btmConfig
})
inputStream.close()
return bitmap!!
}
fun isSimilar(buffer : Bitmap, frame : Bitmap, diff : Utils.Companion.DiffResult) : Boolean {
val btm = buffer.copy(Bitmap.Config.ARGB_8888, true)
for (y in 0 until diff.bitmap.height) {
for (x in 0 until diff.bitmap.width) {
val p = diff.bitmap.getPixel(x, y)
if (p != Color.TRANSPARENT || p == Color.TRANSPARENT && diff.blendOp == Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE)
btm.setPixel(diff.offsetX + x, diff.offsetY + y, p)
}
}
for (y in 0 until buffer.height) {
for (x in 0 until buffer.width) {
if (frame.getPixel(x, y) != btm.getPixel(x, y)) {
return false
}
}
}
return true
}
fun areBitmapSimilar(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
}
}
}

View File

@ -47,11 +47,31 @@ class ApngDecoder {
fun onError(error: Exception)
}
data class Config(
val speed: Float = 1f,
val bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888,
val decodeCoverFrame: Boolean = true
)
class Config(
internal var speed: Float = 1f,
internal var bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888,
internal var decodeCoverFrame: Boolean = false
) {
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 {
this.bitmapConfig = config
return this
}
fun isDecodingCoverFrame() : Boolean {
return this.decodeCoverFrame
}
fun setIsDecodingCoverFrame(decodeCoverFrame : Boolean) : Config {
this.decodeCoverFrame = decodeCoverFrame
return this
}
}
companion object {
private const val TAG = "ApngDecoder"
@ -70,9 +90,8 @@ class ApngDecoder {
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context needed for the animation drawable
* @param inStream Input Stream to decode. Will be closed at the end.
* @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @return [AnimationDrawable] 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].
* @param config Decoder configuration
* @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].
*/
// TODO DOCUMENT CONFIG
@Suppress("MemberVisibilityCanBePrivate")
@ -87,6 +106,7 @@ class ApngDecoder {
val bytes = ByteArray(8)
inputStream.mark(8)
inputStream.read(bytes)
if (isPng(bytes)) {
var png: ByteArrayOutputStream? = null
var cover: ByteArrayOutputStream? = null
@ -118,6 +138,7 @@ class ApngDecoder {
if (byteRead == -1)
break
val length = Utils.uIntFromBytesBigEndian(lengthChunk)
val chunk = ByteArray(length + 8)
@ -515,8 +536,7 @@ class ApngDecoder {
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context needed for animation drawable.
* @param file File to decode.
* @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
* @return [AnimationDrawable] 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")
@ -536,8 +556,7 @@ class ApngDecoder {
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver and animation drawable.
* @param uri Uri to open.
* @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
* @return [AnimationDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
*/
@Suppress("unused")
@ -559,8 +578,7 @@ class ApngDecoder {
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver and animation drawable.
* @param res Resource to decode.
* @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
* @return [AnimationDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
*/
@Suppress("unused")
@ -580,8 +598,7 @@ class ApngDecoder {
* Decode Apng and return a Drawable who can be an [AnimationDrawable] if it end successfully. Can also be an [android.graphics.drawable.AnimatedImageDrawable].
* @param context Context is needed for contentResolver and animation drawable.
* @param url URL to decode.
* @param speed Optional parameter.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
* @return [AnimationDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
*/
@Suppress("unused", "BlockingMethodInNonBlockingContext")
@ -604,9 +621,8 @@ class ApngDecoder {
* @param context Context needed for animation drawable.
* @param file File to decode.
* @param imageView Image View.
* @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
*/
@Suppress("unused")
@JvmStatic
@ -647,9 +663,8 @@ class ApngDecoder {
* @param context Context needed for animation drawable and content resolver.
* @param uri Uri to load.
* @param imageView Image View.
* @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
*/
@Suppress("unused")
@JvmStatic
@ -691,9 +706,8 @@ class ApngDecoder {
* @param context Context needed to decode the resource and for the animation drawable.
* @param res Raw resource to load.
* @param imageView Image View.
* @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
*/
@Suppress("unused")
@JvmStatic
@ -734,9 +748,8 @@ class ApngDecoder {
* @param context Context needed for the animation drawable.
* @param url URL to load.
* @param imageView Image View.
* @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
*/
@Suppress("unused")
@JvmStatic
@ -780,9 +793,8 @@ class ApngDecoder {
* @param context Context needed for decoding the image and creating the animation drawable.
* @param string URL to load
* @param imageView Image View.
* @param speed Optional parameter.
* @param callback [ApngDecoder.Callback] to handle success and error.
* @param config Configuration applied to the bitmap added to the animation. Please note that the frame is decoded in ARGB_8888 and converted after, for the buffer.
* @param config Decoder configuration
*/
@Suppress("unused")
@JvmStatic

View File

@ -1,8 +1,5 @@
package oupson.apng.exceptions
class NoFrameException : Exception()
class NotPngException : Exception()
class NotApngException : Exception()
class BadCRCException : Exception()
// TODO BETTER MESSAGES

View File

@ -50,7 +50,7 @@ public class JavaFragment extends Fragment {
public void onError(@NotNull Exception error) {
Log.e(TAG, "Error : " + error.toString());
}
});
}, new ApngDecoder.Config().setIsDecodingCoverFrame(false));
}
return v;