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