Fix color controls bug affecting YPbPr + direct_video (#735)
This commit is contained in:
343
video.cpp
343
video.cpp
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user