mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
code cleanup
This commit is contained in:
@@ -25,78 +25,6 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
private val idct8TempBuffer = FloatArray(64)
|
||||
private val idct16TempBuffer = FloatArray(256) // For 16x16 IDCT
|
||||
private val idct16SeparableBuffer = FloatArray(256) // For separable 16x16 IDCT
|
||||
|
||||
// Lossless IDCT functions for float16 coefficients (no quantization)
|
||||
private fun tevIdct8x8_lossless(coeffs: FloatArray): IntArray {
|
||||
val result = IntArray(64)
|
||||
|
||||
// Fast separable IDCT (row-column decomposition) for lossless coefficients
|
||||
// First pass: Process rows (8 1D IDCTs)
|
||||
for (row in 0 until 8) {
|
||||
for (col in 0 until 8) {
|
||||
var sum = 0f
|
||||
for (u in 0 until 8) {
|
||||
sum += dctBasis8[u][col] * coeffs[row * 8 + u]
|
||||
}
|
||||
idct8TempBuffer[row * 8 + col] = sum * 0.5f
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Process columns (8 1D IDCTs)
|
||||
for (col in 0 until 8) {
|
||||
for (row in 0 until 8) {
|
||||
var sum = 0f
|
||||
for (v in 0 until 8) {
|
||||
sum += dctBasis8[v][row] * idct8TempBuffer[v * 8 + col]
|
||||
}
|
||||
val finalValue = sum * 0.5f + 128f
|
||||
result[row * 8 + col] = if (finalValue.isNaN() || finalValue.isInfinite()) {
|
||||
println("NaN/Inf detected in 8x8 IDCT at ($row,$col): sum=$sum, finalValue=$finalValue")
|
||||
128 // Default to middle gray
|
||||
} else {
|
||||
finalValue.roundToInt().coerceIn(0, 255)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun tevIdct16x16_lossless(coeffs: FloatArray): IntArray {
|
||||
val result = IntArray(256)
|
||||
|
||||
// Fast separable IDCT (row-column decomposition) for 16x16 lossless coefficients
|
||||
// First pass: Process rows (16 1D IDCTs)
|
||||
for (row in 0 until 16) {
|
||||
for (col in 0 until 16) {
|
||||
var sum = 0f
|
||||
for (u in 0 until 16) {
|
||||
sum += dctBasis16[u][col] * coeffs[row * 16 + u]
|
||||
}
|
||||
idct16TempBuffer[row * 16 + col] = sum * 0.25f
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Process columns (16 1D IDCTs)
|
||||
for (col in 0 until 16) {
|
||||
for (row in 0 until 16) {
|
||||
var sum = 0f
|
||||
for (v in 0 until 16) {
|
||||
sum += dctBasis16[v][row] * idct16TempBuffer[v * 16 + col]
|
||||
}
|
||||
val finalValue = sum * 0.25f + 128f
|
||||
result[row * 16 + col] = if (finalValue.isNaN() || finalValue.isInfinite()) {
|
||||
println("NaN/Inf detected in 16x16 IDCT at ($row,$col): sum=$sum, finalValue=$finalValue")
|
||||
128 // Default to middle gray
|
||||
} else {
|
||||
finalValue.roundToInt().coerceIn(0, 255)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
private fun getFirstGPU(): GraphicsAdapter? {
|
||||
return vm.findPeribyType(VM.PERITYPE_GPU_AND_TERM)?.peripheral as? GraphicsAdapter
|
||||
@@ -3665,52 +3593,6 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertAndDoNothing(
|
||||
blocks: Array<ShortArray?>, quantTable: IntArray, qScale: Int, rateControlFactors: FloatArray,
|
||||
blocksX: Int, blocksY: Int,
|
||||
kLinearGradient: IntArray, kAlphaSqrt2: IntArray, kHalfSqrt2: Int
|
||||
): Array<FloatArray?> {
|
||||
val coeffsSize = 16 * 16
|
||||
val numBlocks = blocksX * blocksY
|
||||
|
||||
val blocksMid = Array(numBlocks) { IntArray(coeffsSize) }
|
||||
|
||||
for (blockIndex in 0 until numBlocks) {
|
||||
val block = blocks[blockIndex]
|
||||
if (block != null) {
|
||||
val rateControlFactor = rateControlFactors[blockIndex]
|
||||
for (i in 0 until coeffsSize) {
|
||||
val quantIdx = i.coerceIn(0, quantTable.size - 1)
|
||||
|
||||
if (i == 0) {
|
||||
// DC coefficient: lossless (no quantization)
|
||||
val dcValue = block[i].toInt()
|
||||
blocksMid[blockIndex][i] = dcValue
|
||||
} else {
|
||||
// AC coefficients: use quantization intervals
|
||||
val quant = (quantTable[quantIdx] * jpeg_quality_to_mult(qScale * rateControlFactor)).coerceIn(1f, 255f).toInt()
|
||||
|
||||
// Standard dequantized value (midpoint)
|
||||
blocksMid[blockIndex][i] = block[i].toInt() * quant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = Array<FloatArray?>(blocks.size) { null }
|
||||
for (blockIndex in 0 until numBlocks) {
|
||||
val block = blocks[blockIndex]
|
||||
if (block != null) {
|
||||
result[blockIndex] = FloatArray(coeffsSize) { i ->
|
||||
blocksMid[blockIndex][i].toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
private fun convertAndOptimize8x8Blocks(
|
||||
blocks: Array<ShortArray?>, quantTable: IntArray, qScale: Int, rateControlFactors: FloatArray,
|
||||
blocksX: Int, blocksY: Int,
|
||||
@@ -3982,10 +3864,6 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
} catch (e: Exception) {
|
||||
println("TAV decode error: ${e.message}")
|
||||
}
|
||||
|
||||
// if (enableDeblocking) {
|
||||
// tavAdaptiveDeblockingFilter(currentRGBAddr, width, height)
|
||||
// }
|
||||
}
|
||||
|
||||
private fun decodeDWTIntraTileRGB(readPtr: Long, tileX: Int, tileY: Int, currentRGBAddr: Long,
|
||||
@@ -4310,51 +4188,6 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyDWT53Forward(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 5/3 forward DWT
|
||||
// Lifting scheme implementation for 5/3 reversible filter
|
||||
}
|
||||
|
||||
private fun applyDWT53Inverse(data: FloatArray, width: Int, height: Int) {
|
||||
// 5/3 reversible DWT inverse using lifting scheme
|
||||
// First apply horizontal inverse DWT on all rows
|
||||
val tempRow = FloatArray(width)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
tempRow[x] = data[y * width + x]
|
||||
}
|
||||
applyLift53InverseHorizontal(tempRow, width)
|
||||
for (x in 0 until width) {
|
||||
data[y * width + x] = tempRow[x]
|
||||
}
|
||||
}
|
||||
|
||||
// Then apply vertical inverse DWT on all columns
|
||||
val tempCol = FloatArray(height)
|
||||
for (x in 0 until width) {
|
||||
for (y in 0 until height) {
|
||||
tempCol[y] = data[y * width + x]
|
||||
}
|
||||
applyLift53InverseVertical(tempCol, height)
|
||||
for (y in 0 until height) {
|
||||
data[y * width + x] = tempCol[y]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyDWT97Forward(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 9/7 forward DWT
|
||||
// Lifting scheme implementation for 9/7 irreversible filter
|
||||
}
|
||||
|
||||
private fun generateWindowFunction(window: FloatArray, size: Int) {
|
||||
// Raised cosine (Hann) window for smooth blending
|
||||
for (i in 0 until size) {
|
||||
val t = i.toFloat() / (size - 1)
|
||||
window[i] = 0.5f * (1.0f - kotlin.math.cos(PI * t))
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyDWTInverseMultiLevel(data: FloatArray, width: Int, height: Int, levels: Int, filterType: Int) {
|
||||
// Multi-level inverse DWT - reconstruct from smallest to largest (reverse of encoder)
|
||||
val size = width // Full tile size (112 for TAV)
|
||||
@@ -4411,84 +4244,6 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyDWT97Inverse(data: FloatArray, width: Int, height: Int) {
|
||||
// 9/7 irreversible DWT inverse using lifting scheme
|
||||
// First apply horizontal inverse DWT on all rows
|
||||
val tempRow = FloatArray(width)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
tempRow[x] = data[y * width + x]
|
||||
}
|
||||
applyLift97InverseHorizontal(tempRow, width)
|
||||
for (x in 0 until width) {
|
||||
data[y * width + x] = tempRow[x]
|
||||
}
|
||||
}
|
||||
|
||||
// Then apply vertical inverse DWT on all columns
|
||||
val tempCol = FloatArray(height)
|
||||
for (x in 0 until width) {
|
||||
for (y in 0 until height) {
|
||||
tempCol[y] = data[y * width + x]
|
||||
}
|
||||
applyLift97InverseVertical(tempCol, height)
|
||||
for (y in 0 until height) {
|
||||
data[y * width + x] = tempCol[y]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyLift97InverseHorizontal(row: FloatArray, width: Int) { TODO() }
|
||||
private fun applyLift97InverseVertical(col: FloatArray, height: Int) { TODO() }
|
||||
|
||||
// 1D lifting scheme implementations for 5/3 filter
|
||||
private fun applyLift53InverseHorizontal(data: FloatArray, length: Int) {
|
||||
if (length < 2) return
|
||||
|
||||
val temp = FloatArray(length)
|
||||
val half = (length + 1) / 2
|
||||
|
||||
// Separate even and odd samples (inverse interleaving)
|
||||
for (i in 0 until half) {
|
||||
temp[i] = data[2 * i] // Even samples (low-pass)
|
||||
}
|
||||
for (i in 0 until length / 2) {
|
||||
temp[half + i] = data[2 * i + 1] // Odd samples (high-pass)
|
||||
}
|
||||
|
||||
// Inverse lifting steps for 5/3 filter
|
||||
// Step 2: Undo update step - even[i] -= (odd[i-1] + odd[i] + 2) >> 2
|
||||
for (i in 1 until half) {
|
||||
val oddPrev = if (i - 1 >= 0) temp[half + i - 1] else 0.0f
|
||||
val oddCurr = if (i < length / 2) temp[half + i] else 0.0f
|
||||
temp[i] += (oddPrev + oddCurr + 2.0f) / 4.0f
|
||||
}
|
||||
if (half > 0) {
|
||||
val oddCurr = if (0 < length / 2) temp[half] else 0.0f
|
||||
temp[0] += oddCurr / 2.0f
|
||||
}
|
||||
|
||||
// Step 1: Undo predict step - odd[i] += (even[i] + even[i+1]) >> 1
|
||||
for (i in 0 until length / 2) {
|
||||
val evenCurr = temp[i]
|
||||
val evenNext = if (i + 1 < half) temp[i + 1] else temp[half - 1]
|
||||
temp[half + i] -= (evenCurr + evenNext) / 2.0f
|
||||
}
|
||||
|
||||
// Interleave back
|
||||
for (i in 0 until half) {
|
||||
data[2 * i] = temp[i]
|
||||
}
|
||||
for (i in 0 until length / 2) {
|
||||
data[2 * i + 1] = temp[half + i]
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyLift53InverseVertical(data: FloatArray, length: Int) {
|
||||
// Same as horizontal but for vertical direction
|
||||
applyLift53InverseHorizontal(data, length)
|
||||
}
|
||||
|
||||
// 1D lifting scheme implementations for 9/7 irreversible filter
|
||||
private fun applyDWT97Inverse1D(data: FloatArray, length: Int) {
|
||||
if (length < 2) return
|
||||
@@ -4643,426 +4398,4 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun tavAdaptiveDeblockingFilter(rgbAddr: Long, width: Int, height: Int) {
|
||||
val tileSize = 112
|
||||
val tilesX = (width + tileSize - 1) / tileSize
|
||||
val tilesY = (height + tileSize - 1) / tileSize
|
||||
|
||||
// Process vertical seams (between horizontally adjacent tiles)
|
||||
for (tileY in 0 until tilesY) {
|
||||
for (tileX in 0 until tilesX - 1) {
|
||||
val seamX = (tileX + 1) * tileSize // Actual boundary between tiles
|
||||
deblockVerticalSeamStrong(rgbAddr, width, height, seamX, tileY * tileSize, tileSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Process horizontal seams (between vertically adjacent tiles)
|
||||
for (tileY in 0 until tilesY - 1) {
|
||||
for (tileX in 0 until tilesX) {
|
||||
val seamY = (tileY + 1) * tileSize // Actual boundary between tiles
|
||||
deblockHorizontalSeamStrong(rgbAddr, width, height, tileX * tileSize, seamY, tileSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deblockVerticalSeamStrong(rgbAddr: Long, width: Int, height: Int, seamX: Int, startY: Int, tileHeight: Int) {
|
||||
if (seamX >= width) return
|
||||
|
||||
val endY = minOf(startY + tileHeight, height)
|
||||
|
||||
for (y in startY until endY) {
|
||||
if (y >= height) break
|
||||
|
||||
// Check for discontinuity across the seam
|
||||
val leftX = seamX - 1
|
||||
val rightX = seamX
|
||||
|
||||
if (leftX >= 0 && rightX < width) {
|
||||
val leftOffset = (y * width + leftX) * 3L
|
||||
val rightOffset = (y * width + rightX) * 3L
|
||||
|
||||
val leftR = vm.peek(rgbAddr + leftOffset).toInt() and 0xFF
|
||||
val leftG = vm.peek(rgbAddr + leftOffset + 1).toInt() and 0xFF
|
||||
val leftB = vm.peek(rgbAddr + leftOffset + 2).toInt() and 0xFF
|
||||
|
||||
val rightR = vm.peek(rgbAddr + rightOffset).toInt() and 0xFF
|
||||
val rightG = vm.peek(rgbAddr + rightOffset + 1).toInt() and 0xFF
|
||||
val rightB = vm.peek(rgbAddr + rightOffset + 2).toInt() and 0xFF
|
||||
|
||||
// Calculate discontinuity strength
|
||||
val diffR = abs(leftR - rightR)
|
||||
val diffG = abs(leftG - rightG)
|
||||
val diffB = abs(leftB - rightB)
|
||||
val maxDiff = maxOf(diffR, diffG, diffB)
|
||||
|
||||
// Only apply deblocking if there's a significant discontinuity
|
||||
if (maxDiff in 2 until 120) {
|
||||
// Adaptive filter radius: wider for smooth gradients, narrower for sharp edges
|
||||
val filterRadius = when {
|
||||
maxDiff <= 15 -> 6 // Very smooth gradients: wide filter (13 pixels)
|
||||
maxDiff <= 30 -> 4 // Moderate gradients: medium filter (9 pixels)
|
||||
maxDiff <= 60 -> 3 // Sharp transitions: narrow filter (7 pixels)
|
||||
else -> 2 // Very sharp edges: minimal filter (5 pixels)
|
||||
}
|
||||
|
||||
for (dx in -filterRadius..filterRadius) {
|
||||
val x = seamX + dx
|
||||
if (x in 0 until width) {
|
||||
val offset = (y * width + x) * 3L
|
||||
|
||||
val currentR = vm.peek(rgbAddr + offset).toInt() and 0xFF
|
||||
val currentG = vm.peek(rgbAddr + offset + 1).toInt() and 0xFF
|
||||
val currentB = vm.peek(rgbAddr + offset + 2).toInt() and 0xFF
|
||||
|
||||
var sumR = 0.0f
|
||||
var sumG = 0.0f
|
||||
var sumB = 0.0f
|
||||
var weightSum = 0.0f
|
||||
|
||||
// Bilateral filtering with spatial and intensity weights
|
||||
for (sx in maxOf(0, x-filterRadius)..minOf(width-1, x+filterRadius)) {
|
||||
val sOffset = (y * width + sx) * 3L
|
||||
val sR = vm.peek(rgbAddr + sOffset).toInt() and 0xFF
|
||||
val sG = vm.peek(rgbAddr + sOffset + 1).toInt() and 0xFF
|
||||
val sB = vm.peek(rgbAddr + sOffset + 2).toInt() and 0xFF
|
||||
|
||||
// Spatial weight (distance from current pixel)
|
||||
val spatialWeight = 1.0f / (1.0f + abs(sx - x))
|
||||
|
||||
// Intensity weight (color similarity)
|
||||
val colorDiff = sqrt(((sR - currentR) * (sR - currentR) +
|
||||
(sG - currentG) * (sG - currentG) +
|
||||
(sB - currentB) * (sB - currentB)).toFloat())
|
||||
val intensityWeight = exp(-colorDiff / 30.0f)
|
||||
|
||||
val totalWeight = spatialWeight * intensityWeight
|
||||
|
||||
sumR += sR * totalWeight
|
||||
sumG += sG * totalWeight
|
||||
sumB += sB * totalWeight
|
||||
weightSum += totalWeight
|
||||
}
|
||||
|
||||
if (weightSum > 0) {
|
||||
val filteredR = (sumR / weightSum).toInt()
|
||||
val filteredG = (sumG / weightSum).toInt()
|
||||
val filteredB = (sumB / weightSum).toInt()
|
||||
|
||||
// Concentrate blur heavily at the seam boundary
|
||||
val distance = abs(dx).toFloat()
|
||||
val blendWeight = when {
|
||||
distance == 0.0f -> 0.95f // Maximum blur at exact seam
|
||||
distance == 1.0f -> 0.8f // Strong blur adjacent to seam
|
||||
distance == 2.0f -> 0.5f // Medium blur 2 pixels away
|
||||
else -> exp(-distance * distance / 1.5f) * 0.3f // Gentle falloff beyond
|
||||
}
|
||||
|
||||
val finalR = (currentR * (1 - blendWeight) + filteredR * blendWeight).toInt().coerceIn(0, 255)
|
||||
val finalG = (currentG * (1 - blendWeight) + filteredG * blendWeight).toInt().coerceIn(0, 255)
|
||||
val finalB = (currentB * (1 - blendWeight) + filteredB * blendWeight).toInt().coerceIn(0, 255)
|
||||
|
||||
vm.poke(rgbAddr + offset, finalR.toByte())
|
||||
vm.poke(rgbAddr + offset + 1, finalG.toByte())
|
||||
vm.poke(rgbAddr + offset + 2, finalB.toByte())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deblockHorizontalSeamStrong(rgbAddr: Long, width: Int, height: Int, startX: Int, seamY: Int, tileWidth: Int) {
|
||||
if (seamY >= height) return
|
||||
|
||||
val endX = minOf(startX + tileWidth, width)
|
||||
|
||||
for (x in startX until endX) {
|
||||
if (x >= width) break
|
||||
|
||||
// Check for discontinuity across the seam
|
||||
val topY = seamY - 1
|
||||
val bottomY = seamY
|
||||
|
||||
if (topY >= 0 && bottomY < height) {
|
||||
val topOffset = (topY * width + x) * 3L
|
||||
val bottomOffset = (bottomY * width + x) * 3L
|
||||
|
||||
val topR = vm.peek(rgbAddr + topOffset).toInt() and 0xFF
|
||||
val topG = vm.peek(rgbAddr + topOffset + 1).toInt() and 0xFF
|
||||
val topB = vm.peek(rgbAddr + topOffset + 2).toInt() and 0xFF
|
||||
|
||||
val bottomR = vm.peek(rgbAddr + bottomOffset).toInt() and 0xFF
|
||||
val bottomG = vm.peek(rgbAddr + bottomOffset + 1).toInt() and 0xFF
|
||||
val bottomB = vm.peek(rgbAddr + bottomOffset + 2).toInt() and 0xFF
|
||||
|
||||
// Calculate discontinuity strength
|
||||
val diffR = abs(topR - bottomR)
|
||||
val diffG = abs(topG - bottomG)
|
||||
val diffB = abs(topB - bottomB)
|
||||
val maxDiff = maxOf(diffR, diffG, diffB)
|
||||
|
||||
// Only apply deblocking if there's a significant discontinuity
|
||||
if (maxDiff in 2 until 120) {
|
||||
// Adaptive filter radius: wider for smooth gradients, narrower for sharp edges
|
||||
val filterRadius = when {
|
||||
maxDiff <= 15 -> 6 // Very smooth gradients: wide filter (13 pixels)
|
||||
maxDiff <= 30 -> 4 // Moderate gradients: medium filter (9 pixels)
|
||||
maxDiff <= 60 -> 3 // Sharp transitions: narrow filter (7 pixels)
|
||||
else -> 2 // Very sharp edges: minimal filter (5 pixels)
|
||||
}
|
||||
|
||||
for (dy in -filterRadius..filterRadius) {
|
||||
val y = seamY + dy
|
||||
if (y in 0 until height) {
|
||||
val offset = (y * width + x) * 3L
|
||||
|
||||
val currentR = vm.peek(rgbAddr + offset).toInt() and 0xFF
|
||||
val currentG = vm.peek(rgbAddr + offset + 1).toInt() and 0xFF
|
||||
val currentB = vm.peek(rgbAddr + offset + 2).toInt() and 0xFF
|
||||
|
||||
var sumR = 0.0f
|
||||
var sumG = 0.0f
|
||||
var sumB = 0.0f
|
||||
var weightSum = 0.0f
|
||||
|
||||
// Bilateral filtering with spatial and intensity weights
|
||||
for (sy in maxOf(0, y-filterRadius)..minOf(height-1, y+filterRadius)) {
|
||||
val sOffset = (sy * width + x) * 3L
|
||||
val sR = vm.peek(rgbAddr + sOffset).toInt() and 0xFF
|
||||
val sG = vm.peek(rgbAddr + sOffset + 1).toInt() and 0xFF
|
||||
val sB = vm.peek(rgbAddr + sOffset + 2).toInt() and 0xFF
|
||||
|
||||
// Spatial weight (distance from current pixel)
|
||||
val spatialWeight = 1.0f / (1.0f + abs(sy - y))
|
||||
|
||||
// Intensity weight (color similarity)
|
||||
val colorDiff = sqrt(((sR - currentR) * (sR - currentR) +
|
||||
(sG - currentG) * (sG - currentG) +
|
||||
(sB - currentB) * (sB - currentB)).toFloat())
|
||||
val intensityWeight = exp(-colorDiff / 30.0f)
|
||||
|
||||
val totalWeight = spatialWeight * intensityWeight
|
||||
|
||||
sumR += sR * totalWeight
|
||||
sumG += sG * totalWeight
|
||||
sumB += sB * totalWeight
|
||||
weightSum += totalWeight
|
||||
}
|
||||
|
||||
if (weightSum > 0) {
|
||||
val filteredR = (sumR / weightSum).toInt()
|
||||
val filteredG = (sumG / weightSum).toInt()
|
||||
val filteredB = (sumB / weightSum).toInt()
|
||||
|
||||
// Concentrate blur heavily at the seam boundary
|
||||
val distance = abs(dy).toFloat()
|
||||
val blendWeight = when {
|
||||
distance == 0.0f -> 0.95f // Maximum blur at exact seam
|
||||
distance == 1.0f -> 0.8f // Strong blur adjacent to seam
|
||||
distance == 2.0f -> 0.5f // Medium blur 2 pixels away
|
||||
else -> exp(-distance * distance / 1.5f) * 0.3f // Gentle falloff beyond
|
||||
}
|
||||
|
||||
val finalR = (currentR * (1 - blendWeight) + filteredR * blendWeight).toInt().coerceIn(0, 255)
|
||||
val finalG = (currentG * (1 - blendWeight) + filteredG * blendWeight).toInt().coerceIn(0, 255)
|
||||
val finalB = (currentB * (1 - blendWeight) + filteredB * blendWeight).toInt().coerceIn(0, 255)
|
||||
|
||||
vm.poke(rgbAddr + offset, finalR.toByte())
|
||||
vm.poke(rgbAddr + offset + 1, finalG.toByte())
|
||||
vm.poke(rgbAddr + offset + 2, finalB.toByte())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun analyzeTextureComplexity(rgbAddr: Long, width: Int, height: Int, centerX: Int, centerY: Int, isVerticalSeam: Boolean): Float {
|
||||
val radius = 4
|
||||
var totalVariance = 0.0f
|
||||
var count = 0
|
||||
|
||||
// Calculate variance in a small window around the seam
|
||||
for (dy in -radius..radius) {
|
||||
for (dx in -radius..radius) {
|
||||
val x = centerX + dx
|
||||
val y = centerY + dy
|
||||
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
val offset = (y * width + x) * 3L
|
||||
val r = vm.peek(rgbAddr + offset).toInt() and 0xFF
|
||||
val g = vm.peek(rgbAddr + offset + 1).toInt() and 0xFF
|
||||
val b = vm.peek(rgbAddr + offset + 2).toInt() and 0xFF
|
||||
|
||||
val luma = 0.299f * r + 0.587f * g + 0.114f * b
|
||||
|
||||
// Compare with adjacent pixels to measure local variance
|
||||
if (x > 0) {
|
||||
val leftOffset = (y * width + (x-1)) * 3L
|
||||
val leftR = vm.peek(rgbAddr + leftOffset).toInt() and 0xFF
|
||||
val leftG = vm.peek(rgbAddr + leftOffset + 1).toInt() and 0xFF
|
||||
val leftB = vm.peek(rgbAddr + leftOffset + 2).toInt() and 0xFF
|
||||
val leftLuma = 0.299f * leftR + 0.587f * leftG + 0.114f * leftB
|
||||
|
||||
totalVariance += abs(luma - leftLuma)
|
||||
count++
|
||||
}
|
||||
|
||||
if (y > 0) {
|
||||
val topOffset = ((y-1) * width + x) * 3L
|
||||
val topR = vm.peek(rgbAddr + topOffset).toInt() and 0xFF
|
||||
val topG = vm.peek(rgbAddr + topOffset + 1).toInt() and 0xFF
|
||||
val topB = vm.peek(rgbAddr + topOffset + 2).toInt() and 0xFF
|
||||
val topLuma = 0.299f * topR + 0.587f * topG + 0.114f * topB
|
||||
|
||||
totalVariance += abs(luma - topLuma)
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (count > 0) totalVariance / count else 0.0f
|
||||
}
|
||||
|
||||
private fun bilinearInterpolate(
|
||||
dataPtr: Long, width: Int, height: Int,
|
||||
x: Float, y: Float
|
||||
): Float {
|
||||
val x0 = floor(x).toInt()
|
||||
val y0 = floor(y).toInt()
|
||||
val x1 = x0 + 1
|
||||
val y1 = y0 + 1
|
||||
|
||||
if (x0 < 0 || y0 < 0 || x1 >= width || y1 >= height) {
|
||||
return 0.0f // Out of bounds
|
||||
}
|
||||
|
||||
val fx = x - x0
|
||||
val fy = y - y0
|
||||
|
||||
val p00 = vm.peekFloat(dataPtr + (y0 * width + x0) * 4L)!!
|
||||
val p10 = vm.peekFloat(dataPtr + (y0 * width + x1) * 4L)!!
|
||||
val p01 = vm.peekFloat(dataPtr + (y1 * width + x0) * 4L)!!
|
||||
val p11 = vm.peekFloat(dataPtr + (y1 * width + x1) * 4L)!!
|
||||
|
||||
return p00 * (1 - fx) * (1 - fy) +
|
||||
p10 * fx * (1 - fy) +
|
||||
p01 * (1 - fx) * fy +
|
||||
p11 * fx * fy
|
||||
}
|
||||
|
||||
/**
|
||||
* TAV deblocking filter - reduces DWT quantization artifacts and tile boundary artifacts
|
||||
* Applies a gentle smoothing filter across tile boundaries and high-frequency areas
|
||||
*/
|
||||
private fun tavDeblockingFilter(rgbAddr: Long, width: Int, height: Int) {
|
||||
val tileSize = 112 // TAV uses 112x112 tiles
|
||||
val tilesX = (width + tileSize - 1) / tileSize
|
||||
val tilesY = (height + tileSize - 1) / tileSize
|
||||
val thisAddrIncVec: Long = if (rgbAddr < 0) -1 else 1
|
||||
|
||||
// Process tile boundaries (horizontal and vertical)
|
||||
for (tileY in 0 until tilesY) {
|
||||
for (tileX in 0 until tilesX) {
|
||||
val startX = tileX * tileSize
|
||||
val startY = tileY * tileSize
|
||||
val endX = kotlin.math.min(startX + tileSize, width)
|
||||
val endY = kotlin.math.min(startY + tileSize, height)
|
||||
|
||||
// Smooth vertical tile boundaries
|
||||
if (tileX > 0 && startX < width) {
|
||||
for (y in startY until endY) {
|
||||
smoothVerticalBoundary(rgbAddr, width, height, startX - 1, y, thisAddrIncVec)
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth horizontal tile boundaries
|
||||
if (tileY > 0 && startY < height) {
|
||||
for (x in startX until endX) {
|
||||
smoothHorizontalBoundary(rgbAddr, width, height, x, startY - 1, thisAddrIncVec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply gentle smoothing to reduce DWT quantization artifacts
|
||||
applyDWTSmoothing(rgbAddr, width, height, thisAddrIncVec)
|
||||
}
|
||||
|
||||
private fun smoothVerticalBoundary(rgbAddr: Long, width: Int, height: Int, x: Int, y: Int, addrInc: Long) {
|
||||
if (x < 1 || x >= width - 1 || y < 0 || y >= height) return
|
||||
|
||||
for (channel in 0 until 3) {
|
||||
val leftOffset = (y.toLong() * width + (x - 1)) * 3 + channel
|
||||
val centerOffset = (y.toLong() * width + x) * 3 + channel
|
||||
val rightOffset = (y.toLong() * width + (x + 1)) * 3 + channel
|
||||
|
||||
val left = vm.peek(rgbAddr + leftOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
val center = vm.peek(rgbAddr + centerOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
val right = vm.peek(rgbAddr + rightOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
|
||||
// Apply gentle 3-tap filter: [0.25, 0.5, 0.25]
|
||||
val smoothed = ((left + 2 * center + right) / 4).coerceIn(0, 255)
|
||||
vm.poke(rgbAddr + centerOffset * addrInc, smoothed.toByte())
|
||||
}
|
||||
}
|
||||
|
||||
private fun smoothHorizontalBoundary(rgbAddr: Long, width: Int, height: Int, x: Int, y: Int, addrInc: Long) {
|
||||
if (x < 0 || x >= width || y < 1 || y >= height - 1) return
|
||||
|
||||
for (channel in 0 until 3) {
|
||||
val topOffset = ((y - 1).toLong() * width + x) * 3 + channel
|
||||
val centerOffset = (y.toLong() * width + x) * 3 + channel
|
||||
val bottomOffset = ((y + 1).toLong() * width + x) * 3 + channel
|
||||
|
||||
val top = vm.peek(rgbAddr + topOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
val center = vm.peek(rgbAddr + centerOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
val bottom = vm.peek(rgbAddr + bottomOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
|
||||
// Apply gentle 3-tap filter: [0.25, 0.5, 0.25]
|
||||
val smoothed = ((top + 2 * center + bottom) / 4).coerceIn(0, 255)
|
||||
vm.poke(rgbAddr + centerOffset * addrInc, smoothed.toByte())
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyDWTSmoothing(rgbAddr: Long, width: Int, height: Int, addrInc: Long) {
|
||||
// Apply very gentle smoothing to reduce DWT quantization artifacts
|
||||
// Uses a 3x3 Gaussian-like kernel with low strength
|
||||
val kernel = arrayOf(
|
||||
arrayOf(1, 2, 1),
|
||||
arrayOf(2, 4, 2),
|
||||
arrayOf(1, 2, 1)
|
||||
)
|
||||
val kernelSum = 16
|
||||
|
||||
// Process inner pixels only to avoid boundary issues
|
||||
for (y in 1 until height - 1) {
|
||||
for (x in 1 until width - 1) {
|
||||
for (channel in 0 until 3) {
|
||||
var sum = 0
|
||||
|
||||
for (ky in -1..1) {
|
||||
for (kx in -1..1) {
|
||||
val pixelOffset = ((y + ky).toLong() * width + (x + kx)) * 3 + channel
|
||||
val pixelValue = vm.peek(rgbAddr + pixelOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
sum += pixelValue * kernel[ky + 1][kx + 1]
|
||||
}
|
||||
}
|
||||
|
||||
val centerOffset = (y.toLong() * width + x) * 3 + channel
|
||||
val originalValue = vm.peek(rgbAddr + centerOffset * addrInc)?.toUint()?.toInt() ?: 0
|
||||
|
||||
// Blend original with smoothed (low strength: 75% original, 25% smoothed)
|
||||
val smoothedValue = sum / kernelSum
|
||||
val blendedValue = ((originalValue * 3 + smoothedValue) / 4).coerceIn(0, 255)
|
||||
|
||||
vm.poke(rgbAddr + centerOffset * addrInc, blendedValue.toByte())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -203,19 +203,7 @@ typedef struct {
|
||||
|
||||
} tav_encoder_t;
|
||||
|
||||
// 5/3 Wavelet filter coefficients (reversible)
|
||||
static const float WAVELET_5_3_LP[] = {0.5f, 1.0f, 0.5f};
|
||||
static const float WAVELET_5_3_HP[] = {-0.125f, -0.25f, 0.75f, -0.25f, -0.125f};
|
||||
|
||||
// 9/7 Wavelet filter coefficients (irreversible - Daubechies)
|
||||
static const float WAVELET_9_7_LP[] = {
|
||||
0.037828455507f, -0.023849465020f, -0.110624404418f, 0.377402855613f,
|
||||
0.852698679009f, 0.377402855613f, -0.110624404418f, -0.023849465020f, 0.037828455507f
|
||||
};
|
||||
static const float WAVELET_9_7_HP[] = {
|
||||
0.064538882629f, -0.040689417609f, -0.418092273222f, 0.788485616406f,
|
||||
-0.418092273222f, -0.040689417609f, 0.064538882629f
|
||||
};
|
||||
// Wavelet filter constants removed - using lifting scheme implementation instead
|
||||
|
||||
// Function prototypes
|
||||
static void show_usage(const char *program_name);
|
||||
@@ -223,15 +211,9 @@ static tav_encoder_t* create_encoder(void);
|
||||
static void cleanup_encoder(tav_encoder_t *enc);
|
||||
static int initialize_encoder(tav_encoder_t *enc);
|
||||
static void rgb_to_ycocg(const uint8_t *rgb, float *y, float *co, float *cg, int width, int height);
|
||||
static void dwt_2d_forward(float *tile_data, int levels, int filter_type);
|
||||
static void dwt_2d_inverse(dwt_tile_t *tile, float *output, int filter_type);
|
||||
static void quantize_subbands(dwt_tile_t *tile, int q_y, int q_co, int q_cg, float rcf);
|
||||
static int estimate_motion_112x112(const float *current, const float *reference,
|
||||
int width, int height, int tile_x, int tile_y,
|
||||
motion_vector_t *mv);
|
||||
static size_t compress_tile_data(tav_encoder_t *enc, const dwt_tile_t *tiles,
|
||||
const motion_vector_t *mvs, int num_tiles,
|
||||
uint8_t packet_type);
|
||||
|
||||
// Audio and subtitle processing prototypes (from TEV)
|
||||
static int start_audio_conversion(tav_encoder_t *enc);
|
||||
@@ -393,32 +375,6 @@ static void dwt_53_forward_1d(float *data, int length) {
|
||||
free(temp);
|
||||
}
|
||||
|
||||
static void dwt_53_inverse_1d(float *data, int length) {
|
||||
if (length < 2) return;
|
||||
|
||||
float *temp = malloc(length * sizeof(float));
|
||||
int half = (length + 1) / 2; // Handle odd lengths properly
|
||||
|
||||
// Inverse update step
|
||||
for (int i = 0; i < half; i++) {
|
||||
float update = 0.25f * ((i > 0 ? data[half + i - 1] : 0) +
|
||||
(i < half - 1 ? data[half + i] : 0));
|
||||
temp[2 * i] = data[i] - update;
|
||||
}
|
||||
|
||||
// Inverse predict step
|
||||
for (int i = 0; i < half; i++) {
|
||||
int idx = 2 * i + 1;
|
||||
if (idx < length) {
|
||||
float pred = 0.5f * (temp[2 * i] + (2 * i + 2 < length ? temp[2 * i + 2] : temp[2 * i]));
|
||||
temp[idx] = data[half + i] + pred;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy back
|
||||
memcpy(data, temp, length * sizeof(float));
|
||||
free(temp);
|
||||
}
|
||||
|
||||
// 1D DWT using lifting scheme for 9/7 irreversible filter
|
||||
static void dwt_97_forward_1d(float *data, int length) {
|
||||
@@ -574,59 +530,6 @@ static void dwt_2d_forward_padded(float *tile_data, int levels, int filter_type)
|
||||
|
||||
|
||||
|
||||
// 2D DWT forward transform for 112x112 tile
|
||||
static void dwt_2d_forward(float *tile_data, int levels, int filter_type) {
|
||||
const int size = TILE_SIZE;
|
||||
float *temp_row = malloc(size * sizeof(float));
|
||||
float *temp_col = malloc(size * sizeof(float));
|
||||
|
||||
for (int level = 0; level < levels; level++) {
|
||||
int current_size = size >> level;
|
||||
if (current_size < 1) break;
|
||||
if (current_size == 1) {
|
||||
// Level 6: 1x1 - single DC coefficient, no DWT needed
|
||||
// The single coefficient is already in the correct position
|
||||
continue;
|
||||
}
|
||||
|
||||
// Row transform
|
||||
for (int y = 0; y < current_size; y++) {
|
||||
for (int x = 0; x < current_size; x++) {
|
||||
temp_row[x] = tile_data[y * size + x];
|
||||
}
|
||||
|
||||
if (filter_type == WAVELET_5_3_REVERSIBLE) {
|
||||
dwt_53_forward_1d(temp_row, current_size);
|
||||
} else {
|
||||
dwt_97_forward_1d(temp_row, current_size);
|
||||
}
|
||||
|
||||
for (int x = 0; x < current_size; x++) {
|
||||
tile_data[y * size + x] = temp_row[x];
|
||||
}
|
||||
}
|
||||
|
||||
// Column transform
|
||||
for (int x = 0; x < current_size; x++) {
|
||||
for (int y = 0; y < current_size; y++) {
|
||||
temp_col[y] = tile_data[y * size + x];
|
||||
}
|
||||
|
||||
if (filter_type == WAVELET_5_3_REVERSIBLE) {
|
||||
dwt_53_forward_1d(temp_col, current_size);
|
||||
} else {
|
||||
dwt_97_forward_1d(temp_col, current_size);
|
||||
}
|
||||
|
||||
for (int y = 0; y < current_size; y++) {
|
||||
tile_data[y * size + x] = temp_col[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(temp_row);
|
||||
free(temp_col);
|
||||
}
|
||||
|
||||
// Quantization for DWT subbands with rate control
|
||||
static void quantize_dwt_coefficients(float *coeffs, int16_t *quantized, int size, int quantizer, float rcf) {
|
||||
@@ -1802,7 +1705,6 @@ int main(int argc, char *argv[]) {
|
||||
printf("Starting encoding...\n");
|
||||
|
||||
// Main encoding loop - process frames until EOF or frame limit
|
||||
int keyframe_interval = 30; // I-frame every 30 frames
|
||||
int frame_count = 0;
|
||||
int continue_encoding = 1;
|
||||
|
||||
@@ -1871,8 +1773,8 @@ int main(int argc, char *argv[]) {
|
||||
// Frame parity: even frames (0,2,4...) = bottom fields, odd frames (1,3,5...) = top fields
|
||||
}
|
||||
|
||||
// Determine frame type
|
||||
int is_keyframe = 1;//(frame_count % keyframe_interval == 0);
|
||||
// Determine frame type (all frames are keyframes in current implementation)
|
||||
int is_keyframe = 1;
|
||||
|
||||
// Debug: check RGB input data
|
||||
/*if (frame_count < 3) {
|
||||
|
||||
Reference in New Issue
Block a user