1 /*
2  * OpenBOR - http://www.LavaLit.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2011 OpenBOR Team
7  */
8 
9 /************************ OpenGL video backend *************************/
10 /*
11  * New video backend that uses OpenGL as a cross-platform API for optimized
12  * hardware blitting and scaling.  Supports both OpenGL 1.2+ and OpenGL ES 1.x.
13 
14  * It also offers better fullscreen than software SDL because the picture is
15  * guaranteed to display at the correct aspect ratio and not have black bars on
16  * all 4 sides.
17  */
18 
19 #include <SDL.h>
20 #include <SDL_framerate.h>
21 #include <math.h>
22 #include "openbor.h"
23 #include "opengl.h"
24 #include "video.h"
25 #include "sdlport.h"
26 #include "loadgl.h"
27 
28 #define nextpowerof2(x) pow(2,ceil(log(x)/log(2)))
29 #define abs(x)			((x<0)?-(x):(x))
30 
31 static SDL_Surface* screen = NULL;
32 static SDL_Surface* bscreen = NULL; // FIXME: unnecessary SDL dependency; this should be an s_screen
33 
34 static int textureDepths[4] = {16,16,24,32};
35 static const char* glExtensions;
36 
37 static float ycompress = 1.0;
38 
39 static GLushort glpalette[256];
40 static GLuint gltexture;
41 
42 static GLint textureTarget;
43 static GLint textureInternalColorFormat;
44 static GLint textureColorFormat;
45 static GLint texturePixelFormat;
46 
47 static int viewportWidth, viewportHeight;      // dimensions of display area
48 static int scaledWidth, scaledHeight;          // dimensions of game screen after scaling
49 static int textureWidth, textureHeight;        // dimensions of game screen and GL texture
50 static int xOffset, yOffset;                   // offset of game screen on display area
51 static int bytesPerPixel;
52 
53 #ifdef GLES
54 static GLfixed tcx, tcy; // maximum x and y texture coords in fixed-point form
55 #else
56 static GLfloat tcx, tcy; // maximum x and y texture coords in floating-point form
57 #endif
58 
59 // use some variables declared in video.c that are common to both backends
60 extern s_videomodes stored_videomodes;
61 extern FPSmanager framerate_manager;
62 extern int stretch;
63 extern int nativeWidth, nativeHeight;
64 
65 // Macros for compatibility between OpenGL ES and normal OpenGL
66 #ifdef GLES
67 #define glOrtho(l,r,b,t,n,f) glOrthox((l)<<16,(r)<<16,(b)<<16,(t)<<16,(n)<<16,(f)<<16)
68 #define glTexParameter glTexParameterx
69 #define BOR_UNSIGNED_SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5
70 #define BOR_CLAMP GL_CLAMP_TO_EDGE
71 #else
72 #define glTexParameter glTexParameteri
73 #define BOR_UNSIGNED_SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5_REV
74 #define BOR_CLAMP GL_CLAMP
75 #endif
76 
77 // calculates scale factors and offsets based on viewport and texture sizes
video_gl_setup_screen()78 void video_gl_setup_screen()
79 {
80 	// set up the viewport
81 	glViewport(0, 0, viewportWidth, viewportHeight);
82 
83 	// set up orthographic projection
84 	glMatrixMode(GL_PROJECTION);
85 	glLoadIdentity();
86 	glOrtho(0, viewportWidth-1, 0, viewportHeight-1, -1, 1);
87 
88 	// reset the model view
89 	glMatrixMode(GL_MODELVIEW);
90 	glLoadIdentity();
91 }
92 
93 // initializes the OpenGL texture for the game screen
video_gl_init_textures()94 void video_gl_init_textures()
95 {
96 	int allocTextureWidth, allocTextureHeight;
97 	bool npotTexturesSupported;
98 
99 	// set texture target, format, etc.
100 	textureTarget = GL_TEXTURE_2D;
101 	textureColorFormat = (bytesPerPixel == 4) ? GL_RGBA : GL_RGB;
102 	textureInternalColorFormat = textureColorFormat;
103 	texturePixelFormat = (bytesPerPixel <= 2) ? BOR_UNSIGNED_SHORT_5_6_5 : GL_UNSIGNED_BYTE;
104 
105 	// enable 2D textures
106 	glEnable(textureTarget);
107 
108 	// detect support for non-power-of-two texture dimensions
109 #ifdef GLES
110 	npotTexturesSupported = strstr(glExtensions, "GL_IMG_texture_npot") || strstr(glExtensions, "GL_OES_texture_npot");
111 #else
112 	npotTexturesSupported = strstr(glExtensions, "GL_ARB_texture_non_power_of_two") ? true : false;
113 #endif
114 
115 	/*
116 	 * FIXME: NPOT textures are disabled for now because they cause a software fallback with
117 	 * a few drivers for older Nvidia cards. But POT textures eat up more memory than
118 	 * is necessary on computers with NPOT texture support. There has to be a
119 	 * better solution to this problem than just disabling them for everyone...the
120 	 * "GL_*_texture_rectangle" extensions might solve the problem in some cases.
121 	 */
122 	npotTexturesSupported = false;
123 
124 	if(npotTexturesSupported)
125 	{
126 		allocTextureWidth = textureWidth;
127 		allocTextureHeight = textureHeight;
128 	}
129 	else
130 	{
131 		allocTextureWidth = nextpowerof2(textureWidth);
132 		allocTextureHeight = nextpowerof2(textureHeight);
133 	}
134 
135 	// calculate maximum texture coordinates
136 #ifdef GLES
137 	tcx = (textureWidth == 0) ? 0 : (textureWidth<<16) / allocTextureWidth;
138 	tcy = (textureHeight == 0) ? 0 : (textureHeight<<16) / allocTextureHeight;
139 #else
140 	tcx = (textureWidth == 0) ? 0 : (float)textureWidth / (float)allocTextureWidth;
141 	tcy = (textureHeight == 0) ? 0 : (float)textureHeight / (float)allocTextureHeight;
142 #endif
143 
144 	// allocate a surface to initialize the texture with
145 	bscreen = SDL_AllocSurface(SDL_SWSURFACE, allocTextureWidth, allocTextureHeight, textureDepths[bytesPerPixel-1], 0,0,0,0);
146 
147 	// create texture object
148 	glDeleteTextures(1, &gltexture);
149 	glGenTextures(1, &gltexture);
150 	glBindTexture(textureTarget, gltexture);
151 	glTexImage2D(textureTarget, 0, textureInternalColorFormat, allocTextureWidth,
152 			allocTextureHeight, 0, textureColorFormat, texturePixelFormat, bscreen->pixels);
153 	glTexParameter(textureTarget, GL_TEXTURE_WRAP_S, BOR_CLAMP);
154 	glTexParameter(textureTarget, GL_TEXTURE_WRAP_T, BOR_CLAMP);
155 
156 	// we don't need bscreen anymore, except in 8-bit mode
157 	SDL_FreeSurface(bscreen);
158 	bscreen = NULL;
159 
160 	if(bytesPerPixel == 1) bscreen = SDL_AllocSurface(SDL_SWSURFACE, textureWidth, textureHeight, 16, 0,0,0,0);
161 }
162 
video_gl_set_mode(s_videomodes videomodes)163 int video_gl_set_mode(s_videomodes videomodes)
164 {
165 	GLint maxTextureSize;
166 
167 	bytesPerPixel = videomodes.pixel;
168 	textureWidth = videomodes.hRes;
169 	textureHeight = videomodes.vRes;
170 
171 	// use the current monitor resolution in fullscreen mode to prevent aspect ratio distortion
172 	viewportWidth = savedata.fullscreen ? nativeWidth : (int)(videomodes.hRes * MAX(0.25,savedata.glscale));
173 	viewportHeight = savedata.fullscreen ? nativeHeight : (int)(videomodes.vRes * MAX(0.25,savedata.glscale));
174 
175 	// zero width/height means close the window, not make it enormous!
176 	if((viewportWidth == 0) || (viewportHeight == 0)) return 0;
177 
178 	// set up OpenGL double buffering
179 	if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0)
180 	{
181 		printf("Can't set up OpenGL double buffering (%s)...", SDL_GetError());
182 		goto error;
183 	}
184 
185 	// try to disable vertical retrace syncing (VSync)
186 #ifdef SDL13
187 	if(SDL_GL_SetSwapInterval(0) < 0)
188 #else
189 	if(SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0) != 0)
190 #endif
191 	{
192 		printf("Warning: can't disable vertical retrace sync (%s)\n", SDL_GetError());
193 	}
194 
195 	// free existing surfaces
196 	// if(screen) SDL_FreeAndNullVideoSurface(screen);
197 	if(bscreen) { SDL_FreeSurface(bscreen); bscreen=NULL; }
198 
199 	// create main video surface and initialize OpenGL context
200 	screen = SDL_SetVideoMode(viewportWidth, viewportHeight, 0, savedata.fullscreen ? (SDL_OPENGL|SDL_FULLSCREEN) : SDL_OPENGL);
201 
202 	// make sure the surface was created successfully
203 	if(!screen)
204 	{
205 		printf("OpenGL initialization failed (%s)...", SDL_GetError());
206 		goto error;
207 	}
208 
209 	// update viewport size based on actual dimensions
210 	viewportWidth = screen->w;
211 	viewportHeight = screen->h;
212 
213 #ifdef LOADGL
214 	// load OpenGL functions dynamically in Linux/Windows/OSX
215 	if(LoadGLFunctions() == 0) goto error;
216 #endif
217 
218 	// reject the MesaGL software renderer (swrast) because it's much slower than SDL software blitting
219 	if(stricmp((const char*)glGetString(GL_RENDERER), "Software Rasterizer") == 0)
220 	{
221 		printf("Not going to use the Mesa software renderer...");
222 		goto error;
223 	}
224 
225 	// get the list of available extensions
226 	glExtensions = (const char*)glGetString(GL_EXTENSIONS);
227 
228 	// don't try to create a texture larger than the maximum allowed dimensions
229 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
230 	if((textureWidth > maxTextureSize) || (textureHeight > maxTextureSize))
231 	{
232 		printf("Unable to create a %ix%i OpenGL texture (max texture size %i)...", textureWidth, textureHeight, maxTextureSize);
233 		goto error;
234 	}
235 
236 	// set background to black
237 	glClearColor(0.0, 0.0, 0.0, 0.0);
238 	glClear(GL_COLOR_BUFFER_BIT);
239 
240 	// disable unneeded features
241 	glDisable(GL_BLEND);
242 	glDisable(GL_LIGHTING);
243 	glDisable(GL_DEPTH_TEST);
244 	glDisable(GL_DITHER);
245 
246 #ifdef GLES
247 	// enable vertex/texcoord arrays in OpenGL ES since we can't use glBegin()/glEnd()
248 	glEnableClientState(GL_VERTEX_ARRAY);
249 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
250 #endif
251 
252 	// initialize texture object
253 	video_gl_init_textures();
254 
255 	// set up offsets, scale factors, and viewport
256 	video_gl_setup_screen();
257 
258 	// FIXME: don't use savedata for this; use the previous brightness and gamma (will need to coordinate with software SDL)
259 	video_gl_set_color_correction(savedata.gamma, savedata.brightness);
260 
261 	opengl = 1;
262 	return 1;
263 error:
264 	printf("falling back on SDL video backend\n");
265 	if(screen) SDL_FreeAndNullVideoSurface(screen);
266 	if(bscreen) { SDL_FreeSurface(bscreen); bscreen=NULL; }
267 	opengl = 0;
268 	savedata.usegl[savedata.fullscreen] = 0;
269 	return 0;
270 }
271 
video_gl_clearscreen()272 void video_gl_clearscreen()
273 {
274 	// clear both buffers in a double-buffered setup
275 	glClear(GL_COLOR_BUFFER_BIT);
276 	SDL_GL_SwapBuffers();
277 	glClear(GL_COLOR_BUFFER_BIT);
278 	SDL_GL_SwapBuffers();
279 }
280 
video_gl_fullscreen_flip()281 void video_gl_fullscreen_flip()
282 {
283 	savedata.fullscreen ^= 1;
284 	video_set_mode(stored_videomodes);
285 }
286 
video_gl_draw_quad(int x,int y,int width,int height)287 void video_gl_draw_quad(int x, int y, int width, int height)
288 {
289 #ifdef GLES
290 	// OpenGL ES supports neither quads nor glBegin()/glEnd()  :(
291 	GLshort box[] = {x,y,    x+width-1,y,  x+width-1,y+height-1,  x,y+height-1};
292 	GLfixed tex[] = {0,tcy,  tcx,tcy,      tcx,0,                 0,0,        };
293 	GLubyte indices[] = {0,1,3,2};
294 
295 	glActiveTexture(GL_TEXTURE0);
296 	glTexCoordPointer(2, GL_FIXED, 0, tex);
297 	glActiveTexture(GL_TEXTURE1);
298 	glTexCoordPointer(2, GL_FIXED, 0, tex);
299 	glVertexPointer(2, GL_SHORT, 0, box);
300 	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
301 #else
302 	glBegin(GL_QUADS);
303 		// Top left
304 		glMultiTexCoord2f(GL_TEXTURE0, 0.0, tcy);
305 		glMultiTexCoord2f(GL_TEXTURE1, 0.0, tcy);
306 		glVertex2i(x, y);
307 
308 		// Top right
309 		glMultiTexCoord2f(GL_TEXTURE0, tcx, tcy);
310 		glMultiTexCoord2f(GL_TEXTURE1, tcx, tcy);
311 		glVertex2i(x+width-1, y);
312 
313 		// Bottom right
314 		glMultiTexCoord2f(GL_TEXTURE0, tcx, 0.0);
315 		glMultiTexCoord2f(GL_TEXTURE1, tcx, 0.0);
316 		glVertex2i(x+width-1, y+height-1);
317 
318 		// Bottom left
319 		glMultiTexCoord2f(GL_TEXTURE0, 0.0, 0.0);
320 		glMultiTexCoord2f(GL_TEXTURE1, 0.0, 0.0);
321 		glVertex2i(x, y+height-1);
322 	glEnd();
323 #endif
324 }
325 
video_gl_copy_screen(s_screen * src)326 int video_gl_copy_screen(s_screen* src)
327 {
328 	unsigned char *sp;
329 	unsigned char *dp;
330 	int width, height, linew, slinew;
331 	int h, i;
332 	float texScale;
333 	SDL_Surface* ds = NULL;
334 
335 	// Convert 8-bit s_screen to a 16-bit SDL_Surface
336 	if(bscreen)
337 	{
338 		if(SDL_MUSTLOCK(bscreen)) SDL_LockSurface(bscreen);
339 
340 		width = bscreen->w;
341 		if(width > src->width) width = src->width;
342 		height = bscreen->h;
343 		if(height > src->height) height = src->height;
344 		if(!width || !height) return 0;
345 		h = height;
346 
347 		sp = (unsigned char*)src->data;
348 		ds = bscreen;
349 		dp = ds->pixels;
350 
351 		linew = width*bytesPerPixel;
352 		slinew = src->width*bytesPerPixel;
353 
354 		do{
355 			//u16pcpy((unsigned short*)dp, sp, glpalette, linew);
356 			i=linew-1;
357 			do
358 			{
359 			   ((unsigned short*)dp)[i] = glpalette[sp[i]];
360 			}while(i--);
361 			sp += slinew;
362 			dp += ds->pitch;
363 		}while(--h);
364 
365 		if(SDL_MUSTLOCK(bscreen)) SDL_UnlockSurface(bscreen);
366 	}
367 
368 	// update texture contents with new surface contents
369 	glActiveTexture(GL_TEXTURE0);
370 	glBindTexture(textureTarget, gltexture);
371 	glTexSubImage2D(textureTarget, 0, 0, 0, textureWidth, textureHeight, textureColorFormat,
372 			texturePixelFormat, bscreen ? bscreen->pixels : src->data);
373 
374 	// determine x and y scale factors
375 	texScale = MIN((float)viewportWidth/(float)textureWidth, (float)viewportHeight/(float)textureHeight);
376 
377 	// determine on-screen dimensions
378 	scaledWidth = (int)(textureWidth * texScale);
379 	scaledHeight = (int)(textureHeight * texScale) * ycompress;
380 
381 	// determine offsets
382 	xOffset = (viewportWidth - scaledWidth) / 2;
383 	yOffset = (viewportHeight - scaledHeight) / 2;
384 
385 	// set texture scaling type
386 	if((savedata.glfilter[savedata.fullscreen]) || (textureWidth == (stretch ? viewportWidth : scaledWidth)))
387 	{
388 		glTexParameter(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
389 		glTexParameter(textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
390 	}
391 	else
392 	{
393 		glTexParameter(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
394 		glTexParameter(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
395 	}
396 
397 	// render the quad
398 	if(stretch)
399 		video_gl_draw_quad(0, 0, viewportWidth, viewportHeight);
400 	else
401 		video_gl_draw_quad(xOffset, yOffset, scaledWidth, scaledHeight);
402 
403 	// blit the image to the screen
404 	SDL_GL_SwapBuffers();
405 
406 #if WIN || LINUX
407 	// limit framerate to 200 fps
408 	SDL_framerateDelay(&framerate_manager);
409 #endif
410 
411 	return 1;
412 }
413 
video_gl_setpalette(unsigned char * pal)414 void video_gl_setpalette(unsigned char* pal)
415 {
416 	int i;
417 	for(i=0; i<256; i++)
418 	{
419 		glpalette[i] = colour16(pal[0], pal[1], pal[2]);
420 		pal += 3;
421 	}
422 }
423 
424 // Set the brightness and gamma values
video_gl_set_color_correction(int gamma,int brightness)425 void video_gl_set_color_correction(int gamma, int brightness)
426 {
427 	int numTexEnvStages = 0;
428 
429 	if(gamma < -256) gamma = -256;
430 	if(gamma > 256) gamma = 256;
431 	if(brightness < -256) brightness = -256;
432 	if(brightness > 256) brightness = 256;
433 	glColor4f(abs(gamma / 256.0), abs(gamma / 256.0), abs(gamma / 256.0), abs(brightness / 256.0));
434 
435 #if GLES
436 	if(gamma)
437 #else
438 	if(strstr(glExtensions, "GL_ARB_texture_env_combine"))
439 #endif
440 	{
441 		if(gamma > 0)
442 		{
443 			numTexEnvStages = 3;
444 
445 			// first stage: c*g
446 			glActiveTexture(GL_TEXTURE0);
447 			glBindTexture(textureTarget, gltexture);
448 			glEnable(textureTarget);
449 		    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_COMBINE);
450 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
451 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
452 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
453 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR_ARB);
454 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
455 
456 			// second stage: (1.0-c)*(1.0-prev) = (1.0-c)*(1.0-(c*g))
457 			glActiveTexture(GL_TEXTURE1);
458 			glBindTexture(textureTarget, gltexture);
459 			glEnable(textureTarget);
460 		    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_COMBINE);
461 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
462 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
463 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
464 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
465 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
466 
467 			// third stage: 1.0-prev = 1.0-((1.0-c)*(1.0-(c*g)))
468 			glActiveTexture(GL_TEXTURE2);
469 			glBindTexture(textureTarget, gltexture);
470 			glEnable(textureTarget);
471 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
472 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
473 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
474 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
475 		}
476 		else if(gamma < 0)
477 		{
478 			numTexEnvStages = 2;
479 
480 			// first stage: (1.0-c)*g
481 			glActiveTexture(GL_TEXTURE0);
482 			glBindTexture(textureTarget, gltexture);
483 			glEnable(textureTarget);
484 		    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_COMBINE);
485 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
486 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
487 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
488 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR_ARB);
489 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
490 
491 			// second stage: c*(1.0-prev) = c*(1.0-((1.0-c)*g))
492 			glActiveTexture(GL_TEXTURE1);
493 			glBindTexture(textureTarget, gltexture);
494 			glEnable(textureTarget);
495 		    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_COMBINE);
496 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
497 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
498 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
499 			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
500 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
501 
502 			// third stage: disabled
503 			glActiveTexture(GL_TEXTURE2);
504 			glBindTexture(textureTarget, 0);
505 			glDisable(textureTarget);
506 		}
507 		else // gamma == 0 -> no gamma correction
508 		{
509 			numTexEnvStages = 0;
510 
511 			// first stage: sampled color from texture
512 			glActiveTexture(GL_TEXTURE0);
513 			glBindTexture(textureTarget, gltexture);
514 			glEnable(textureTarget);
515 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,  GL_REPLACE);
516 
517 			// second stage: disabled
518 			glActiveTexture(GL_TEXTURE1);
519 			glBindTexture(textureTarget, 0);
520 			glDisable(textureTarget);
521 
522 			// third stage: disabled
523 			glActiveTexture(GL_TEXTURE2);
524 			glBindTexture(textureTarget, 0);
525 			glDisable(textureTarget);
526 		}
527 	}
528 
529 	if(brightness)
530 	{
531 		// brightness correction
532 		GLfloat blendColor = brightness > 0 ? 1.0 : 0.0; // white (positive brightess) or black (0 or negative)
533 		GLfloat rgba[4] = {blendColor, blendColor, blendColor, 1.0};
534 
535 		glActiveTexture(GL_TEXTURE0 + numTexEnvStages);
536 		glBindTexture(textureTarget, gltexture);
537 		glEnable(textureTarget);
538 		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
539 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
540 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
541 		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, numTexEnvStages > 0 ? GL_PREVIOUS : GL_TEXTURE);
542 		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
543 		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
544 		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
545 		glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PRIMARY_COLOR);
546 		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_ALPHA);
547 	}
548 
549 	//fprintf(stderr, "set brightness=%d and gamma=%d in %d texenv stages\n", brightness, gamma, numTexEnvStages + 1);
550 }
551 
552 
553