Bitmap Diff + Instrumented tests
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 184 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 175 KiB |
After Width: | Height: | Size: 181 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 172 KiB |
|
@ -0,0 +1,57 @@
|
||||||
|
package oupson.apng
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import junit.framework.TestCase.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import oupson.apng.encoder.ApngEncoder
|
||||||
|
|
||||||
|
|
||||||
|
class ApngEncoderInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun testDiff() { // TODO BLEND / DISPOSE OP
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
|
val frame1 = getFrame(context, "bunny/frame_apngframe01.png")
|
||||||
|
val frame2 = getFrame(context, "bunny/frame_apngframe02.png")
|
||||||
|
|
||||||
|
val diff = ApngEncoder.getDiffBitmap(frame1, frame2)
|
||||||
|
|
||||||
|
assertTrue(isSimilar(frame1, frame2, diff))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimilar(buffer : Bitmap, frame : Bitmap, diff : Triple<Bitmap, Int, Int>) : Boolean {
|
||||||
|
val btm = buffer.copy(Bitmap.Config.ARGB_8888, true)
|
||||||
|
|
||||||
|
for (y in 0 until diff.first.height) {
|
||||||
|
for (x in 0 until diff.first.width) {
|
||||||
|
val p = diff.first.getPixel(x, y)
|
||||||
|
if (p != Color.TRANSPARENT)
|
||||||
|
btm.setPixel(diff.second + x, diff.third + 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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!!
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package oupson.apng;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
public void useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
assertEquals("oupson.apng.test", appContext.getPackageName());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package oupson.apng.encoder
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import oupson.apng.exceptions.InvalidFrameSizeException
|
import oupson.apng.exceptions.InvalidFrameSizeException
|
||||||
import oupson.apng.utils.Utils
|
import oupson.apng.utils.Utils
|
||||||
|
@ -51,6 +52,51 @@ class ApngEncoder(
|
||||||
|
|
||||||
/** Constants for filter (LAST) */
|
/** Constants for filter (LAST) */
|
||||||
const val FILTER_LAST = 2
|
const val FILTER_LAST = 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun getDiffBitmap(bitmapBuffer : Bitmap, btm : Bitmap) : Triple<Bitmap, Int, Int> { // TODO BLEND / DISPOSE OP
|
||||||
|
if (bitmapBuffer.width < btm.width || bitmapBuffer.height < btm.height) {
|
||||||
|
TODO("EXCEPTION BAD IMAGE SIZE")
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultBtm = Bitmap.createBitmap(btm.width, btm.height, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
|
var offsetX = resultBtm.width + 1
|
||||||
|
var offsetY = resultBtm.height + 1
|
||||||
|
|
||||||
|
var lastX = 0
|
||||||
|
var lastY = 0
|
||||||
|
|
||||||
|
for (y in 0 until btm.height) {
|
||||||
|
for (x in 0 until btm.width) {
|
||||||
|
if (bitmapBuffer.getPixel(x, y) == btm.getPixel(x, y)) {
|
||||||
|
resultBtm.setPixel(x, y, Color.TRANSPARENT)
|
||||||
|
} else {
|
||||||
|
resultBtm.setPixel(x, y, btm.getPixel(x, y))
|
||||||
|
if (x < offsetX)
|
||||||
|
offsetX = x
|
||||||
|
if (y < offsetY)
|
||||||
|
offsetY = y
|
||||||
|
if (x > lastX)
|
||||||
|
lastX = x
|
||||||
|
if (y > lastY)
|
||||||
|
lastY = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastX++
|
||||||
|
lastY++
|
||||||
|
|
||||||
|
val newWidth = lastX - offsetX
|
||||||
|
val newHeight = lastY - offsetY
|
||||||
|
|
||||||
|
resultBtm = Bitmap.createBitmap(resultBtm, offsetX, offsetY, newWidth, newHeight)
|
||||||
|
|
||||||
|
return Triple(resultBtm, offsetX, offsetY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Current Frame. **/
|
/** Current Frame. **/
|
||||||
|
@ -95,6 +141,9 @@ class ApngEncoder(
|
||||||
// TODO DOC + CODE
|
// TODO DOC + CODE
|
||||||
private var optimise : Boolean = true
|
private var optimise : Boolean = true
|
||||||
|
|
||||||
|
// TODO DOC + CODE
|
||||||
|
private var bitmapBuffer : Bitmap? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
outputStream.write(Utils.pngSignature)
|
outputStream.write(Utils.pngSignature)
|
||||||
writeHeader()
|
writeHeader()
|
||||||
|
@ -550,4 +599,6 @@ class ApngEncoder(
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,4 +2,5 @@ package oupson.apng.utils
|
||||||
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
|
||||||
|
@Deprecated("Deprecated, Use ApngEncoder and ApngDecoder instead", level = DeprecationLevel.WARNING)
|
||||||
class ApngAnimatorOptions(val scaleType: ImageView.ScaleType? = ImageView.ScaleType.FIT_CENTER)
|
class ApngAnimatorOptions(val scaleType: ImageView.ScaleType? = ImageView.ScaleType.FIT_CENTER)
|