summaryrefslogtreecommitdiff
path: root/src/libs/graphics/sdl/opengl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/graphics/sdl/opengl.c')
-rw-r--r--src/libs/graphics/sdl/opengl.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/libs/graphics/sdl/opengl.c b/src/libs/graphics/sdl/opengl.c
new file mode 100644
index 0000000..d8fe33f
--- /dev/null
+++ b/src/libs/graphics/sdl/opengl.c
@@ -0,0 +1,575 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_OPENGL
+
+#include "libs/graphics/sdl/opengl.h"
+#include "libs/graphics/bbox.h"
+#include "scalers.h"
+#include "options.h"
+#include "libs/log.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+typedef struct _gl_screeninfo {
+ SDL_Surface *scaled;
+ GLuint texture;
+ BOOLEAN dirty, active;
+ SDL_Rect updated;
+} TFB_GL_SCREENINFO;
+
+static TFB_GL_SCREENINFO GL_Screens[TFB_GFX_NUMSCREENS];
+
+static int ScreenFilterMode;
+
+static TFB_ScaleFunc scaler = NULL;
+static BOOLEAN first_init = TRUE;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define R_MASK 0xff000000
+#define G_MASK 0x00ff0000
+#define B_MASK 0x0000ff00
+#define A_MASK 0x000000ff
+#else
+#define R_MASK 0x000000ff
+#define G_MASK 0x0000ff00
+#define B_MASK 0x00ff0000
+#define A_MASK 0xff000000
+#endif
+
+static void TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_GL_Postprocess (void);
+static void TFB_GL_UploadTransitionScreen (void);
+static void TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND opengl_scaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Scaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND opengl_unscaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Unscaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+
+static int
+AttemptColorDepth (int flags, int width, int height, int bpp)
+{
+ SDL_Surface *SDL_Video;
+ int videomode_flags;
+ ScreenColorDepth = bpp;
+ ScreenWidthActual = width;
+ ScreenHeightActual = height;
+
+ switch (bpp) {
+ case 15:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 16:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 6);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 24:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+
+ case 32:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+ default:
+ break;
+ }
+
+ SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 0);
+ SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
+
+ videomode_flags = SDL_OPENGL;
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ videomode_flags |= SDL_FULLSCREEN;
+ videomode_flags |= SDL_ANYFORMAT;
+
+ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual,
+ bpp, videomode_flags);
+ if (SDL_Video == NULL)
+ {
+ log_add (log_Error, "Couldn't set OpenGL %ix%ix%i video mode: %s",
+ ScreenWidthActual, ScreenHeightActual, bpp,
+ SDL_GetError ());
+ return -1;
+ }
+ else
+ {
+ log_add (log_Info, "Set the resolution to: %ix%ix%i"
+ " (surface reports %ix%ix%i)",
+ width, height, bpp,
+ SDL_GetVideoSurface()->w, SDL_GetVideoSurface()->h,
+ SDL_GetVideoSurface()->format->BitsPerPixel);
+
+ log_add (log_Info, "OpenGL renderer: %s version: %s",
+ glGetString (GL_RENDERER), glGetString (GL_VERSION));
+ }
+ return 0;
+}
+
+int
+TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i, texture_width, texture_height;
+ GraphicsDriver = driver;
+
+ if (AttemptColorDepth (flags, width, height, 32) &&
+ AttemptColorDepth (flags, width, height, 24) &&
+ AttemptColorDepth (flags, width, height, 16))
+ {
+ log_add (log_Error, "Couldn't set any OpenGL %ix%i video mode!",
+ width, height);
+ return -1;
+ }
+
+ if (!togglefullscreen)
+ {
+ if (format_conv_surf)
+ SDL_FreeSurface (format_conv_surf);
+ format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, 32,
+ R_MASK, G_MASK, B_MASK, A_MASK);
+ if (format_conv_surf == NULL)
+ {
+ log_add (log_Error, "Couldn't create format_conv_surf: %s",
+ SDL_GetError());
+ return -1;
+ }
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ }
+
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+
+ if (first_init)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ GL_Screens[i].scaled = NULL;
+ GL_Screens[i].dirty = TRUE;
+ GL_Screens[i].active = TRUE;
+ }
+ GL_Screens[1].active = FALSE;
+ first_init = FALSE;
+ }
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY)
+ {
+ if (!togglefullscreen)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ if (0 != SDL1_ReInit_Screen (&GL_Screens[i].scaled, format_conv_surf,
+ ScreenWidth * 2, ScreenHeight * 2))
+ return -1;
+ }
+ scaler = Scale_PrepPlatform (flags, SDL_Screen->format);
+ }
+
+ texture_width = 1024;
+ texture_height = 512;
+
+ graphics_backend = &opengl_scaled_backend;
+ }
+ else
+ {
+ texture_width = 512;
+ texture_height = 256;
+
+ scaler = NULL;
+ graphics_backend = &opengl_unscaled_backend;
+ }
+
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY)
+ ScreenFilterMode = GL_LINEAR;
+ else
+ ScreenFilterMode = GL_NEAREST;
+
+ glViewport (0, 0, ScreenWidthActual, ScreenHeightActual);
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ SDL_GL_SwapBuffers ();
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glDisable (GL_DITHER);
+ glDepthMask(GL_FALSE);
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ glGenTextures (1, &GL_Screens[i].texture);
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[i].texture);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, texture_width, texture_height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
+
+ return 0;
+}
+
+int
+TFB_GL_InitGraphics (int driver, int flags, int width, int height)
+{
+ char VideoName[256];
+
+ log_add (log_Info, "Initializing SDL with OpenGL support.");
+
+ SDL_VideoDriverName (VideoName, sizeof (VideoName));
+ log_add (log_Info, "SDL driver used: %s", VideoName);
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+
+ if (TFB_GL_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: "
+ "no fallback at start of program!");
+ exit (EXIT_FAILURE);
+ }
+
+ // Initialize scalers (let them precompute whatever)
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_GL_UninitGraphics (void)
+{
+ int i;
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++) {
+ UnInit_Screen (&GL_Screens[i].scaled);
+ }
+}
+
+static void
+TFB_GL_UploadTransitionScreen (void)
+{
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.x = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.y = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE;
+}
+
+static void
+TFB_GL_ScanLines (void)
+{
+ int y;
+
+ glDisable (GL_TEXTURE_2D);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_DST_COLOR, GL_ZERO);
+ glColor3f (0.85f, 0.85f, 0.85f);
+ for (y = 0; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+
+ glBlendFunc (GL_DST_COLOR, GL_ONE);
+ glColor3f (0.2f, 0.2f, 0.2f);
+ for (y = 1; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+}
+
+static void
+TFB_GL_DrawQuad (SDL_Rect *r)
+{
+ BOOLEAN keep_aspect_ratio = optKeepAspectRatio;
+ int x1 = 0, y1 = 0, x2 = ScreenWidthActual, y2 = ScreenHeightActual;
+ int sx = 0, sy = 0;
+ int sw, sh;
+ float sx_multiplier = 1;
+ float sy_multiplier = 1;
+
+ if (keep_aspect_ratio)
+ {
+ float threshold = 0.75f;
+ float ratio = ScreenHeightActual / (float)ScreenWidthActual;
+
+ if (ratio > threshold)
+ {
+ // screen is narrower than 4:3
+ int height = (int)(ScreenWidthActual * threshold);
+ y1 = (ScreenHeightActual - height) / 2;
+ y2 = ScreenHeightActual - y1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = height / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)(((ScreenHeight - (r->y + r->h)) * sy_multiplier) + y1);
+ }
+ }
+ else if (ratio < threshold)
+ {
+ // screen is wider than 4:3
+ int width = (int)(ScreenHeightActual / threshold);
+ x1 = (ScreenWidthActual - width) / 2;
+ x2 = ScreenWidthActual - x1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = width / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)((r->x * sx_multiplier) + x1);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ }
+ else
+ {
+ // screen is 4:3
+ keep_aspect_ratio = 0;
+ }
+ }
+
+ if (r != NULL)
+ {
+ if (!keep_aspect_ratio)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ sw = (int)(r->w * sx_multiplier);
+ sh = (int)(r->h * sy_multiplier);
+ glScissor (sx, sy, sw, sh);
+ glEnable (GL_SCISSOR_TEST);
+ }
+
+ glBegin (GL_TRIANGLE_FAN);
+ glTexCoord2f (0, 0);
+ glVertex2i (x1, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, 0);
+ glVertex2i (x2, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, ScreenHeight / 256.0f);
+ glVertex2i (x2, y2);
+ glTexCoord2f (0, ScreenHeight / 256.0f);
+ glVertex2i (x1, y2);
+ glEnd ();
+ if (r != NULL)
+ {
+ glDisable (GL_SCISSOR_TEST);
+ }
+}
+
+static void
+TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0,ScreenWidthActual,ScreenHeightActual, 0, -1, 1);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ if (optKeepAspectRatio)
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ (void) transition_amount;
+ (void) fade_amount;
+
+ if (force_full_redraw == TFB_REDRAW_YES)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+ else if (TFB_BBox.valid)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+}
+
+static void
+TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = SDL_Screens[screen]->pitch / 4;
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (SDL_Screens[screen]);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x,
+ GL_Screens[screen].updated.y,
+ GL_Screens[screen].updated.w,
+ GL_Screens[screen].updated.h,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)SDL_Screens[screen]->pixels +
+ (GL_Screens[screen].updated.y * PitchWords +
+ GL_Screens[screen].updated.x));
+ SDL_UnlockSurface (SDL_Screens[screen]);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = GL_Screens[screen].scaled->pitch / 4;
+ scaler (SDL_Screens[screen], GL_Screens[screen].scaled, &GL_Screens[screen].updated);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (GL_Screens[screen].scaled);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x * 2,
+ GL_Screens[screen].updated.y * 2,
+ GL_Screens[screen].updated.w * 2,
+ GL_Screens[screen].updated.h * 2,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)GL_Screens[screen].scaled->pixels +
+ (GL_Screens[screen].updated.y * 2 * PitchWords +
+ GL_Screens[screen].updated.x * 2));
+ SDL_UnlockSurface (GL_Screens[screen].scaled);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ float r_f = r / 255.0f;
+ float g_f = g / 255.0f;
+ float b_f = b / 255.0f;
+ float a_f = a / 255.0f;
+ glColor4f(r_f, g_f, b_f, a_f);
+
+ glDisable (GL_TEXTURE_2D);
+ if (a != 255)
+ {
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ }
+ else
+ {
+ glDisable (GL_BLEND);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Postprocess (void)
+{
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ TFB_GL_ScanLines ();
+
+ SDL_GL_SwapBuffers ();
+}
+
+#endif
+#endif