# HG changeset patch # User reimar # Date 1126735684 0 # Node ID 3191dcb27a12cca58bcfe7a29495ab20e751952d # Parent bafefab46f1271e2c9707085ba6bb7870b0d7de9 hardware color-space conversion for vo_gl and vo_gl2 diff -r bafefab46f12 -r 3191dcb27a12 ChangeLog --- a/ChangeLog Wed Sep 14 21:15:38 2005 +0000 +++ b/ChangeLog Wed Sep 14 22:08:04 2005 +0000 @@ -20,6 +20,8 @@ * 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 @@ 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 -r bafefab46f12 -r 3191dcb27a12 DOCS/man/en/mplayer.1 --- a/DOCS/man/en/mplayer.1 Wed Sep 14 21:15:38 2005 +0000 +++ b/DOCS/man/en/mplayer.1 Wed Sep 14 22:08:04 2005 +0000 @@ -2714,7 +2714,7 @@ .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 @@ 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= +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= +Load a custom fragment program from . +See TOOLS/edgedect.fp for an example. .REss .RE .PD 1 @@ -2737,6 +2767,39 @@ .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= +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 -r bafefab46f12 -r 3191dcb27a12 TOOLS/edgedetect.fp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TOOLS/edgedetect.fp Wed Sep 14 22:08:04 2005 +0000 @@ -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 -r bafefab46f12 -r 3191dcb27a12 TOOLS/emboss.fp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TOOLS/emboss.fp Wed Sep 14 22:08:04 2005 +0000 @@ -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 -r bafefab46f12 -r 3191dcb27a12 libvo/gl_common.c --- a/libvo/gl_common.c Wed Sep 14 21:15:38 2005 +0000 +++ b/libvo/gl_common.c Wed Sep 14 22:08:04 2005 +0000 @@ -1,5 +1,14 @@ +/** + * Common OpenGL routines. + * Copyleft (C) Reimar Döffinger , 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 +#include #include +#include #include "gl_common.h" void (APIENTRY *GenBuffers)(GLsizei, GLuint *); @@ -18,6 +27,8 @@ 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 @@ 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 @@ } /** + * \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 @@ * \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 -r bafefab46f12 -r 3191dcb27a12 libvo/gl_common.h --- a/libvo/gl_common.h Wed Sep 14 21:15:38 2005 +0000 +++ b/libvo/gl_common.h Wed Sep 14 22:08:04 2005 +0000 @@ -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 @@ 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 *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 -r bafefab46f12 -r 3191dcb27a12 libvo/vo_gl.c --- a/libvo/vo_gl.c Wed Sep 14 21:15:38 2005 +0000 +++ b/libvo/vo_gl.c Wed Sep 14 22:08:04 2005 +0000 @@ -1,6 +1,7 @@ #include #include #include +#include #include "config.h" #include "mp_msg.h" @@ -56,10 +57,12 @@ 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_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 @@ } } +#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 @@ * \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 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 @@ 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 @@ { 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 @@ // 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 @@ // 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 @@ //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 @@ err_shown = 1; return VO_FALSE; } + if (mpi->imgfmt == IMGFMT_YV12) { + // YV12 + mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE; + mpi->stride[0] = mpi->width; + mpi->planes[1] = mpi->planes[0] + mpi->stride[0] * mpi->height; + mpi->stride[1] = mpi->width >> 1; + mpi->planes[2] = mpi->planes[1] + mpi->stride[1] * (mpi->height >> 1); + mpi->stride[2] = mpi->width >> 1; + } mpi->flags |= MP_IMGFLAG_DIRECT; return VO_TRUE; } @@ -493,6 +601,17 @@ } glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[0], mpi->x, mpi->y, mpi->w, mpi->h, slice); + if (mpi->imgfmt == IMGFMT_YV12) { + data += mpi->planes[1] - mpi->planes[0]; + ActiveTexture(GL_TEXTURE1); + glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[1], + mpi->x / 2, mpi->y / 2, mpi->w / 2, mpi->h / 2, slice); + data += mpi->planes[2] - mpi->planes[1]; + ActiveTexture(GL_TEXTURE2); + glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[2], + mpi->x / 2, mpi->y / 2, mpi->w / 2, mpi->h / 2, slice); + ActiveTexture(GL_TEXTURE0); + } if (mpi->flags & MP_IMGFLAG_DIRECT) BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return VO_TRUE; @@ -513,6 +632,8 @@ caps |= VFCAP_OSD; if ((format == IMGFMT_RGB24) || (format == IMGFMT_RGBA)) return caps; + if (use_yuv && format == IMGFMT_YV12) + return caps; if (many_fmts && glFindFormat(format, NULL, NULL, NULL, NULL)) return caps; @@ -526,6 +647,8 @@ if ( !vo_config_count ) return; uninitGl(); releaseGlContext(&gl_vinfo, &gl_context); + if (custom_prog) free(custom_prog); + custom_prog = NULL; #ifdef GL_WIN32 vo_w32_uninit(); #else @@ -540,8 +663,10 @@ {"aspect", OPT_ARG_BOOL, &use_aspect, NULL}, {"slice-height", OPT_ARG_INT, &slice_height, (opt_test_f)int_non_neg}, {"rectangle", OPT_ARG_INT, &use_rectangle,(opt_test_f)int_non_neg}, + {"yuv", OPT_ARG_INT, &use_yuv, (opt_test_f)int_non_neg}, {"glfinish", OPT_ARG_BOOL, &use_glFinish, NULL}, {"swapinterval", OPT_ARG_INT, &swap_interval,NULL}, + {"customprog", OPT_ARG_MSTRZ,&custom_prog, NULL}, {NULL} }; @@ -552,10 +677,12 @@ use_osd = 1; scaled_osd = 0; use_aspect = 1; + use_yuv = 0; use_rectangle = 0; use_glFinish = 0; swap_interval = 1; slice_height = 4; + custom_prog = NULL; if (subopt_parse(arg, subopts) != 0) { mp_msg(MSGT_VO, MSGL_FATAL, "\n-vo gl command line help:\n" @@ -579,6 +706,16 @@ " Interval in displayed frames between to buffer swaps.\n" " 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n" " Requires GLX_SGI_swap_control support to work.\n" + " yuv=\n" + " 0: use software YUV to RGB conversion.\n" + " 1: use register combiners (nVidia only).\n" + " 2: use fragment program.\n" + " 3: use fragment program with gamma correction.\n" + " 4: use fragment program with gamma correction via lookup.\n" + " customprog=\n" + " use a custom YUV conversion program\n" + " customtex=\n" + " use a custom YUV conversion lookup texture\n" "\n" ); return -1; } @@ -632,6 +769,81 @@ if (!use_aspect) return VO_NOTIMPL; resize (vo_dwidth, vo_dheight); return VO_TRUE; + case VOCTRL_GET_EQUALIZER: + if (image_format == IMGFMT_YV12) { + va_list va; + int *value; + va_start(va, data); + value = va_arg(va, int *); + va_end(va); + if (strcasecmp(data, "brightness") == 0) { + *value = eq_bri; + if (use_yuv == YUV_CONVERSION_COMBINERS) break; // not supported + } else if (strcasecmp(data, "contrast") == 0) { + *value = eq_cont; + if (use_yuv == YUV_CONVERSION_COMBINERS) break; // not supported + } else if (strcasecmp(data, "saturation") == 0) { + *value = eq_sat; + } else if (strcasecmp(data, "hue") == 0) { + *value = eq_hue; + } else if (strcasecmp(data, "gamma") == 0) { + *value = eq_rgamma; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "red_gamma") == 0) { + *value = eq_rgamma; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "green_gamma") == 0) { + *value = eq_ggamma; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "blue_gamma") == 0) { + *value = eq_bgamma; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } + return VO_TRUE; + } + break; + case VOCTRL_SET_EQUALIZER: + if (image_format == IMGFMT_YV12) { + va_list va; + int value; + va_start(va, data); + value = va_arg(va, int); + va_end(va); + if (strcasecmp(data, "brightness") == 0) { + eq_bri = value; + if (use_yuv == YUV_CONVERSION_COMBINERS) break; // not supported + } else if (strcasecmp(data, "contrast") == 0) { + eq_cont = value; + if (use_yuv == YUV_CONVERSION_COMBINERS) break; // not supported + } else if (strcasecmp(data, "saturation") == 0) { + eq_sat = value; + } else if (strcasecmp(data, "hue") == 0) { + eq_hue = value; + } else if (strcasecmp(data, "gamma") == 0) { + eq_rgamma = eq_ggamma = eq_bgamma = value; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "red_gamma") == 0) { + eq_rgamma = value; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "green_gamma") == 0) { + eq_ggamma = value; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } else if (strcasecmp(data, "blue_gamma") == 0) { + eq_bgamma = value; + if (use_yuv == YUV_CONVERSION_COMBINERS || + use_yuv == YUV_CONVERSION_FRAGMENT) break; // not supported + } + update_yuvconv(); + return VO_TRUE; + } + break; } return VO_NOTIMPL; } diff -r bafefab46f12 -r 3191dcb27a12 libvo/vo_gl2.c --- a/libvo/vo_gl2.c Wed Sep 14 21:15:38 2005 +0000 +++ b/libvo/vo_gl2.c Wed Sep 14 22:08:04 2005 +0000 @@ -63,6 +63,8 @@ static uint32_t texture_height; static int texnumx, texnumy, raw_line_len; static struct TexSquare * texgrid = NULL; +static GLuint fragprog; +static GLuint lookupTex; static GLint gl_internal_format; static int rgb_sz, r_sz, g_sz, b_sz, a_sz; static GLenum gl_bitmap_format; @@ -71,6 +73,7 @@ static int gl_bilinear=1; static int gl_antialias=0; +static int use_yuv; static int use_glFinish; static void (*draw_alpha_fnc) @@ -83,6 +86,7 @@ { GLubyte *texture; GLuint texobj; + GLuint uvtexobjs[2]; int isTexture; GLfloat fx, fy, fw, fh; int isDirty; @@ -159,6 +163,7 @@ s*=2; texture_height=s; + if (image_format != IMGFMT_YV12) gl_internal_format = getInternalFormat(); /* Test the max texture size */ @@ -234,11 +239,20 @@ tsq->isDirty=GL_FALSE; tsq->isTexture=GL_FALSE; tsq->texobj=0; + tsq->uvtexobjs[0] = tsq->uvtexobjs[1] = 0; tsq->dirtyXoff=0; tsq->dirtyYoff=0; tsq->dirtyWidth=-1; tsq->dirtyHeight=-1; glGenTextures (1, &(tsq->texobj)); glBindTexture (GL_TEXTURE_2D, tsq->texobj); + if (image_format == IMGFMT_YV12) { + glGenTextures(2, tsq->uvtexobjs); + ActiveTexture(GL_TEXTURE1); + glBindTexture (GL_TEXTURE_2D, tsq->uvtexobjs[0]); + ActiveTexture(GL_TEXTURE2); + glBindTexture (GL_TEXTURE_2D, tsq->uvtexobjs[1]); + ActiveTexture(GL_TEXTURE0); + } err = glGetError (); if(err==GL_INVALID_ENUM) { @@ -257,6 +271,15 @@ texture_width, texture_height, 0); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if (image_format == IMGFMT_YV12) { + ActiveTexture(GL_TEXTURE1); + glCreateClearTex(GL_TEXTURE_2D, gl_internal_format, GL_LINEAR, + texture_width / 2, texture_height / 2, 0); + ActiveTexture(GL_TEXTURE2); + glCreateClearTex(GL_TEXTURE_2D, gl_internal_format, GL_LINEAR, + texture_width / 2, texture_height / 2, 0); + ActiveTexture(GL_TEXTURE0); + } tsq++; } /* for all texnumx */ @@ -439,6 +462,8 @@ glColor3f(1.0,1.0,1.0); + if (image_format == IMGFMT_YV12) + glEnableYUVConversion(GL_TEXTURE_2D, use_yuv); for (y = 0; y < texnumy; y++) { for (x = 0; x < texnumx; x++) @@ -451,6 +476,13 @@ } glBindTexture (GL_TEXTURE_2D, square->texobj); + if (image_format == IMGFMT_YV12) { + ActiveTexture(GL_TEXTURE1); + glBindTexture (GL_TEXTURE_2D, square->uvtexobjs[0]); + ActiveTexture(GL_TEXTURE2); + glBindTexture (GL_TEXTURE_2D, square->uvtexobjs[1]); + ActiveTexture(GL_TEXTURE0); + } err = glGetError (); if(err==GL_INVALID_ENUM) { @@ -486,10 +518,12 @@ glDrawTex(square->fx, square->fy, square->fw, square->fh, 0, 0, texture_width, texture_height, - texture_width, texture_height, 0); + texture_width, texture_height, 0, image_format == IMGFMT_YV12); square++; } /* for all texnumx */ } /* for all texnumy */ + if (image_format == IMGFMT_YV12) + glDisableYUVConversion(GL_TEXTURE_2D, use_yuv); /* YES - let's catch this error ... */ @@ -706,6 +740,7 @@ static int initGl(uint32_t d_width, uint32_t d_height) { + fragprog = lookupTex = 0; if (initTextures() < 0) return -1; @@ -714,6 +749,22 @@ glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); glEnable (GL_TEXTURE_2D); + if (image_format == IMGFMT_YV12) { + switch (use_yuv) { + case YUV_CONVERSION_FRAGMENT_LOOKUP: + glGenTextures(1, &lookupTex); + ActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, lookupTex); + ActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + case YUV_CONVERSION_FRAGMENT_POW: + case YUV_CONVERSION_FRAGMENT: + GenPrograms(1, &fragprog); + BindProgram(GL_FRAGMENT_PROGRAM, fragprog); + break; + } + glSetupYUVConversion(use_yuv, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0); + } gl_set_antialias(0); gl_set_bilinear(1); @@ -793,7 +844,7 @@ mp_msg(MSGT_VO, MSGL_INFO, "[gl2] You have OpenGL < 1.2 drivers, BAD (16bpp and BGR may be damaged!)\n"); } - glFindFormat(format, &image_bpp, NULL, &gl_bitmap_format, &gl_bitmap_type); + glFindFormat(format, &image_bpp, &gl_internal_format, &gl_bitmap_format, &gl_bitmap_type); image_bytes=(image_bpp+7)/8; @@ -908,12 +959,60 @@ //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) { + int rem_h = h; + struct TexSquare *texline = &texgrid[y / texture_height * texnumx]; + int subtex_y = y % texture_width; + while (rem_h > 0) { + int rem_w = w; + struct TexSquare *tsq = &texline[x / texture_width]; + int subtex_x = x % texture_height; + int subtex_h = rem_h; + if (subtex_y + subtex_h > texture_height) + subtex_h = texture_height - subtex_y; + while (rem_w > 0) { + int subtex_w = rem_w; + if (subtex_x + subtex_w > texture_width) + subtex_w = texture_width - subtex_x; + ActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tsq->texobj); + glUploadTex(GL_TEXTURE_2D, gl_bitmap_format, gl_bitmap_type, + src[0], stride[0], subtex_x, subtex_y, + subtex_w, subtex_h, 0); + ActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, tsq->uvtexobjs[0]); + glUploadTex(GL_TEXTURE_2D, gl_bitmap_format, gl_bitmap_type, + src[1], stride[1], subtex_x, subtex_y, + subtex_w / 2, subtex_h / 2, 0); + ActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, tsq->uvtexobjs[1]); + glUploadTex(GL_TEXTURE_2D, gl_bitmap_format, gl_bitmap_type, + src[2], stride[2], subtex_x, subtex_y, + subtex_w / 2, subtex_h / 2, 0); + subtex_x = 0; + src[0] += subtex_w; + src[1] += subtex_w / 2; + src[2] += subtex_w / 2; + tsq++; + rem_w -= subtex_w; + } + subtex_y = 0; + src[0] += subtex_h * stride[0] - w; + src[1] += subtex_h / 2 * stride[1] - w / 2; + src[2] += subtex_h / 2 * stride[2] - w / 2; + texline += texnumx; + rem_h -= subtex_h; + } + ActiveTexture(GL_TEXTURE0); return 0; } static int draw_frame(uint8_t *src[]) { + if (image_format == IMGFMT_YV12) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl2] error: draw_frame called for YV12!\n"); + return 0; + } ImageData=(unsigned char *)src[0]; resetTexturePointers(ImageData); setupTextureDirtyArea(0, 0, image_width, image_height); @@ -924,6 +1023,11 @@ query_format(uint32_t format) { switch(format){ + case IMGFMT_YV12: + if (use_yuv) + return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD | + VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE; + break; #ifdef SYS_DARWIN case IMGFMT_RGB32: #else @@ -955,6 +1059,7 @@ } static opt_t subopts[] = { + {"yuv", OPT_ARG_INT, &use_yuv, (opt_test_f)int_non_neg}, {"glfinish", OPT_ARG_BOOL, &use_glFinish, NULL}, {NULL} }; @@ -962,6 +1067,7 @@ static int preinit(const char *arg) { // set defaults + use_yuv = 0; use_glFinish = 1; if (subopt_parse(arg, subopts) != 0) { mp_msg(MSGT_VO, MSGL_FATAL, @@ -970,6 +1076,12 @@ "\nOptions:\n" " noglfinish\n" " Do not call glFinish() before swapping buffers\n" + " yuv=\n" + " 0: use software YUV to RGB conversion.\n" + " 1: use register combiners (nVidia only).\n" + " 2: use fragment program.\n" + " 3: use fragment program with gamma correction.\n" + " 4: use fragment program with gamma correction via lookup.\n" "\n" ); return -1; }