3 * \brief OpenGL helper functions used by vo_gl.c and vo_gl2.c
5 * Common OpenGL routines.
6 * Copyleft (C) Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>, 2005
7 * Licensed under the GNU GPL v2 or later.
8 * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
9 * gave me lots of good ideas.
16 #include "gl_common.h"
19 * \defgroup glextfunctions OpenGL extension functions
21 * the pointers to these functions are acquired when the OpenGL
25 void (APIENTRY
*GenBuffers
)(GLsizei
, GLuint
*);
26 void (APIENTRY
*DeleteBuffers
)(GLsizei
, const GLuint
*);
27 void (APIENTRY
*BindBuffer
)(GLenum
, GLuint
);
28 GLvoid
* (APIENTRY
*MapBuffer
)(GLenum
, GLenum
);
29 GLboolean (APIENTRY
*UnmapBuffer
)(GLenum
);
30 void (APIENTRY
*BufferData
)(GLenum
, intptr_t, const GLvoid
*, GLenum
);
31 void (APIENTRY
*CombinerParameterfv
)(GLenum
, const GLfloat
*);
32 void (APIENTRY
*CombinerParameteri
)(GLenum
, GLint
);
33 void (APIENTRY
*CombinerInput
)(GLenum
, GLenum
, GLenum
, GLenum
, GLenum
,
35 void (APIENTRY
*CombinerOutput
)(GLenum
, GLenum
, GLenum
, GLenum
, GLenum
,
36 GLenum
, GLenum
, GLboolean
, GLboolean
,
38 void (APIENTRY
*BeginFragmentShader
)(void);
39 void (APIENTRY
*EndFragmentShader
)(void);
40 void (APIENTRY
*SampleMap
)(GLuint
, GLuint
, GLenum
);
41 void (APIENTRY
*ColorFragmentOp2
)(GLenum
, GLuint
, GLuint
, GLuint
, GLuint
,
42 GLuint
, GLuint
, GLuint
, GLuint
, GLuint
);
43 void (APIENTRY
*ColorFragmentOp3
)(GLenum
, GLuint
, GLuint
, GLuint
, GLuint
,
44 GLuint
, GLuint
, GLuint
, GLuint
, GLuint
,
45 GLuint
, GLuint
, GLuint
);
46 void (APIENTRY
*SetFragmentShaderConstant
)(GLuint
, const GLfloat
*);
47 void (APIENTRY
*ActiveTexture
)(GLenum
);
48 void (APIENTRY
*BindTexture
)(GLenum
, GLuint
);
49 void (APIENTRY
*MultiTexCoord2f
)(GLenum
, GLfloat
, GLfloat
);
50 void (APIENTRY
*GenPrograms
)(GLsizei
, GLuint
*);
51 void (APIENTRY
*DeletePrograms
)(GLsizei
, const GLuint
*);
52 void (APIENTRY
*BindProgram
)(GLenum
, GLuint
);
53 void (APIENTRY
*ProgramString
)(GLenum
, GLenum
, GLsizei
, const GLvoid
*);
54 void (APIENTRY
*GetProgramiv
)(GLenum
, GLenum
, GLint
*);
55 void (APIENTRY
*ProgramEnvParameter4f
)(GLenum
, GLuint
, GLfloat
, GLfloat
,
57 int (APIENTRY
*SwapInterval
)(int);
58 void (APIENTRY
*TexImage3D
)(GLenum
, GLint
, GLenum
, GLsizei
, GLsizei
, GLsizei
,
59 GLint
, GLenum
, GLenum
, const GLvoid
*);
60 /** \} */ // end of glextfunctions group
62 //! \defgroup glgeneral OpenGL general helper functions
64 //! \defgroup glcontext OpenGL context management helper functions
66 //! \defgroup gltexture OpenGL texture handling helper functions
68 //! \defgroup glconversion OpenGL conversion helper functions
70 static GLint hqtexfmt
;
73 * \brief adjusts the GL_UNPACK_ALIGNMENT to fit the stride.
74 * \param stride number of bytes per line for which alignment should fit.
77 void glAdjustAlignment(int stride
) {
81 else if (stride
% 4 == 0)
83 else if (stride
% 2 == 0)
87 glPixelStorei (GL_UNPACK_ALIGNMENT
, gl_alignment
);
90 struct gl_name_map_struct
{
96 #define MAP(a) {a, #a}
97 //! mapping table for the glValName function
98 static const struct gl_name_map_struct gl_name_map
[] = {
100 MAP(GL_R3_G3_B2
), MAP(GL_RGB4
), MAP(GL_RGB5
), MAP(GL_RGB8
),
101 MAP(GL_RGB10
), MAP(GL_RGB12
), MAP(GL_RGB16
), MAP(GL_RGBA2
),
102 MAP(GL_RGBA4
), MAP(GL_RGB5_A1
), MAP(GL_RGBA8
), MAP(GL_RGB10_A2
),
103 MAP(GL_RGBA12
), MAP(GL_RGBA16
), MAP(GL_LUMINANCE8
),
106 MAP(GL_RGB
), MAP(GL_RGBA
), MAP(GL_RED
), MAP(GL_GREEN
), MAP(GL_BLUE
),
107 MAP(GL_ALPHA
), MAP(GL_LUMINANCE
), MAP(GL_LUMINANCE_ALPHA
),
110 MAP(GL_BGR
), MAP(GL_BGRA
),
113 MAP(GL_BYTE
), MAP(GL_UNSIGNED_BYTE
), MAP(GL_SHORT
), MAP(GL_UNSIGNED_SHORT
),
114 MAP(GL_INT
), MAP(GL_UNSIGNED_INT
), MAP(GL_FLOAT
), MAP(GL_DOUBLE
),
115 MAP(GL_2_BYTES
), MAP(GL_3_BYTES
), MAP(GL_4_BYTES
),
117 MAP(GL_UNSIGNED_BYTE_3_3_2
), MAP(GL_UNSIGNED_BYTE_2_3_3_REV
),
118 MAP(GL_UNSIGNED_SHORT_5_6_5
), MAP(GL_UNSIGNED_SHORT_5_6_5_REV
),
119 MAP(GL_UNSIGNED_SHORT_4_4_4_4
), MAP(GL_UNSIGNED_SHORT_4_4_4_4_REV
),
120 MAP(GL_UNSIGNED_SHORT_5_5_5_1
), MAP(GL_UNSIGNED_SHORT_1_5_5_5_REV
),
121 MAP(GL_UNSIGNED_INT_8_8_8_8
), MAP(GL_UNSIGNED_INT_8_8_8_8_REV
),
122 MAP(GL_UNSIGNED_INT_10_10_10_2
), MAP(GL_UNSIGNED_INT_2_10_10_10_REV
),
128 * \brief return the name of an OpenGL constant
129 * \param value the constant
130 * \return name of the constant or "Unknown format!"
133 const char *glValName(GLint value
)
137 while (gl_name_map
[i
].name
) {
138 if (gl_name_map
[i
].value
== value
)
139 return gl_name_map
[i
].name
;
142 return "Unknown format!";
145 //! always return this format as internal texture format in glFindFormat
146 #define TEXTUREFORMAT_ALWAYS GL_RGB8
147 #undef TEXTUREFORMAT_ALWAYS
150 * \brief find the OpenGL settings coresponding to format.
152 * All parameters may be NULL.
153 * \param fmt MPlayer format to analyze.
154 * \param bpp [OUT] bits per pixel of that format.
155 * \param gl_texfmt [OUT] internal texture format that fits the
156 * image format, not necessarily the best for performance.
157 * \param gl_format [OUT] OpenGL format for this image format.
158 * \param gl_type [OUT] OpenGL type for this image format.
159 * \return 1 if format is supported by OpenGL, 0 if not.
162 int glFindFormat(uint32_t fmt
, int *bpp
, GLint
*gl_texfmt
,
163 GLenum
*gl_format
, GLenum
*gl_type
)
169 if (bpp
== NULL
) bpp
= &dummy1
;
170 if (gl_texfmt
== NULL
) gl_texfmt
= &dummy3
;
171 if (gl_format
== NULL
) gl_format
= &dummy2
;
172 if (gl_type
== NULL
) gl_type
= &dummy2
;
174 *bpp
= IMGFMT_IS_BGR(fmt
)?IMGFMT_BGR_DEPTH(fmt
):IMGFMT_RGB_DEPTH(fmt
);
179 *gl_type
= GL_UNSIGNED_BYTE
;
183 *gl_format
= GL_RGBA
;
184 *gl_type
= GL_UNSIGNED_BYTE
;
187 supported
= 0; // no native YV12 support
192 *gl_format
= GL_LUMINANCE
;
193 *gl_type
= GL_UNSIGNED_BYTE
;
196 // we do not support palettized formats, although the format the
197 // swscale produces works
200 gl_type
= GL_UNSIGNED_BYTE_2_3_3_REV
;
204 *gl_format
= GL_RGBA
;
205 *gl_type
= GL_UNSIGNED_SHORT_1_5_5_5_REV
;
209 *gl_type
= GL_UNSIGNED_SHORT_5_6_5_REV
;
213 // special case as red and blue have a differen number of bits.
214 // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least
215 // by nVidia drivers, and in addition would give more bits to
216 // blue than to red, which isn't wanted
218 gl_type
= GL_UNSIGNED_BYTE_3_3_2
;
222 *gl_format
= GL_BGRA
;
223 *gl_type
= GL_UNSIGNED_SHORT_1_5_5_5_REV
;
227 *gl_type
= GL_UNSIGNED_SHORT_5_6_5
;
231 *gl_type
= GL_UNSIGNED_BYTE
;
235 *gl_format
= GL_BGRA
;
236 *gl_type
= GL_UNSIGNED_BYTE
;
240 *gl_format
= GL_RGBA
;
241 *gl_type
= GL_UNSIGNED_BYTE
;
244 #ifdef TEXTUREFORMAT_ALWAYS
245 *gl_texfmt
= TEXTUREFORMAT_ALWAYS
;
250 static void *setNull(const GLubyte
*s
) {
257 const char *funcnames
[7];
260 static const extfunc_desc_t extfuncs
[] = {
261 {(void **)&GenBuffers
, NULL
, {"glGenBuffers", "glGenBuffersARB", NULL
}},
262 {(void **)&DeleteBuffers
, NULL
, {"glDeleteBuffers", "glDeleteBuffersARB", NULL
}},
263 {(void **)&BindBuffer
, NULL
, {"glBindBuffer", "glBindBufferARB", NULL
}},
264 {(void **)&MapBuffer
, NULL
, {"glMapBuffer", "glMapBufferARB", NULL
}},
265 {(void **)&UnmapBuffer
, NULL
, {"glUnmapBuffer", "glUnmapBufferARB", NULL
}},
266 {(void **)&BufferData
, NULL
, {"glBufferData", "glBufferDataARB", NULL
}},
267 {(void **)&CombinerParameterfv
, "NV_register_combiners", {"glCombinerParameterfv", "glCombinerParameterfvNV", NULL
}},
268 {(void **)&CombinerParameteri
, "NV_register_combiners", {"glCombinerParameteri", "glCombinerParameteriNV", NULL
}},
269 {(void **)&CombinerInput
, "NV_register_combiners", {"glCombinerInput", "glCombinerInputNV", NULL
}},
270 {(void **)&CombinerOutput
, "NV_register_combiners", {"glCombinerOutput", "glCombinerOutputNV", NULL
}},
271 {(void **)&BeginFragmentShader
, "ATI_fragment_shader", {"glBeginFragmentShaderATI", NULL
}},
272 {(void **)&EndFragmentShader
, "ATI_fragment_shader", {"glEndFragmentShaderATI", NULL
}},
273 {(void **)&SampleMap
, "ATI_fragment_shader", {"glSampleMapATI", NULL
}},
274 {(void **)&ColorFragmentOp2
, "ATI_fragment_shader", {"glColorFragmentOp2ATI", NULL
}},
275 {(void **)&ColorFragmentOp3
, "ATI_fragment_shader", {"glColorFragmentOp3ATI", NULL
}},
276 {(void **)&SetFragmentShaderConstant
, "ATI_fragment_shader", {"glSetFragmentShaderConstantATI", NULL
}},
277 {(void **)&ActiveTexture
, NULL
, {"glActiveTexture", "glActiveTextureARB", NULL
}},
278 {(void **)&BindTexture
, NULL
, {"glBindTexture", "glBindTextureARB", "glBindTextureEXT", NULL
}},
279 {(void **)&MultiTexCoord2f
, NULL
, {"glMultiTexCoord2f", "glMultiTexCoord2fARB", NULL
}},
280 {(void **)&GenPrograms
, "_program", {"glGenProgramsARB", NULL
}},
281 {(void **)&DeletePrograms
, "_program", {"glDeleteProgramsARB", NULL
}},
282 {(void **)&BindProgram
, "_program", {"glBindProgramARB", NULL
}},
283 {(void **)&ProgramString
, "_program", {"glProgramStringARB", NULL
}},
284 {(void **)&GetProgramiv
, "_program", {"glGetProgramivARB", NULL
}},
285 {(void **)&ProgramEnvParameter4f
, "_program", {"glProgramEnvParameter4fARB", NULL
}},
286 {(void **)&SwapInterval
, "_swap_control", {"glXSwapInterval", "glXSwapIntervalEXT", "glXSwapIntervalSGI", "wglSwapInterval", "wglSwapIntervalEXT", "wglSwapIntervalSGI", NULL
}},
287 {(void **)&TexImage3D
, NULL
, {"glTexImage3D", NULL
}},
292 * \brief find the function pointers of some useful OpenGL extensions
293 * \param getProcAddress function to resolve function names, may be NULL
294 * \param ext2 an extra extension string
296 static void getFunctions(void *(*getProcAddress
)(const GLubyte
*),
298 const extfunc_desc_t
*dsc
;
299 const char *extensions
= (const char *)glGetString(GL_EXTENSIONS
);
301 if (!extensions
) extensions
= "";
302 if (!ext2
) ext2
= "";
303 allexts
= malloc(strlen(extensions
) + strlen(ext2
) + 2);
304 strcpy(allexts
, extensions
);
305 strcat(allexts
, " ");
306 strcat(allexts
, ext2
);
307 mp_msg(MSGT_VO
, MSGL_V
, "OpenGL extensions string:\n%s\n", allexts
);
309 getProcAddress
= setNull
;
310 for (dsc
= extfuncs
; dsc
->funcptr
; dsc
++) {
313 if (!dsc
->extstr
|| strstr(allexts
, dsc
->extstr
)) {
314 for (i
= 0; !ptr
&& dsc
->funcnames
[i
]; i
++)
315 ptr
= getProcAddress((const GLubyte
*)dsc
->funcnames
[i
]);
317 *(dsc
->funcptr
) = ptr
;
319 if (strstr(allexts
, "_texture_float"))
320 hqtexfmt
= GL_RGB32F
;
321 else if (strstr(allexts
, "NV_float_buffer"))
322 hqtexfmt
= GL_FLOAT_RGB32_NV
;
329 * \brief create a texture and set some defaults
330 * \param target texture taget, usually GL_TEXTURE_2D
331 * \param fmt internal texture format
332 * \param filter filter used for scaling, e.g. GL_LINEAR
333 * \param w texture width
334 * \param h texture height
335 * \param val luminance value to fill texture with
338 void glCreateClearTex(GLenum target
, GLenum fmt
, GLint filter
,
339 int w
, int h
, unsigned char val
) {
340 GLfloat fval
= (GLfloat
)val
/ 255.0;
341 GLfloat border
[4] = {fval
, fval
, fval
, fval
};
342 GLenum clrfmt
= (fmt
== GL_ALPHA
) ? GL_ALPHA
: GL_LUMINANCE
;
343 char *init
= malloc(w
* h
);
344 memset(init
, val
, w
* h
);
345 glAdjustAlignment(w
);
346 glPixelStorei(GL_UNPACK_ROW_LENGTH
, w
);
347 glTexImage2D(target
, 0, fmt
, w
, h
, 0, clrfmt
, GL_UNSIGNED_BYTE
, init
);
348 glTexParameterf(target
, GL_TEXTURE_PRIORITY
, 1.0);
349 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, filter
);
350 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, filter
);
351 glTexParameteri(target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
352 glTexParameteri(target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
353 // Border texels should not be used with CLAMP_TO_EDGE
354 // We set a sane default anyway.
355 glTexParameterfv(target
, GL_TEXTURE_BORDER_COLOR
, border
);
360 * \brief skips whitespace and comments
361 * \param f file to read from
363 static void ppm_skip(FILE *f
) {
371 } while (c
!= EOF
&& (isspace(c
) || comment
));
376 #define MAXDIM (16 * 1024)
379 * \brief creates a texture from a PPM file
380 * \param target texture taget, usually GL_TEXTURE_2D
381 * \param fmt internal texture format, 0 for default
382 * \param filter filter used for scaling, e.g. GL_LINEAR
383 * \param f file to read PPM from
384 * \param width [out] width of texture
385 * \param height [out] height of texture
386 * \param maxval [out] maxval value from PPM file
387 * \return 0 on error, 1 otherwise
390 int glCreatePPMTex(GLenum target
, GLenum fmt
, GLint filter
,
391 FILE *f
, int *width
, int *height
, int *maxval
) {
392 unsigned w
, h
, m
, val
, bpp
;
395 if (fgetc(f
) != 'P' || fgetc(f
) != '6')
398 if (fscanf(f
, "%u", &w
) != 1)
401 if (fscanf(f
, "%u", &h
) != 1)
404 if (fscanf(f
, "%u", &m
) != 1)
409 if (w
> MAXDIM
|| h
> MAXDIM
)
411 bpp
= (m
> 255) ? 6 : 3;
412 data
= malloc(w
* h
* bpp
);
413 if (fread(data
, w
* bpp
, h
, f
) != h
)
416 fmt
= (m
> 255) ? hqtexfmt
: 3;
417 if (fmt
== GL_FLOAT_RGB32_NV
&& target
!= GL_TEXTURE_RECTANGLE
)
420 glCreateClearTex(target
, fmt
, filter
, w
, h
, 0);
421 glUploadTex(target
, GL_RGB
, (m
> 255) ? GL_UNSIGNED_SHORT
: GL_UNSIGNED_BYTE
,
422 data
, w
* bpp
, 0, 0, w
, h
, 0);
424 if (width
) *width
= w
;
425 if (height
) *height
= h
;
426 if (maxval
) *maxval
= m
;
431 * \brief return the number of bytes per pixel for the given format
432 * \param format OpenGL format
433 * \param type OpenGL type
434 * \return bytes per pixel
437 * Does not handle all possible variants, just those used by MPlayer
439 int glFmt2bpp(GLenum format
, GLenum type
) {
440 int component_size
= 0;
442 case GL_UNSIGNED_BYTE_3_3_2
:
443 case GL_UNSIGNED_BYTE_2_3_3_REV
:
445 case GL_UNSIGNED_SHORT_5_5_5_1
:
446 case GL_UNSIGNED_SHORT_1_5_5_5_REV
:
447 case GL_UNSIGNED_SHORT_5_6_5
:
448 case GL_UNSIGNED_SHORT_5_6_5_REV
:
450 case GL_UNSIGNED_BYTE
:
453 case GL_UNSIGNED_SHORT
:
460 return component_size
;
463 return 3 * component_size
;
466 return 4 * component_size
;
472 * \brief upload a texture, handling things like stride and slices
473 * \param target texture target, usually GL_TEXTURE_2D
474 * \param format OpenGL format of data
475 * \param type OpenGL type of data
476 * \param dataptr data to upload
477 * \param stride data stride
478 * \param x x offset in texture
479 * \param y y offset in texture
480 * \param w width of the texture part to upload
481 * \param h height of the texture part to upload
482 * \param slice height of an upload slice, 0 for all at once
485 void glUploadTex(GLenum target
, GLenum format
, GLenum type
,
486 const void *dataptr
, int stride
,
487 int x
, int y
, int w
, int h
, int slice
) {
488 const uint8_t *data
= dataptr
;
490 if (w
<= 0 || h
<= 0) return;
494 data
+= (h
- 1) * stride
;
497 // this is not always correct, but should work for MPlayer
498 glAdjustAlignment(stride
);
499 glPixelStorei(GL_UNPACK_ROW_LENGTH
, stride
/ glFmt2bpp(format
, type
));
500 for (; y
+ slice
<= y_max
; y
+= slice
) {
501 glTexSubImage2D(target
, 0, x
, y
, w
, slice
, format
, type
, data
);
502 data
+= stride
* slice
;
505 glTexSubImage2D(target
, 0, x
, y
, w
, y_max
- y
, format
, type
, data
);
508 static void fillUVcoeff(GLfloat
*ucoef
, GLfloat
*vcoef
,
509 float uvcos
, float uvsin
) {
511 ucoef
[0] = 0 * uvcos
+ 1.403 * uvsin
;
512 vcoef
[0] = 0 * uvsin
+ 1.403 * uvcos
;
513 ucoef
[1] = -0.344 * uvcos
+ -0.714 * uvsin
;
514 vcoef
[1] = -0.344 * uvsin
+ -0.714 * uvcos
;
515 ucoef
[2] = 1.770 * uvcos
+ 0 * uvsin
;
516 vcoef
[2] = 1.770 * uvsin
+ 0 * uvcos
;
519 // Coefficients (probably) must be in [0, 1] range, whereas they originally
520 // are in [-2, 2] range, so here comes the trick:
521 // First put them in the [-0.5, 0.5] range, then add 0.5.
522 // This can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments
523 // for CombinerInput and CombinerOutput (or the respective ATI variants)
524 for (i
= 0; i
< 4; i
++) {
525 ucoef
[i
] = ucoef
[i
] * 0.25 + 0.5;
526 vcoef
[i
] = vcoef
[i
] * 0.25 + 0.5;
531 * \brief Setup register combiners for YUV to RGB conversion.
532 * \param uvcos used for saturation and hue adjustment
533 * \param uvsin used for saturation and hue adjustment
535 static void glSetupYUVCombiners(float uvcos
, float uvsin
) {
539 if (!CombinerInput
|| !CombinerOutput
||
540 !CombinerParameterfv
|| !CombinerParameteri
) {
541 mp_msg(MSGT_VO
, MSGL_FATAL
, "[gl] Combiner functions missing!\n");
544 glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV
, &i
);
546 mp_msg(MSGT_VO
, MSGL_ERR
,
547 "[gl] 2 general combiners needed for YUV combiner support (found %i)\n", i
);
548 glGetIntegerv (GL_MAX_TEXTURE_UNITS
, &i
);
550 mp_msg(MSGT_VO
, MSGL_ERR
,
551 "[gl] 3 texture units needed for YUV combiner support (found %i)\n", i
);
552 fillUVcoeff(ucoef
, vcoef
, uvcos
, uvsin
);
553 CombinerParameterfv(GL_CONSTANT_COLOR0_NV
, ucoef
);
554 CombinerParameterfv(GL_CONSTANT_COLOR1_NV
, vcoef
);
556 // UV first, like this green component cannot overflow
557 CombinerInput(GL_COMBINER0_NV
, GL_RGB
, GL_VARIABLE_A_NV
,
558 GL_TEXTURE1
, GL_HALF_BIAS_NORMAL_NV
, GL_RGB
);
559 CombinerInput(GL_COMBINER0_NV
, GL_RGB
, GL_VARIABLE_B_NV
,
560 GL_CONSTANT_COLOR0_NV
, GL_HALF_BIAS_NORMAL_NV
, GL_RGB
);
561 CombinerInput(GL_COMBINER0_NV
, GL_RGB
, GL_VARIABLE_C_NV
,
562 GL_TEXTURE2
, GL_HALF_BIAS_NORMAL_NV
, GL_RGB
);
563 CombinerInput(GL_COMBINER0_NV
, GL_RGB
, GL_VARIABLE_D_NV
,
564 GL_CONSTANT_COLOR1_NV
, GL_HALF_BIAS_NORMAL_NV
, GL_RGB
);
565 CombinerOutput(GL_COMBINER0_NV
, GL_RGB
, GL_DISCARD_NV
, GL_DISCARD_NV
,
566 GL_SPARE0_NV
, GL_SCALE_BY_FOUR_NV
, GL_NONE
, GL_FALSE
,
570 CombinerInput(GL_COMBINER1_NV
, GL_RGB
, GL_VARIABLE_A_NV
, GL_SPARE0_NV
,
571 GL_SIGNED_IDENTITY_NV
, GL_RGB
);
572 CombinerInput(GL_COMBINER1_NV
, GL_RGB
, GL_VARIABLE_B_NV
, GL_ZERO
,
573 GL_UNSIGNED_INVERT_NV
, GL_RGB
);
574 CombinerInput(GL_COMBINER1_NV
, GL_RGB
, GL_VARIABLE_C_NV
,
575 GL_TEXTURE0
, GL_SIGNED_IDENTITY_NV
, GL_RGB
);
576 CombinerInput(GL_COMBINER1_NV
, GL_RGB
, GL_VARIABLE_D_NV
, GL_ZERO
,
577 GL_UNSIGNED_INVERT_NV
, GL_RGB
);
578 CombinerOutput(GL_COMBINER1_NV
, GL_RGB
, GL_DISCARD_NV
, GL_DISCARD_NV
,
579 GL_SPARE0_NV
, GL_NONE
, GL_NONE
, GL_FALSE
,
582 // leave final combiner stage in default mode
583 CombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV
, 2);
587 * \brief Setup ATI version of register combiners for YUV to RGB conversion.
588 * \param uvcos used for saturation and hue adjustment
589 * \param uvsin used for saturation and hue adjustment
591 * ATI called this fragment shader, but the name is confusing in the
592 * light of a very different OpenGL 2.0 extension with the same name
594 static void glSetupYUVCombinersATI(float uvcos
, float uvsin
) {
598 if (!BeginFragmentShader
|| !EndFragmentShader
||
599 !SetFragmentShaderConstant
|| !SampleMap
||
600 !ColorFragmentOp2
|| !ColorFragmentOp3
) {
601 mp_msg(MSGT_VO
, MSGL_FATAL
, "[gl] Combiner (ATI) functions missing!\n");
604 glGetIntegerv(GL_NUM_FRAGMENT_REGISTERS_ATI
, &i
);
606 mp_msg(MSGT_VO
, MSGL_ERR
,
607 "[gl] 3 registers needed for YUV combiner (ATI) support (found %i)\n", i
);
608 glGetIntegerv (GL_MAX_TEXTURE_UNITS
, &i
);
610 mp_msg(MSGT_VO
, MSGL_ERR
,
611 "[gl] 3 texture units needed for YUV combiner (ATI) support (found %i)\n", i
);
612 fillUVcoeff(ucoef
, vcoef
, uvcos
, uvsin
);
613 BeginFragmentShader();
614 SetFragmentShaderConstant(GL_CON_0_ATI
, ucoef
);
615 SetFragmentShaderConstant(GL_CON_1_ATI
, vcoef
);
616 SampleMap(GL_REG_0_ATI
, GL_TEXTURE0
, GL_SWIZZLE_STR_ATI
);
617 SampleMap(GL_REG_1_ATI
, GL_TEXTURE1
, GL_SWIZZLE_STR_ATI
);
618 SampleMap(GL_REG_2_ATI
, GL_TEXTURE2
, GL_SWIZZLE_STR_ATI
);
619 // UV first, like this green component cannot overflow
620 ColorFragmentOp2(GL_MUL_ATI
, GL_REG_1_ATI
, GL_NONE
, GL_NONE
,
621 GL_REG_1_ATI
, GL_NONE
, GL_BIAS_BIT_ATI
,
622 GL_CON_0_ATI
, GL_NONE
, GL_BIAS_BIT_ATI
);
623 ColorFragmentOp3(GL_MAD_ATI
, GL_REG_2_ATI
, GL_NONE
, GL_4X_BIT_ATI
,
624 GL_REG_2_ATI
, GL_NONE
, GL_BIAS_BIT_ATI
,
625 GL_CON_1_ATI
, GL_NONE
, GL_BIAS_BIT_ATI
,
626 GL_REG_1_ATI
, GL_NONE
, GL_NONE
);
627 ColorFragmentOp2(GL_ADD_ATI
, GL_REG_0_ATI
, GL_NONE
, GL_NONE
,
628 GL_REG_0_ATI
, GL_NONE
, GL_NONE
,
629 GL_REG_2_ATI
, GL_NONE
, GL_NONE
);
634 * \brief helper function for gen_spline_lookup_tex
635 * \param x subpixel-position ((0,1) range) to calculate weights for
636 * \param dst where to store transformed weights, must provide space for 4 GLfloats
638 * calculates the weights and stores them after appropriate transformation
639 * for the scaler fragment program.
641 static void store_weights(float x
, GLfloat
*dst
) {
642 float w0
= (((-1 * x
+ 3) * x
- 3) * x
+ 1) / 6;
643 float w1
= ((( 3 * x
- 6) * x
+ 0) * x
+ 4) / 6;
644 float w2
= (((-3 * x
+ 3) * x
+ 3) * x
+ 1) / 6;
645 float w3
= ((( 1 * x
+ 0) * x
+ 0) * x
+ 0) / 6;
646 *dst
++ = 1 + x
- w1
/ (w0
+ w1
);
647 *dst
++ = 1 - x
+ w3
/ (w2
+ w3
);
652 //! to avoid artefacts this should be rather large
653 #define LOOKUP_BSPLINE_RES (2 * 1024)
655 * \brief creates the 1D lookup texture needed for fast higher-order filtering
656 * \param unit texture unit to attach texture to
658 static void gen_spline_lookup_tex(GLenum unit
) {
659 GLfloat tex
[4 * LOOKUP_BSPLINE_RES
];
662 for (i
= 0; i
< LOOKUP_BSPLINE_RES
; i
++) {
663 float x
= (float)(i
+ 0.5) / LOOKUP_BSPLINE_RES
;
664 store_weights(x
, tp
);
667 store_weights(0, tex
);
668 store_weights(1, &tex
[4 * (LOOKUP_BSPLINE_RES
- 1)]);
670 glTexImage1D(GL_TEXTURE_1D
, 0, GL_RGBA16
, LOOKUP_BSPLINE_RES
, 0, GL_RGBA
, GL_FLOAT
, tex
);
671 glTexParameterf(GL_TEXTURE_1D
, GL_TEXTURE_PRIORITY
, 1.0);
672 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
673 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
674 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
675 ActiveTexture(GL_TEXTURE0
);
678 static const char *bilin_filt_template
=
679 "TEX yuv.%c, fragment.texcoord[%c], texture[%c], %s;";
681 #define BICUB_FILT_MAIN(textype) \
682 /* first y-interpolation */ \
683 "ADD coord, fragment.texcoord[%c].xyxy, cdelta.xyxw;" \
684 "ADD coord2, fragment.texcoord[%c].xyxy, cdelta.zyzw;" \
685 "TEX a.r, coord.xyxy, texture[%c], "textype";" \
686 "TEX a.g, coord.zwzw, texture[%c], "textype";" \
687 /* second y-interpolation */ \
688 "TEX b.r, coord2.xyxy, texture[%c], "textype";" \
689 "TEX b.g, coord2.zwzw, texture[%c], "textype";" \
690 "LRP a.b, parmy.b, a.rrrr, a.gggg;" \
691 "LRP a.a, parmy.b, b.rrrr, b.gggg;" \
692 /* x-interpolation */ \
693 "LRP yuv.%c, parmx.b, a.bbbb, a.aaaa;"
695 static const char *bicub_filt_template_2D
=
696 "MAD coord.xy, fragment.texcoord[%c], {%f, %f}, {0.5, 0.5};"
697 "TEX parmx, coord.x, texture[%c], 1D;"
698 "MUL cdelta.xz, parmx.rrgg, {-%f, 0, %f, 0};"
699 "TEX parmy, coord.y, texture[%c], 1D;"
700 "MUL cdelta.yw, parmy.rrgg, {0, -%f, 0, %f};"
701 BICUB_FILT_MAIN("2D");
703 static const char *bicub_filt_template_RECT
=
704 "ADD coord, fragment.texcoord[%c], {0.5, 0.5};"
705 "TEX parmx, coord.x, texture[%c], 1D;"
706 "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};"
707 "TEX parmy, coord.y, texture[%c], 1D;"
708 "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};"
709 BICUB_FILT_MAIN("RECT");
711 #define CALCWEIGHTS(t, s) \
712 "MAD "t", {-0.5, 0.1666, 0.3333, -0.3333}, "s", {1, 0, -0.5, 0.5};" \
713 "MAD "t", "t", "s", {0, 0, -0.5, 0.5};" \
714 "MAD "t", "t", "s", {-0.6666, 0, 0.8333, 0.1666};" \
717 "MAD "t".xy, "t".xyxy, a.xyxy, {1, 1, 0, 0};" \
718 "ADD "t".x, "t".xxxx, "s";" \
719 "SUB "t".y, "t".yyyy, "s";"
721 static const char *bicub_notex_filt_template_2D
=
722 "MAD coord.xy, fragment.texcoord[%c], {%f, %f}, {0.5, 0.5};"
723 "FRC coord.xy, coord.xyxy;"
724 CALCWEIGHTS("parmx", "coord.xxxx")
725 "MUL cdelta.xz, parmx.rrgg, {-%f, 0, %f, 0};"
726 CALCWEIGHTS("parmy", "coord.yyyy")
727 "MUL cdelta.yw, parmy.rrgg, {0, -%f, 0, %f};"
728 BICUB_FILT_MAIN("2D");
730 static const char *bicub_notex_filt_template_RECT
=
731 "ADD coord, fragment.texcoord[%c], {0.5, 0.5};"
732 "FRC coord.xy, coord.xyxy;"
733 CALCWEIGHTS("parmx", "coord.xxxx")
734 "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};"
735 CALCWEIGHTS("parmy", "coord.yyyy")
736 "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};"
737 BICUB_FILT_MAIN("RECT");
739 #define BICUB_X_FILT_MAIN(textype) \
740 "ADD coord, fragment.texcoord[%c].xyxy, cdelta.xyxw;" \
741 "ADD coord2, fragment.texcoord[%c].xyxy, cdelta.zyzw;" \
742 "TEX a.r, coord, texture[%c], "textype";" \
743 "TEX b.r, coord2, texture[%c], "textype";" \
744 /* x-interpolation */ \
745 "LRP yuv.%c, parmx.b, a.rrrr, b.rrrr;"
747 static const char *bicub_x_filt_template_2D
=
748 "MAD coord.x, fragment.texcoord[%c], {%f, %f}, {0.5, 0.5};"
749 "TEX parmx, coord, texture[%c], 1D;"
750 "MUL cdelta.xz, parmx.rrgg, {-%f, 0, %f, 0};"
751 BICUB_X_FILT_MAIN("2D");
753 static const char *bicub_x_filt_template_RECT
=
754 "ADD coord.x, fragment.texcoord[%c], {0.5, 0.5};"
755 "TEX parmx, coord, texture[%c], 1D;"
756 "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};"
757 BICUB_X_FILT_MAIN("RECT");
759 static const char *yuv_prog_template
=
760 "PARAM ycoef = {%.4f, %.4f, %.4f};"
761 "PARAM ucoef = {%.4f, %.4f, %.4f};"
762 "PARAM vcoef = {%.4f, %.4f, %.4f};"
763 "PARAM offsets = {%.4f, %.4f, %.4f};"
765 "MAD res.rgb, yuv.rrrr, ycoef, offsets;"
766 "MAD res.rgb, yuv.gggg, ucoef, res;"
767 "MAD result.color.rgb, yuv.bbbb, vcoef, res;"
770 static const char *yuv_pow_prog_template
=
771 "PARAM ycoef = {%.4f, %.4f, %.4f};"
772 "PARAM ucoef = {%.4f, %.4f, %.4f};"
773 "PARAM vcoef = {%.4f, %.4f, %.4f};"
774 "PARAM offsets = {%.4f, %.4f, %.4f};"
775 "PARAM gamma = {%.4f, %.4f, %.4f};"
777 "MAD res.rgb, yuv.rrrr, ycoef, offsets;"
778 "MAD res.rgb, yuv.gggg, ucoef, res;"
779 "MAD_SAT res.rgb, yuv.bbbb, vcoef, res;"
780 "POW result.color.r, res.r, gamma.r;"
781 "POW result.color.g, res.g, gamma.g;"
782 "POW result.color.b, res.b, gamma.b;"
785 static const char *yuv_lookup_prog_template
=
786 "PARAM ycoef = {%.4f, %.4f, %.4f, 0};"
787 "PARAM ucoef = {%.4f, %.4f, %.4f, 0};"
788 "PARAM vcoef = {%.4f, %.4f, %.4f, 0};"
789 "PARAM offsets = {%.4f, %.4f, %.4f, 0.125};"
791 "MAD res, yuv.rrrr, ycoef, offsets;"
792 "MAD res.rgb, yuv.gggg, ucoef, res;"
793 "MAD res.rgb, yuv.bbbb, vcoef, res;"
794 "TEX result.color.r, res.raaa, texture[%c], 2D;"
795 "ADD res.a, res.a, 0.25;"
796 "TEX result.color.g, res.gaaa, texture[%c], 2D;"
797 "ADD res.a, res.a, 0.25;"
798 "TEX result.color.b, res.baaa, texture[%c], 2D;"
801 static const char *yuv_lookup3d_prog_template
=
802 "TEX result.color, yuv, texture[%c], 3D;"
806 * \brief creates and initializes helper textures needed for scaling texture read
807 * \param scaler scaler type to create texture for
808 * \param texu contains next free texture unit number
809 * \param texs texture unit ids for the scaler are stored in this array
811 static void create_scaler_textures(int scaler
, int *texu
, char *texs
) {
813 case YUV_SCALER_BILIN
:
815 case YUV_SCALER_BICUB
:
816 case YUV_SCALER_BICUB_X
:
818 gen_spline_lookup_tex(GL_TEXTURE0
+ texs
[0]);
822 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] unknown scaler type %i\n", scaler
);
826 static void gen_gamma_map(unsigned char *map
, int size
, float gamma
);
828 static void get_yuv2rgb_coeffs(float brightness
, float contrast
, float uvcos
, float uvsin
,
829 float *ry
, float *ru
, float *rv
, float *rc
,
830 float *gy
, float *gu
, float *gv
, float *gc
,
831 float *by
, float *bu
, float *bv
, float *bc
) {
832 *ry
= 1.164 * contrast
;
833 *gy
= 1.164 * contrast
;
834 *by
= 1.164 * contrast
;
835 *ru
= 0 * uvcos
+ 1.596 * uvsin
;
836 *rv
= 0 * uvsin
+ 1.596 * uvcos
;
837 *gu
= -0.391 * uvcos
+ -0.813 * uvsin
;
838 *gv
= -0.391 * uvsin
+ -0.813 * uvcos
;
839 *bu
= 2.018 * uvcos
+ 0 * uvsin
;
840 *bv
= 2.018 * uvsin
+ 0 * uvcos
;
841 *rc
= (-16 * *ry
+ (-128) * *ru
+ (-128) * *rv
) / 255.0 + brightness
;
842 *gc
= (-16 * *gy
+ (-128) * *gu
+ (-128) * *gv
) / 255.0 + brightness
;
843 *bc
= (-16 * *by
+ (-128) * *bu
+ (-128) * *bv
) / 255.0 + brightness
;
844 // these "center" contrast control so that e.g. a contrast of 0
845 // leads to a grey image, not a black one
846 *rc
+= 0.5 - contrast
/ 2.0;
847 *gc
+= 0.5 - contrast
/ 2.0;
848 *bc
+= 0.5 - contrast
/ 2.0;
851 //! size of gamma map use to avoid slow exp function in gen_yuv2rgb_map
852 #define GMAP_SIZE (1024)
854 * \brief generate a 3D YUV -> RGB map
855 * \param map where to store map. Must provide space for (size + 2)^3 elements
856 * \param size size of the map, excluding border
857 * \param brightness desired brightness adjustment for conversion
858 * \param contrast desired contrast adjustment for conversion
859 * \param uvcos desired hue/saturation adjustment for conversion
860 * \param uvsin desired hue/saturation adjustment for conversion
861 * \param rgamma desired red gamma adjustment for conversion
862 * \param ggamma desired green gamma adjustment for conversion
863 * \param bgamma desired blue gamma adjustment for conversion
865 static void gen_yuv2rgb_map(unsigned char *map
, int size
, float brightness
,
866 float contrast
, float uvcos
, float uvsin
,
867 float rgamma
, float ggamma
, float bgamma
) {
869 float step
= 1.0 / size
;
872 float ry
, ru
, rv
, rc
;
873 float gy
, gu
, gv
, gc
;
874 float by
, bu
, bv
, bc
;
875 unsigned char gmaps
[3][GMAP_SIZE
];
876 gen_gamma_map(gmaps
[0], GMAP_SIZE
, rgamma
);
877 gen_gamma_map(gmaps
[1], GMAP_SIZE
, ggamma
);
878 gen_gamma_map(gmaps
[2], GMAP_SIZE
, bgamma
);
879 get_yuv2rgb_coeffs(brightness
, contrast
, uvcos
, uvsin
,
880 &ry
, &ru
, &rv
, &rc
, &gy
, &gu
, &gv
, &gc
, &by
, &bu
, &bv
, &bc
);
881 ry
*= GMAP_SIZE
- 1; ru
*= GMAP_SIZE
- 1; rv
*= GMAP_SIZE
- 1; rc
*= GMAP_SIZE
- 1;
882 gy
*= GMAP_SIZE
- 1; gu
*= GMAP_SIZE
- 1; gv
*= GMAP_SIZE
- 1; gc
*= GMAP_SIZE
- 1;
883 by
*= GMAP_SIZE
- 1; bu
*= GMAP_SIZE
- 1; bv
*= GMAP_SIZE
- 1; bc
*= GMAP_SIZE
- 1;
885 for (i
= -1; i
<= size
; i
++) {
887 for (j
= -1; j
<= size
; j
++) {
889 for (k
= -1; k
<= size
; k
++) {
890 r
= ry
* y
+ ru
* u
+ rv
* v
+ rc
;
891 g
= gy
* y
+ gu
* u
+ gv
* v
+ gc
;
892 b
= by
* y
+ bu
* u
+ bv
* v
+ bc
;
893 if (r
> GMAP_SIZE
- 1) r
= GMAP_SIZE
- 1;
895 if (g
> GMAP_SIZE
- 1) g
= GMAP_SIZE
- 1;
897 if (b
> GMAP_SIZE
- 1) b
= GMAP_SIZE
- 1;
899 *map
++ = gmaps
[0][(int)r
];
900 *map
++ = gmaps
[1][(int)g
];
901 *map
++ = gmaps
[2][(int)b
];
902 y
+= (k
== -1 || k
== size
- 1) ? step
/ 2 : step
;
904 u
+= (j
== -1 || j
== size
- 1) ? step
/ 2 : step
;
906 v
+= (i
== -1 || i
== size
- 1) ? step
/ 2 : step
;
910 //! resolution of texture for gamma lookup table
911 #define LOOKUP_RES 512
912 //! resolution for 3D yuv->rgb conversion lookup table
913 #define LOOKUP_3DRES 32
915 * \brief creates and initializes helper textures needed for yuv conversion
916 * \param texu contains next free texture unit number
917 * \param texs texture unit ids for the conversion are stored in this array
918 * \param brightness desired brightness adjustment for conversion
919 * \param contrast desired contrast adjustment for conversion
920 * \param uvcos desired hue/saturation adjustment for conversion
921 * \param uvsin desired hue/saturation adjustment for conversion
922 * \param rgamma desired red gamma adjustment for conversion
923 * \param ggamma desired green gamma adjustment for conversion
924 * \param bgamma desired blue gamma adjustment for conversion
926 static void create_conv_textures(int conv
, int *texu
, char *texs
,
927 float brightness
, float contrast
, float uvcos
, float uvsin
,
928 float rgamma
, float ggamma
, float bgamma
) {
929 unsigned char *lookup_data
= NULL
;
931 case YUV_CONVERSION_FRAGMENT
:
932 case YUV_CONVERSION_FRAGMENT_POW
:
934 case YUV_CONVERSION_FRAGMENT_LOOKUP
:
936 ActiveTexture(GL_TEXTURE0
+ texs
[0]);
937 lookup_data
= malloc(4 * LOOKUP_RES
);
938 gen_gamma_map(lookup_data
, LOOKUP_RES
, rgamma
);
939 gen_gamma_map(&lookup_data
[LOOKUP_RES
], LOOKUP_RES
, ggamma
);
940 gen_gamma_map(&lookup_data
[2 * LOOKUP_RES
], LOOKUP_RES
, bgamma
);
941 glCreateClearTex(GL_TEXTURE_2D
, GL_LUMINANCE8
, GL_LINEAR
,
943 glUploadTex(GL_TEXTURE_2D
, GL_LUMINANCE
, GL_UNSIGNED_BYTE
, lookup_data
,
944 LOOKUP_RES
, 0, 0, LOOKUP_RES
, 4, 0);
945 ActiveTexture(GL_TEXTURE0
);
948 case YUV_CONVERSION_FRAGMENT_LOOKUP3D
:
950 int sz
= LOOKUP_3DRES
+ 2; // texture size including borders
952 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] Missing 3D texture function!\n");
956 ActiveTexture(GL_TEXTURE0
+ texs
[0]);
957 lookup_data
= malloc(3 * sz
* sz
* sz
);
958 gen_yuv2rgb_map(lookup_data
, LOOKUP_3DRES
, brightness
, contrast
,
959 uvcos
, uvsin
, rgamma
, ggamma
, bgamma
);
960 glAdjustAlignment(sz
);
961 glPixelStorei(GL_UNPACK_ROW_LENGTH
, 0);
962 TexImage3D(GL_TEXTURE_3D
, 0, 3, sz
, sz
, sz
, 1,
963 GL_RGB
, GL_UNSIGNED_BYTE
, lookup_data
);
964 glTexParameterf(GL_TEXTURE_3D
, GL_TEXTURE_PRIORITY
, 1.0);
965 glTexParameteri(GL_TEXTURE_3D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
966 glTexParameteri(GL_TEXTURE_3D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
967 glTexParameteri(GL_TEXTURE_3D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
968 glTexParameteri(GL_TEXTURE_3D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
969 glTexParameteri(GL_TEXTURE_3D
, GL_TEXTURE_WRAP_R
, GL_CLAMP
);
970 ActiveTexture(GL_TEXTURE0
);
975 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] unknown conversion type %i\n", conv
);
982 * \brief adds a scaling texture read at the current fragment program position
983 * \param scaler type of scaler to insert
984 * \param prog_pos current position in fragment program
985 * \param remain how many bytes remain in the buffer given by prog_pos
986 * \param texs array containing the texture unit identifiers for this scaler
987 * \param in_tex texture unit the scaler should read from
988 * \param out_comp component of the yuv variable the scaler stores the result in
989 * \param rect if rectangular (pixel) adressing should be used for in_tex
990 * \param texw width of the in_tex texture
991 * \param texh height of the in_tex texture
993 static void add_scaler(int scaler
, char **prog_pos
, int *remain
, char *texs
,
994 char in_tex
, char out_comp
, int rect
, int texw
, int texh
) {
996 case YUV_SCALER_BILIN
:
997 snprintf(*prog_pos
, *remain
, bilin_filt_template
, out_comp
, in_tex
,
998 in_tex
, rect
? "RECT" : "2D");
1000 case YUV_SCALER_BICUB
:
1002 snprintf(*prog_pos
, *remain
, bicub_filt_template_RECT
,
1003 in_tex
, texs
[0], texs
[0],
1004 in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1006 snprintf(*prog_pos
, *remain
, bicub_filt_template_2D
,
1007 in_tex
, (float)texw
, (float)texh
,
1008 texs
[0], (float)1.0 / texw
, (float)1.0 / texw
,
1009 texs
[0], (float)1.0 / texh
, (float)1.0 / texh
,
1010 in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1012 case YUV_SCALER_BICUB_X
:
1014 snprintf(*prog_pos
, *remain
, bicub_x_filt_template_RECT
,
1016 in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1018 snprintf(*prog_pos
, *remain
, bicub_x_filt_template_2D
,
1019 in_tex
, (float)texw
, (float)texh
,
1020 texs
[0], (float)1.0 / texw
, (float)1.0 / texw
,
1021 in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1023 case YUV_SCALER_BICUB_NOTEX
:
1025 snprintf(*prog_pos
, *remain
, bicub_notex_filt_template_RECT
,
1027 in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1029 snprintf(*prog_pos
, *remain
, bicub_notex_filt_template_2D
,
1030 in_tex
, (float)texw
, (float)texh
,
1031 (float)1.0 / texw
, (float)1.0 / texw
,
1032 (float)1.0 / texh
, (float)1.0 / texh
,
1033 in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, in_tex
, out_comp
);
1036 *remain
-= strlen(*prog_pos
);
1037 *prog_pos
+= strlen(*prog_pos
);
1040 static const struct {
1045 {"instructions", 0x88A0, 0x88A1},
1046 {"native instructions", 0x88A2, 0x88A3},
1047 {"temporaries", 0x88A4, 0x88A5},
1048 {"native temporaries", 0x88A6, 0x88A7},
1049 {"parameters", 0x88A8, 0x88A9},
1050 {"native parameters", 0x88AA, 0x88AB},
1051 {"attribs", 0x88AC, 0x88AD},
1052 {"native attribs", 0x88AE, 0x88AF},
1053 {"ALU instructions", 0x8805, 0x880B},
1054 {"TEX instructions", 0x8806, 0x880C},
1055 {"TEX indirections", 0x8807, 0x880D},
1056 {"native ALU instructions", 0x8808, 0x880E},
1057 {"native TEX instructions", 0x8809, 0x880F},
1058 {"native TEX indirections", 0x880A, 0x8810},
1063 * \brief load the specified GPU Program
1064 * \param target program target to load into, only GL_FRAGMENT_PROGRAM is tested
1065 * \param prog program string
1066 * \return 1 on success, 0 otherwise
1068 int loadGPUProgram(GLenum target
, char *prog
) {
1070 GLint cur
= 0, max
= 0, err
= 0;
1071 if (!ProgramString
) {
1072 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] Missing GPU program function\n");
1075 ProgramString(target
, GL_PROGRAM_FORMAT_ASCII
, strlen(prog
), prog
);
1076 glGetIntegerv(GL_PROGRAM_ERROR_POSITION
, &err
);
1078 mp_msg(MSGT_VO
, MSGL_ERR
,
1079 "[gl] Error compiling fragment program, make sure your card supports\n"
1080 "[gl] GL_ARB_fragment_program (use glxinfo to check).\n"
1081 "[gl] Error message:\n %s at %.10s\n",
1082 glGetString(GL_PROGRAM_ERROR_STRING
), &prog
[err
]);
1085 if (!GetProgramiv
|| !mp_msg_test(MSGT_VO
, MSGL_V
))
1087 mp_msg(MSGT_VO
, MSGL_V
, "[gl] Program statistics:\n");
1088 for (i
= 0; progstats
[i
].name
; i
++) {
1089 GetProgramiv(target
, progstats
[i
].cur
, &cur
);
1090 GetProgramiv(target
, progstats
[i
].max
, &max
);
1091 mp_msg(MSGT_VO
, MSGL_V
, "[gl] %s: %i/%i\n", progstats
[i
].name
, cur
, max
);
1097 * \brief setup a fragment program that will do YUV->RGB conversion
1098 * \param brightness brightness adjustment offset
1099 * \param contrast contrast adjustment factor
1100 * \param uvcos used for saturation and hue adjustment
1101 * \param uvsin used for saturation and hue adjustment
1102 * \param lookup use fragment program that uses texture unit 4 to
1103 * do additional conversion via lookup.
1105 static void glSetupYUVFragprog(float brightness
, float contrast
,
1106 float uvcos
, float uvsin
, float rgamma
,
1107 float ggamma
, float bgamma
, int type
, int rect
,
1108 int texw
, int texh
) {
1109 char yuv_prog
[4000] =
1111 "OPTION ARB_precision_hint_fastest;"
1112 // all scaler variables must go here so they aren't defined
1113 // multiple times when the same scaler is used more than once
1114 "TEMP coord, coord2, cdelta, parmx, parmy, a, b, yuv;";
1115 int prog_remain
= sizeof(yuv_prog
) - strlen(yuv_prog
);
1116 char *prog_pos
= &yuv_prog
[strlen(yuv_prog
)];
1118 char lum_scale_texs
[1];
1119 char chrom_scale_texs
[1];
1122 // this is the conversion matrix, with y, u, v factors
1123 // for red, green, blue and the constant offsets
1124 float ry
, ru
, rv
, rc
;
1125 float gy
, gu
, gv
, gc
;
1126 float by
, bu
, bv
, bc
;
1127 create_scaler_textures(YUV_LUM_SCALER(type
), &cur_texu
, lum_scale_texs
);
1128 if (YUV_CHROM_SCALER(type
) == YUV_LUM_SCALER(type
))
1129 memcpy(chrom_scale_texs
, lum_scale_texs
, sizeof(chrom_scale_texs
));
1131 create_scaler_textures(YUV_CHROM_SCALER(type
), &cur_texu
, chrom_scale_texs
);
1132 create_conv_textures(YUV_CONVERSION(type
), &cur_texu
, conv_texs
,
1133 brightness
, contrast
, uvcos
, uvsin
, rgamma
, ggamma
, bgamma
);
1134 glGetIntegerv(GL_MAX_TEXTURE_UNITS
, &i
);
1136 mp_msg(MSGT_VO
, MSGL_ERR
,
1137 "[gl] %i texture units needed for this type of YUV fragment support (found %i)\n",
1139 if (!ProgramString
) {
1140 mp_msg(MSGT_VO
, MSGL_FATAL
, "[gl] ProgramString function missing!\n");
1143 add_scaler(YUV_LUM_SCALER(type
), &prog_pos
, &prog_remain
, lum_scale_texs
,
1144 '0', 'r', rect
, texw
, texh
);
1145 add_scaler(YUV_CHROM_SCALER(type
), &prog_pos
, &prog_remain
, chrom_scale_texs
,
1146 '1', 'g', rect
, texw
/ 2, texh
/ 2);
1147 add_scaler(YUV_CHROM_SCALER(type
), &prog_pos
, &prog_remain
, chrom_scale_texs
,
1148 '2', 'b', rect
, texw
/ 2, texh
/ 2);
1149 get_yuv2rgb_coeffs(brightness
, contrast
, uvcos
, uvsin
,
1150 &ry
, &ru
, &rv
, &rc
, &gy
, &gu
, &gv
, &gc
, &by
, &bu
, &bv
, &bc
);
1151 switch (YUV_CONVERSION(type
)) {
1152 case YUV_CONVERSION_FRAGMENT
:
1153 snprintf(prog_pos
, prog_remain
, yuv_prog_template
,
1154 ry
, gy
, by
, ru
, gu
, bu
, rv
, gv
, bv
, rc
, gc
, bc
);
1156 case YUV_CONVERSION_FRAGMENT_POW
:
1157 snprintf(prog_pos
, prog_remain
, yuv_pow_prog_template
,
1158 ry
, gy
, by
, ru
, gu
, bu
, rv
, gv
, bv
, rc
, gc
, bc
,
1159 (float)1.0 / rgamma
, (float)1.0 / bgamma
, (float)1.0 / bgamma
);
1161 case YUV_CONVERSION_FRAGMENT_LOOKUP
:
1162 snprintf(prog_pos
, prog_remain
, yuv_lookup_prog_template
,
1163 ry
, gy
, by
, ru
, gu
, bu
, rv
, gv
, bv
, rc
, gc
, bc
,
1164 conv_texs
[0], conv_texs
[0], conv_texs
[0]);
1166 case YUV_CONVERSION_FRAGMENT_LOOKUP3D
:
1167 snprintf(prog_pos
, prog_remain
, yuv_lookup3d_prog_template
, conv_texs
[0]);
1170 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] unknown conversion type %i\n", YUV_CONVERSION(type
));
1173 mp_msg(MSGT_VO
, MSGL_V
, "[gl] generated fragment program:\n%s\n", yuv_prog
);
1174 loadGPUProgram(GL_FRAGMENT_PROGRAM
, yuv_prog
);
1178 * \brief little helper function to create a lookup table for gamma
1179 * \param map buffer to create map into
1180 * \param size size of buffer
1181 * \param gamma gamma value
1183 static void gen_gamma_map(unsigned char *map
, int size
, float gamma
) {
1186 for (i
= 0; i
< size
; i
++)
1187 map
[i
] = 255 * i
/ (size
- 1);
1190 gamma
= 1.0 / gamma
;
1191 for (i
= 0; i
< size
; i
++) {
1192 float tmp
= (float)i
/ (size
- 1.0);
1193 tmp
= pow(tmp
, gamma
);
1194 if (tmp
> 1.0) tmp
= 1.0;
1195 if (tmp
< 0.0) tmp
= 0.0;
1201 * \brief setup YUV->RGB conversion
1202 * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
1203 * \param type YUV conversion type
1204 * \param brightness brightness adjustment offset
1205 * \param contrast contrast adjustment factor
1206 * \param hue hue adjustment angle
1207 * \param saturation saturation adjustment factor
1208 * \param rgamma gamma value for red channel
1209 * \param ggamma gamma value for green channel
1210 * \param bgamma gamma value for blue channel
1211 * \ingroup glconversion
1213 void glSetupYUVConversion(GLenum target
, int type
,
1214 float brightness
, float contrast
,
1215 float hue
, float saturation
,
1216 float rgamma
, float ggamma
, float bgamma
,
1217 int texw
, int texh
) {
1218 float uvcos
= saturation
* cos(hue
);
1219 float uvsin
= saturation
* sin(hue
);
1220 switch (YUV_CONVERSION(type
)) {
1221 case YUV_CONVERSION_COMBINERS
:
1222 glSetupYUVCombiners(uvcos
, uvsin
);
1224 case YUV_CONVERSION_COMBINERS_ATI
:
1225 glSetupYUVCombinersATI(uvcos
, uvsin
);
1227 case YUV_CONVERSION_FRAGMENT_LOOKUP
:
1228 case YUV_CONVERSION_FRAGMENT_LOOKUP3D
:
1229 case YUV_CONVERSION_FRAGMENT
:
1230 case YUV_CONVERSION_FRAGMENT_POW
:
1231 glSetupYUVFragprog(brightness
, contrast
, uvcos
, uvsin
,
1232 rgamma
, ggamma
, bgamma
, type
,
1233 target
== GL_TEXTURE_RECTANGLE
,
1237 mp_msg(MSGT_VO
, MSGL_ERR
, "[gl] unknown conversion type %i\n", YUV_CONVERSION(type
));
1242 * \brief enable the specified YUV conversion
1243 * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
1244 * \param type type of YUV conversion
1245 * \ingroup glconversion
1247 void glEnableYUVConversion(GLenum target
, int type
) {
1248 if (type
<= 0) return;
1249 switch (YUV_CONVERSION(type
)) {
1250 case YUV_CONVERSION_COMBINERS
:
1251 ActiveTexture(GL_TEXTURE1
);
1253 ActiveTexture(GL_TEXTURE2
);
1255 ActiveTexture(GL_TEXTURE0
);
1256 glEnable(GL_REGISTER_COMBINERS_NV
);
1258 case YUV_CONVERSION_COMBINERS_ATI
:
1259 ActiveTexture(GL_TEXTURE1
);
1261 ActiveTexture(GL_TEXTURE2
);
1263 ActiveTexture(GL_TEXTURE0
);
1264 glEnable(GL_FRAGMENT_SHADER_ATI
);
1266 case YUV_CONVERSION_FRAGMENT_LOOKUP3D
:
1267 case YUV_CONVERSION_FRAGMENT_LOOKUP
:
1268 case YUV_CONVERSION_FRAGMENT_POW
:
1269 case YUV_CONVERSION_FRAGMENT
:
1270 glEnable(GL_FRAGMENT_PROGRAM
);
1276 * \brief disable the specified YUV conversion
1277 * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
1278 * \param type type of YUV conversion
1279 * \ingroup glconversion
1281 void glDisableYUVConversion(GLenum target
, int type
) {
1282 if (type
<= 0) return;
1283 switch (YUV_CONVERSION(type
)) {
1284 case YUV_CONVERSION_COMBINERS
:
1285 ActiveTexture(GL_TEXTURE1
);
1287 ActiveTexture(GL_TEXTURE2
);
1289 ActiveTexture(GL_TEXTURE0
);
1290 glDisable(GL_REGISTER_COMBINERS_NV
);
1292 case YUV_CONVERSION_COMBINERS_ATI
:
1293 ActiveTexture(GL_TEXTURE1
);
1295 ActiveTexture(GL_TEXTURE2
);
1297 ActiveTexture(GL_TEXTURE0
);
1298 glDisable(GL_FRAGMENT_SHADER_ATI
);
1300 case YUV_CONVERSION_FRAGMENT_LOOKUP3D
:
1301 case YUV_CONVERSION_FRAGMENT_LOOKUP
:
1302 case YUV_CONVERSION_FRAGMENT_POW
:
1303 case YUV_CONVERSION_FRAGMENT
:
1304 glDisable(GL_FRAGMENT_PROGRAM
);
1310 * \brief draw a texture part at given 2D coordinates
1311 * \param x screen top coordinate
1312 * \param y screen left coordinate
1313 * \param w screen width coordinate
1314 * \param h screen height coordinate
1315 * \param tx texture top coordinate in pixels
1316 * \param ty texture left coordinate in pixels
1317 * \param tw texture part width in pixels
1318 * \param th texture part height in pixels
1319 * \param sx width of texture in pixels
1320 * \param sy height of texture in pixels
1321 * \param rect_tex whether this texture uses texture_rectangle extension
1322 * \param is_yv12 if set, also draw the textures from units 1 and 2
1323 * \param flip flip the texture upside down
1324 * \ingroup gltexture
1326 void glDrawTex(GLfloat x
, GLfloat y
, GLfloat w
, GLfloat h
,
1327 GLfloat tx
, GLfloat ty
, GLfloat tw
, GLfloat th
,
1328 int sx
, int sy
, int rect_tex
, int is_yv12
, int flip
) {
1329 GLfloat tx2
= tx
/ 2, ty2
= ty
/ 2, tw2
= tw
/ 2, th2
= th
/ 2;
1331 tx
/= sx
; ty
/= sy
; tw
/= sx
; th
/= sy
;
1332 tx2
= tx
, ty2
= ty
, tw2
= tw
, th2
= th
;
1339 glTexCoord2f(tx
, ty
);
1341 MultiTexCoord2f(GL_TEXTURE1
, tx2
, ty2
);
1342 MultiTexCoord2f(GL_TEXTURE2
, tx2
, ty2
);
1345 glTexCoord2f(tx
, ty
+ th
);
1347 MultiTexCoord2f(GL_TEXTURE1
, tx2
, ty2
+ th2
);
1348 MultiTexCoord2f(GL_TEXTURE2
, tx2
, ty2
+ th2
);
1350 glVertex2f(x
, y
+ h
);
1351 glTexCoord2f(tx
+ tw
, ty
+ th
);
1353 MultiTexCoord2f(GL_TEXTURE1
, tx2
+ tw2
, ty2
+ th2
);
1354 MultiTexCoord2f(GL_TEXTURE2
, tx2
+ tw2
, ty2
+ th2
);
1356 glVertex2f(x
+ w
, y
+ h
);
1357 glTexCoord2f(tx
+ tw
, ty
);
1359 MultiTexCoord2f(GL_TEXTURE1
, tx2
+ tw2
, ty2
);
1360 MultiTexCoord2f(GL_TEXTURE2
, tx2
+ tw2
, ty2
);
1362 glVertex2f(x
+ w
, y
);
1367 #include "w32_common.h"
1369 * \brief little helper since wglGetProcAddress definition does not fit our
1371 * \param procName name of function to look up
1372 * \return function pointer returned by wglGetProcAddress
1374 static void *w32gpa(const GLubyte
*procName
) {
1375 return wglGetProcAddress(procName
);
1378 int setGlWindow(int *vinfo
, HGLRC
*context
, HWND win
)
1381 HDC windc
= GetDC(win
);
1382 HGLRC new_context
= 0;
1383 int keep_context
= 0;
1384 int res
= SET_WINDOW_FAILED
;
1386 // should only be needed when keeping context, but not doing glFinish
1387 // can cause flickering even when we do not keep it.
1390 new_vinfo
= GetPixelFormat(windc
);
1391 if (*context
&& *vinfo
&& new_vinfo
&& *vinfo
== new_vinfo
) {
1392 // we can keep the wglContext
1393 new_context
= *context
;
1397 new_context
= wglCreateContext(windc
);
1399 mp_msg(MSGT_VO
, MSGL_FATAL
, "[gl] Could not create GL context!\n");
1405 if (!wglMakeCurrent(windc
, new_context
)) {
1406 mp_msg (MSGT_VO
, MSGL_FATAL
, "[gl] Could not set GL context!\n");
1407 if (!keep_context
) {
1408 wglDeleteContext(new_context
);
1414 vo_w32_window
= win
;
1417 GetClientRect(win
, &rect
);
1418 vo_dwidth
= rect
.right
;
1419 vo_dheight
= rect
.bottom
;
1421 if (!keep_context
) {
1423 wglDeleteContext(*context
);
1424 *context
= new_context
;
1426 getFunctions(w32gpa
, NULL
);
1428 // and inform that reinit is neccessary
1429 res
= SET_WINDOW_REINIT
;
1431 res
= SET_WINDOW_OK
;
1434 ReleaseDC(win
, windc
);
1438 void releaseGlContext(int *vinfo
, HGLRC
*context
) {
1441 wglMakeCurrent(0, 0);
1442 wglDeleteContext(*context
);
1447 void swapGlBuffers() {
1448 HDC vo_hdc
= GetDC(vo_w32_window
);
1449 SwapBuffers(vo_hdc
);
1450 ReleaseDC(vo_w32_window
, vo_hdc
);
1456 #include "x11_common.h"
1458 * \brief find address of a linked function
1459 * \param s name of function to find
1460 * \return address of function or NULL if not found
1464 static void *getdladdr(const char *s
) {
1466 #if defined(__sun) || defined(__sgi)
1467 static void *handle
= NULL
;
1469 handle
= dlopen(NULL
, RTLD_LAZY
);
1470 return dlsym(handle
, s
);
1480 * \brief Returns the XVisualInfo associated with Window win.
1481 * \param win Window whose XVisualInfo is returne.
1482 * \return XVisualInfo of the window. Caller must use XFree to free it.
1484 static XVisualInfo
*getWindowVisualInfo(Window win
) {
1485 XWindowAttributes xw_attr
;
1486 XVisualInfo vinfo_template
;
1488 XGetWindowAttributes(mDisplay
, win
, &xw_attr
);
1489 vinfo_template
.visualid
= XVisualIDFromVisual(xw_attr
.visual
);
1490 return XGetVisualInfo(mDisplay
, VisualIDMask
, &vinfo_template
, &tmp
);
1494 * \brief Changes the window in which video is displayed.
1495 * If possible only transfers the context to the new window, otherwise
1496 * creates a new one, which must be initialized by the caller.
1497 * \param vinfo Currently used visual.
1498 * \param context Currently used context.
1499 * \param win window that should be used for drawing.
1500 * \return one of SET_WINDOW_FAILED, SET_WINDOW_OK or SET_WINDOW_REINIT.
1501 * In case of SET_WINDOW_REINIT the context could not be transfered
1502 * and the caller must initialize it correctly.
1503 * \ingroup glcontext
1505 int setGlWindow(XVisualInfo
**vinfo
, GLXContext
*context
, Window win
)
1507 XVisualInfo
*new_vinfo
;
1508 GLXContext new_context
= NULL
;
1509 int keep_context
= 0;
1511 // should only be needed when keeping context, but not doing glFinish
1512 // can cause flickering even when we do not keep it.
1515 new_vinfo
= getWindowVisualInfo(win
);
1516 if (*context
&& *vinfo
&& new_vinfo
&&
1517 (*vinfo
)->visualid
== new_vinfo
->visualid
) {
1518 // we can keep the GLXContext
1519 new_context
= *context
;
1525 new_context
= glXCreateContext(mDisplay
, new_vinfo
, NULL
, True
);
1527 mp_msg(MSGT_VO
, MSGL_FATAL
, "[gl] Could not create GLX context!\n");
1529 return SET_WINDOW_FAILED
;
1534 if (!glXMakeCurrent(mDisplay
, vo_window
, new_context
)) {
1535 mp_msg (MSGT_VO
, MSGL_FATAL
, "[gl] Could not set GLX context!\n");
1536 if (!keep_context
) {
1537 glXDestroyContext (mDisplay
, new_context
);
1540 return SET_WINDOW_FAILED
;
1549 XGetGeometry(mDisplay
, vo_window
, &root
, &tmp
, &tmp
,
1550 (unsigned *)&vo_dwidth
, (unsigned *)&vo_dheight
, &utmp
, &utmp
);
1552 if (!keep_context
) {
1553 void *(*getProcAddress
)(const GLubyte
*);
1554 const char *(*glXExtStr
)(Display
*, int);
1556 glXDestroyContext(mDisplay
, *context
);
1557 *context
= new_context
;
1561 getProcAddress
= getdladdr("glXGetProcAddress");
1562 if (!getProcAddress
)
1563 getProcAddress
= getdladdr("glXGetProcAddressARB");
1564 if (!getProcAddress
)
1565 getProcAddress
= (void *)getdladdr
;
1566 glXExtStr
= getdladdr("glXQueryExtensionsString");
1567 getFunctions(getProcAddress
, !glXExtStr
? NULL
:
1568 glXExtStr(mDisplay
, DefaultScreen(mDisplay
)));
1570 // and inform that reinit is neccessary
1571 return SET_WINDOW_REINIT
;
1573 return SET_WINDOW_OK
;
1577 * \brief free the VisualInfo and GLXContext of an OpenGL context.
1578 * \ingroup glcontext
1580 void releaseGlContext(XVisualInfo
**vinfo
, GLXContext
*context
) {
1587 glXMakeCurrent(mDisplay
, None
, NULL
);
1588 glXDestroyContext(mDisplay
, *context
);
1593 void swapGlBuffers(void) {
1594 glXSwapBuffers(mDisplay
, vo_window
);