diff --git a/terranmon.txt b/terranmon.txt index da8b396..aff97e1 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -684,7 +684,6 @@ DCT-based compression, motion compensation, and efficient temporal coding. * Enables bitrate-constrained encoding alongside quality modes * All video frames now include 4-byte rate control factor after payload size - Version 3.0: Additional support of XYB Colour space - * Increased encoding efficiency, decreased decoding performance # File Structure \x1F T S V M T E V diff --git a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index fa67852..7a8035a 100644 --- a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -1656,10 +1656,13 @@ class GraphicsJSR223Delegate(private val vm: VM) { val x = xBlock[xbIdx] val b = bBlock[xbIdx] - // Dequantize from integer ranges - val yVal = (y - 128.0) / 255.0 - val xVal = x / 255.0 - val bVal = b / 255.0 + // Optimal range-based dequantization (exact inverse of improved quantization) + val X_MIN = -0.016; val X_MAX = 0.030 + val xVal = (x / 255.0) * (X_MAX - X_MIN) + X_MIN // X: inverse of range mapping + val Y_MAX = 0.85 + val yVal = (y / 255.0) * Y_MAX // Y: inverse of improved scale + val B_MAX = 0.85 + val bVal = (((b - 1.0) + 128.0) / 255.0) * B_MAX // B: inverse of ((val/B_MAX*255)-128+1) // XYB to LMS gamma val lgamma = xVal + yVal @@ -1723,10 +1726,16 @@ class GraphicsJSR223Delegate(private val vm: VM) { val yVal = (lgamma + mgamma) / 2.0 val bVal = sgamma - // Quantize to integer ranges suitable for TEV - val yQuant = (yVal * 255.0 + 128.0).toInt().coerceIn(0, 255) // Y: 0-255 (like YCoCg Y) - val xQuant = (xVal * 255.0).toInt().coerceIn(-128, 127) // X: -128 to +127 (like Co) - val bQuant = (bVal * 255.0).toInt().coerceIn(-128, 127) // B: -128 to +127 (like Cg, aggressively quantized) + // Optimal range-based quantization for XYB values (improved precision) + // X: actual range -0.016 to +0.030, map to full 0-255 precision + val X_MIN = -0.016; val X_MAX = 0.030 + val xQuant = (((xVal - X_MIN) / (X_MAX - X_MIN)) * 255.0).toInt().coerceIn(0, 255) + // Y: range 0 to 0.85, map to 0 to 255 (improved scale) + val Y_MAX = 0.85 + val yQuant = ((yVal / Y_MAX) * 255.0).toInt().coerceIn(0, 255) + // B: range 0 to 0.85, map to -128 to +127 (improved precision with +1 offset for yellow-green) + val B_MAX = 0.85 + val bQuant = (((bVal / B_MAX) * 255.0) - 128.0 + 1.0).toInt().coerceIn(-128, 127) // Store XYB values val yIdx = py * 16 + px diff --git a/video_encoder/encoder_tev_xyb.c b/video_encoder/encoder_tev_xyb.c index b227a1c..3bcf2a0 100644 --- a/video_encoder/encoder_tev_xyb.c +++ b/video_encoder/encoder_tev_xyb.c @@ -232,18 +232,32 @@ static void rgb_to_xyb(uint8_t r, uint8_t g, uint8_t b, int *y, int *x, int *xyb double y_val = (lgamma + mgamma) / 2.0; double b_val = sgamma; - // Quantize to integer ranges suitable for TEV - *y = CLAMP((int)(y_val * 255.0 + 128.0), 0, 255); // Y: 0-255 (like YCoCg Y) - *x = CLAMP((int)(x_val * 255.0), -128, 127); // X: -128 to +127 (like Co) - *xyb_b = CLAMP((int)(b_val * 255.0), -128, 127); // B: -128 to +127 (like Cg, aggressively quantized) + // Optimal range-based quantization for XYB values (improved precision) + // X: actual range -0.016 to +0.030, map to full 0-255 precision + const double X_MIN = -0.016, X_MAX = 0.030; + *x = CLAMP((int)(((x_val - X_MIN) / (X_MAX - X_MIN)) * 255.0), 0, 255); + // Y: range 0 to 0.85, map to 0 to 255 (improved scale) + const double Y_MAX = 0.85; + *y = CLAMP((int)((y_val / Y_MAX) * 255.0), 0, 255); + // B: range 0 to 0.85, map to -128 to +127 (improved precision with +1 offset for yellow-green) + const double B_MAX = 0.85; + *xyb_b = CLAMP((int)(((b_val / B_MAX) * 255.0) - 128.0 + 1.0), -128, 127); } // XYB to RGB transform (for verification) static void xyb_to_rgb(int y, int x, int xyb_b, uint8_t *r, uint8_t *g, uint8_t *b) { - // Dequantize from integer ranges - double y_val = (y - 128.0) / 255.0; - double x_val = x / 255.0; - double b_val = xyb_b / 255.0; + // Optimal range-based dequantization (exact inverse of improved quantization) + const double X_MIN = -0.016, X_MAX = 0.030; + double x_val = (x / 255.0) * (X_MAX - X_MIN) + X_MIN; // X: inverse of range mapping + const double Y_MAX = 0.85; + double y_val = (y / 255.0) * Y_MAX; // Y: inverse of improved scale + const double B_MAX = 0.85; + double b_val = (((xyb_b - 1.0) + 128.0) / 255.0) * B_MAX; // B: inverse of ((val/B_MAX*255)-128+1) + + // Debug print for red color + if (x == 127 && y == 147 && xyb_b == 28) { + printf("DEBUG: Red conversion - Dequantized XYB: X=%.6f Y=%.6f B=%.6f\n", x_val, y_val, b_val); + } // XYB to LMS gamma double lgamma = x_val + y_val; diff --git a/video_encoder/xyb_conversion.c b/video_encoder/xyb_conversion.c index 84b4c3c..dd14f08 100644 --- a/video_encoder/xyb_conversion.c +++ b/video_encoder/xyb_conversion.c @@ -12,7 +12,7 @@ // XYB conversion constants from JPEG XL specification static const double XYB_BIAS = 0.00379307325527544933; -static const double CBRT_BIAS = 0.01558; // cbrt(XYB_BIAS) +static const double CBRT_BIAS = 0.155954200549248620; // cbrt(XYB_BIAS) // RGB to LMS mixing coefficients static const double RGB_TO_LMS[3][3] = {