Bitmap diff with blend op
This commit is contained in:
parent
4580c6bd78
commit
919feee3e9
|
@ -5,6 +5,7 @@ import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import junit.framework.TestCase.assertFalse
|
||||||
import junit.framework.TestCase.assertTrue
|
import junit.framework.TestCase.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import oupson.apng.utils.Utils
|
import oupson.apng.utils.Utils
|
||||||
|
@ -12,7 +13,7 @@ import oupson.apng.utils.Utils
|
||||||
|
|
||||||
class ApngEncoderInstrumentedTest {
|
class ApngEncoderInstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun testDiffBunny() { // TODO BLEND / DISPOSE OP
|
fun testDiffBunny() {
|
||||||
val context = InstrumentationRegistry.getInstrumentation().context
|
val context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
val bunnyFrame1 = getFrame(context, "bunny/frame_apngframe01.png")
|
val bunnyFrame1 = getFrame(context, "bunny/frame_apngframe01.png")
|
||||||
|
@ -31,17 +32,28 @@ class ApngEncoderInstrumentedTest {
|
||||||
|
|
||||||
val diffBall1to2 = Utils.getDiffBitmap(ballFrame1, ballFrame2)
|
val diffBall1to2 = Utils.getDiffBitmap(ballFrame1, ballFrame2)
|
||||||
|
|
||||||
assertTrue(isSimilar(ballFrame1, ballFrame2, diffBall1to2)) // TODO FIX THIS WITH BLEND OP / DISPOSE OP
|
assertTrue(isSimilar(ballFrame1, ballFrame2, diffBall1to2))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSimilar(buffer : Bitmap, frame : Bitmap, diff : Triple<Bitmap, Int, Int>) : Boolean {
|
@Test
|
||||||
|
fun containTransparency() {
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
|
val bunnyFrame1 = getFrame(context, "bunny/frame_apngframe01.png")
|
||||||
|
assertFalse(Utils.containTransparency(bunnyFrame1))
|
||||||
|
|
||||||
|
val ballFrame1 = getFrame(context, "ball/apngframe01.png")
|
||||||
|
assertTrue(Utils.containTransparency(ballFrame1))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimilar(buffer : Bitmap, frame : Bitmap, diff : Utils.Companion.DiffResult) : Boolean {
|
||||||
val btm = buffer.copy(Bitmap.Config.ARGB_8888, true)
|
val btm = buffer.copy(Bitmap.Config.ARGB_8888, true)
|
||||||
|
|
||||||
for (y in 0 until diff.first.height) {
|
for (y in 0 until diff.bitmap.height) {
|
||||||
for (x in 0 until diff.first.width) {
|
for (x in 0 until diff.bitmap.width) {
|
||||||
val p = diff.first.getPixel(x, y)
|
val p = diff.bitmap.getPixel(x, y)
|
||||||
if (p != Color.TRANSPARENT)
|
if (p != Color.TRANSPARENT || p == Color.TRANSPARENT && diff.blendOp == Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE)
|
||||||
btm.setPixel(diff.second + x, diff.third + y, p)
|
btm.setPixel(diff.offsetX + x, diff.offsetY + y, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -553,6 +553,4 @@ class ApngEncoder(
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -208,16 +208,28 @@ class Utils {
|
||||||
val IHDR: ByteArray by lazy { byteArrayOf(0x49, 0x48, 0x44, 0x52) }
|
val IHDR: ByteArray by lazy { byteArrayOf(0x49, 0x48, 0x44, 0x52) }
|
||||||
val acTL: ByteArray by lazy { byteArrayOf(0x61, 0x63, 0x54, 0x4c) }
|
val acTL: ByteArray by lazy { byteArrayOf(0x61, 0x63, 0x54, 0x4c) }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* A class that contain the difference between two bitmaps
|
||||||
|
* @property bitmap A resized [Bitmap] that contain the difference between two bitmaps
|
||||||
|
* @property offsetX the x offset
|
||||||
|
* @property offsetY the y offset
|
||||||
|
* @property blendOp a [BlendOp]
|
||||||
*/
|
*/
|
||||||
// TODO DOC
|
data class DiffResult(val bitmap: Bitmap, val offsetX : Int, val offsetY : Int, val blendOp: BlendOp)
|
||||||
fun getDiffBitmap(bitmapBuffer : Bitmap, btm : Bitmap) : Triple<Bitmap, Int, Int> { // TODO BLEND / DISPOSE OP
|
|
||||||
if (bitmapBuffer.width < btm.width || bitmapBuffer.height < btm.height) {
|
/**
|
||||||
|
* Get the difference between two bitmaps
|
||||||
|
* @param firstBitmap A [Bitmap], the first bitmap
|
||||||
|
* @param secondBitmap A [Bitmap], a second bitmap
|
||||||
|
* @return [DiffResult], the difference between the second and the first bitmap
|
||||||
|
*/
|
||||||
|
fun getDiffBitmap(firstBitmap : Bitmap, secondBitmap : Bitmap) : DiffResult {
|
||||||
|
if (firstBitmap.width < secondBitmap.width || firstBitmap.height < secondBitmap.height) {
|
||||||
TODO("EXCEPTION BAD IMAGE SIZE")
|
TODO("EXCEPTION BAD IMAGE SIZE")
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultBtm = Bitmap.createBitmap(btm.width, btm.height, Bitmap.Config.ARGB_8888)
|
var resultBtm = Bitmap.createBitmap(secondBitmap.width, secondBitmap.height, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
var offsetX = resultBtm.width + 1
|
var offsetX = resultBtm.width + 1
|
||||||
var offsetY = resultBtm.height + 1
|
var offsetY = resultBtm.height + 1
|
||||||
|
@ -225,12 +237,19 @@ class Utils {
|
||||||
var lastX = 0
|
var lastX = 0
|
||||||
var lastY = 0
|
var lastY = 0
|
||||||
|
|
||||||
for (y in 0 until btm.height) {
|
// Find if the image contain transparent pixels, if true, then transparent pixels must replace the pixels in the buffer
|
||||||
for (x in 0 until btm.width) {
|
val blendOp = if(containTransparency(secondBitmap)) APNG_BLEND_OP_SOURCE else APNG_BLEND_OP_OVER
|
||||||
if (bitmapBuffer.getPixel(x, y) == btm.getPixel(x, y)) {
|
|
||||||
resultBtm.setPixel(x, y, Color.TRANSPARENT)
|
for (y in 0 until secondBitmap.height) {
|
||||||
} else {
|
for (x in 0 until secondBitmap.width) {
|
||||||
resultBtm.setPixel(x, y, btm.getPixel(x, y))
|
val btmPixel = secondBitmap.getPixel(x, y)
|
||||||
|
if (firstBitmap.getPixel(x, y) == btmPixel) { // Similar pixels could be forgotten
|
||||||
|
if (blendOp == APNG_BLEND_OP_OVER)
|
||||||
|
resultBtm.setPixel(x, y, Color.TRANSPARENT)
|
||||||
|
else
|
||||||
|
resultBtm.setPixel(x, y, firstBitmap.getPixel(x, y))
|
||||||
|
} else { // Otherwise, track image bounds
|
||||||
|
resultBtm.setPixel(x, y, btmPixel)
|
||||||
if (x < offsetX)
|
if (x < offsetX)
|
||||||
offsetX = x
|
offsetX = x
|
||||||
if (y < offsetY)
|
if (y < offsetY)
|
||||||
|
@ -249,9 +268,33 @@ class Utils {
|
||||||
val newWidth = lastX - offsetX
|
val newWidth = lastX - offsetX
|
||||||
val newHeight = lastY - offsetY
|
val newHeight = lastY - offsetY
|
||||||
|
|
||||||
|
// Resize bitmap
|
||||||
resultBtm = Bitmap.createBitmap(resultBtm, offsetX, offsetY, newWidth, newHeight)
|
resultBtm = Bitmap.createBitmap(resultBtm, offsetX, offsetY, newWidth, newHeight)
|
||||||
|
|
||||||
return Triple(resultBtm, offsetX, offsetY)
|
return DiffResult(resultBtm, offsetX, offsetY, blendOp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function return true if the bitmap contain transparent pixels
|
||||||
|
* @param btm A [Bitmap]
|
||||||
|
* @return [Boolean] true if if the bitmap contain transparent pixels
|
||||||
|
*/
|
||||||
|
fun containTransparency(btm : Bitmap) : Boolean {
|
||||||
|
var result = false
|
||||||
|
var y = 0
|
||||||
|
var x = 0
|
||||||
|
while (y < btm.height && !result) {
|
||||||
|
if (btm.getPixel(x, y) == Color.TRANSPARENT) {
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
|
||||||
|
x++
|
||||||
|
if (x == btm.width) {
|
||||||
|
x = 0
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue