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.Color
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import junit.framework.TestCase.assertFalse
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Test
|
||||
import oupson.apng.utils.Utils
|
||||
|
@ -12,7 +13,7 @@ import oupson.apng.utils.Utils
|
|||
|
||||
class ApngEncoderInstrumentedTest {
|
||||
@Test
|
||||
fun testDiffBunny() { // TODO BLEND / DISPOSE OP
|
||||
fun testDiffBunny() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
val bunnyFrame1 = getFrame(context, "bunny/frame_apngframe01.png")
|
||||
|
@ -31,17 +32,28 @@ class ApngEncoderInstrumentedTest {
|
|||
|
||||
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)
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -553,6 +553,4 @@ class ApngEncoder(
|
|||
i++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -208,16 +208,28 @@ class Utils {
|
|||
val IHDR: ByteArray by lazy { byteArrayOf(0x49, 0x48, 0x44, 0x52) }
|
||||
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
|
||||
fun getDiffBitmap(bitmapBuffer : Bitmap, btm : Bitmap) : Triple<Bitmap, Int, Int> { // TODO BLEND / DISPOSE OP
|
||||
if (bitmapBuffer.width < btm.width || bitmapBuffer.height < btm.height) {
|
||||
data class DiffResult(val bitmap: Bitmap, val offsetX : Int, val offsetY : Int, val blendOp: BlendOp)
|
||||
|
||||
/**
|
||||
* 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")
|
||||
}
|
||||
|
||||
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 offsetY = resultBtm.height + 1
|
||||
|
@ -225,12 +237,19 @@ class Utils {
|
|||
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)) {
|
||||
// Find if the image contain transparent pixels, if true, then transparent pixels must replace the pixels in the buffer
|
||||
val blendOp = if(containTransparency(secondBitmap)) APNG_BLEND_OP_SOURCE else APNG_BLEND_OP_OVER
|
||||
|
||||
for (y in 0 until secondBitmap.height) {
|
||||
for (x in 0 until secondBitmap.width) {
|
||||
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, btm.getPixel(x, y))
|
||||
else
|
||||
resultBtm.setPixel(x, y, firstBitmap.getPixel(x, y))
|
||||
} else { // Otherwise, track image bounds
|
||||
resultBtm.setPixel(x, y, btmPixel)
|
||||
if (x < offsetX)
|
||||
offsetX = x
|
||||
if (y < offsetY)
|
||||
|
@ -249,9 +268,33 @@ class Utils {
|
|||
val newWidth = lastX - offsetX
|
||||
val newHeight = lastY - offsetY
|
||||
|
||||
// Resize bitmap
|
||||
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