diff options
author | reimar <reimar@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2005-09-14 22:08:04 +0000 |
---|---|---|
committer | reimar <reimar@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2005-09-14 22:08:04 +0000 |
commit | 4e60e039f5b4791f3f9f15c8915670590c04f9c9 (patch) | |
tree | 3c2c43569ccd21a1bd3f90a2d75e2cfa5877461e | |
parent | 0981a91aa4de3aa3348e809f8e0352db25fcf83f (diff) | |
download | mpv-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-- | ChangeLog | 3 | ||||
-rw-r--r-- | DOCS/man/en/mplayer.1 | 65 | ||||
-rw-r--r-- | TOOLS/edgedetect.fp | 41 | ||||
-rw-r--r-- | TOOLS/emboss.fp | 33 | ||||
-rw-r--r-- | libvo/gl_common.c | 337 | ||||
-rw-r--r-- | libvo/gl_common.h | 95 | ||||
-rw-r--r-- | libvo/vo_gl.c | 218 | ||||
-rw-r--r-- | libvo/vo_gl2.c | 116 |
8 files changed, 900 insertions, 8 deletions
@@ -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; |