Fix color controls bug affecting YPbPr + direct_video (#735)

This commit is contained in:
Sam Hardeman
2023-01-30 04:35:29 +09:00
committed by GitHub
parent 7cca599fd3
commit 12597108ab

343
video.cpp
View File

@@ -1066,12 +1066,6 @@ static void hdmi_config_set_csc()
// default color conversion matrices
// for the original hexadecimal versions please refer
// to the ADV7513 programming guide section 4.3.7
float ypbpr_coeffs[] = {
0.42944335937f, 1.64038085938f, 1.93017578125f, 0.49389648437f,
0.25683593750f, 0.50415039062f, 0.09790039062f, 0.06250f,
1.85498046875f, 1.71557617188f, 0.42944335937f, 0.49389648437f,
0.0f, 0.0f, 0.0f, 1.0f
};
// no transformation, so use identity matrix
float hdmi_full_coeffs[] = {
@@ -1104,193 +1098,188 @@ static void hdmi_config_set_csc()
const float pi = float(M_PI);
// select the base CSC
int ypbpr = cfg.ypbpr && cfg.direct_video;
// out-of-scope defines, not used with ypbpr
int16_t csc_int16[12];
int hdmi_limited_1 = cfg.hdmi_limited & 1;
int hdmi_limited_2 = cfg.hdmi_limited & 2;
int hdr = cfg.hdr;
mat4x4 coeffs = hdmi_full_coeffs;
if (hdr == 1)
coeffs = hdmi_full_coeffs;
else if (hdr == 2)
coeffs = hdr_dcip3_coeffs;
else
if (!ypbpr)
{
if (ypbpr)
coeffs = ypbpr_coeffs;
else if (hdmi_limited_1)
coeffs = hdmi_limited_1_coeffs;
else if (hdmi_limited_2)
coeffs = hdmi_limited_2_coeffs;
else
// select the base CSC
int hdr = cfg.hdr;
mat4x4 coeffs = hdmi_full_coeffs;
if (hdr == 1)
coeffs = hdmi_full_coeffs;
else if (hdr == 2)
coeffs = hdr_dcip3_coeffs;
else
{
if (hdmi_limited_1)
coeffs = hdmi_limited_1_coeffs;
else if (hdmi_limited_2)
coeffs = hdmi_limited_2_coeffs;
else
coeffs = hdmi_full_coeffs;
}
mat4x4 csc(coeffs);
// apply color controls
float brightness = (((cfg.video_brightness/100.0f) - 0.5f)); // [-0.5 .. 0.5]
float contrast = ((cfg.video_contrast/100.0f) - 0.5f) * 2.0f + 1.0f; // [0 .. 2]
float saturation = ((cfg.video_saturation/100.0f)); // [0 .. 1]
float hue = (cfg.video_hue * pi / 180.0f);
char* gain_offset = cfg.video_gain_offset;
// we have to parse these
float gain_red = 1;
float gain_green = 1;
float gain_blue = 1;
float off_red = 0;
float off_green = 0;
float off_blue = 0;
size_t target = 0;
float* targets[6] = { &gain_red, &off_red, &gain_green, &off_green, &gain_blue, &off_blue };
for (size_t i = 0; i < strlen(gain_offset) && target < 6; i++)
{
// skip whitespace
if (gain_offset[i] == ' ' || gain_offset[i] == ',')
continue;
int numRead = 0;
int match = sscanf(gain_offset + i, "%f%n", targets[target], &numRead);
i += numRead > 0 ? numRead - 1 : 0;
if (match == 1)
target++;
}
// first apply hue matrix, because it does not touch luminance
float cos_hue = cos(hue);
float sin_hue = sin(hue);
float lr = 0.213f;
float lg = 0.715f;
float lb = 0.072f;
float ca = 0.143f;
float cb = 0.140f;
float cc = 0.283f;
mat4x4 mat_hue;
mat_hue.setIdentity();
mat_hue.m11 = lr+cos_hue*(1-lr)+sin_hue*(-lr);
mat_hue.m12 = lg+cos_hue*(-lg) +sin_hue*(-lg);
mat_hue.m13 = lb+cos_hue*(-lb) +sin_hue*(1-lb);
mat_hue.m21 = lr+cos_hue*(-lr) +sin_hue*(ca);
mat_hue.m22 = lg+cos_hue*(1-lg)+sin_hue*(cb);
mat_hue.m23 = lb+cos_hue*(-lb) +sin_hue*(cc);
mat_hue.m31 = lr+cos_hue*(-lr) +sin_hue*(-(1-lr));
mat_hue.m32 = lg+cos_hue*(-lg) +sin_hue*(lg);
mat_hue.m33 = lb+cos_hue*(1-lb)+sin_hue*(lb);
csc = csc * mat_hue;
// now saturation
float s = saturation;
float sr = ( 1.0f - s ) * .3086f;
float sg = ( 1.0f - s ) * .6094f;
float sb = ( 1.0f - s ) * .0920f;
float mat_saturation[] = {
sr + s, sg, sb, 0,
sr, sg + s, sb, 0,
sr, sg, sb + s, 0,
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_saturation);
// now brightness and contrast
float b = brightness;
float c = contrast;
float t = (1.0f - c) / 2.0f;
float mat_brightness_contrast[] = {
c, 0, 0, (t+b),
0, c, 0, (t+b),
0, 0, c, (t+b),
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_brightness_contrast);
// gain and offset
float rg = gain_red;
float ro = off_red;
float gg = gain_green;
float go = off_green;
float bg = gain_blue;
float bo = off_blue;
float mat_gain_off[] = {
rg, 0, 0, ro,
0, gg, 0, go,
0, 0, bg, bo,
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_gain_off);
// final compression
csc.compress(2.0f);
// finally, apply a fixed multiplier to get it in
// correct range for ADV7513 chip
for (size_t i = 0; i < 12; i++)
{
csc_int16[i] = int16_t(csc.comp[i] * 2048.0f);
}
}
mat4x4 csc(coeffs);
// apply color controls
float brightness = (((cfg.video_brightness/100.0f) - 0.5f)); // [-0.5 .. 0.5]
float contrast = ((cfg.video_contrast/100.0f) - 0.5f) * 2.0f + 1.0f; // [0 .. 2]
float saturation = ((cfg.video_saturation/100.0f)); // [0 .. 1]
float hue = (cfg.video_hue * pi / 180.0f);
char* gain_offset = cfg.video_gain_offset;
// we have to parse these
float gain_red = 1;
float gain_green = 1;
float gain_blue = 1;
float off_red = 0;
float off_green = 0;
float off_blue = 0;
size_t target = 0;
float* targets[6] = { &gain_red, &off_red, &gain_green, &off_green, &gain_blue, &off_blue };
for (size_t i = 0; i < strlen(gain_offset) && target < 6; i++)
{
// skip whitespace
if (gain_offset[i] == ' ' || gain_offset[i] == ',')
continue;
int numRead = 0;
int match = sscanf(gain_offset + i, "%f%n", targets[target], &numRead);
i += numRead > 0 ? numRead - 1 : 0;
if (match == 1)
target++;
}
// first apply hue matrix, because it does not touch luminance
float cos_hue = cos(hue);
float sin_hue = sin(hue);
float lr = 0.213f;
float lg = 0.715f;
float lb = 0.072f;
float ca = 0.143f;
float cb = 0.140f;
float cc = 0.283f;
mat4x4 mat_hue;
mat_hue.setIdentity();
mat_hue.m11 = lr+cos_hue*(1-lr)+sin_hue*(-lr);
mat_hue.m12 = lg+cos_hue*(-lg) +sin_hue*(-lg);
mat_hue.m13 = lb+cos_hue*(-lb) +sin_hue*(1-lb);
mat_hue.m21 = lr+cos_hue*(-lr) +sin_hue*(ca);
mat_hue.m22 = lg+cos_hue*(1-lg)+sin_hue*(cb);
mat_hue.m23 = lb+cos_hue*(-lb) +sin_hue*(cc);
mat_hue.m31 = lr+cos_hue*(-lr) +sin_hue*(-(1-lr));
mat_hue.m32 = lg+cos_hue*(-lg) +sin_hue*(lg);
mat_hue.m33 = lb+cos_hue*(1-lb)+sin_hue*(lb);
csc = csc * mat_hue;
// now saturation
float s = saturation;
float sr = ( 1.0f - s ) * .3086f;
float sg = ( 1.0f - s ) * .6094f;
float sb = ( 1.0f - s ) * .0920f;
float mat_saturation[] = {
sr + s, sg, sb, 0,
sr, sg + s, sb, 0,
sr, sg, sb + s, 0,
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_saturation);
// now brightness and contrast
float b = brightness;
float c = contrast;
float t = (1.0f - c) / 2.0f;
float mat_brightness_contrast[] = {
c, 0, 0, (t+b),
0, c, 0, (t+b),
0, 0, c, (t+b),
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_brightness_contrast);
// gain and offset
float rg = gain_red;
float ro = off_red;
float gg = gain_green;
float go = off_green;
float bg = gain_blue;
float bo = off_blue;
float mat_gain_off[] = {
rg, 0, 0, ro,
0, gg, 0, go,
0, 0, bg, bo,
0, 0, 0, 1.0f
};
csc = csc * mat4x4(mat_gain_off);
// final compression
csc.compress(2.0f);
// finally, apply a fixed multiplier to get it in
// correct range for ADV7513 chip
const int16_t csc_int16[12] = {
int16_t(csc.comp[0] * 2048.0f),
int16_t(csc.comp[1] * 2048.0f),
int16_t(csc.comp[2] * 2048.0f),
int16_t(csc.comp[3] * 2048.0f),
int16_t(csc.comp[4] * 2048.0f),
int16_t(csc.comp[5] * 2048.0f),
int16_t(csc.comp[6] * 2048.0f),
int16_t(csc.comp[7] * 2048.0f),
int16_t(csc.comp[8] * 2048.0f),
int16_t(csc.comp[9] * 2048.0f),
int16_t(csc.comp[10] * 2048.0f),
int16_t(csc.comp[11] * 2048.0f),
};
// Clamps to reinforce limited if necessary
// 0x100 = 16/256 * 4096 (12-bit mul)
// 0xEB0 = 235/256 * 4096
// 0xFFF = 4095 (12-bit max)
uint16_t clipMin = (hdmi_limited_1 || hdmi_limited_2) ? 0x100 : 0x000;
uint16_t clipMax = hdmi_limited_1 ? 0xEB0 : 0xFFF;
uint16_t clipMin = (!ypbpr && (hdmi_limited_1 || hdmi_limited_2)) ? 0x100 : 0x000;
uint16_t clipMax = (!ypbpr && hdmi_limited_1) ? 0xEB0 : 0xFFF;
// pass to HDMI, use 0xA0 to set a mode of [-2 .. 2] per ADV7513 programming guide
uint8_t csc_data[] = {
0x18, (uint8_t)(0b10100000 | (( csc_int16[0] >> 8) & 0b00011111)), // csc Coefficients, Channel A
0x19, (uint8_t)(csc_int16[0] & 0xff),
0x1A, (uint8_t)(csc_int16[1] >> 8),
0x1B, (uint8_t)(csc_int16[1] & 0xff),
0x1C, (uint8_t)(csc_int16[2] >> 8),
0x1D, (uint8_t)(csc_int16[2] & 0xff),
0x1E, (uint8_t)(csc_int16[3] >> 8),
0x1F, (uint8_t)(csc_int16[3] & 0xff),
0x18, (uint8_t)(ypbpr ? 0x86 : (0b10100000 | (( (csc_int16[0] >> 8) & 0b00011111)))), // csc Coefficients, Channel A
0x19, (uint8_t)(ypbpr ? 0xDF : (csc_int16[0] & 0xff)),
0x1A, (uint8_t)(ypbpr ? 0x1A : (csc_int16[1] >> 8)),
0x1B, (uint8_t)(ypbpr ? 0x3F : (csc_int16[1] & 0xff)),
0x1C, (uint8_t)(ypbpr ? 0x1E : (csc_int16[2] >> 8)),
0x1D, (uint8_t)(ypbpr ? 0xE2 : (csc_int16[2] & 0xff)),
0x1E, (uint8_t)(ypbpr ? 0x07 : (csc_int16[3] >> 8)),
0x1F, (uint8_t)(ypbpr ? 0xE7 : (csc_int16[3] & 0xff)),
0x20, (uint8_t)(csc_int16[4] >> 8), // csc Coefficients, Channel B
0x21, (uint8_t)(csc_int16[4] & 0xff),
0x22, (uint8_t)(csc_int16[5] >> 8),
0x23, (uint8_t)(csc_int16[5] & 0xff),
0x24, (uint8_t)(csc_int16[6] >> 8),
0x25, (uint8_t)(csc_int16[6] & 0xff),
0x26, (uint8_t)(csc_int16[7] >> 8),
0x27, (uint8_t)(csc_int16[7] & 0xff),
0x20, (uint8_t)(ypbpr ? 0x04 : (csc_int16[4] >> 8)), // csc Coefficients, Channel B
0x21, (uint8_t)(ypbpr ? 0x1C : (csc_int16[4] & 0xff)),
0x22, (uint8_t)(ypbpr ? 0x08 : (csc_int16[5] >> 8)),
0x23, (uint8_t)(ypbpr ? 0x11 : (csc_int16[5] & 0xff)),
0x24, (uint8_t)(ypbpr ? 0x01 : (csc_int16[6] >> 8)),
0x25, (uint8_t)(ypbpr ? 0x91 : (csc_int16[6] & 0xff)),
0x26, (uint8_t)(ypbpr ? 0x01 : (csc_int16[7] >> 8)),
0x27, (uint8_t)(ypbpr ? 0x00 : (csc_int16[7] & 0xff)),
0x28, (uint8_t)(csc_int16[8] >> 8), // csc Coefficients, Channel C
0x29, (uint8_t)(csc_int16[8] & 0xff),
0x2A, (uint8_t)(csc_int16[9] >> 8),
0x2B, (uint8_t)(csc_int16[9] & 0xff),
0x2C, (uint8_t)(csc_int16[10] >> 8),
0x2D, (uint8_t)(csc_int16[10] & 0xff),
0x2E, (uint8_t)(csc_int16[11] >> 8),
0x2F, (uint8_t)(csc_int16[11] & 0xff),
0x28, (uint8_t)(ypbpr ? 0x1D : (csc_int16[8] >> 8)), // csc Coefficients, Channel C
0x29, (uint8_t)(ypbpr ? 0xAE : (csc_int16[8] & 0xff)),
0x2A, (uint8_t)(ypbpr ? 0x1B : (csc_int16[9] >> 8)),
0x2B, (uint8_t)(ypbpr ? 0x73 : (csc_int16[9] & 0xff)),
0x2C, (uint8_t)(ypbpr ? 0x06 : (csc_int16[10] >> 8)),
0x2D, (uint8_t)(ypbpr ? 0xDF : (csc_int16[10] & 0xff)),
0x2E, (uint8_t)(ypbpr ? 0x07 : (csc_int16[11] >> 8)),
0x2F, (uint8_t)(ypbpr ? 0xE7 : (csc_int16[11] & 0xff)),
0xC0, (uint8_t)(clipMin >> 8), // HDMI limited clamps
0xC1, (uint8_t)(clipMin & 0xff),