render based on a variable number of vertices per window (for shaped windows in the...
[dana/dcompmgr.git] / glxrender.c
1 #include "efence.h"
2
3 #include "render.h"
4 #include "screen.h"
5 #include "window.h"
6 #include "display.h"
7 #include "list.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <stdlib.h>
12
13 #include <GL/gl.h>
14 #include <GL/glx.h>
15 #include <GL/glxtokens.h>
16 #include <GL/glext.h>
17 #include <GL/glxext.h>
18
19 #define MAX_DEPTH 32
20
21 static int plugin_id;
22
23 #define LEFT   0
24 #define TOP    1
25 #define RIGHT  2
26 #define BOTTOM 3
27
28 typedef void (*BindEXTFunc)(Display *, GLXDrawable, int, const int *);
29 typedef void (*ReleaseEXTFunc)(Display *, GLXDrawable, int);
30
31 typedef struct {
32     void (*screen_paint)(d_screen_t *sc);
33     void (*screen_root_pixmap_change)(d_screen_t *sc);
34     void (*window_show)(d_window_t *w);
35     void (*window_zombie_dead)(d_window_t *w);
36     void (*window_resize)(d_window_t *w);
37     void (*window_reshape)(d_window_t *w);
38
39     float shadowalpha;
40     int xshadowoff;
41     int yshadowoff;
42
43     GLXFBConfig fbconfig[MAX_DEPTH + 1];
44
45     GLXContext context;
46     GLuint root_texname;
47     GLXPixmap root_glpixmap;
48
49     BindEXTFunc bind_func;
50     ReleaseEXTFunc release_func;
51 } data_t;
52
53 typedef struct {
54     GLuint texname;
55     GLXPixmap glpixmap;
56
57     GLfloat *texcoords;
58     GLint *vertices;
59     int nrects;
60 } window_data_t;
61
62 static gboolean glxrender_find_fb_config(d_screen_t *sc, data_t *d);
63
64 static void glxrender_paint(d_screen_t *sc);
65 static void glxrender_root_pixmap_change(d_screen_t *sc);
66 static void paint_root(d_screen_t *sc, data_t *d);
67 static void paint_window(d_window_t *window, data_t *d, window_data_t *wd,
68                          gboolean opaque, int x, int y);
69 static void paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
70                          int x, int y);
71 static void glxrender_update_window_pixmap(d_window_t *w, data_t *d,
72                                            window_data_t *wd);
73 static void glxrender_free_window_pixmap(d_window_t *w, data_t *d,
74                                          window_data_t *wd);
75 static void glxrender_free_root_pixmap(d_screen_t *sc, data_t *d);
76 static void glxrender_update_root_pixmap(d_screen_t *sc, data_t *d);
77
78 static void glxrender_window_show(d_window_t *window);
79 static void glxrender_window_zombie_dead(d_window_t *window);
80 static void glxrender_window_resize(d_window_t *window);
81 static void glxrender_window_reshape(d_window_t *window);
82
83 void
84 glxrender_init(d_screen_t *sc, int id)
85 {
86     static int context_visual_config[] = {
87         GLX_DEPTH_SIZE, 1,
88         GLX_DOUBLEBUFFER,
89         GLX_RGBA,
90         XCB_NONE
91     };
92     XVisualInfo *vi;
93     data_t *d;
94
95     plugin_id = id;
96
97     d = malloc(sizeof(data_t));
98     d->screen_paint = sc->screen_paint;
99     d->screen_root_pixmap_change = sc->screen_root_pixmap_change;
100     d->window_show = sc->window_show;
101     d->window_zombie_dead = sc->window_zombie_dead;
102     d->window_resize = sc->window_resize;
103     d->window_reshape = sc->window_reshape;
104     screen_add_plugin_data(sc, plugin_id, d);
105
106     sc->screen_paint = glxrender_paint;
107     sc->screen_root_pixmap_change = glxrender_root_pixmap_change;
108     sc->window_show = glxrender_window_show;
109     sc->window_zombie_dead = glxrender_window_zombie_dead;
110     sc->window_resize = glxrender_window_resize;
111     sc->window_reshape = glxrender_window_reshape;
112
113     d->shadowalpha = 0.2f; /* 20% */
114     d->xshadowoff = 2;
115     d->yshadowoff = 2;
116
117     vi = glXChooseVisual(sc->dpy->xlib_dpy, sc->num, context_visual_config);
118     if (!vi) {
119         printf("unable to find a valid double buffered GL context to use\n");
120         exit(1);
121     }
122
123     d->context = glXCreateContext(sc->dpy->xlib_dpy, vi, NULL, GL_TRUE);
124     glXMakeCurrent(sc->dpy->xlib_dpy, sc->overlay, d->context);
125
126
127     if (!(glxrender_find_fb_config(sc, d))) {
128         printf("unable to find FB configs\n");
129         exit(1);
130     }
131
132     glViewport(0, 0, sc->super->width_in_pixels, sc->super->height_in_pixels);
133     glMatrixMode(GL_PROJECTION);
134     glLoadIdentity();
135     glOrtho(0, sc->super->width_in_pixels, sc->super->height_in_pixels, 0,
136             -100.0, 100.0);
137     glMatrixMode(GL_MODELVIEW);
138     glLoadIdentity();
139     glEnable(GL_TEXTURE_2D);
140
141     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
142     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
143     glDisable(GL_BLEND);
144
145     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
146     glClear(GL_COLOR_BUFFER_BIT);
147     glXSwapBuffers(sc->dpy->xlib_dpy, sc->overlay);
148
149     d->bind_func = (BindEXTFunc)
150         glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
151     d->release_func = (ReleaseEXTFunc)
152         glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
153
154     glGenTextures(1, &d->root_texname);
155     d->root_glpixmap = XCB_NONE;
156 }
157
158 static gboolean
159 glxrender_find_fb_config(d_screen_t *sc, data_t *d)
160 {
161     static const int drawable_tfp_attrs[] = {
162         GLX_CONFIG_CAVEAT, GLX_NONE,
163         GLX_DOUBLEBUFFER, FALSE,
164         GLX_DEPTH_SIZE, 0,
165         GLX_RED_SIZE, 1,
166         GLX_GREEN_SIZE, 1,
167         GLX_BLUE_SIZE, 1,
168         GLX_ALPHA_SIZE, 1,
169         GLX_RENDER_TYPE, GLX_RGBA_BIT,
170         GLX_BIND_TO_TEXTURE_RGBA_EXT, TRUE, /* For TextureFromPixmap */
171         XCB_NONE
172     };
173     int db, stencil, depth, numfb, i;
174     GLXFBConfig *fbcons;
175     XVisualInfo tvis, *visinfo;
176
177     fbcons = glXChooseFBConfig(sc->dpy->xlib_dpy, sc->num,
178                                drawable_tfp_attrs, &numfb);
179     if (!fbcons) return FALSE;
180
181     for (i = 0; i <= MAX_DEPTH; i++) {
182         int j, count, value;
183         VisualID vid;
184
185         vid = 0;
186         d->fbconfig[i] = 0;
187         db = 32767;
188         stencil = 32767;
189         depth = 32767;
190
191         printf("looking for depth %d\n", i);
192
193         tvis.depth = i;
194         visinfo = XGetVisualInfo(sc->dpy->xlib_dpy, VisualDepthMask,
195                                  &tvis, &count);
196         /* pick the nicest visual for the depth */
197         for (j = 0; j < count; j++) {
198             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_USE_GL, &value);
199             if (!value)
200                 continue;
201
202             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_DOUBLEBUFFER,
203                          &value);
204             if (value > db)
205                 continue;
206             db = value;
207
208             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_STENCIL_SIZE,
209                          &value);
210             if (value > stencil)
211                 continue;
212             stencil = value;
213
214             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_DEPTH_SIZE,
215                          &value);
216             if (value > depth)
217                 continue;
218             depth = value;
219
220             /* use this visual */
221             vid = visinfo[j].visualid;
222         }
223
224         if (!vid) continue;
225
226         /* look for an fbconfig for this visual */
227         for (j = 0; j < numfb; ++j) {
228             glXGetFBConfigAttrib(sc->dpy->xlib_dpy, fbcons[j],
229                                  GLX_VISUAL_ID, &value);
230             if ((unsigned)value == vid) {
231                 d->fbconfig[i] = fbcons[j];
232
233                 printf("found visual 0x%x fbconfig 0x%x for depth %d\n",
234                        (uint32_t)vid, (uint32_t)fbcons[j], i);
235                 break;
236             }
237         }
238     }
239
240     XFree(fbcons);
241     return TRUE;
242 }
243
244 void
245 glxrender_free(d_screen_t *sc)
246 {
247     data_t *d = screen_find_plugin_data(sc, plugin_id);
248     glxrender_free_root_pixmap(sc, d);
249     free(d);
250     screen_remove_plugin_data(sc, plugin_id);
251 }
252
253 int
254 glxrender_next_timeout(struct d_screen *sc, struct timeval *tv)
255 {
256     (void)sc;
257     (void)tv;
258     return FALSE;
259 }
260
261 void
262 glxrender_timeout(struct d_screen *sc, const struct timeval *now)
263 {
264     (void)sc; (void)now;
265 }
266
267 void
268 glxrender_window_free_data(d_window_t *w, data_t *d, window_data_t *wd)
269 {
270     glxrender_free_window_pixmap(w, d, wd);
271     glDeleteTextures(1, &wd->texname);
272     free(wd);
273 }
274
275 static void
276 glxrender_window_show(d_window_t *w)
277 {
278     data_t *d;
279     window_data_t *wd;
280
281     d = screen_find_plugin_data(w->sc, plugin_id);
282
283     /* pass it on */
284     d->window_show(w);
285
286     wd = window_find_plugin_data(w, plugin_id);
287     if (wd)
288         glxrender_window_free_data(w, d, wd);
289    
290     wd = malloc(sizeof(window_data_t));
291     glGenTextures(1, &wd->texname);
292     wd->glpixmap = XCB_NONE;
293     wd->nrects = 0;
294
295     window_add_plugin_data(w, plugin_id, wd);
296
297     glxrender_update_window_pixmap(w, d, wd);
298 }
299
300 static void
301 glxrender_window_zombie_dead(d_window_t *w)
302 {
303     data_t *d;
304     window_data_t *wd;
305
306     d = screen_find_plugin_data(w->sc, plugin_id);
307     wd = window_find_plugin_data(w, plugin_id);
308     if (wd) {
309         glxrender_window_free_data(w, d, wd);
310         window_remove_plugin_data(w, plugin_id);
311     }
312
313     /* pass it on */
314     d->window_zombie_dead(w);
315 }
316
317 static void
318 glxrender_free_window_pixmap(d_window_t *w, data_t *d, window_data_t *wd)
319 {
320     /* this might cause an error, oh well */
321     if (wd->glpixmap) {
322         glBindTexture(GL_TEXTURE_2D, wd->texname);
323         d->release_func(w->sc->dpy->xlib_dpy,
324                         wd->glpixmap, GLX_FRONT_LEFT_EXT);
325         glBindTexture(GL_TEXTURE_2D, 0);
326
327         //xcb_glx_destroy_pixmap(w->sc->dpy->conn, wd->glpixmap);
328         glXDestroyPixmap(w->sc->dpy->xlib_dpy, wd->glpixmap);
329         wd->glpixmap = XCB_NONE;
330
331         free(wd->texcoords);
332         free(wd->vertices);
333         wd->nrects = 0;
334     }
335 }
336
337 static void
338 glxrender_free_root_pixmap(d_screen_t *sc, data_t *d)
339 {
340     /* this might cause an error, oh well */
341     if (d->root_glpixmap) {
342         glBindTexture(GL_TEXTURE_2D, d->root_texname);
343         d->release_func(sc->dpy->xlib_dpy,
344                         d->root_glpixmap, GLX_FRONT_LEFT_EXT);
345         glBindTexture(GL_TEXTURE_2D, 0);
346
347         //xcb_glx_destroy_pixmap(w->sc->dpy->conn, wd->glpixmap);
348         glXDestroyPixmap(sc->dpy->xlib_dpy, d->root_glpixmap);
349         d->root_glpixmap = XCB_NONE;
350     }
351 }
352
353 static void
354 glxrender_update_window_pixmap(d_window_t *w, data_t *d, window_data_t *wd)
355 {
356     xcb_pixmap_t px;
357     uint8_t depth;
358
359     static int attrs[] = {
360         GLX_TEXTURE_FORMAT_EXT,
361         XCB_NONE,
362         XCB_NONE
363     };
364
365     px = window_get_pixmap(w);
366     depth = window_get_depth(w);
367
368     if (!px) return;
369
370     if (!d->fbconfig[depth]) {
371         printf("no GL visual for depth %d\n", depth);
372         return;
373     }
374
375     if (window_is_argb(w))
376         attrs[1] = GLX_TEXTURE_FORMAT_RGBA_EXT;
377     else
378         attrs[1] = GLX_TEXTURE_FORMAT_RGB_EXT;
379
380     wd->glpixmap = glXCreatePixmap(w->sc->dpy->xlib_dpy,
381                                    d->fbconfig[depth],
382                                    px, attrs);
383
384
385     glBindTexture(GL_TEXTURE_2D, wd->texname);
386     d->bind_func(w->sc->dpy->xlib_dpy,
387                  wd->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
388
389     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
390     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
391
392     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
393     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
394
395     glBindTexture(GL_TEXTURE_2D, 0);
396
397     {
398         int x, y, width, height, bwidth;
399
400         window_get_area(w, &x, &y, &width, &height, &bwidth);
401
402         wd->texcoords = (GLfloat*)malloc(sizeof(GLfloat) * 4);
403         wd->texcoords[LEFT] = 0.0f;
404         wd->texcoords[TOP] = 0.0f;
405         wd->texcoords[RIGHT] = 1.0f;
406         wd->texcoords[BOTTOM] = 1.0f;
407
408         wd->vertices = (GLint*)malloc(sizeof(GLint) * 4);
409         wd->vertices[LEFT] = 0;
410         wd->vertices[TOP] = 0;
411         wd->vertices[RIGHT] = width + bwidth;
412         wd->vertices[BOTTOM] = height + bwidth;
413
414         wd->nrects = 1;
415     }
416 }
417
418 static void
419 glxrender_update_root_pixmap(d_screen_t *sc, data_t *d)
420 {
421     xcb_pixmap_t px;
422     static int attrs[] = {
423         GLX_TEXTURE_FORMAT_EXT,
424         GLX_TEXTURE_FORMAT_RGB_EXT,
425         XCB_NONE
426     };
427
428     px = screen_get_root_pixmap(sc);
429     if (!px) return;
430
431     if (!d->fbconfig[sc->super->root_depth]) {
432         printf("no GL visual for depth %d\n", sc->super->root_depth);
433         return;
434     }
435
436     d->root_glpixmap = glXCreatePixmap(sc->dpy->xlib_dpy,
437                                        d->fbconfig[sc->super->root_depth],
438                                        px, attrs);
439
440
441     glBindTexture(GL_TEXTURE_2D, d->root_texname);
442     d->bind_func(sc->dpy->xlib_dpy,
443                  d->root_glpixmap, GLX_FRONT_LEFT_EXT, NULL);
444
445     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
446     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
447
448     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
449     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
450
451     glBindTexture(GL_TEXTURE_2D, 0);
452 }
453
454 static void
455 glxrender_window_resize(d_window_t *w)
456 {
457     data_t *d;
458     window_data_t *wd;
459
460     d = screen_find_plugin_data(w->sc, plugin_id);
461     wd = window_find_plugin_data(w, plugin_id);
462
463     /* pass it on */
464     d->window_resize(w);
465
466     assert(wd != NULL);
467     glxrender_free_window_pixmap(w, d, wd);
468     glxrender_update_window_pixmap(w, d, wd);
469 }
470
471 static void
472 glxrender_window_reshape(d_window_t *w)
473 {
474     data_t *d;
475     window_data_t *wd;
476
477     d = screen_find_plugin_data(w->sc, plugin_id);
478     wd = window_find_plugin_data(w, plugin_id);
479
480     /* pass it on */
481     d->window_reshape(w);
482
483     assert(wd != NULL);
484     glxrender_free_window_pixmap(w, d, wd);
485     glxrender_update_window_pixmap(w, d, wd);
486 }
487
488 static void
489 glxrender_root_pixmap_change(d_screen_t *sc)
490 {
491     data_t *d;
492
493     d = screen_find_plugin_data(sc, plugin_id);
494     glxrender_free_root_pixmap(sc, d);
495
496     /* pass it on */
497     d->screen_root_pixmap_change(sc);
498 }
499
500 static void
501 glxrender_paint(d_screen_t *sc)
502 {
503     data_t *d = screen_find_plugin_data(sc, plugin_id);
504     d_list_it_t *it;
505
506     //printf("painting\n");
507
508     paint_root(sc, d);
509
510     for (it = list_bottom(sc->stacking); it; it = it->prev) {
511         d_window_t *w = it->data;
512
513         if (!window_is_input_only(w) &&
514             (window_is_mapped(w) || window_is_zombie(w)))
515         {
516             int x, y, width, height, bwidth;
517             gboolean opaque;
518             window_data_t *wd;
519
520             window_get_area(w, &x, &y, &width, &height, &bwidth);
521
522             if (!(x < sc->super->width_in_pixels &&
523                   y < sc->super->height_in_pixels &&
524                   (x + width > 0 || x + width + d->xshadowoff > 0) &&
525                   (y + height > 0 || y + height + d->yshadowoff > 0)))
526             {
527                 continue;
528             }
529
530             opaque = !window_is_argb(w) && window_get_opacity(w) == 0xffff;
531
532             wd = window_find_plugin_data(w, plugin_id);
533
534             glPushMatrix();
535
536             glTranslatef(d->xshadowoff, d->yshadowoff, 0.0f);
537             paint_shadow(w, d, wd, x, y);
538
539             glTranslatef(-d->xshadowoff, -d->yshadowoff, 0.0f);
540             paint_window(w, d, wd, opaque, x, y);
541
542             glPopMatrix();
543         }
544     }
545
546     //xcb_glx_swap_buffers(sc->dpy->conn, d->context_tag, sc->overlay);
547     glXSwapBuffers(sc->dpy->xlib_dpy, sc->overlay);
548
549     /* call the function we replaced in the chain */
550     d->screen_paint(sc);
551 }
552
553 static void
554 paint_root(d_screen_t *sc, data_t *d)
555 {
556     if (!d->root_glpixmap)
557         glxrender_update_root_pixmap(sc, d);
558
559     glClear(GL_COLOR_BUFFER_BIT);
560
561     glBindTexture(GL_TEXTURE_2D, d->root_texname);
562     glBegin(GL_QUADS);
563     glTexCoord2f(0, 0);
564     glVertex2i(0, 0);
565     glTexCoord2f(1, 0);
566     glVertex2i(sc->super->width_in_pixels, 0);
567     glTexCoord2f(1, 1);
568     glVertex2i(sc->super->width_in_pixels, sc->super->height_in_pixels);
569     glTexCoord2f(0, 1);
570     glVertex2i(0, sc->super->height_in_pixels);
571     glEnd();
572
573     glBindTexture(GL_TEXTURE_2D, 0);
574 }
575
576 static void
577 paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque,
578              int x, int y)
579 {
580     uint16_t o = window_get_opacity(w);
581     int i;
582
583     if (!wd->glpixmap)
584         glxrender_update_window_pixmap(w, d, wd);
585
586     if (wd->nrects < 1) return;
587
588     glBindTexture(GL_TEXTURE_2D, wd->texname);
589
590     if (!opaque) {
591         glEnable(GL_BLEND);
592
593         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
594         glColor4us(o, o, o, o);
595     }
596
597     glBegin(GL_QUADS);
598     for (i = 0; i < wd->nrects * 4; i += 4) {
599         // XXX use glVertex3i
600         glTexCoord2f(wd->texcoords[i+LEFT], wd->texcoords[i+TOP]);
601         glVertex2i(x + wd->vertices[i+LEFT], y + wd->vertices[i+TOP]);
602         glTexCoord2f(wd->texcoords[i+RIGHT], wd->texcoords[i+TOP]);
603         glVertex2i(x + wd->vertices[i+RIGHT], y + wd->vertices[i+TOP]);
604         glTexCoord2f(wd->texcoords[i+RIGHT], wd->texcoords[i+BOTTOM]);
605         glVertex2i(x + wd->vertices[i+RIGHT], y + wd->vertices[i+BOTTOM]);
606         glTexCoord2f(wd->texcoords[i+LEFT], wd->texcoords[i+BOTTOM]);
607         glVertex2i(x + wd->vertices[i+LEFT], y + wd->vertices[i+BOTTOM]);
608     }
609     glEnd();
610
611     glBindTexture(GL_TEXTURE_2D, 0);
612
613     if (!opaque) {
614         glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
615         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
616         glDisable(GL_BLEND);
617     }
618 }
619
620 static void
621 paint_shadow(d_window_t *w, data_t *d, window_data_t *wd, int x, int y)
622 {
623     float alpha = d->shadowalpha;
624     int i;
625
626     if (wd->nrects < 1) return;
627
628     alpha *= window_get_opacity(w);
629     alpha /= 0xffff;
630
631     if (alpha < 0.01) return;
632
633     glEnable(GL_BLEND);
634     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
635     /* black shadow */
636     glColor4f(0.0f, 0.0f, 0.0f, alpha);
637
638     /* shape the shadow to the window */
639     glBindTexture(GL_TEXTURE_2D, wd->texname);
640
641     glBegin(GL_QUADS);
642     for (i = 0; i < wd->nrects * 4; i += 4) {
643         glTexCoord2f(wd->texcoords[i+LEFT], wd->texcoords[i+TOP]);
644         glVertex2i(x + wd->vertices[i+LEFT], y + wd->vertices[i+TOP]);
645         glTexCoord2f(wd->texcoords[i+RIGHT], wd->texcoords[i+TOP]);
646         glVertex2i(x + wd->vertices[i+RIGHT], y + wd->vertices[i+TOP]);
647         glTexCoord2f(wd->texcoords[i+RIGHT], wd->texcoords[i+BOTTOM]);
648         glVertex2i(x + wd->vertices[i+RIGHT], y + wd->vertices[i+BOTTOM]);
649         glTexCoord2f(wd->texcoords[i+LEFT], wd->texcoords[i+BOTTOM]);
650         glVertex2i(x + wd->vertices[i+LEFT], y + wd->vertices[i+BOTTOM]);
651     }
652     glEnd();
653
654     glBindTexture(GL_TEXTURE_2D, 0);
655
656     glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
657     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
658     glDisable(GL_BLEND);
659 }