summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreimar <reimar@b3059339-0415-0410-9bf9-f77b7e298cf2>2005-09-14 22:08:04 +0000
committerreimar <reimar@b3059339-0415-0410-9bf9-f77b7e298cf2>2005-09-14 22:08:04 +0000
commit4e60e039f5b4791f3f9f15c8915670590c04f9c9 (patch)
tree3c2c43569ccd21a1bd3f90a2d75e2cfa5877461e
parent0981a91aa4de3aa3348e809f8e0352db25fcf83f (diff)
downloadmpv-4e60e039f5b4791f3f9f15c8915670590c04f9c9.tar.bz2
mpv-4e60e039f5b4791f3f9f15c8915670590c04f9c9.tar.xz
hardware color-space conversion for vo_gl and vo_gl2
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@16489 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--ChangeLog3
-rw-r--r--DOCS/man/en/mplayer.165
-rw-r--r--TOOLS/edgedetect.fp41
-rw-r--r--TOOLS/emboss.fp33
-rw-r--r--libvo/gl_common.c337
-rw-r--r--libvo/gl_common.h95
-rw-r--r--libvo/vo_gl.c218
-rw-r--r--libvo/vo_gl2.c116
8 files changed, 900 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 2cd2ce22ab..985fd94463 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -20,6 +20,8 @@ MPlayer (1.0)
* for -vo gl manyfmts is now default (since it is a lot faster), use
-vo gl:nomanyfmts if it does not work for you
* streaming textures for -vo gl, much faster if supported (use -dr)
+ * hardware YUV to RGB conversion for -vo gl and -vo gl2, see yuv suboption
+ * support for custom fragment programs for -vo gl (see TOOLS/*.fp)
* use libvbe from vesautils as vesa video driver
Decoders:
@@ -105,6 +107,7 @@ MPlayer (1.0)
done playing all files
* lots of new slave commands (check DOCS/tech/slave.txt)
* lots of new information provided by the -identify option
+ * fixed ugly looking OSD with -vo gl2 and MMX
pre7try2: August 26, 2005
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1
index c75be0231e..ff9aa07c13 100644
--- a/DOCS/man/en/mplayer.1
+++ b/DOCS/man/en/mplayer.1
@@ -2714,7 +2714,7 @@ slower (default: 0).
.RSss
0: Use power-of-two textures (default).
.br
-1: Use texture_rectangle.
+1: Use texture_rectangle, not compatible with hardware YUV conversion.
.br
2: Use texture_non_power_of_two.
.REss
@@ -2729,6 +2729,36 @@ Values below 0 will leave it at the system default.
This limits the framerate to (horizontal refresh rate / n).
Requires GLX_SGI_swap_control support to work.
With some (most/all?) implementations this only works in fullscreen mode.
+.IPs yuv=<n>
+Select the type of YUV to RGB conversion.
+.RSss
+0: Use software conversion (default).
+Compatible with all OpenGL version.
+Provides brightness, contrast and saturation control.
+.br
+1: Use register combiners.
+This uses an nVidia-specific extension (GL_NV_register_combiners).
+At least three texture units are needed.
+Provides saturation and hue control.
+.br
+2: Use a fragment program.
+Needs GL_ARB_fragment_program extension and at least three texture units.
+Provides brightness, contrast, saturation and hue control.
+.br
+3: Use a fragment program using the POW instruction.
+Needs GL_ARB_fragment_program extension and at least three texture units.
+Provides brightness, contrast, saturation, hue and gamma control.
+Gamma can also be set independently for red, green and blue.
+Method 4 is usually faster.
+.br
+4: Use a fragment program with additional lookup.
+Needs GL_ARB_fragment_program extension and at least four texture units.
+Provides brightness, contrast, saturation, hue and gamma control.
+Gamma can also be set independently for red, green and blue.
+.REss
+.IPs customprog=<filename>
+Load a custom fragment program from <filename>.
+See TOOLS/edgedect.fp for an example.
.REss
.RE
.PD 1
@@ -2737,6 +2767,39 @@ With some (most/all?) implementations this only works in fullscreen mode.
.B gl2\ \ \ \
OpenGL video output driver, second generation.
Supports OSD and videos larger than the maximum texture size.
+.PD 0
+.RSs
+.IPs (no)glfinish
+Call glFinish() before swapping buffers.
+Slower but in some cases more correct output (default: enabled).
+.IPs yuv=<n>
+Select the type of YUV to RGB conversion.
+.RSss
+0: Use software conversion (default).
+Compatible with all OpenGL version.
+Provides brightness, contrast and saturation control.
+.br
+1: Use register combiners.
+This uses an nVidia-specific extension (GL_NV_register_combiners).
+At least three texture units are needed.
+Provides saturation and hue control.
+.br
+2: Use a fragment program.
+Needs GL_ARB_fragment_program extension and at least three texture units.
+Provides brightness, contrast, saturation and hue control.
+.br
+3: Use a fragment program using the POW instruction.
+Needs GL_ARB_fragment_program extension and at least three texture units.
+Provides brightness, contrast, saturation, hue and gamma control.
+Gamma can also be set independently for red, green and blue.
+Method 4 is usually faster.
+.br
+4: Use a fragment program with additional lookup.
+Needs GL_ARB_fragment_program extension and at least four texture units.
+Provides brightness, contrast, saturation, hue and gamma control.
+Gamma can also be set independently for red, green and blue.
+.REss
+.REss
.
.TP
.B null\ \ \
diff --git a/TOOLS/edgedetect.fp b/TOOLS/edgedetect.fp
new file mode 100644
index 0000000000..814b28bc78
--- /dev/null
+++ b/TOOLS/edgedetect.fp
@@ -0,0 +1,41 @@
+!!ARBfp1.0
+# Custom YUV->RGB conversion program for MPlayer's -vo gl.
+# Copyleft (C) Reimar Döffinger, 2005
+# Licensed under the GNU GPL v2
+# Usage: mplayer -vo gl:yuv=4:customprog=edgedetect.fp
+# This is some custom edge-detect like effect.
+# Try adjusting the gamma!
+# program.env[0].xy contains the size of one source texel
+PARAM sizes = program.env[0];
+TEMP res, y, u, v, pos, tmp;
+TEX y, fragment.texcoord[0], texture[0], 2D;
+MUL y, y, {4, 4, 4, 0};
+ADD pos, fragment.texcoord[0], sizes.xwww; # texel to the right
+TEX tmp, pos, texture[0], 2D;
+SUB y, y, tmp;
+SUB pos, fragment.texcoord[0], sizes.xwww; # texel to the left
+TEX tmp, pos, texture[0], 2D;
+SUB y, y, tmp;
+ADD pos, fragment.texcoord[0], sizes.wyww; # texel... umm.. above?
+TEX tmp, pos, texture[0], 2D;
+SUB y, y, tmp;
+SUB pos, fragment.texcoord[0], sizes.wyww; # texel... umm.. below?
+TEX tmp, pos, texture[0], 2D;
+SUB y, y, tmp;
+MAD res, y, {2, 2, 2, 0}, {0.5, 0.5, 0.5, 0};
+# now do the normal YUV -> RGB conversion
+MAD res, res, {1.164, 1.164, 1.164, 0}, {-0.87416, 0.53133, -1.08599, 0};
+TEX u, fragment.texcoord[1], texture[1], 2D;
+MAD res, u, {0, -0.391, 2.018, 0}, res;
+TEX v, fragment.texcoord[2], texture[2], 2D;
+MAD res, v, {1.596, -0.813, 0, 0}, res;
+# do gamma texture lookup
+ADD res.a, res.a, 0.125;
+TEX res.r, res.raaa, texture[3], 2D;
+ADD res.a, res.a, 0.25;
+TEX res.g, res.gaaa, texture[3], 2D;
+ADD res.a, res.a, 0.25;
+TEX res.b, res.baaa, texture[3], 2D;
+# move res into result, this allows easily commenting out some parts.
+ADD result.color, res, {0, 0, 0, 0};
+END
diff --git a/TOOLS/emboss.fp b/TOOLS/emboss.fp
new file mode 100644
index 0000000000..a8ca611aae
--- /dev/null
+++ b/TOOLS/emboss.fp
@@ -0,0 +1,33 @@
+!!ARBfp1.0
+# Custom YUV->RGB conversion program for MPlayer's -vo gl.
+# Copyleft (C) Reimar Döffinger, 2005
+# Licensed under the GNU GPL v2
+# Usage: mplayer -vo gl:yuv=4:customprog=emboss.fp
+# This is an emboss effect.
+PARAM sizes = program.env[0];
+TEMP res, y, u, v, xdiff, ydiff, pos, tmp;
+TEX y, fragment.texcoord[0], texture[0], 2D;
+SUB pos, fragment.texcoord[0], sizes.xwww;
+TEX tmp, pos, texture[0], 2D;
+SUB xdiff, y, tmp;
+MAD xdiff, xdiff, {0.5, 0.5, 0.5, 0}, {0.5, 0.5, 0.5, 0};
+SUB pos, fragment.texcoord[0], sizes.wyww;
+TEX tmp, pos, texture[0], 2D;
+SUB ydiff, y, tmp;
+MAD res, ydiff, {0.8660, 0.8660, 0.8660, 0}, xdiff;
+# now do the normal YUV -> RGB conversion
+MAD res, res, {1.164, 1.164, 1.164, 0}, {-0.87416, 0.53133, -1.08599, 0};
+TEX u, fragment.texcoord[1], texture[1], 2D;
+MAD res, u, {0, -0.391, 2.018, 0}, res;
+TEX v, fragment.texcoord[2], texture[2], 2D;
+MAD res, v, {1.596, -0.813, 0, 0}, res;
+# do gamma texture lookup
+ADD res.a, res.a, 0.125;
+TEX res.r, res.raaa, texture[3], 2D;
+ADD res.a, res.a, 0.25;
+TEX res.g, res.gaaa, texture[3], 2D;
+ADD res.a, res.a, 0.25;
+TEX res.b, res.baaa, texture[3], 2D;
+# move res into result, this allows easily commenting out some parts.
+ADD result.color, res, {0, 0, 0, 0};
+END
diff --git a/libvo/gl_common.c b/libvo/gl_common.c
index 752c80ef78..7031890be7 100644
--- a/libvo/gl_common.c
+++ b/libvo/gl_common.c
@@ -1,5 +1,14 @@
+/**
+ * Common OpenGL routines.
+ * Copyleft (C) Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>, 2005
+ * Licensend under the GNU GPL v2.
+ * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
+ * gave me lots of good ideas.
+ */
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
+#include <math.h>
#include "gl_common.h"
void (APIENTRY *GenBuffers)(GLsizei, GLuint *);
@@ -18,6 +27,8 @@ void (APIENTRY *CombinerOutput)(GLenum, GLenum, GLenum, GLenum, GLenum,
void (APIENTRY *ActiveTexture)(GLenum);
void (APIENTRY *BindTexture)(GLenum, GLuint);
void (APIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat);
+void (APIENTRY *GenPrograms)(GLsizei, GLuint *);
+void (APIENTRY *DeletePrograms)(GLsizei, const GLuint *);
void (APIENTRY *BindProgram)(GLenum, GLuint);
void (APIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *);
void (APIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat,
@@ -249,6 +260,16 @@ static void getFunctions() {
MultiTexCoord2f = getProcAddress("glMultiTexCoord2f");
if (!MultiTexCoord2f)
MultiTexCoord2f = getProcAddress("glMultiTexCoord2fARB");
+ GenPrograms = getProcAddress("glGenPrograms");
+ if (!GenPrograms)
+ GenPrograms = getProcAddress("glGenProgramsARB");
+ if (!GenPrograms)
+ GenPrograms = getProcAddress("glGenProgramsNV");
+ DeletePrograms = getProcAddress("glDeletePrograms");
+ if (!DeletePrograms)
+ DeletePrograms = getProcAddress("glDeleteProgramsARB");
+ if (!DeletePrograms)
+ DeletePrograms = getProcAddress("glDeleteProgramsNV");
BindProgram = getProcAddress("glBindProgram");
if (!BindProgram)
BindProgram = getProcAddress("glBindProgramARB");
@@ -374,6 +395,301 @@ void glUploadTex(GLenum target, GLenum format, GLenum type,
}
/**
+ * \brief Setup register combiners for YUV to RGB conversion.
+ * \param uvcos used for saturation and hue adjustment
+ * \param uvsin used for saturation and hue adjustment
+ */
+static void glSetupYUVCombiners(float uvcos, float uvsin) {
+ GLfloat ucoef[4];
+ GLfloat vcoef[4];
+ GLint i;
+ glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &i);
+ if (i < 2)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 2 general combiners needed for YUV combiner support (found %i)\n", i);
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS, &i);
+ if (i < 3)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 3 texture units needed for YUV combiner support (found %i)\n", i);
+ if (!CombinerInput || !CombinerOutput ||
+ !CombinerParameterfv || !CombinerParameteri) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner functions missing!\n");
+ return;
+ }
+ ucoef[0] = 0 * uvcos + 1.403 * uvsin;
+ vcoef[0] = 0 * uvsin + 1.403 * uvcos;
+ ucoef[1] = -0.344 * uvcos + -0.714 * uvsin;
+ vcoef[1] = -0.344 * uvsin + -0.714 * uvcos;
+ ucoef[2] = 1.770 * uvcos + 0 * uvsin;
+ vcoef[2] = 1.770 * uvsin + 0 * uvcos;
+ ucoef[3] = 0;
+ vcoef[3] = 0;
+ // Coefficients (probably) must be in [0, 1] range, whereas they originally
+ // are in [-2, 2] range, so here comes the trick:
+ // First put them in the [-0.5, 0.5] range, then add 0.5.
+ // This can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments
+ // for CombinerInput and CombinerOutput
+ for (i = 0; i < 4; i++) {
+ ucoef[i] = ucoef[i] * 0.25 + 0.5;
+ vcoef[i] = vcoef[i] * 0.25 + 0.5;
+ }
+ CombinerParameterfv(GL_CONSTANT_COLOR0_NV, ucoef);
+ CombinerParameterfv(GL_CONSTANT_COLOR1_NV, vcoef);
+
+ // UV first, like this green component cannot overflow
+ CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
+ GL_TEXTURE1, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+ CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
+ GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+ CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
+ GL_TEXTURE2, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+ CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
+ GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+ CombinerOutput(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
+ GL_SPARE0_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE,
+ GL_FALSE, GL_FALSE);
+
+ // stage 2
+ CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV,
+ GL_SIGNED_IDENTITY_NV, GL_RGB);
+ CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO,
+ GL_UNSIGNED_INVERT_NV, GL_RGB);
+ CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
+ GL_TEXTURE0, GL_SIGNED_IDENTITY_NV, GL_RGB);
+ CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO,
+ GL_UNSIGNED_INVERT_NV, GL_RGB);
+ CombinerOutput(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
+ GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE,
+ GL_FALSE, GL_FALSE);
+
+ // leave final combiner stage in default mode
+ CombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV, 2);
+}
+
+static const char *yuv_prog_template =
+ "!!ARBfp1.0\n"
+ "TEMP res, y, u, v;"
+ "TEX y, fragment.texcoord[0], texture[0], 2D;"
+ "MAD res, y, {%.4f, %.4f, %.4f}, {%.4f, %.4f, %.4f};"
+ "TEX u, fragment.texcoord[1], texture[1], 2D;"
+ "MAD res, u, {%.4f, %.4f, %.4f}, res;"
+ "TEX v, fragment.texcoord[2], texture[2], 2D;"
+ "MAD result.color, v, {%.4f, %.4f, %.4f}, res;"
+ "END";
+
+static const char *yuv_pow_prog_template =
+ "!!ARBfp1.0\n"
+ "TEMP res, y, u, v;"
+ "TEX y, fragment.texcoord[0], texture[0], 2D;"
+ "MAD res, y, {%.4f, %.4f, %.4f}, {%.4f, %.4f, %.4f};"
+ "TEX u, fragment.texcoord[1], texture[1], 2D;"
+ "MAD res, u, {%.4f, %.4f, %.4f}, res;"
+ "TEX v, fragment.texcoord[2], texture[2], 2D;"
+ "MAD_SAT res, v, {%.4f, %.4f, %.4f}, res;"
+ "POW result.color.r, res.r, %.4f.r;"
+ "POW result.color.g, res.g, %.4f.g;"
+ "POW result.color.b, res.b, %.4f.b;"
+ "END";
+
+static const char *yuv_lookup_prog_template =
+ "!!ARBfp1.0\n"
+ "TEMP res, y, u, v;"
+ "TEX y, fragment.texcoord[0], texture[0], 2D;"
+ "MAD res, y, {%.4f, %.4f, %.4f, 0}, {%.4f, %.4f, %.4f, 0};"
+ "TEX u, fragment.texcoord[1], texture[1], 2D;"
+ "MAD res, u, {%.4f, %.4f, %.4f, 0}, res;"
+ "TEX v, fragment.texcoord[2], texture[2], 2D;"
+ "MAD res, v, {%.4f, %.4f, %.4f, 0}, res;"
+ "ADD res.a, res.a, 0.125;"
+ "TEX result.color.r, res.raaa, texture[3], 2D;"
+ "ADD res.a, res.a, 0.25;"
+ "TEX result.color.g, res.gaaa, texture[3], 2D;"
+ "ADD res.a, res.a, 0.25;"
+ "TEX result.color.b, res.baaa, texture[3], 2D;"
+ "END";
+
+/**
+ * \brief setup a fragment program that will do YUV->RGB conversion
+ * \param brightness brightness adjustment offset
+ * \param contrast contrast adjustment factor
+ * \param uvcos used for saturation and hue adjustment
+ * \param uvsin used for saturation and hue adjustment
+ * \param lookup use fragment program that uses texture unit 4 to
+ * do additional conversion via lookup.
+ */
+static void glSetupYUVFragprog(float brightness, float contrast,
+ float uvcos, float uvsin, float rgamma,
+ float ggamma, float bgamma, int type) {
+ char yuv_prog[1000];
+ const char *prog_template = yuv_prog_template;
+ int lookup = 0;
+ GLint i;
+ // this is the conversion matrix, with y, u, v factors
+ // for red, green, blue and the constant offsets
+ float ry, ru, rv, rc;
+ float gy, gu, gv, gc;
+ float by, bu, bv, bc;
+ switch (type) {
+ case YUV_CONVERSION_FRAGMENT_POW:
+ prog_template = yuv_pow_prog_template;
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ prog_template = yuv_lookup_prog_template;
+ lookup = 1;
+ break;
+ }
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS, &i);
+ if (i < 3)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 3 texture units needed for YUV fragment support (found %i)\n", i);
+ if (lookup && i < 4)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] 4 texture units needed for YUV fragment support with lookup (found %i)\n", i);
+ if (!ProgramString) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[gl] ProgramString function missing!\n");
+ return;
+ }
+ ry = 1.164 * contrast;
+ gy = 1.164 * contrast;
+ by = 1.164 * contrast;
+ ru = 0 * uvcos + 1.596 * uvsin;
+ rv = 0 * uvsin + 1.596 * uvcos;
+ gu = -0.391 * uvcos + -0.813 * uvsin;
+ gv = -0.391 * uvsin + -0.813 * uvcos;
+ bu = 2.018 * uvcos + 0 * uvsin;
+ bv = 2.018 * uvsin + 0 * uvcos;
+ rc = (-16 * ry + (-128) * ru + (-128) * rv) / 255.0 + brightness;
+ gc = (-16 * gy + (-128) * gu + (-128) * gv) / 255.0 + brightness;
+ bc = (-16 * by + (-128) * bu + (-128) * bv) / 255.0 + brightness;
+ rgamma = 1.0 / rgamma;
+ ggamma = 1.0 / ggamma;
+ bgamma = 1.0 / bgamma;
+ snprintf(yuv_prog, 1000, prog_template, ry, gy, by, rc, gc, bc, ru, gu, bu,
+ rv, gv, bv, rgamma, bgamma, bgamma);
+ ProgramString(GL_FRAGMENT_PROGRAM, GL_PROGRAM_FORMAT_ASCII,
+ strlen(yuv_prog), yuv_prog);
+ glGetIntegerv(GL_PROGRAM_ERROR_POSITION, &i);
+ if (i != -1)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] Error compiling fragment program, make sure your card supports\n"
+ "GL_ARB_fragment_program (use glxinfo to check).%.10s\n", &yuv_prog[i]);
+}
+
+/**
+ * \brief little helper function to create a lookup table for gamma
+ * \param map buffer to create map into
+ * \param size size of buffer
+ * \param gamma gamma value
+ */
+static void gen_gamma_map(unsigned char *map, int size, float gamma) {
+ int i;
+ gamma = 1.0 / gamma;
+ for (i = 0; i < size; i++) {
+ float tmp = (float)i / (size - 1.0);
+ tmp = pow(tmp, gamma);
+ if (tmp > 1.0) tmp = 1.0;
+ if (tmp < 0.0) tmp = 0.0;
+ map[i] = 255 * tmp;
+ }
+}
+
+//! resolution of texture for gamma lookup table
+#define LOOKUP_RES 512
+
+/**
+ * \brief setup YUV->RGB conversion
+ * \param brightness brightness adjustment offset
+ * \param contrast contrast adjustment factor
+ * \param hue hue adjustment angle
+ * \param saturation saturation adjustment factor
+ * \param rgamma gamma value for red channel
+ * \param ggamma gamma value for green channel
+ * \param bgamma gamma value for blue channel
+ * \param type YUV conversion type
+ */
+void glSetupYUVConversion(int type, float brightness, float contrast,
+ float hue, float saturation,
+ float rgamma, float ggamma, float bgamma) {
+ float uvcos = saturation * cos(hue);
+ float uvsin = saturation * sin(hue);
+ switch (type) {
+ case YUV_CONVERSION_COMBINERS:
+ glSetupYUVCombiners(uvcos, uvsin);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ {
+ unsigned char lookup_data[4 * LOOKUP_RES];
+ gen_gamma_map(lookup_data, LOOKUP_RES, rgamma);
+ gen_gamma_map(&lookup_data[LOOKUP_RES], LOOKUP_RES, ggamma);
+ gen_gamma_map(&lookup_data[2 * LOOKUP_RES], LOOKUP_RES, bgamma);
+ ActiveTexture(GL_TEXTURE3);
+ glCreateClearTex(GL_TEXTURE_2D, GL_LUMINANCE8, GL_LINEAR,
+ LOOKUP_RES, 4, 0);
+ glUploadTex(GL_TEXTURE_2D, GL_LUMINANCE, GL_UNSIGNED_BYTE, lookup_data,
+ LOOKUP_RES, 0, 0, LOOKUP_RES, 4, 0);
+ ActiveTexture(GL_TEXTURE0);
+ }
+ case YUV_CONVERSION_FRAGMENT:
+ case YUV_CONVERSION_FRAGMENT_POW:
+ glSetupYUVFragprog(brightness, contrast, uvcos, uvsin,
+ rgamma, ggamma, bgamma, type);
+ break;
+ }
+}
+
+/**
+ * \brief enable the specified YUV conversion
+ * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
+ * \param type type of YUV conversion
+ */
+void inline glEnableYUVConversion(GLenum target, int type) {
+ if (type <= 0) return;
+ ActiveTexture(GL_TEXTURE1);
+ glEnable(target);
+ ActiveTexture(GL_TEXTURE2);
+ glEnable(target);
+ switch (type) {
+ case YUV_CONVERSION_COMBINERS:
+ glEnable(GL_REGISTER_COMBINERS_NV);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ ActiveTexture(GL_TEXTURE3);
+ glEnable(GL_TEXTURE_2D);
+ case YUV_CONVERSION_FRAGMENT_POW:
+ case YUV_CONVERSION_FRAGMENT:
+ glEnable(GL_FRAGMENT_PROGRAM);
+ break;
+ }
+ ActiveTexture(GL_TEXTURE0);
+}
+
+/**
+ * \brief disable the specified YUV conversion
+ * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
+ * \param type type of YUV conversion
+ */
+void inline glDisableYUVConversion(GLenum target, int type) {
+ if (type <= 0) return;
+ ActiveTexture(GL_TEXTURE1);
+ glDisable(target);
+ ActiveTexture(GL_TEXTURE2);
+ glDisable(target);
+ switch (type) {
+ case YUV_CONVERSION_COMBINERS:
+ glDisable(GL_REGISTER_COMBINERS_NV);
+ break;
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ ActiveTexture(GL_TEXTURE3);
+ glDisable(GL_TEXTURE_2D);
+ case YUV_CONVERSION_FRAGMENT_POW:
+ case YUV_CONVERSION_FRAGMENT:
+ glDisable(GL_FRAGMENT_PROGRAM);
+ break;
+ }
+ ActiveTexture(GL_TEXTURE0);
+}
+
+/**
* \brief draw a texture part at given 2D coordinates
* \param x screen top coordinate
* \param y screen left coordinate
@@ -386,21 +702,40 @@ void glUploadTex(GLenum target, GLenum format, GLenum type,
* \param sx width of texture in pixels
* \param sy height of texture in pixels
* \param rect_tex whether this texture uses texture_rectangle extension
+ * \param is_yv12 if set, also draw the textures from units 1 and 2
*/
void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h,
GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
- int sx, int sy, int rect_tex) {
+ int sx, int sy, int rect_tex, int is_yv12) {
+ GLfloat tx2 = tx / 2, ty2 = ty / 2, tw2 = tw / 2, th2 = th / 2;
if (!rect_tex) {
tx /= sx; ty /= sy; tw /= sx; th /= sy;
+ tx2 = tx, ty2 = ty, tw2 = tw, th2 = th;
}
glBegin(GL_QUADS);
glTexCoord2f(tx, ty);
+ if (is_yv12) {
+ MultiTexCoord2f(GL_TEXTURE1, tx2, ty2);
+ MultiTexCoord2f(GL_TEXTURE2, tx2, ty2);
+ }
glVertex2f(x, y);
glTexCoord2f(tx, ty + th);
+ if (is_yv12) {
+ MultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2);
+ MultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2);
+ }
glVertex2f(x, y + h);
glTexCoord2f(tx + tw, ty + th);
+ if (is_yv12) {
+ MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2);
+ MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2);
+ }
glVertex2f(x + w, y + h);
glTexCoord2f(tx + tw, ty);
+ if (is_yv12) {
+ MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2);
+ MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2);
+ }
glVertex2f(x + w, y);
glEnd();
}
diff --git a/libvo/gl_common.h b/libvo/gl_common.h
index 9223d81c21..ebb15028f7 100644
--- a/libvo/gl_common.h
+++ b/libvo/gl_common.h
@@ -17,6 +17,72 @@
#include "x11_common.h"
#endif
+// conditionally define all extension defines used
+// vendor specific extensions should be marked as such
+// (e.g. _NV), _ARB is not used to ease readability.
+#ifndef GL_MAX_GENERAL_COMBINERS_NV
+#define GL_MAX_GENERAL_COMBINERS_NV 0x854D
+#endif
+#ifndef GL_NUM_GENERAL_COMBINERS_NV
+#define GL_NUM_GENERAL_COMBINERS_NV 0x854E
+#endif
+#ifndef GL_CONSTANT_COLOR0_NV
+#define GL_CONSTANT_COLOR0_NV 0x852A
+#endif
+#ifndef GL_CONSTANT_COLOR1_NV
+#define GL_CONSTANT_COLOR1_NV 0x852B
+#endif
+#ifndef GL_COMBINER0_NV
+#define GL_COMBINER0_NV 0x8550
+#endif
+#ifndef GL_COMBINER1_NV
+#define GL_COMBINER1_NV 0x8551
+#endif
+#ifndef GL_VARIABLE_A_NV
+#define GL_VARIABLE_A_NV 0x8523
+#endif
+#ifndef GL_VARIABLE_B_NV
+#define GL_VARIABLE_B_NV 0x8524
+#endif
+#ifndef GL_VARIABLE_C_NV
+#define GL_VARIABLE_C_NV 0x8525
+#endif
+#ifndef GL_VARIABLE_D_NV
+#define GL_VARIABLE_D_NV 0x8526
+#endif
+#ifndef GL_UNSIGNED_INVERT_NV
+#define GL_UNSIGNED_INVERT_NV 0x8537
+#endif
+#ifndef GL_HALF_BIAS_NORMAL_NV
+#define GL_HALF_BIAS_NORMAL_NV 0x853A
+#endif
+#ifndef GL_SIGNED_IDENTITY_NV
+#define GL_SIGNED_IDENTITY_NV 0x853C
+#endif
+#ifndef GL_SCALE_BY_FOUR_NV
+#define GL_SCALE_BY_FOUR_NV 0x853F
+#endif
+#ifndef GL_DISCARD_NV
+#define GL_DISCARD_NV 0x8530
+#endif
+#ifndef GL_SPARE0_NV
+#define GL_SPARE0_NV 0x852E
+#endif
+#ifndef GL_MAX_TEXTURE_UNITS
+#define GL_MAX_TEXTURE_UNITS 0x84E2
+#endif
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+#ifndef GL_TEXTURE2
+#define GL_TEXTURE2 0x84C2
+#endif
+#ifndef GL_TEXTURE3
+#define GL_TEXTURE3 0x84C3
+#endif
#ifndef GL_TEXTURE_RECTANGLE
#define GL_TEXTURE_RECTANGLE 0x84F5
#endif
@@ -56,6 +122,15 @@
#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
#endif
+#ifndef GL_FRAGMENT_PROGRAM
+#define GL_FRAGMENT_PROGRAM 0x8804
+#endif
+#ifndef GL_PROGRAM_FORMAT_ASCII
+#define GL_PROGRAM_FORMAT_ASCII 0x8875
+#endif
+#ifndef GL_PROGRAM_ERROR_POSITION
+#define GL_PROGRAM_ERROR_POSITION 0x864B
+#endif
void glAdjustAlignment(int stride);
@@ -71,7 +146,23 @@ void glUploadTex(GLenum target, GLenum format, GLenum type,
int x, int y, int w, int h, int slice);
void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h,
GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
- int sx, int sy, int rect_tex);
+ int sx, int sy, int rect_tex, int is_yv12);
+
+//! do not use YUV conversion, this should always stay 0
+#define YUV_CONVERSION_NONE 0
+//! use nVidia specific register combiners for YUV conversion
+#define YUV_CONVERSION_COMBINERS 1
+//! use a fragment program for YUV conversion
+#define YUV_CONVERSION_FRAGMENT 2
+//! use a fragment program for YUV conversion with gamma using POW
+#define YUV_CONVERSION_FRAGMENT_POW 3
+//! use a fragment program with additional table lookup for YUV conversion
+#define YUV_CONVERSION_FRAGMENT_LOOKUP 4
+void glSetupYUVConversion(int type, float brightness, float contrast,
+ float hue, float saturation,
+ float rgamma, float ggamma, float bgamma);
+void inline glEnableYUVConversion(GLenum target, int type);
+void inline glDisableYUVConversion(GLenum target, int type);
//! could not set new window, will continue drawing into the old one.
#define SET_WINDOW_FAILED -1
@@ -104,6 +195,8 @@ extern void (APIENTRY *CombinerOutput)(GLenum, GLenum, GLenum, GLenum, GLenum,
extern void (APIENTRY *ActiveTexture)(GLenum);
extern void (APIENTRY *BindTexture)(GLenum, GLuint);
extern void (APIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat);
+extern void (APIENTRY *GenPrograms)(GLsizei, GLuint *);
+extern void (APIENTRY *DeletePrograms)(GLsizei, const GLuint *);
extern void (APIENTRY *BindProgram)(GLenum, GLuint);
extern void (APIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *);
extern void (APIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat,
diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c
index 90e1e58635..81a8426e89 100644
--- a/libvo/vo_gl.c
+++ b/libvo/vo_gl.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include "config.h"
#include "mp_msg.h"
@@ -56,10 +57,12 @@ static GLuint osdDispList[MAX_OSD_PARTS];
static int osdtexCnt;
static int use_aspect;
+static int use_yuv;
static int use_rectangle;
static int err_shown;
static uint32_t image_width;
static uint32_t image_height;
+static uint32_t image_format;
static int many_fmts;
static int use_glFinish;
static int swap_interval;
@@ -69,8 +72,19 @@ static GLenum gl_format;
static GLenum gl_type;
static GLuint gl_buffer;
static int gl_buffersize;
+static GLuint fragprog;
+static GLuint uvtexs[2];
+static GLuint lookupTex;
+static char *custom_prog;
static int int_pause;
+static int eq_bri = 0;
+static int eq_cont = 0;
+static int eq_sat = 0;
+static int eq_hue = 0;
+static int eq_rgamma = 0;
+static int eq_ggamma = 0;
+static int eq_bgamma = 0;
static uint32_t texture_width;
static uint32_t texture_height;
@@ -126,6 +140,39 @@ static void texSize(int w, int h, int *texw, int *texh) {
}
}
+#define MAX_CUSTOM_PROG_SIZE (1024 * 1024)
+static void update_yuvconv() {
+ float bri = eq_bri / 100.0;
+ float cont = (eq_cont + 100) / 100.0;
+ float hue = eq_hue / 100.0 * 3.1415927;
+ float sat = (eq_sat + 100) / 100.0;
+ float rgamma = exp(log(8.0) * eq_rgamma / 100.0);
+ float ggamma = exp(log(8.0) * eq_ggamma / 100.0);
+ float bgamma = exp(log(8.0) * eq_bgamma / 100.0);
+ glSetupYUVConversion(use_yuv, bri, cont, hue, sat, rgamma, ggamma, bgamma);
+ if (custom_prog) {
+ FILE *f = fopen(custom_prog, "r");
+ if (!f)
+ mp_msg(MSGT_VO, MSGL_WARN,
+ "[gl] Could not read customprog %s\n", custom_prog);
+ else {
+ int i;
+ char *prog = calloc(1, MAX_CUSTOM_PROG_SIZE + 1);
+ fread(prog, 1, MAX_CUSTOM_PROG_SIZE, f);
+ fclose(f);
+ ProgramString(GL_FRAGMENT_PROGRAM, GL_PROGRAM_FORMAT_ASCII,
+ strlen(prog), prog);
+ glGetIntegerv(GL_PROGRAM_ERROR_POSITION, &i);
+ if (i != -1)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[gl] Error in custom program at pos %i (%.20s)\n", i, &prog[i]);
+ free(prog);
+ }
+ ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 0,
+ 1.0 / texture_width, 1.0 / texture_height, 0, 0);
+ }
+}
+
/**
* \brief remove all OSD textures and display-lists, thus clearing it.
*/
@@ -144,6 +191,15 @@ static void clearOSD() {
* \brief uninitialize OpenGL context, freeing textures, buffers etc.
*/
static void uninitGl() {
+ if (DeletePrograms && fragprog)
+ DeletePrograms(1, &fragprog);
+ fragprog = 0;
+ if (uvtexs[0] || uvtexs[1])
+ glDeleteTextures(2, uvtexs);
+ uvtexs[0] = uvtexs[1] = 0;
+ if (lookupTex)
+ glDeleteTextures(1, &lookupTex);
+ lookupTex = 0;
clearOSD();
if (DeleteBuffers && gl_buffer)
DeleteBuffers(1, &gl_buffer);
@@ -157,6 +213,7 @@ static void uninitGl() {
*/
static int initGl(uint32_t d_width, uint32_t d_height) {
osdtexCnt = 0; gl_buffer = 0; gl_buffersize = 0; err_shown = 0;
+ fragprog = 0; uvtexs[0] = 0; uvtexs[1] = 0; lookupTex = 0;
texSize(image_width, image_height, &texture_width, &texture_height);
glDisable(GL_BLEND);
@@ -169,6 +226,31 @@ static int initGl(uint32_t d_width, uint32_t d_height) {
mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n",
texture_width, texture_height);
+ if (image_format == IMGFMT_YV12) {
+ glGenTextures(2, uvtexs);
+ ActiveTexture(GL_TEXTURE1);
+ BindTexture(gl_target, uvtexs[0]);
+ glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
+ texture_width / 2, texture_height / 2, 128);
+ ActiveTexture(GL_TEXTURE2);
+ BindTexture(gl_target, uvtexs[1]);
+ glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
+ texture_width / 2, texture_height / 2, 128);
+ switch (use_yuv) {
+ case YUV_CONVERSION_FRAGMENT_LOOKUP:
+ glGenTextures(1, &lookupTex);
+ ActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D, lookupTex);
+ case YUV_CONVERSION_FRAGMENT_POW:
+ case YUV_CONVERSION_FRAGMENT:
+ GenPrograms(1, &fragprog);
+ BindProgram(GL_FRAGMENT_PROGRAM, fragprog);
+ break;
+ }
+ ActiveTexture(GL_TEXTURE0);
+ BindTexture(gl_target, 0);
+ update_yuvconv();
+ }
glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
texture_width, texture_height, 0);
@@ -189,6 +271,7 @@ config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uin
{
image_height = height;
image_width = width;
+ image_format = format;
glFindFormat(format, NULL, &gl_texfmt, &gl_format, &gl_type);
int_pause = 0;
@@ -374,12 +457,12 @@ static void create_osd_texture(int x0, int y0, int w, int h,
// render alpha
glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
BindTexture(gl_target, osdatex[osdtexCnt]);
- glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1);
+ glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0);
#endif
// render OSD
glBlendFunc (GL_ONE, GL_ONE);
BindTexture(gl_target, osdtex[osdtexCnt]);
- glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1);
+ glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0);
glEndList();
osdtexCnt++;
@@ -404,9 +487,14 @@ flip_page(void)
// glBindTexture(GL_TEXTURE_2D, texture_id);
glColor3f(1,1,1);
+ if (image_format == IMGFMT_YV12)
+ glEnableYUVConversion(gl_target, use_yuv);
glDrawTex(0, 0, texture_width, texture_height,
0, 0, texture_width, texture_height,
- texture_width, texture_height, use_rectangle == 1);
+ texture_width, texture_height,
+ use_rectangle == 1, image_format == IMGFMT_YV12);
+ if (image_format == IMGFMT_YV12)
+ glDisableYUVConversion(gl_target, use_yuv);
if (osdtexCnt > 0) {
// set special rendering parameters
@@ -443,6 +531,17 @@ flip_page(void)
//static inline uint32_t draw_slice_x11(uint8_t *src[], uint32_t slice_num)
static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y)
{
+ glUploadTex(gl_target, gl_format, gl_type, src[0], stride[0],
+ x, y, w, h, slice_height);
+ if (image_format == IMGFMT_YV12) {
+ ActiveTexture(GL_TEXTURE1);
+ glUploadTex(gl_target, gl_format, gl_type, src[1], stride[1],
+ x / 2, y / 2, w / 2, h / 2, slice_height);
+ ActiveTexture(GL_TEXTURE2);
+ glUploadTex(gl_target, gl_format, gl_type, src[2], stride[2],
+ x / 2, y / 2, w / 2, h / 2, slice_height);
+ ActiveTexture(GL_TEXTURE0);
+ }
return 0;
}
@@ -476,6 +575,15 @@ static uint32_t get_image(mp_image_t *mpi) {
err_shown = 1;
return VO_FALSE;
}
+ if (mpi->imgfmt == IMGFMT_YV12) {
+ // YV12
+ mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE;
+ mpi->stride