1 /*****************************************************************************
2 * opengl.c: OpenGL and OpenGL ES output common code
3 *****************************************************************************
4 * Copyright (C) 2004 the VideoLAN team
5 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Cyril Deguet <asmax@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Eric Petit <titer@m0k.org>
10 * Cedric Cocquebert <cedric.cocquebert@supelec.fr>
11 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_picture_pool.h>
33 #include <vlc_subpicture.h>
34 #include <vlc_opengl.h>
37 // Define USE_OPENGL_ES to the GL ES Version you want to select
39 #if !defined (__APPLE__)
40 # if USE_OPENGL_ES == 2
41 # include <GLES2/gl2ext.h>
42 # elif USE_OPENGL_ES == 1
43 # include <GLES/glext.h>
45 # include <GL/glext.h>
48 # if USE_OPENGL_ES == 2
49 # include <OpenGLES/ES2/gl.h>
50 # elif USE_OPENGL_ES == 1
51 # include <OpenGLES/ES1/gl.h>
54 # include <OpenGL/glext.h>
59 #ifndef GL_UNSIGNED_SHORT_5_6_5
60 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
62 #ifndef GL_CLAMP_TO_EDGE
63 # define GL_CLAMP_TO_EDGE 0x812F
67 # define VLCGL_TEXTURE_COUNT 1
68 # define VLCGL_PICTURE_MAX 1
69 #elif defined(MACOS_OPENGL)
70 # define VLCGL_TEXTURE_COUNT 2
71 # define VLCGL_PICTURE_MAX 2
73 # define VLCGL_TEXTURE_COUNT 1
74 # define VLCGL_PICTURE_MAX 128
77 static const vlc_fourcc_t gl_subpicture_chromas[] = {
97 struct vout_display_opengl_t {
101 const vlc_chroma_description_t *chroma;
107 int tex_width[PICTURE_PLANE_MAX];
108 int tex_height[PICTURE_PLANE_MAX];
110 GLuint texture[VLCGL_TEXTURE_COUNT][PICTURE_PLANE_MAX];
116 picture_pool_t *pool;
120 GLfloat local_value[16][4];
122 /* fragment_program */
123 void (*GenProgramsARB)(GLsizei, GLuint *);
124 void (*BindProgramARB)(GLenum, GLuint);
125 void (*ProgramStringARB)(GLenum, GLenum, GLsizei, const GLvoid *);
126 void (*DeleteProgramsARB)(GLsizei, const GLuint *);
127 void (*ProgramLocalParameter4fvARB)(GLenum, GLuint, const GLfloat *);
130 void (*ActiveTextureARB)(GLenum);
131 void (*MultiTexCoord2fARB)(GLenum, GLfloat, GLfloat);
134 static inline int GetAlignedSize(unsigned size)
136 /* Return the smallest larger or equal power of 2 */
137 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
138 return ((align >> 1) == size) ? size : align;
141 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
142 const vlc_fourcc_t **subpicture_chromas,
145 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
150 if (vlc_gl_Lock(vgl->gl)) {
155 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
159 /* Load extensions */
160 bool supports_fp = false;
161 if (strstr(extensions, "GL_ARB_fragment_program")) {
162 vgl->GenProgramsARB = (void (*)(GLsizei, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
163 vgl->BindProgramARB = (void (*)(GLenum, GLuint))vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
164 vgl->ProgramStringARB = (void (*)(GLenum, GLenum, GLsizei, const GLvoid *))vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
165 vgl->DeleteProgramsARB = (void (*)(GLsizei, const GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
166 vgl->ProgramLocalParameter4fvARB = (void (*)(GLenum, GLuint, const GLfloat *))vlc_gl_GetProcAddress(vgl->gl, "glProgramLocalParameter4fvARB");
168 supports_fp = vgl->GenProgramsARB &&
169 vgl->BindProgramARB &&
170 vgl->ProgramStringARB &&
171 vgl->DeleteProgramsARB &&
172 vgl->ProgramLocalParameter4fvARB;
174 bool supports_multitexture = false;
175 if (strstr(extensions, "GL_ARB_multitexture")) {
176 vgl->ActiveTextureARB = (void (*)(GLenum))vlc_gl_GetProcAddress(vgl->gl, "glActiveTextureARB");
177 vgl->MultiTexCoord2fARB = (void (*)(GLenum, GLfloat, GLfloat))vlc_gl_GetProcAddress(vgl->gl, "glMultiTexCoord2fARB");
179 supports_multitexture = vgl->ActiveTextureARB &&
180 vgl->MultiTexCoord2fARB;
183 /* Initialize with default chroma */
186 vgl->fmt.i_chroma = VLC_CODEC_RGB16;
187 # if defined(WORDS_BIGENDIAN)
188 vgl->fmt.i_rmask = 0x001f;
189 vgl->fmt.i_gmask = 0x07e0;
190 vgl->fmt.i_bmask = 0xf800;
192 vgl->fmt.i_rmask = 0xf800;
193 vgl->fmt.i_gmask = 0x07e0;
194 vgl->fmt.i_bmask = 0x001f;
196 vgl->tex_target = GL_TEXTURE_2D;
197 vgl->tex_format = GL_RGB;
198 vgl->tex_type = GL_UNSIGNED_SHORT_5_6_5;
199 #elif defined(MACOS_OPENGL)
200 # if defined(WORDS_BIGENDIAN)
201 vgl->fmt.i_chroma = VLC_CODEC_YUYV;
203 vgl->fmt.i_chroma = VLC_CODEC_UYVY;
205 vgl->tex_target = GL_TEXTURE_RECTANGLE_EXT;
206 vgl->tex_format = GL_YCBCR_422_APPLE;
207 vgl->tex_type = GL_UNSIGNED_SHORT_8_8_APPLE;
209 vgl->fmt.i_chroma = VLC_CODEC_RGB32;
210 # if defined(WORDS_BIGENDIAN)
211 vgl->fmt.i_rmask = 0xff000000;
212 vgl->fmt.i_gmask = 0x00ff0000;
213 vgl->fmt.i_bmask = 0x0000ff00;
215 vgl->fmt.i_rmask = 0x000000ff;
216 vgl->fmt.i_gmask = 0x0000ff00;
217 vgl->fmt.i_bmask = 0x00ff0000;
219 vgl->tex_target = GL_TEXTURE_2D;
220 vgl->tex_format = GL_RGBA;
221 vgl->tex_type = GL_UNSIGNED_BYTE;
223 /* Use YUV if possible and needed */
224 bool need_fs_yuv = false;
225 if (supports_fp && supports_multitexture &&
226 vlc_fourcc_IsYUV(fmt->i_chroma) && !vlc_fourcc_IsYUV(vgl->fmt.i_chroma)) {
227 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
229 const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
230 if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
233 vgl->fmt.i_chroma = *list;
234 vgl->tex_format = GL_LUMINANCE;
235 vgl->tex_type = GL_UNSIGNED_BYTE;
242 vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
244 bool supports_npot = false;
245 #if USE_OPENGL_ES == 2
246 supports_npot = true;
247 #elif defined(MACOS_OPENGL)
248 supports_npot = true;
250 supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
251 strstr(extensions, "GL_ARB_texture_non_power_of_two");
255 * TODO calculate the size such that the pictures can be used as
258 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
259 int w = vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
260 int h = vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
262 vgl->tex_width[j] = w;
263 vgl->tex_height[j] = h;
266 /* A texture must have a size aligned on a power of 2 */
267 vgl->tex_width[j] = GetAlignedSize(w);
268 vgl->tex_height[j] = GetAlignedSize(h);
272 /* Build fragment program if needed */
274 vgl->local_count = 0;
279 /* [R/G/B][Y U V O] from TV range to full range
280 * XXX we could also do hue/brightness/constrast/gamma
281 * by simply changing the coefficients
283 const float matrix_bt601_tv2full[3][4] = {
284 { 1.1640, 0.0000, 1.4030, -0.7773 },
285 { 1.1640, -0.3440, -0.7140, 0.4580 },
286 { 1.1640, 1.7730, 0.0000, -0.9630 },
288 const float matrix_bt709_tv2full[3][4] = {
289 { 1.1640, 0.0000, 1.5701, -0.8612 },
290 { 1.1640, -0.1870, -0.4664, 0.2549 },
291 { 1.1640, 1.8556, 0.0000, -1.0045 },
293 const float (*matrix)[4] = fmt->i_height > 576 ? matrix_bt709_tv2full
294 : matrix_bt601_tv2full;
296 /* Basic linear YUV -> RGB conversion using bilinear interpolation */
297 const char *template_yuv =
299 "OPTION ARB_precision_hint_fastest;"
302 "TEX src.x, fragment.texcoord[0], texture[0], 2D;"
303 "TEX src.%c, fragment.texcoord[1], texture[1], 2D;"
304 "TEX src.%c, fragment.texcoord[2], texture[2], 2D;"
306 "PARAM coefficient[4] = { program.local[0..3] };"
309 "MAD tmp.rgb, src.xxxx, coefficient[0], coefficient[3];"
310 "MAD tmp.rgb, src.yyyy, coefficient[1], tmp;"
311 "MAD result.color.rgb, src.zzzz, coefficient[2], tmp;"
313 bool swap_uv = vgl->fmt.i_chroma == VLC_CODEC_YV12 ||
314 vgl->fmt.i_chroma == VLC_CODEC_YV9;
315 if (asprintf(&code, template_yuv,
317 swap_uv ? 'y' : 'z') < 0)
320 for (int i = 0; i < 4; i++)
321 for (int j = 0; j < 4; j++)
322 vgl->local_value[vgl->local_count + i][j] = j < 3 ? matrix[j][i] : 0.0;
323 vgl->local_count += 4;
326 vgl->GenProgramsARB(1, &vgl->program);
327 vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
328 vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
329 GL_PROGRAM_FORMAT_ASCII_ARB,
330 strlen(code), (const GLbyte*)code);
331 if (glGetError() == GL_INVALID_OPERATION) {
332 /* FIXME if the program was needed for YUV, the video will be broken */
335 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);
337 const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
338 fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
340 vgl->DeleteProgramsARB(1, &vgl->program);
349 glDisable(GL_DEPTH_TEST);
350 glDepthMask(GL_FALSE);
351 glDisable(GL_CULL_FACE);
352 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
353 glClear(GL_COLOR_BUFFER_BIT);
355 vlc_gl_Unlock(vgl->gl);
358 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
359 for (int j = 0; j < PICTURE_PLANE_MAX; j++)
360 vgl->texture[i][j] = 0;
362 vgl->region_count = 0;
367 if (subpicture_chromas) {
368 *subpicture_chromas = NULL;
369 #if !defined(MACOS_OPENGL) && !USE_OPENGL_ES
371 *subpicture_chromas = gl_subpicture_chromas;
377 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
380 if (!vlc_gl_Lock(vgl->gl)) {
384 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
385 glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
388 vgl->DeleteProgramsARB(1, &vgl->program);
390 vlc_gl_Unlock(vgl->gl);
393 picture_pool_Delete(vgl->pool);
398 struct picture_sys_t {
399 vout_display_opengl_t *vgl;
404 static inline GLuint PictureGetTexture(picture_t *picture)
406 return *picture->p_sys->texture;
409 static int PictureLock(picture_t *picture)
414 vout_display_opengl_t *vgl = picture->p_sys->vgl;
415 if (!vlc_gl_Lock(vgl->gl)) {
416 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
417 glTexSubImage2D(vgl->tex_target, 0,
418 0, 0, vgl->fmt.i_width, vgl->fmt.i_height,
419 vgl->tex_format, vgl->tex_type, picture->p[0].p_pixels);
421 vlc_gl_Unlock(vgl->gl);
426 static void PictureUnlock(picture_t *picture)
432 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
437 /* Allocate our pictures */
438 picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
441 for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
442 picture[count] = picture_NewFromFormat(&vgl->fmt);
447 picture_sys_t *sys = picture[count]->p_sys = malloc(sizeof(*sys));
450 sys->texture = vgl->texture[count];
457 /* Wrap the pictures into a pool */
458 picture_pool_configuration_t cfg;
459 memset(&cfg, 0, sizeof(cfg));
460 cfg.picture_count = count;
461 cfg.picture = picture;
463 cfg.lock = PictureLock;
464 cfg.unlock = PictureUnlock;
466 vgl->pool = picture_pool_NewExtended(&cfg);
470 /* Allocates our textures */
471 if (vlc_gl_Lock(vgl->gl))
474 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
475 glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
476 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
477 if (vgl->chroma->plane_count > 1)
478 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
479 glBindTexture(vgl->tex_target, vgl->texture[i][j]);
482 /* Set the texture parameters */
483 glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
484 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
487 glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
488 glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
489 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
490 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
493 /* Tell the driver not to make a copy of the texture but to use
495 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
496 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
499 /* Use VRAM texturing */
500 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
501 GL_STORAGE_CACHED_APPLE);
503 /* Use AGP texturing */
504 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
505 GL_STORAGE_SHARED_APPLE);
509 /* Call glTexImage2D only once, and use glTexSubImage2D later */
510 glTexImage2D(vgl->tex_target, 0,
511 vgl->tex_format, vgl->tex_width[j], vgl->tex_height[j],
512 0, vgl->tex_format, vgl->tex_type, NULL);
516 vlc_gl_Unlock(vgl->gl);
521 for (unsigned i = 0; i < count; i++)
522 picture_Delete(picture[i]);
526 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
527 picture_t *picture, subpicture_t *subpicture)
529 /* On Win32/GLX, we do this the usual way:
530 + Fill the buffer with new content,
531 + Reload the texture,
534 On OS X with VRAM or AGP texturing, the order has to be:
535 + Reload the texture,
536 + Fill the buffer with new content,
539 (Thanks to gcc from the Arstechnica forums for the tip)
541 Therefore on OSX, we have to use two buffers and textures and use a
542 lock(/unlock) managed picture pool.
545 if (vlc_gl_Lock(vgl->gl))
549 /* Bind to the texture for drawing */
550 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
552 /* Update the texture */
553 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
554 if (vgl->chroma->plane_count > 1)
555 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
556 glBindTexture(vgl->tex_target, vgl->texture[0][j]);
557 glPixelStorei(GL_UNPACK_ROW_LENGTH, picture->p[j].i_pitch / picture->p[j].i_pixel_pitch);
558 glTexSubImage2D(vgl->tex_target, 0,
560 vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den,
561 vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den,
562 vgl->tex_format, vgl->tex_type, picture->p[j].p_pixels);
566 for (int i = 0; i < vgl->region_count; i++) {
567 glDeleteTextures(1, &vgl->region[i].texture);
570 vgl->region_count = 0;
576 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
579 vgl->region_count = count;
580 vgl->region = calloc(count, sizeof(*vgl->region));
582 if (vgl->chroma->plane_count > 1)
583 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + 0);
585 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
586 gl_region_t *glr = &vgl->region[i];
588 glr->format = GL_RGBA;
589 glr->type = GL_UNSIGNED_BYTE;
590 glr->width = r->fmt.i_visible_width;
591 glr->height = r->fmt.i_visible_height;
592 glr->alpha = (float)subpicture->i_alpha * r->i_alpha / 255 / 255;
593 glr->left = 2.0 * (r->i_x ) / subpicture->i_original_picture_width - 1.0;
594 glr->top = -2.0 * (r->i_y ) / subpicture->i_original_picture_height + 1.0;
595 glr->right = 2.0 * (r->i_x + r->fmt.i_visible_width ) / subpicture->i_original_picture_width - 1.0;
596 glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;
598 glGenTextures(1, &glr->texture);
599 glBindTexture(GL_TEXTURE_2D, glr->texture);
600 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 1.0);
601 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
602 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
603 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
604 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
605 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
606 /* TODO set GL_UNPACK_ALIGNMENT */
607 glPixelStorei(GL_UNPACK_ROW_LENGTH, r->p_picture->p->i_pitch / r->p_picture->p->i_pixel_pitch);
608 glTexImage2D(GL_TEXTURE_2D, 0, glr->format,
609 glr->width, glr->height, 0, glr->format, glr->type,
610 r->p_picture->p->p_pixels);
614 vlc_gl_Unlock(vgl->gl);
615 VLC_UNUSED(subpicture);
619 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
620 const video_format_t *source)
622 if (vlc_gl_Lock(vgl->gl))
625 /* glTexCoord works differently with GL_TEXTURE_2D and
626 GL_TEXTURE_RECTANGLE_EXT */
627 float left[PICTURE_PLANE_MAX];
628 float top[PICTURE_PLANE_MAX];
629 float right[PICTURE_PLANE_MAX];
630 float bottom[PICTURE_PLANE_MAX];
631 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
632 float scale_w, scale_h;
633 if (vgl->tex_target == GL_TEXTURE_2D) {
634 scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
635 scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
641 left[j] = (source->i_x_offset + 0 ) * scale_w;
642 top[j] = (source->i_y_offset + 0 ) * scale_h;
643 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
644 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
648 /* Why drawing here and not in Render()? Because this way, the
649 OpenGL providers can call vout_display_opengl_Display to force redraw.i
650 Currently, the OS X provider uses it to get a smooth window resizing */
652 glClear(GL_COLOR_BUFFER_BIT);
655 glEnable(GL_FRAGMENT_PROGRAM_ARB);
656 for (int i = 0; i < vgl->local_count; i++)
657 vgl->ProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, i, vgl->local_value[i]);
659 glEnable(vgl->tex_target);
663 static const GLfloat vertexCoord[] = {
670 const GLfloat textureCoord[8] = {
677 glEnableClientState(GL_VERTEX_ARRAY);
678 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
679 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
680 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
682 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
684 #if !defined(MACOS_OPENGL)
685 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
686 if (vgl->chroma->plane_count > 1)
687 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
688 glBindTexture(vgl->tex_target, vgl->texture[0][j]);
693 glTexCoord2f(left[0], top[0]);
694 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
695 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], top[j]);
696 glVertex2f(-1.0, 1.0);
698 glTexCoord2f(right[0], top[0]);
699 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
700 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], top[j]);
701 glVertex2f( 1.0, 1.0);
703 glTexCoord2f(right[0], bottom[0]);
704 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
705 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], bottom[j]);
706 glVertex2f( 1.0, -1.0);
708 glTexCoord2f(left[0], bottom[0]);
709 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
710 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], bottom[j]);
711 glVertex2f(-1.0, -1.0);
717 glDisable(GL_FRAGMENT_PROGRAM_ARB);
719 glDisable(vgl->tex_target);
722 if (vgl->chroma->plane_count > 1)
723 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + 0);
724 glEnable(GL_TEXTURE_2D);
726 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
727 for (int i = 0; i < vgl->region_count; i++) {
728 gl_region_t *glr = &vgl->region[i];
730 glBindTexture(GL_TEXTURE_2D, glr->texture);
734 glColor4f(1.0, 1.0, 1.0, glr->alpha);
736 glTexCoord2f(0.0, 0.0);
737 glVertex2f(glr->left, glr->top);
739 glTexCoord2f(1.0, 0.0);
740 glVertex2f(glr->right, glr->top);
742 glTexCoord2f(1.0, 1.0);
743 glVertex2f(glr->right, glr->bottom);
745 glTexCoord2f(0.0, 1.0);
746 glVertex2f(glr->left, glr->bottom);
751 glDisable(GL_TEXTURE_2D);
754 vlc_gl_Swap(vgl->gl);
756 vlc_gl_Unlock(vgl->gl);