mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-13 08:04:03 +09:00
video patch encoding with state machine
This commit is contained in:
@@ -192,7 +192,7 @@ for (let f = 1; ; f++) {
|
|||||||
// get the difference map
|
// get the difference map
|
||||||
let patchEncodedSize = graphics.encodeIpf1d(ipfAreaOld, ipfAreaNew, ipfDelta, WIDTH, HEIGHT)
|
let patchEncodedSize = graphics.encodeIpf1d(ipfAreaOld, ipfAreaNew, ipfDelta, WIDTH, HEIGHT)
|
||||||
|
|
||||||
if (f < 2 || patchEncodedSize > WIDTH * HEIGHT * 0.90) patchEncodedSize = 0
|
if (f < 2 || f == TOTAL_FRAMES || patchEncodedSize > WIDTH * HEIGHT * 0.70) patchEncodedSize = 0
|
||||||
|
|
||||||
// decide whether or not the patch encoding should be used
|
// decide whether or not the patch encoding should be used
|
||||||
let gzlen = gzip.compFromTo(
|
let gzlen = gzip.compFromTo(
|
||||||
|
|||||||
@@ -722,6 +722,14 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
val tempBlockA = ByteArray(blockSize)
|
val tempBlockA = ByteArray(blockSize)
|
||||||
val tempBlockB = ByteArray(blockSize)
|
val tempBlockB = ByteArray(blockSize)
|
||||||
|
|
||||||
|
var currentState: Byte? = null
|
||||||
|
fun emitState(newState: Byte) {
|
||||||
|
if (currentState != newState) {
|
||||||
|
currentState = newState
|
||||||
|
vm.poke(outOffset++, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun writeVarInt(n: Int) {
|
fun writeVarInt(n: Int) {
|
||||||
var value = n
|
var value = n
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -732,6 +740,23 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val blockBuffer = ArrayList<ByteArray>()
|
||||||
|
|
||||||
|
fun flushBlockBuffer() {
|
||||||
|
if (blockBuffer.isNotEmpty()) {
|
||||||
|
// change state
|
||||||
|
emitState(PATCH)
|
||||||
|
// write length
|
||||||
|
writeVarInt(blockBuffer.size)
|
||||||
|
blockBuffer.forEach {
|
||||||
|
for (i in 0 until blockSize) {
|
||||||
|
vm.poke(outOffset++, it[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockBuffer.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (blockIndex in 0 until (blocksPerRow * blocksPerCol)) {
|
for (blockIndex in 0 until (blocksPerRow * blocksPerCol)) {
|
||||||
val offsetA = previousPtr.toLong() + blockIndex * blockSize
|
val offsetA = previousPtr.toLong() + blockIndex * blockSize
|
||||||
val offsetB = currentPtr.toLong() + blockIndex * blockSize
|
val offsetB = currentPtr.toLong() + blockIndex * blockSize
|
||||||
@@ -744,21 +769,20 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
if (isSignificantlyDifferent(tempBlockA, tempBlockB)) {
|
if (isSignificantlyDifferent(tempBlockA, tempBlockB)) {
|
||||||
// [skip payload]
|
// [skip payload]
|
||||||
if (skipCount > 0) {
|
if (skipCount > 0) {
|
||||||
vm.poke(outOffset++, SKIP)
|
emitState(SKIP)
|
||||||
writeVarInt(skipCount)
|
writeVarInt(skipCount)
|
||||||
}
|
}
|
||||||
skipCount = 0
|
skipCount = 0
|
||||||
|
|
||||||
// [block payload]
|
// [block payload]
|
||||||
vm.poke(outOffset++, PATCH)
|
blockBuffer.add(tempBlockB.copyOf())
|
||||||
for (i in 0 until blockSize) {
|
|
||||||
vm.poke(outOffset++, tempBlockB[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
flushBlockBuffer()
|
||||||
skipCount++
|
skipCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flushBlockBuffer()
|
||||||
|
|
||||||
vm.poke(outOffset++, -1)
|
vm.poke(outOffset++, -1)
|
||||||
|
|
||||||
@@ -766,35 +790,52 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isSignificantlyDifferent(a: ByteArray, b: ByteArray): Boolean {
|
private fun isSignificantlyDifferent(a: ByteArray, b: ByteArray): Boolean {
|
||||||
var score = 0
|
var score = 0.0
|
||||||
|
|
||||||
|
fun contrastWeight(v1: Int, v2: Int, delta: Int, weight: Int): Double {
|
||||||
|
val avg = (v1 + v2) / 2.0
|
||||||
|
val contrast = if (avg < 4 || avg > 11) 1.5 else 1.0
|
||||||
|
return delta * weight * contrast
|
||||||
|
}
|
||||||
|
|
||||||
// Co (bytes 0–1): 4 nybbles
|
// Co (bytes 0–1): 4 nybbles
|
||||||
val coA = (a[0].toInt() and 0xFF) or ((a[1].toInt() and 0xFF) shl 8)
|
val coA = (a[0].toInt() and 0xFF) or ((a[1].toInt() and 0xFF) shl 8)
|
||||||
val coB = (b[0].toInt() and 0xFF) or ((b[1].toInt() and 0xFF) shl 8)
|
val coB = (b[0].toInt() and 0xFF) or ((b[1].toInt() and 0xFF) shl 8)
|
||||||
for (i in 0 until 4) {
|
for (i in 0 until 4) {
|
||||||
val delta = abs((coA shr (i * 4) and 0xF) - (coB shr (i * 4) and 0xF))
|
val va = (coA shr (i * 4)) and 0xF
|
||||||
score += delta * 3
|
val vb = (coB shr (i * 4)) and 0xF
|
||||||
|
val delta = abs(va - vb)
|
||||||
|
score += contrastWeight(va, vb, delta, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cg (bytes 2–3): 4 nybbles
|
// Cg (bytes 2–3): 4 nybbles
|
||||||
val cgA = (a[2].toInt() and 0xFF) or ((a[3].toInt() and 0xFF) shl 8)
|
val cgA = (a[2].toInt() and 0xFF) or ((a[3].toInt() and 0xFF) shl 8)
|
||||||
val cgB = (b[2].toInt() and 0xFF) or ((b[3].toInt() and 0xFF) shl 8)
|
val cgB = (b[2].toInt() and 0xFF) or ((b[3].toInt() and 0xFF) shl 8)
|
||||||
for (i in 0 until 4) {
|
for (i in 0 until 4) {
|
||||||
val delta = abs((cgA shr (i * 4) and 0xF) - (cgB shr (i * 4) and 0xF))
|
val va = (cgA shr (i * 4)) and 0xF
|
||||||
score += delta * 3
|
val vb = (cgB shr (i * 4)) and 0xF
|
||||||
|
val delta = abs(va - vb)
|
||||||
|
score += contrastWeight(va, vb, delta, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y (bytes 4–9): 16 nybbles
|
// Y (bytes 4–9): 16 nybbles
|
||||||
for (i in 4 until 10) {
|
for (i in 4 until 10) {
|
||||||
val byteA = a[i].toInt() and 0xFF
|
val byteA = a[i].toInt() and 0xFF
|
||||||
val byteB = b[i].toInt() and 0xFF
|
val byteB = b[i].toInt() and 0xFF
|
||||||
val highDelta = abs((byteA shr 4) - (byteB shr 4))
|
|
||||||
val lowDelta = abs((byteA and 0xF) - (byteB and 0xF))
|
val yAHigh = (byteA shr 4) and 0xF
|
||||||
score += highDelta * 2
|
val yALow = byteA and 0xF
|
||||||
score += lowDelta * 2
|
val yBHigh = (byteB shr 4) and 0xF
|
||||||
|
val yBLow = byteB and 0xF
|
||||||
|
|
||||||
|
val deltaHigh = abs(yAHigh - yBHigh)
|
||||||
|
val deltaLow = abs(yALow - yBLow)
|
||||||
|
|
||||||
|
score += contrastWeight(yAHigh, yBHigh, deltaHigh, 2)
|
||||||
|
score += contrastWeight(yALow, yBLow, deltaLow, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return score > 0
|
return score > 14.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeIpf2(srcPtr: Int, destPtr: Int, width: Int, height: Int, channels: Int, hasAlpha: Boolean, pattern: Int) {
|
fun encodeIpf2(srcPtr: Int, destPtr: Int, width: Int, height: Int, channels: Int, hasAlpha: Boolean, pattern: Int) {
|
||||||
@@ -1068,6 +1109,9 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PATCH -> { // Write literal patch
|
PATCH -> { // Write literal patch
|
||||||
|
val count = readVarInt()
|
||||||
|
|
||||||
|
for (i in 0 until count) {
|
||||||
if (blockIndex >= totalBlocks) break
|
if (blockIndex >= totalBlocks) break
|
||||||
|
|
||||||
val co = readShort()
|
val co = readShort()
|
||||||
@@ -1122,6 +1166,7 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
|
|
||||||
blockIndex++
|
blockIndex++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
REPEAT -> { // Repeat last literal
|
REPEAT -> { // Repeat last literal
|
||||||
val repeatCount = readVarInt()
|
val repeatCount = readVarInt()
|
||||||
|
|||||||
Reference in New Issue
Block a user