summaryrefslogtreecommitdiffstats
path: root/libvo
diff options
context:
space:
mode:
Diffstat (limited to 'libvo')
-rw-r--r--libvo/csputils.c170
-rw-r--r--libvo/csputils.h4
2 files changed, 97 insertions, 77 deletions
diff --git a/libvo/csputils.c b/libvo/csputils.c
index 7c488f77b0..831e5c774a 100644
--- a/libvo/csputils.c
+++ b/libvo/csputils.c
@@ -27,7 +27,9 @@
#include <stdint.h>
#include <math.h>
-#include "libavutil/common.h"
+#include <assert.h>
+#include <libavutil/common.h>
+
#include "csputils.h"
/**
@@ -55,92 +57,112 @@ void mp_gen_gamma_map(uint8_t *map, int size, float gamma)
}
}
+/* Fill in the Y, U, V vectors of a yuv2rgb conversion matrix
+ * based on the given luma weights of the R, G and B components (lr, lg, lb).
+ * lr+lg+lb is assumed to equal 1.
+ * This function is meant for colorspaces satisfying the following
+ * conditions (which are true for common YUV colorspaces):
+ * - The mapping from input [Y, U, V] to output [R, G, B] is linear.
+ * - Y is the vector [1, 1, 1]. (meaning input Y component maps to 1R+1G+1B)
+ * - U maps to a value with zero R and positive B ([0, x, y], y > 0;
+ * i.e. blue and green only).
+ * - V maps to a value with zero B and positive R ([x, y, 0], x > 0;
+ * i.e. red and green only).
+ * - U and V are orthogonal to the luma vector [lr, lg, lb].
+ * - The magnitudes of the vectors U and V are the minimal ones for which
+ * the image of the set Y=[0...1],U=[-0.5...0.5],V=[-0.5...0.5] under the
+ * conversion function will cover the set R=[0...1],G=[0...1],B=[0...1]
+ * (the resulting matrix can be converted for other input/output ranges
+ * outside this function).
+ * Under these conditions the given parameters lr, lg, lb uniquely
+ * determine the mapping of Y, U, V to R, G, B.
+ */
+static void luma_coeffs(float m[3][4], float lr, float lg, float lb)
+{
+ assert(fabs(lr+lg+lb - 1) < 1e-6);
+ m[0][0] = m[1][0] = m[2][0] = 1;
+ m[0][1] = 0;
+ m[1][1] = -2 * (1-lb) * lb/lg;
+ m[2][1] = 2 * (1-lb);
+ m[0][2] = 2 * (1-lr);
+ m[1][2] = -2 * (1-lr) * lr/lg;
+ m[2][2] = 0;
+ // Constant coefficients (m[x][3]) not set here
+}
+
/**
* \brief get the coefficients of the yuv -> rgb conversion matrix
* \param params struct specifying the properties of the conversion like
* brightness, ...
- * \param yuv2rgb array to store coefficients into
- *
- * Note: contrast, hue and saturation will only work as expected with YUV
- * formats, not with e.g. MP_CSP_XYZ
+ * \param m array to store coefficients into
*/
-void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float yuv2rgb[3][4])
+void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
{
- float depth_multiplier = params->input_shift >= 0 ?
- (1 << params->input_shift) :
- (1.0 / (1 << -params->input_shift));
- float uvcos = params->saturation * cos(params->hue);
- float uvsin = params->saturation * sin(params->hue);
int format = params->format;
- int levelconv = params->levelconv;
- int i;
- const float (*uv_coeffs)[3];
- const float *level_adjust;
- static const float yuv_level_adjust[MP_CSP_LEVELCONV_COUNT][4] = {
- {-16 / 255.0, -128 / 255.0, -128 / 255.0, 1.164},
- { 16 / 255.0 * 1.164, -128 / 255.0, -128 / 255.0, 1.0 / 1.164},
- { 0, -128 / 255.0, -128 / 255.0, 1},
- };
- static const float xyz_level_adjust[4] = {
- 0, 0, 0, 0
- };
- static const float uv_coeffs_table[MP_CSP_COUNT][3][3] = {
- [MP_CSP_DEFAULT] = {
- {1, 0.000, 1.596},
- {1, -0.391, -0.813},
- {1, 2.018, 0.000}
- },
- [MP_CSP_BT_601] = {
- {1, 0.000, 1.403},
- {1, -0.344, -0.714},
- {1, 1.773, 0.000}
- },
- [MP_CSP_BT_709] = {
- {1, 0.0000, 1.5701},
- {1, -0.1870, -0.4664},
- {1, 1.8556, 0.0000}
- },
- [MP_CSP_SMPTE_240M] = {
- {1, 0.0000, 1.5756},
- {1, -0.2253, -0.5000},
- {1, 1.8270, 0.0000}
- },
- [MP_CSP_EBU] = {
- {1, 0.000, 1.140},
- {1, -0.396, -0.581},
- {1, 2.029, 0.000}
- },
- [MP_CSP_XYZ] = {
- { 3.2404542, -1.5371385, -0.4985314},
- {-0.9692660, 1.8760108, 0.0415560},
- { 0.0556434, -0.2040259, 1.0572252}
- },
+ if (format <= MP_CSP_DEFAULT || format >= MP_CSP_COUNT)
+ format = MP_CSP_BT_601;
+ switch (format) {
+ case MP_CSP_BT_601: luma_coeffs(m, 0.299, 0.587, 0.114 ); break;
+ case MP_CSP_BT_709: luma_coeffs(m, 0.2126, 0.7152, 0.0722); break;
+ case MP_CSP_SMPTE_240M: luma_coeffs(m, 0.2122, 0.7013, 0.0865); break;
+ default:
+ abort();
};
- if (format < 0 || format >= MP_CSP_COUNT)
- format = MP_CSP_DEFAULT;
- uv_coeffs = uv_coeffs_table[format];
+ // Hue is equivalent to rotating input [U, V] subvector around the origin.
+ // Saturation scales [U, V].
+ float huecos = params->saturation * cos(params->hue);
+ float huesin = params->saturation * sin(params->hue);
+ for (int i = 0; i < 3; i++) {
+ float u = m[i][COL_U];
+ m[i][COL_U] = huecos * u - huesin * m[i][COL_V];
+ m[i][COL_V] = huesin * u + huecos * m[i][COL_V];
+ }
+
+ int levelconv = params->levelconv;
if (levelconv < 0 || levelconv >= MP_CSP_LEVELCONV_COUNT)
levelconv = MP_CSP_LEVELCONV_TV_TO_PC;
- level_adjust = yuv_level_adjust[levelconv];
- if (format == MP_CSP_XYZ)
- level_adjust = xyz_level_adjust;
+ // The values below are written in 0-255 scale
+ struct yuvlevels { double ymin, ymax, cmin, cmid; }
+ yuvlim = { 16, 235, 16, 128 },
+ yuvfull = { 16, 235, 1, 128 }, // '1' to make it symmetric around 128
+ yuvlev;
+ struct rgblevels { double min, max; }
+ rgblim = { 16, 235 },
+ rgbfull = { 0, 255 },
+ rgblev;
+ switch (levelconv) {
+ case MP_CSP_LEVELCONV_TV_TO_PC: yuvlev = yuvlim; rgblev = rgbfull; break;
+ case MP_CSP_LEVELCONV_PC_TO_TV: yuvlev = yuvfull; rgblev = rgblim; break;
+ case MP_CSP_LEVELCONV_TV_TO_TV: yuvlev = yuvlim; rgblev = rgblim; break;
+ default:
+ abort();
+ }
+ double ymul = (rgblev.max - rgblev.min) / (yuvlev.ymax - yuvlev.ymin);
+ double cmul = (rgblev.max - rgblev.min) / (yuvlev.cmid - yuvlev.cmin) / 2;
+ for (int i = 0; i < 3; i++) {
+ m[i][COL_Y] *= ymul;
+ m[i][COL_U] *= cmul;
+ m[i][COL_V] *= cmul;
+ // Set COL_C so that Y=umin,UV=cmid maps to RGB=min (black to black)
+ m[i][COL_C] = (rgblev.min - m[i][COL_Y] * yuvlev.ymin
+ -(m[i][COL_U] + m[i][COL_V]) * yuvlev.cmid) / 255;
+ }
- for (i = 0; i < 3; i++) {
- yuv2rgb[i][COL_C] = params->brightness;
- yuv2rgb[i][COL_Y] = uv_coeffs[i][COL_Y] * level_adjust[COL_C] * params->contrast;
- yuv2rgb[i][COL_C] += level_adjust[COL_Y] * yuv2rgb[i][COL_Y];
- yuv2rgb[i][COL_U] = uv_coeffs[i][COL_U] * uvcos + uv_coeffs[i][COL_V] * uvsin;
- yuv2rgb[i][COL_C] += level_adjust[COL_U] * yuv2rgb[i][COL_U];
- yuv2rgb[i][COL_V] = uv_coeffs[i][COL_U] * uvsin + uv_coeffs[i][COL_V] * uvcos;
- yuv2rgb[i][COL_C] += level_adjust[COL_V] * yuv2rgb[i][COL_V];
- // this "centers" contrast control so that e.g. a contrast of 0
- // leads to a grey image, not a black one
- yuv2rgb[i][COL_C] += 0.5 - params->contrast / 2.0;
- yuv2rgb[i][COL_Y] *= depth_multiplier;
- yuv2rgb[i][COL_U] *= depth_multiplier;
- yuv2rgb[i][COL_V] *= depth_multiplier;
+ // Brightness adds a constant to output R,G,B.
+ // Contrast scales Y around 1/2 (not 0 in this implementation).
+ for (int i = 0; i < 3; i++) {
+ m[i][COL_C] += params->brightness;
+ m[i][COL_Y] *= params->contrast;
+ m[i][COL_C] += (rgblev.max-rgblev.min)/255 * (1 - params->contrast)/2;
}
+
+ float depth_multiplier = params->input_shift >= 0 ?
+ (1 << params->input_shift) :
+ (1.0 / (1 << -params->input_shift));
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ m[i][j] *= depth_multiplier;
}
//! size of gamma map use to avoid slow exp function in gen_yuv2rgb_map
diff --git a/libvo/csputils.h b/libvo/csputils.h
index 64b23b3d57..93f9ca0316 100644
--- a/libvo/csputils.h
+++ b/libvo/csputils.h
@@ -31,15 +31,13 @@ enum mp_csp_standard {
MP_CSP_BT_601,
MP_CSP_BT_709,
MP_CSP_SMPTE_240M,
- MP_CSP_EBU,
- MP_CSP_XYZ,
MP_CSP_COUNT
};
enum mp_csp_levelconv {
MP_CSP_LEVELCONV_TV_TO_PC,
MP_CSP_LEVELCONV_PC_TO_TV,
- MP_CSP_LEVELCONV_NONE,
+ MP_CSP_LEVELCONV_TV_TO_TV,
MP_CSP_LEVELCONV_COUNT
};