New instrumented tests
This commit is contained in:
parent
6b90282056
commit
d5011f3ee8
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
|
@ -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]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!!
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package oupson.apng.exceptions
|
||||
|
||||
class NoFrameException : Exception()
|
||||
class NotPngException : Exception()
|
||||
class NotApngException : Exception()
|
||||
class BadCRCException : Exception()
|
||||
|
||||
// TODO BETTER MESSAGES
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue