aboutsummaryrefslogtreecommitdiff
path: root/backends/graphics/opengl
diff options
context:
space:
mode:
authorMarisa-Chan2014-06-13 21:43:04 +0700
committerMarisa-Chan2014-06-13 21:43:04 +0700
commit45589950c0fb1a449351e6a00ef10d42290d8bae (patch)
tree44e4eedcb7e69d5fc386155b000ed038af07251d /backends/graphics/opengl
parent48360645dcd5f8fddb135b6e31ae5cae4be8d77f (diff)
parent5c005ad3a3f1df0bc968c85c1cf0fc48e36ab0b2 (diff)
downloadscummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.tar.gz
scummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.tar.bz2
scummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.zip
Merge remote-tracking branch 'upstream/master' into zvision
Conflicts: engines/zvision/animation/rlf_animation.cpp engines/zvision/animation_control.h engines/zvision/core/console.cpp engines/zvision/core/events.cpp engines/zvision/cursors/cursor.cpp engines/zvision/cursors/cursor_manager.cpp engines/zvision/cursors/cursor_manager.h engines/zvision/fonts/truetype_font.cpp engines/zvision/graphics/render_manager.cpp engines/zvision/graphics/render_manager.h engines/zvision/inventory/inventory_manager.h engines/zvision/inventory_manager.h engines/zvision/meta_animation.h engines/zvision/module.mk engines/zvision/scripting/actions.cpp engines/zvision/scripting/control.h engines/zvision/scripting/controls/animation_control.cpp engines/zvision/scripting/controls/animation_control.h engines/zvision/scripting/controls/input_control.cpp engines/zvision/scripting/controls/lever_control.cpp engines/zvision/scripting/controls/timer_node.cpp engines/zvision/scripting/controls/timer_node.h engines/zvision/scripting/puzzle.h engines/zvision/scripting/scr_file_handling.cpp engines/zvision/scripting/script_manager.cpp engines/zvision/scripting/script_manager.h engines/zvision/sidefx.cpp engines/zvision/sound/zork_raw.cpp engines/zvision/sound/zork_raw.h engines/zvision/video/video.cpp engines/zvision/video/zork_avi_decoder.h engines/zvision/zvision.cpp engines/zvision/zvision.h
Diffstat (limited to 'backends/graphics/opengl')
-rw-r--r--backends/graphics/opengl/debug.cpp65
-rw-r--r--backends/graphics/opengl/debug.h39
-rw-r--r--backends/graphics/opengl/extensions.cpp48
-rw-r--r--backends/graphics/opengl/extensions.h (renamed from backends/graphics/opengl/glerrorcheck.h)26
-rw-r--r--backends/graphics/opengl/gltexture.cpp225
-rw-r--r--backends/graphics/opengl/gltexture.h131
-rw-r--r--backends/graphics/opengl/opengl-graphics.cpp1842
-rw-r--r--backends/graphics/opengl/opengl-graphics.h511
-rw-r--r--backends/graphics/opengl/opengl-sys.h (renamed from backends/graphics/opengl/glerrorcheck.cpp)50
-rw-r--r--backends/graphics/opengl/texture.cpp374
-rw-r--r--backends/graphics/opengl/texture.h175
11 files changed, 1872 insertions, 1614 deletions
diff --git a/backends/graphics/opengl/debug.cpp b/backends/graphics/opengl/debug.cpp
new file mode 100644
index 0000000000..d5d73fb5ec
--- /dev/null
+++ b/backends/graphics/opengl/debug.cpp
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/debug.h"
+#include "backends/graphics/opengl/opengl-sys.h"
+
+#include "common/str.h"
+#include "common/textconsole.h"
+
+#ifdef OPENGL_DEBUG
+
+namespace OpenGL {
+
+namespace {
+Common::String getGLErrStr(GLenum error) {
+ switch (error) {
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION";
+ case GL_STACK_OVERFLOW:
+ return "GL_STACK_OVERFLOW";
+ case GL_STACK_UNDERFLOW:
+ return "GL_STACK_UNDERFLOW";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY";
+ }
+
+ return Common::String::format("(Unknown GL error code 0x%X)", error);
+}
+} // End of anonymous namespace
+
+void checkGLError(const char *expr, const char *file, int line) {
+ GLenum error;
+
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ // We cannot use error here because we do not know whether we have a
+ // working screen or not.
+ warning("GL ERROR: %s on %s (%s:%d)", getGLErrStr(error).c_str(), expr, file, line);
+ }
+}
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/debug.h b/backends/graphics/opengl/debug.h
new file mode 100644
index 0000000000..ff6b678870
--- /dev/null
+++ b/backends/graphics/opengl/debug.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_DEBUG_H
+#define BACKENDS_GRAPHICS_OPENGL_DEBUG_H
+
+#define OPENGL_DEBUG
+
+#ifdef OPENGL_DEBUG
+
+namespace OpenGL {
+void checkGLError(const char *expr, const char *file, int line);
+} // End of namespace OpenGL
+
+#define GLCALL(x) do { (x); OpenGL::checkGLError(#x, __FILE__, __LINE__); } while (false)
+#else
+#define GLCALL(x) do { (x); } while (false)
+#endif
+
+#endif
diff --git a/backends/graphics/opengl/extensions.cpp b/backends/graphics/opengl/extensions.cpp
new file mode 100644
index 0000000000..4482ef82b5
--- /dev/null
+++ b/backends/graphics/opengl/extensions.cpp
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/extensions.h"
+#include "backends/graphics/opengl/opengl-sys.h"
+
+#include "common/tokenizer.h"
+
+namespace OpenGL {
+
+bool g_extNPOTSupported = false;
+
+void initializeGLExtensions() {
+ const char *extString = (const char *)glGetString(GL_EXTENSIONS);
+
+ // Initialize default state.
+ g_extNPOTSupported = false;
+
+ Common::StringTokenizer tokenizer(extString, " ");
+ while (!tokenizer.empty()) {
+ Common::String token = tokenizer.nextToken();
+
+ if (token == "GL_ARB_texture_non_power_of_two") {
+ g_extNPOTSupported = true;
+ }
+ }
+}
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/glerrorcheck.h b/backends/graphics/opengl/extensions.h
index 2d5491bdfd..87452429e2 100644
--- a/backends/graphics/opengl/glerrorcheck.h
+++ b/backends/graphics/opengl/extensions.h
@@ -8,28 +8,34 @@
* 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
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-#if !defined(DEBUG)
+#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
+#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
-// If not in debug, do nothing
-#define CHECK_GL_ERROR() do {} while (false)
+namespace OpenGL {
-#else
+/**
+ * Checks for availability of extensions we want to use and initializes them
+ * when available.
+ */
+void initializeGLExtensions();
-// If in debug, check for an error after a GL call
-#define CHECK_GL_ERROR() checkGlError(__FILE__, __LINE__)
+/**
+ * Whether non power of two textures are supported
+ */
+extern bool g_extNPOTSupported;
-void checkGlError(const char *file, int line);
+} // End of namespace OpenGL
#endif
diff --git a/backends/graphics/opengl/gltexture.cpp b/backends/graphics/opengl/gltexture.cpp
deleted file mode 100644
index ca674563df..0000000000
--- a/backends/graphics/opengl/gltexture.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/scummsys.h"
-
-#if defined(USE_OPENGL)
-
-#include "backends/graphics/opengl/gltexture.h"
-#include "backends/graphics/opengl/glerrorcheck.h"
-
-#include "common/rect.h"
-#include "common/array.h"
-#include "common/util.h"
-#include "common/tokenizer.h"
-
-// Supported GL extensions
-static bool npot_supported = false;
-static bool glext_inited = false;
-
-/*static inline GLint xdiv(int numerator, int denominator) {
- assert(numerator < (1 << 16));
- return (numerator << 16) / denominator;
-}*/
-
-static GLuint nextHigher2(GLuint v) {
- if (v == 0)
- return 1;
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- return ++v;
-}
-
-void GLTexture::initGLExtensions() {
-
- // Return if extensions were already checked
- if (glext_inited)
- return;
-
- // Get a string with all extensions
- const char *ext_string = (const char *)glGetString(GL_EXTENSIONS);
- CHECK_GL_ERROR();
- Common::StringTokenizer tokenizer(ext_string, " ");
- // Iterate all string tokens
- while (!tokenizer.empty()) {
- Common::String token = tokenizer.nextToken();
- if (token == "GL_ARB_texture_non_power_of_two")
- npot_supported = true;
- }
-
- glext_inited = true;
-}
-
-GLTexture::GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type)
- :
- _bytesPerPixel(bpp),
- _internalFormat(internalFormat),
- _glFormat(format),
- _glType(type),
- _textureWidth(0),
- _textureHeight(0),
- _realWidth(0),
- _realHeight(0),
- _refresh(false),
- _filter(GL_NEAREST) {
-
- // Generate the texture ID
- glGenTextures(1, &_textureName); CHECK_GL_ERROR();
-}
-
-GLTexture::~GLTexture() {
- // Delete the texture
- glDeleteTextures(1, &_textureName); CHECK_GL_ERROR();
-}
-
-void GLTexture::refresh() {
- // Delete previous texture
- glDeleteTextures(1, &_textureName); CHECK_GL_ERROR();
-
- // Generate the texture ID
- glGenTextures(1, &_textureName); CHECK_GL_ERROR();
- _refresh = true;
-}
-
-void GLTexture::allocBuffer(GLuint w, GLuint h) {
- _realWidth = w;
- _realHeight = h;
-
- if (!_refresh) {
- if (npot_supported && _filter == GL_LINEAR) {
- // Check if we already allocated a correctly-sized buffer
- // This is so we don't need to duplicate the last row/column
- if (w == _textureWidth && h == _textureHeight)
- return;
- } else {
- // Check if we already have a large enough buffer
- if (w <= _textureWidth && h <= _textureHeight)
- return;
- }
- }
-
- if (npot_supported) {
- _textureWidth = w;
- _textureHeight = h;
- } else {
- _textureWidth = nextHigher2(w);
- _textureHeight = nextHigher2(h);
- }
-
- // Select this OpenGL texture
- glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
-
- // Set the texture parameters
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _filter); CHECK_GL_ERROR();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _filter); CHECK_GL_ERROR();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR();
-
- // Allocate room for the texture
- glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat,
- _textureWidth, _textureHeight, 0, _glFormat, _glType, NULL); CHECK_GL_ERROR();
-
- _refresh = false;
-}
-
-void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLuint w, GLuint h) {
- // Skip empty updates.
- if (w * h == 0)
- return;
-
- // Select this OpenGL texture
- glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
-
- // Check if the buffer has its data contiguously
- if ((int)w * _bytesPerPixel == pitch) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
- _glFormat, _glType, buf); CHECK_GL_ERROR();
- } else {
- // Update the texture row by row
- const byte *src = (const byte *)buf;
- GLuint curY = y;
- GLuint height = h;
- do {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, curY,
- w, 1, _glFormat, _glType, src); CHECK_GL_ERROR();
- curY++;
- src += pitch;
- } while (--height);
- }
-
- // If we're in linear filter mode, repeat the last row/column if the real dimensions
- // doesn't match the texture dimensions.
- if (_filter == GL_LINEAR) {
- if (_realWidth != _textureWidth && x + w == _realWidth) {
- const byte *src = (const byte *)buf + (w - 1) * _bytesPerPixel;
- GLuint curY = y;
- GLuint height = h;
-
- do {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x + w,
- curY, 1, 1, _glFormat, _glType, src); CHECK_GL_ERROR();
-
- curY++;
- src += pitch;
- } while (--height);
- }
-
- if (_realHeight != _textureHeight && y + h == _realHeight) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + h,
- w, 1, _glFormat, _glType, (const byte *)buf + pitch * (h - 1)); CHECK_GL_ERROR();
- }
- }
-}
-
-void GLTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
- // Select this OpenGL texture
- glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
-
- // Calculate the texture rect that will be drawn
- const GLfloat texWidth = (GLfloat)_realWidth / _textureWidth;//xdiv(_surface.w, _textureWidth);
- const GLfloat texHeight = (GLfloat)_realHeight / _textureHeight;//xdiv(_surface.h, _textureHeight);
- const GLfloat texcoords[] = {
- 0, 0,
- texWidth, 0,
- 0, texHeight,
- texWidth, texHeight,
- };
- glTexCoordPointer(2, GL_FLOAT, 0, texcoords); CHECK_GL_ERROR();
-
- // Calculate the screen rect where the texture will be drawn
- const GLshort vertices[] = {
- x, y,
- (GLshort)(x + w), y,
- x, (GLshort)(y + h),
- (GLshort)(x + w), (GLshort)(y + h),
- };
- glVertexPointer(2, GL_SHORT, 0, vertices); CHECK_GL_ERROR();
-
- // Draw the texture to the screen buffer
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); CHECK_GL_ERROR();
-}
-
-#endif
diff --git a/backends/graphics/opengl/gltexture.h b/backends/graphics/opengl/gltexture.h
deleted file mode 100644
index 6ef80923ae..0000000000
--- a/backends/graphics/opengl/gltexture.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef BACKENDS_GRAPHICS_OPENGL_GLTEXTURE_H
-#define BACKENDS_GRAPHICS_OPENGL_GLTEXTURE_H
-
-#include "common/scummsys.h"
-
-#ifdef WIN32
-#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
-#undef ARRAYSIZE
-#endif
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#undef ARRAYSIZE
-#endif
-
-// HACK: At this point in Windows platforms, common/util.h has been included
-// via common/rect.h (from backends/graphics/sdl/sdl-graphics.h), via
-// backends/graphics/openglsdl/openglsdl-graphics.h. Thus, we end up with
-// COMMON_UTIL_H defined, and ARRAYSIZE undefined (bad!). Therefore,
-// ARRAYSIZE is undefined in openglsdl-graphics.cpp. This is a temporary
-// hackish solution fo fix compilation under Windows.
-#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
-#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
-#endif
-
-#if defined(TIZEN)
-#include <FGraphicsOpengl.h>
-using namespace Tizen::Graphics::Opengl;
-#elif defined(USE_GLES)
-#include <GLES/gl.h>
-#elif defined(SDL_BACKEND)
-#include <SDL_opengl.h>
-#else
-#include <GL/gl.h>
-#endif
-
-#include "graphics/surface.h"
-
-/**
- * OpenGL texture manager class
- */
-class GLTexture {
-public:
- /**
- * Initialize OpenGL Extensions
- */
- static void initGLExtensions();
-
- GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type);
- ~GLTexture();
-
- /**
- * Refresh the texture after a context change. The
- * process will be completed on next allocBuffer call.
- */
- void refresh();
-
- /**
- * Allocates memory needed for the given size.
- */
- void allocBuffer(GLuint width, GLuint height);
-
- /**
- * Updates the texture pixels.
- */
- void updateBuffer(const void *buf, int pitch, GLuint x, GLuint y,
- GLuint w, GLuint h);
-
- /**
- * Draws the texture to the screen buffer.
- */
- void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h);
-
- /**
- * Get the texture width.
- */
- GLuint getWidth() const { return _realWidth; }
-
- /**
- * Get the texture height.
- */
- GLuint getHeight() const { return _realHeight; }
-
- /**
- * Get the bytes per pixel.
- */
- uint getBytesPerPixel() const { return _bytesPerPixel; }
-
- /**
- * Set the texture filter.
- * @filter the filter type, GL_NEAREST or GL_LINEAR
- */
- void setFilter(GLint filter) { _filter = filter; }
-
-private:
- const byte _bytesPerPixel;
- const GLenum _internalFormat;
- const GLenum _glFormat;
- const GLenum _glType;
-
- GLuint _realWidth;
- GLuint _realHeight;
- GLuint _textureName;
- GLuint _textureWidth;
- GLuint _textureHeight;
- GLint _filter;
- bool _refresh;
-};
-
-#endif
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 84be83d524..cbd06e9161 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -8,102 +8,90 @@
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-#include "common/scummsys.h"
-
-#if defined(USE_OPENGL)
#include "backends/graphics/opengl/opengl-graphics.h"
-#include "backends/graphics/opengl/glerrorcheck.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/mutex.h"
+#include "backends/graphics/opengl/texture.h"
+#include "backends/graphics/opengl/debug.h"
+#include "backends/graphics/opengl/extensions.h"
+
#include "common/textconsole.h"
#include "common/translation.h"
+#include "common/algorithm.h"
+#include "common/file.h"
#ifdef USE_OSD
#include "common/tokenizer.h"
+#include "common/rect.h"
#endif
-#include "graphics/font.h"
+
+#include "graphics/conversion.h"
+#ifdef USE_OSD
#include "graphics/fontman.h"
+#include "graphics/font.h"
+#endif
+
+namespace OpenGL {
OpenGLGraphicsManager::OpenGLGraphicsManager()
- :
+ : _currentState(), _oldState(), _transactionMode(kTransactionNone), _screenChangeID(1 << (sizeof(int) * 8 - 2)),
+ _outputScreenWidth(0), _outputScreenHeight(0), _displayX(0), _displayY(0),
+ _displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(),
+ _gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr),
+ _overlayVisible(false), _cursor(nullptr),
+ _cursorX(0), _cursorY(0), _cursorHotspotX(0), _cursorHotspotY(0), _cursorHotspotXScaled(0),
+ _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0), _cursorKeyColor(0),
+ _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false)
#ifdef USE_OSD
- _osdTexture(0), _osdAlpha(0), _osdFadeStartTime(0), _requireOSDUpdate(false),
+ , _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr)
#endif
- _gameTexture(0), _overlayTexture(0), _cursorTexture(0),
- _screenChangeCount(1 << (sizeof(int) * 8 - 2)), _screenNeedsRedraw(false),
- _shakePos(0),
- _overlayVisible(false), _overlayNeedsRedraw(false),
- _transactionMode(kTransactionNone),
- _cursorNeedsRedraw(false), _cursorPaletteDisabled(true),
- _cursorVisible(false), _cursorKeyColor(0),
- _cursorDontScale(false),
- _formatBGR(false),
- _displayX(0), _displayY(0), _displayWidth(0), _displayHeight(0) {
-
- memset(&_oldVideoMode, 0, sizeof(_oldVideoMode));
- memset(&_videoMode, 0, sizeof(_videoMode));
- memset(&_transactionDetails, 0, sizeof(_transactionDetails));
-
- _videoMode.mode = OpenGL::GFX_NORMAL;
- _videoMode.scaleFactor = 2;
- _videoMode.fullscreen = ConfMan.getBool("fullscreen");
- _videoMode.antialiasing = false;
-
- _gamePalette = (byte *)calloc(sizeof(byte) * 3, 256);
- _cursorPalette = (byte *)calloc(sizeof(byte) * 3, 256);
+ {
+ memset(_gamePalette, 0, sizeof(_gamePalette));
}
OpenGLGraphicsManager::~OpenGLGraphicsManager() {
- free(_gamePalette);
- free(_cursorPalette);
-
- _screenData.free();
- _overlayData.free();
- _cursorData.free();
- _osdSurface.free();
-
- delete _gameTexture;
- delete _overlayTexture;
- delete _cursorTexture;
+ delete _gameScreen;
+ delete _overlay;
+ delete _cursor;
+#ifdef USE_OSD
+ delete _osd;
+#endif
}
-//
-// Feature
-//
-
bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) {
- return
- (f == OSystem::kFeatureAspectRatioCorrection) ||
- (f == OSystem::kFeatureCursorPalette);
+ switch (f) {
+ case OSystem::kFeatureAspectRatioCorrection:
+ case OSystem::kFeatureCursorPalette:
+ return true;
+
+ case OSystem::kFeatureOverlaySupportsAlpha:
+ return _defaultFormatAlpha.aBits() > 3;
+
+ default:
+ return false;
+ }
}
void OpenGLGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
switch (f) {
- case OSystem::kFeatureFullscreenMode:
- setFullscreenMode(enable);
- break;
-
case OSystem::kFeatureAspectRatioCorrection:
- _videoMode.aspectRatioCorrection = enable;
- _transactionDetails.needRefresh = true;
+ assert(_transactionMode != kTransactionNone);
+ _currentState.aspectRatioCorrection = enable;
break;
case OSystem::kFeatureCursorPalette:
- _cursorPaletteDisabled = !enable;
- _cursorNeedsRedraw = true;
+ _cursorPaletteEnabled = enable;
+ updateCursorPalette();
break;
default:
@@ -113,1189 +101,1038 @@ void OpenGLGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) {
switch (f) {
- case OSystem::kFeatureFullscreenMode:
- return _videoMode.fullscreen;
-
case OSystem::kFeatureAspectRatioCorrection:
- return _videoMode.aspectRatioCorrection;
+ return _currentState.aspectRatioCorrection;
case OSystem::kFeatureCursorPalette:
- return !_cursorPaletteDisabled;
+ return _cursorPaletteEnabled;
default:
return false;
}
}
-//
-// Screen format and modes
-//
+namespace {
-static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
- {"gl1", _s("OpenGL Normal"), OpenGL::GFX_NORMAL},
- {"gl2", _s("OpenGL Conserve"), OpenGL::GFX_CONSERVE},
- {"gl4", _s("OpenGL Original"), OpenGL::GFX_ORIGINAL},
- {0, 0, 0}
+const OSystem::GraphicsMode glGraphicsModes[] = {
+ { "opengl_linear", _s("OpenGL"), GFX_LINEAR },
+ { "opengl_nearest", _s("OpenGL (No filtering)"), GFX_NEAREST },
+ { nullptr, nullptr, 0 }
};
-const OSystem::GraphicsMode *OpenGLGraphicsManager::supportedGraphicsModes() {
- return s_supportedGraphicsModes;
-}
+} // End of anonymous namespace
const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedGraphicsModes() const {
- return s_supportedGraphicsModes;
+ return glGraphicsModes;
}
int OpenGLGraphicsManager::getDefaultGraphicsMode() const {
- return OpenGL::GFX_NORMAL;
+ return GFX_LINEAR;
}
bool OpenGLGraphicsManager::setGraphicsMode(int mode) {
- assert(_transactionMode == kTransactionActive);
+ assert(_transactionMode != kTransactionNone);
- setScale(2);
+ switch (mode) {
+ case GFX_LINEAR:
+ case GFX_NEAREST:
+ _currentState.graphicsMode = mode;
+
+ if (_gameScreen) {
+ _gameScreen->enableLinearFiltering(mode == GFX_LINEAR);
+ }
+
+ if (_cursor) {
+ _cursor->enableLinearFiltering(mode == GFX_LINEAR);
+ }
- if (_oldVideoMode.setup && _oldVideoMode.mode == mode)
return true;
- switch (mode) {
- case OpenGL::GFX_NORMAL:
- case OpenGL::GFX_CONSERVE:
- case OpenGL::GFX_ORIGINAL:
- break;
default:
- warning("Unknown gfx mode %d", mode);
+ warning("OpenGLGraphicsManager::setGraphicsMode(%d): Unknown graphics mode", mode);
return false;
}
-
- _videoMode.mode = mode;
- _transactionDetails.needRefresh = true;
-
- return true;
}
int OpenGLGraphicsManager::getGraphicsMode() const {
- assert(_transactionMode == kTransactionNone);
- return _videoMode.mode;
-}
-
-void OpenGLGraphicsManager::resetGraphicsScale() {
- setScale(1);
+ return _currentState.graphicsMode;
}
#ifdef USE_RGB_COLOR
Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const {
- return _screenFormat;
+ return _currentState.gameFormat;
}
#endif
-void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
- assert(_transactionMode == kTransactionActive);
-
-#ifdef USE_RGB_COLOR
- Graphics::PixelFormat newFormat;
- if (!format)
- newFormat = Graphics::PixelFormat::createFormatCLUT8();
- else
- newFormat = *format;
-
- assert(newFormat.bytesPerPixel > 0);
-
- // Avoid redundant format changes
- if (newFormat != _videoMode.format) {
- _videoMode.format = newFormat;
- _transactionDetails.formatChanged = true;
- _screenFormat = newFormat;
- }
-#endif
-
- // Avoid redundant res changes
- if ((int)width == _videoMode.screenWidth && (int)height == _videoMode.screenHeight)
- return;
-
- _videoMode.screenWidth = width;
- _videoMode.screenHeight = height;
+void OpenGLGraphicsManager::beginGFXTransaction() {
+ assert(_transactionMode == kTransactionNone);
- _transactionDetails.sizeChanged = true;
+ // Start a transaction.
+ _oldState = _currentState;
+ _transactionMode = kTransactionActive;
}
-int OpenGLGraphicsManager::getScreenChangeID() const {
- return _screenChangeCount;
-}
+OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
+ assert(_transactionMode == kTransactionActive);
-//
-// GFX
-//
+ uint transactionError = OSystem::kTransactionSuccess;
-void OpenGLGraphicsManager::beginGFXTransaction() {
- assert(_transactionMode == kTransactionNone);
+ bool setupNewGameScreen = false;
+ if ( _oldState.gameWidth != _currentState.gameWidth
+ || _oldState.gameHeight != _currentState.gameHeight) {
+ setupNewGameScreen = true;
+ }
- _transactionMode = kTransactionActive;
- _transactionDetails.sizeChanged = false;
- _transactionDetails.needRefresh = false;
- _transactionDetails.needUpdatescreen = false;
- _transactionDetails.filterChanged = false;
#ifdef USE_RGB_COLOR
- _transactionDetails.formatChanged = false;
+ if (_oldState.gameFormat != _currentState.gameFormat) {
+ setupNewGameScreen = true;
+ }
+
+ // Check whether the requested format can actually be used.
+ Common::List<Graphics::PixelFormat> supportedFormats = getSupportedFormats();
+ // In case the requested format is not usable we will fall back to CLUT8.
+ if (Common::find(supportedFormats.begin(), supportedFormats.end(), _currentState.gameFormat) == supportedFormats.end()) {
+ _currentState.gameFormat = Graphics::PixelFormat::createFormatCLUT8();
+ transactionError |= OSystem::kTransactionFormatNotSupported;
+ }
#endif
- _oldVideoMode = _videoMode;
-}
+ do {
+ uint requestedWidth = _currentState.gameWidth;
+ uint requestedHeight = _currentState.gameHeight;
+ const uint desiredAspect = getDesiredGameScreenAspect();
+ requestedHeight = intToFrac(requestedWidth) / desiredAspect;
-OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
- int errors = OSystem::kTransactionSuccess;
+ if (!loadVideoMode(requestedWidth, requestedHeight,
+#ifdef USE_RGB_COLOR
+ _currentState.gameFormat
+#else
+ Graphics::PixelFormat::createFormatCLUT8()
+#endif
+ )
+ // HACK: This is really nasty but we don't have any guarantees of
+ // a context existing before, which means we don't know the maximum
+ // supported texture size before this. Thus, we check whether the
+ // requested game resolution is supported over here.
+ || ( _currentState.gameWidth > (uint)Texture::getMaximumTextureSize()
+ || _currentState.gameHeight > (uint)Texture::getMaximumTextureSize())) {
+ if (_transactionMode == kTransactionActive) {
+ // Try to setup the old state in case its valid and is
+ // actually different from the new one.
+ if (_oldState.valid && _oldState != _currentState) {
+ // Give some hints on what failed to set up.
+ if ( _oldState.gameWidth != _currentState.gameWidth
+ || _oldState.gameHeight != _currentState.gameHeight) {
+ transactionError |= OSystem::kTransactionSizeChangeFailed;
+ }
- assert(_transactionMode != kTransactionNone);
+#ifdef USE_RGB_COLOR
+ if (_oldState.gameFormat != _currentState.gameFormat) {
+ transactionError |= OSystem::kTransactionFormatNotSupported;
+ }
+#endif
- if (_transactionMode == kTransactionRollback) {
- if (_videoMode.fullscreen != _oldVideoMode.fullscreen) {
- errors |= OSystem::kTransactionFullscreenFailed;
+ if (_oldState.aspectRatioCorrection != _currentState.aspectRatioCorrection) {
+ transactionError |= OSystem::kTransactionAspectRatioFailed;
+ }
- _videoMode.fullscreen = _oldVideoMode.fullscreen;
- } else if (_videoMode.mode != _oldVideoMode.mode) {
- errors |= OSystem::kTransactionModeSwitchFailed;
+ if (_oldState.graphicsMode != _currentState.graphicsMode) {
+ transactionError |= OSystem::kTransactionModeSwitchFailed;
+ }
- _videoMode.mode = _oldVideoMode.mode;
- _videoMode.scaleFactor = _oldVideoMode.scaleFactor;
-#ifdef USE_RGB_COLOR
- } else if (_videoMode.format != _oldVideoMode.format) {
- errors |= OSystem::kTransactionFormatNotSupported;
+ // Roll back to the old state.
+ _currentState = _oldState;
+ _transactionMode = kTransactionRollback;
- _videoMode.format = _oldVideoMode.format;
- _screenFormat = _videoMode.format;
-#endif
- } else if (_videoMode.screenWidth != _oldVideoMode.screenWidth || _videoMode.screenHeight != _oldVideoMode.screenHeight) {
- errors |= OSystem::kTransactionSizeChangeFailed;
+ // Try to set up the old state.
+ continue;
+ }
+ }
- _videoMode.screenWidth = _oldVideoMode.screenWidth;
- _videoMode.screenHeight = _oldVideoMode.screenHeight;
- _videoMode.overlayWidth = _oldVideoMode.overlayWidth;
- _videoMode.overlayHeight = _oldVideoMode.overlayHeight;
+ // DON'T use error(), as this tries to bring up the debug
+ // console, which WON'T WORK now that we might no have a
+ // proper screen.
+ warning("OpenGLGraphicsManager::endGFXTransaction: Could not load any graphics mode!");
+ g_system->quit();
}
- if (_videoMode.fullscreen == _oldVideoMode.fullscreen &&
- _videoMode.mode == _oldVideoMode.mode &&
- _videoMode.screenWidth == _oldVideoMode.screenWidth &&
- _videoMode.screenHeight == _oldVideoMode.screenHeight) {
+ // In case we reach this we have a valid state, yay.
+ _transactionMode = kTransactionNone;
+ _currentState.valid = true;
+ } while (_transactionMode == kTransactionRollback);
+
+ if (setupNewGameScreen) {
+ delete _gameScreen;
+ _gameScreen = nullptr;
- _oldVideoMode.setup = false;
+#ifdef USE_RGB_COLOR
+ _gameScreen = createTexture(_currentState.gameFormat);
+#else
+ _gameScreen = createTexture(Graphics::PixelFormat::createFormatCLUT8());
+#endif
+ assert(_gameScreen);
+ if (_gameScreen->hasPalette()) {
+ _gameScreen->setPalette(0, 256, _gamePalette);
}
- }
- if (_transactionDetails.sizeChanged || _transactionDetails.needRefresh) {
- unloadGFXMode();
- if (!loadGFXMode()) {
- if (_oldVideoMode.setup) {
- _transactionMode = kTransactionRollback;
- errors |= endGFXTransaction();
- }
+ _gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight);
+ _gameScreen->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR);
+ // We fill the screen to all black or index 0 for CLUT8.
+#ifdef USE_RGB_COLOR
+ if (_currentState.gameFormat.bytesPerPixel == 1) {
+ _gameScreen->fill(0);
} else {
- clearOverlay();
-
- _videoMode.setup = true;
- _screenChangeCount++;
+ _gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0));
}
-#ifdef USE_RGB_COLOR
- } else if (_transactionDetails.filterChanged || _transactionDetails.formatChanged) {
#else
- } else if (_transactionDetails.filterChanged) {
+ _gameScreen->fill(0);
#endif
- loadTextures();
- internUpdateScreen();
- } else if (_transactionDetails.needUpdatescreen) {
- internUpdateScreen();
}
- _transactionMode = kTransactionNone;
- return (OSystem::TransactionError)errors;
-}
+ // Update our display area and cursor scaling. This makes sure we pick up
+ // aspect ratio correction and game screen changes correctly.
+ recalculateDisplayArea();
+ recalculateCursorScaling();
-//
-// Screen
-//
+ // Something changed, so update the screen change ID.
+ ++_screenChangeID;
-int16 OpenGLGraphicsManager::getHeight() {
- return _videoMode.screenHeight;
+ // Since transactionError is a ORd list of TransactionErrors this is
+ // clearly wrong. But our API is simply broken.
+ return (OSystem::TransactionError)transactionError;
}
-int16 OpenGLGraphicsManager::getWidth() {
- return _videoMode.screenWidth;
+int OpenGLGraphicsManager::getScreenChangeID() const {
+ return _screenChangeID;
}
-void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
- assert(colors);
-
+void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
+ Graphics::PixelFormat requestedFormat;
#ifdef USE_RGB_COLOR
- assert(_screenFormat.bytesPerPixel == 1);
+ if (!format) {
+ requestedFormat = Graphics::PixelFormat::createFormatCLUT8();
+ } else {
+ requestedFormat = *format;
+ }
+ _currentState.gameFormat = requestedFormat;
#endif
- // Save the screen palette
- memcpy(_gamePalette + start * 3, colors, num * 3);
+ _currentState.gameWidth = width;
+ _currentState.gameHeight = height;
+}
- _screenNeedsRedraw = true;
+int16 OpenGLGraphicsManager::getWidth() {
+ return _currentState.gameWidth;
+}
- if (_cursorPaletteDisabled)
- _cursorNeedsRedraw = true;
+int16 OpenGLGraphicsManager::getHeight() {
+ return _currentState.gameHeight;
}
-void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) {
- assert(colors);
+void OpenGLGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
+ _gameScreen->copyRectToTexture(x, y, w, h, buf, pitch);
+}
-#ifdef USE_RGB_COLOR
- assert(_screenFormat.bytesPerPixel == 1);
-#endif
+void OpenGLGraphicsManager::fillScreen(uint32 col) {
+ // FIXME: This does not conform to the OSystem specs because fillScreen
+ // is always taking CLUT8 color values and use color indexed mode. This is,
+ // however, plain odd and probably was a forgotten when we introduced
+ // RGB support. Thus, we simply do the "sane" thing here and hope OSystem
+ // gets fixed one day.
+ _gameScreen->fill(col);
+}
- // Copies current palette to buffer
- memcpy(colors, _gamePalette + start * 3, num * 3);
+void OpenGLGraphicsManager::setShakePos(int shakeOffset) {
+ _gameScreenShakeOffset = shakeOffset;
}
-void OpenGLGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
- assert(x >= 0 && x < _screenData.w);
- assert(y >= 0 && y < _screenData.h);
- assert(h > 0 && y + h <= _screenData.h);
- assert(w > 0 && x + w <= _screenData.w);
-
- // Copy buffer data to game screen internal buffer
- const byte *src = (const byte *)buf;
- byte *dst = (byte *)_screenData.getBasePtr(x, y);
- for (int i = 0; i < h; i++) {
- memcpy(dst, src, w * _screenData.format.bytesPerPixel);
- src += pitch;
- dst += _screenData.pitch;
+void OpenGLGraphicsManager::updateScreen() {
+ if (!_gameScreen) {
+ return;
}
- // Extend dirty area if not full screen redraw is flagged
- if (!_screenNeedsRedraw) {
- const Common::Rect dirtyRect(x, y, x + w, y + h);
- _screenDirtyRect.extend(dirtyRect);
+ // Clear the screen buffer
+ GLCALL(glClear(GL_COLOR_BUFFER_BIT));
+
+ const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight();
+
+ // First step: Draw the (virtual) game screen.
+ _gameScreen->draw(_displayX, _displayY + shakeOffset, _displayWidth, _displayHeight);
+
+ // Second step: Draw the overlay if visible.
+ if (_overlayVisible) {
+ _overlay->draw(0, 0, _outputScreenWidth, _outputScreenHeight);
}
-}
-Graphics::Surface *OpenGLGraphicsManager::lockScreen() {
- return &_screenData;
-}
+ // Third step: Draw the cursor if visible.
+ if (_cursorVisible && _cursor) {
+ // Adjust game screen shake position, but only when the overlay is not
+ // visible.
+ const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset;
-void OpenGLGraphicsManager::unlockScreen() {
- _screenNeedsRedraw = true;
-}
+ _cursor->draw(_cursorX - _cursorHotspotXScaled, _cursorY - _cursorHotspotYScaled + cursorOffset,
+ _cursorWidthScaled, _cursorHeightScaled);
+ }
-void OpenGLGraphicsManager::fillScreen(uint32 col) {
- if (_gameTexture == NULL)
- return;
+#ifdef USE_OSD
+ // Fourth step: Draw the OSD.
+ if (_osdAlpha > 0) {
+ Common::StackLock lock(_osdMutex);
-#ifdef USE_RGB_COLOR
- if (_screenFormat.bytesPerPixel == 1) {
- memset(_screenData.getPixels(), col, _screenData.h * _screenData.pitch);
- } else if (_screenFormat.bytesPerPixel == 2) {
- uint16 *pixels = (uint16 *)_screenData.getPixels();
- uint16 col16 = (uint16)col;
- for (int i = 0; i < _screenData.w * _screenData.h; i++) {
- pixels[i] = col16;
- }
- } else if (_screenFormat.bytesPerPixel == 3) {
- uint8 *pixels = (uint8 *)_screenData.getPixels();
- byte r = (col >> 16) & 0xFF;
- byte g = (col >> 8) & 0xFF;
- byte b = col & 0xFF;
- for (int i = 0; i < _screenData.w * _screenData.h; i++) {
- pixels[0] = r;
- pixels[1] = g;
- pixels[2] = b;
- pixels += 3;
- }
- } else if (_screenFormat.bytesPerPixel == 4) {
- uint32 *pixels = (uint32 *)_screenData.getPixels();
- for (int i = 0; i < _screenData.w * _screenData.h; i++) {
- pixels[i] = col;
+ // Update alpha value.
+ const int diff = g_system->getMillis(false) - _osdFadeStartTime;
+ if (diff > 0) {
+ if (diff >= kOSDFadeOutDuration) {
+ // Back to full transparency.
+ _osdAlpha = 0;
+ } else {
+ // Do a fade out.
+ _osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration;
+ }
}
+
+ // Set the OSD transparency.
+ GLCALL(glColor4f(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f));
+
+ // Draw the OSD texture.
+ _osd->draw(0, 0, _outputScreenWidth, _outputScreenHeight);
+
+ // Reset color.
+ GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
}
-#else
- memset(_screenData.getPixels(), col, _screenData.h * _screenData.pitch);
#endif
- _screenNeedsRedraw = true;
}
-void OpenGLGraphicsManager::updateScreen() {
- assert(_transactionMode == kTransactionNone);
- internUpdateScreen();
+Graphics::Surface *OpenGLGraphicsManager::lockScreen() {
+ return _gameScreen->getSurface();
}
-void OpenGLGraphicsManager::setShakePos(int shakeOffset) {
- assert(_transactionMode == kTransactionNone);
- _shakePos = shakeOffset;
+void OpenGLGraphicsManager::unlockScreen() {
+ _gameScreen->flagDirty();
}
-void OpenGLGraphicsManager::setFocusRectangle(const Common::Rect &rect) {
+void OpenGLGraphicsManager::setFocusRectangle(const Common::Rect& rect) {
}
void OpenGLGraphicsManager::clearFocusRectangle() {
}
-//
-// Overlay
-//
-
-void OpenGLGraphicsManager::showOverlay() {
- assert(_transactionMode == kTransactionNone);
+int16 OpenGLGraphicsManager::getOverlayWidth() {
+ if (_overlay) {
+ return _overlay->getWidth();
+ } else {
+ return 0;
+ }
+}
- if (_overlayVisible)
- return;
+int16 OpenGLGraphicsManager::getOverlayHeight() {
+ if (_overlay) {
+ return _overlay->getHeight();
+ } else {
+ return 0;
+ }
+}
+void OpenGLGraphicsManager::showOverlay() {
_overlayVisible = true;
-
- clearOverlay();
}
void OpenGLGraphicsManager::hideOverlay() {
- assert(_transactionMode == kTransactionNone);
-
- if (!_overlayVisible)
- return;
-
_overlayVisible = false;
-
- clearOverlay();
}
Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
- return _overlayFormat;
+ return _overlay->getFormat();
}
-void OpenGLGraphicsManager::clearOverlay() {
- // Set all pixels to 0
- memset(_overlayData.getPixels(), 0, _overlayData.h * _overlayData.pitch);
- _overlayNeedsRedraw = true;
+void OpenGLGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
+ _overlay->copyRectToTexture(x, y, w, h, buf, pitch);
}
-void OpenGLGraphicsManager::grabOverlay(void *buf, int pitch) {
- const byte *src = (byte *)_overlayData.getPixels();
- byte *dst = (byte *)buf;
- for (int i = 0; i < _overlayData.h; i++) {
- // Copy overlay data to buffer
- memcpy(dst, src, _overlayData.pitch);
- dst += pitch;
- src += _overlayData.pitch;
- }
+void OpenGLGraphicsManager::clearOverlay() {
+ _overlay->fill(0);
}
-void OpenGLGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
- assert(_transactionMode == kTransactionNone);
-
- if (_overlayTexture == NULL)
- return;
-
- const byte *src = (const byte *)buf;
-
- // Clip the coordinates
- if (x < 0) {
- w += x;
- src -= x * 2;
- x = 0;
- }
-
- if (y < 0) {
- h += y;
- src -= y * pitch;
- y = 0;
- }
-
- if (w > _overlayData.w - x)
- w = _overlayData.w - x;
-
- if (h > _overlayData.h - y)
- h = _overlayData.h - y;
-
- if (w <= 0 || h <= 0)
- return;
+void OpenGLGraphicsManager::grabOverlay(void *buf, int pitch) {
+ const Graphics::Surface *overlayData = _overlay->getSurface();
- // Copy buffer data to internal overlay surface
- byte *dst = (byte *)_overlayData.getBasePtr(0, y);
- for (int i = 0; i < h; i++) {
- memcpy(dst + x * _overlayData.format.bytesPerPixel, src, w * _overlayData.format.bytesPerPixel);
- src += pitch;
- dst += _overlayData.pitch;
- }
+ const byte *src = (const byte *)overlayData->getPixels();
+ byte *dst = (byte *)buf;
- // Extend dirty area if not full screen redraw is flagged
- if (!_overlayNeedsRedraw) {
- const Common::Rect dirtyRect(x, y, x + w, y + h);
- _overlayDirtyRect.extend(dirtyRect);
+ for (uint h = overlayData->h; h > 0; --h) {
+ memcpy(dst, src, overlayData->w * overlayData->format.bytesPerPixel);
+ dst += pitch;
+ src += overlayData->pitch;
}
}
-int16 OpenGLGraphicsManager::getOverlayHeight() {
- return _videoMode.overlayHeight;
-}
-
-int16 OpenGLGraphicsManager::getOverlayWidth() {
- return _videoMode.overlayWidth;
-}
-
-//
-// Cursor
-//
-
bool OpenGLGraphicsManager::showMouse(bool visible) {
- if (_cursorVisible == visible)
- return visible;
-
bool last = _cursorVisible;
_cursorVisible = visible;
-
return last;
}
void OpenGLGraphicsManager::warpMouse(int x, int y) {
- int scaledX = x;
- int scaledY = y;
-
- int16 currentX = _cursorState.x;
- int16 currentY = _cursorState.y;
-
+ int16 currentX = _cursorX;
+ int16 currentY = _cursorY;
adjustMousePosition(currentX, currentY);
- // Do not adjust the real screen position, when the current game / overlay
- // coordinates match the requested coordinates. This avoids a slight
- // movement which might occur otherwise when the mouse is at a subpixel
- // position.
- if (x == currentX && y == currentY)
+ // Check whether the (virtual) coordinate actually changed. If not, then
+ // simply do nothing. This avoids ugly "jittering" due to the actual
+ // output screen having a bigger resolution than the virtual coordinates.
+ if (currentX == x && currentY == y) {
return;
+ }
- if (_videoMode.mode == OpenGL::GFX_NORMAL) {
- if (_videoMode.hardwareWidth != _videoMode.overlayWidth)
- scaledX = scaledX * _videoMode.hardwareWidth / _videoMode.overlayWidth;
- if (_videoMode.hardwareHeight != _videoMode.overlayHeight)
- scaledY = scaledY * _videoMode.hardwareHeight / _videoMode.overlayHeight;
-
- if (!_overlayVisible) {
- scaledX *= _videoMode.scaleFactor;
- scaledY *= _videoMode.scaleFactor;
+ // Scale the virtual coordinates into actual physical coordinates.
+ if (_overlayVisible) {
+ if (!_overlay) {
+ return;
}
+
+ // It might be confusing that we actually have to handle something
+ // here when the overlay is visible. This is because for very small
+ // resolutions we have a minimal overlay size and have to adjust
+ // for that.
+ x = (x * _outputScreenWidth) / _overlay->getWidth();
+ y = (y * _outputScreenHeight) / _overlay->getHeight();
} else {
- if (_overlayVisible) {
- if (_displayWidth != _videoMode.overlayWidth)
- scaledX = scaledX * _displayWidth / _videoMode.overlayWidth;
- if (_displayHeight != _videoMode.overlayHeight)
- scaledY = scaledY * _displayHeight / _videoMode.overlayHeight;
- } else {
- if (_displayWidth != _videoMode.screenWidth)
- scaledX = scaledX * _displayWidth / _videoMode.screenWidth;
- if (_displayHeight != _videoMode.screenHeight)
- scaledY = scaledY * _displayHeight / _videoMode.screenHeight;
+ if (!_gameScreen) {
+ return;
}
- scaledX += _displayX;
- scaledY += _displayY;
+ x = (x * _displayWidth) / _gameScreen->getWidth();
+ y = (y * _displayHeight) / _gameScreen->getHeight();
+
+ x += _displayX;
+ y += _displayY;
}
- setMousePosition(scaledX, scaledY);
- setInternalMousePosition(scaledX, scaledY);
+ setMousePosition(x, y);
+ setInternalMousePosition(x, y);
+}
+
+namespace {
+template<typename DstPixel, typename SrcPixel>
+void applyColorKey(DstPixel *dst, const SrcPixel *src, uint w, uint h, uint dstPitch, uint srcPitch, SrcPixel keyColor, DstPixel alphaMask) {
+ const uint srcAdd = srcPitch - w * sizeof(SrcPixel);
+ const uint dstAdd = dstPitch - w * sizeof(DstPixel);
+
+ while (h-- > 0) {
+ for (uint x = w; x > 0; --x, ++dst, ++src) {
+ if (*src == keyColor) {
+ *dst &= ~alphaMask;
+ }
+ }
+
+ dst = (DstPixel *)((byte *)dst + dstAdd);
+ src = (const SrcPixel *)((const byte *)src + srcAdd);
+ }
}
+} // End of anonymous namespace
void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
+ Graphics::PixelFormat inputFormat;
#ifdef USE_RGB_COLOR
- if (format)
- _cursorFormat = *format;
- else
- _cursorFormat = Graphics::PixelFormat::createFormatCLUT8();
+ if (format) {
+ inputFormat = *format;
+ } else {
+ inputFormat = Graphics::PixelFormat::createFormatCLUT8();
+ }
#else
- assert(keycolor <= 255);
- _cursorFormat = Graphics::PixelFormat::createFormatCLUT8();
+ inputFormat = Graphics::PixelFormat::createFormatCLUT8();
#endif
- // Allocate space for cursor data
- if (_cursorData.w != w || _cursorData.h != h ||
- _cursorData.format.bytesPerPixel != _cursorFormat.bytesPerPixel)
- _cursorData.create(w, h, _cursorFormat);
-
- // Save cursor data
- memcpy(_cursorData.getPixels(), buf, h * _cursorData.pitch);
+ // In case the color format has changed we will need to create the texture.
+ if (!_cursor || _cursor->getFormat() != inputFormat) {
+ delete _cursor;
+ _cursor = nullptr;
+
+ GLenum glIntFormat, glFormat, glType;
+
+ Graphics::PixelFormat textureFormat;
+ if (inputFormat.bytesPerPixel == 1 || (inputFormat.aBits() && getGLPixelFormat(inputFormat, glIntFormat, glFormat, glType))) {
+ // There is two cases when we can use the cursor format directly.
+ // The first is when it's CLUT8, here color key handling can
+ // always be applied because we use the alpha channel of
+ // _defaultFormatAlpha for that.
+ // The other is when the input format has alpha bits and
+ // furthermore is directly supported.
+ textureFormat = inputFormat;
+ } else {
+ textureFormat = _defaultFormatAlpha;
+ }
+ _cursor = createTexture(textureFormat, true);
+ assert(_cursor);
+ _cursor->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR);
+ }
- // Set cursor info
- _cursorState.w = w;
- _cursorState.h = h;
- _cursorState.hotX = hotspotX;
- _cursorState.hotY = hotspotY;
_cursorKeyColor = keycolor;
+ _cursorHotspotX = hotspotX;
+ _cursorHotspotY = hotspotY;
_cursorDontScale = dontScale;
- _cursorNeedsRedraw = true;
- refreshCursorScale();
+ _cursor->allocate(w, h);
+ if (inputFormat.bytesPerPixel == 1) {
+ // For CLUT8 cursors we can simply copy the input data into the
+ // texture.
+ _cursor->copyRectToTexture(0, 0, w, h, buf, w * inputFormat.bytesPerPixel);
+ } else {
+ // Otherwise it is a bit more ugly because we have to handle a key
+ // color properly.
+
+ Graphics::Surface *dst = _cursor->getSurface();
+ const uint srcPitch = w * inputFormat.bytesPerPixel;
+
+ // Copy the cursor data to the actual texture surface. This will make
+ // sure that the data is also converted to the expected format.
+ Graphics::crossBlit((byte *)dst->getPixels(), (const byte *)buf, dst->pitch, srcPitch,
+ w, h, dst->format, inputFormat);
+
+ // We apply the color key by setting the alpha bits of the pixels to
+ // fully transparent.
+ const uint32 aMask = (0xFF >> dst->format.aLoss) << dst->format.aShift;
+ if (dst->format.bytesPerPixel == 2) {
+ if (inputFormat.bytesPerPixel == 2) {
+ applyColorKey<uint16, uint16>((uint16 *)dst->getPixels(), (const uint16 *)buf, w, h,
+ dst->pitch, srcPitch, keycolor, aMask);
+ } else if (inputFormat.bytesPerPixel == 4) {
+ applyColorKey<uint16, uint32>((uint16 *)dst->getPixels(), (const uint32 *)buf, w, h,
+ dst->pitch, srcPitch, keycolor, aMask);
+ }
+ } else {
+ if (inputFormat.bytesPerPixel == 2) {
+ applyColorKey<uint32, uint16>((uint32 *)dst->getPixels(), (const uint16 *)buf, w, h,
+ dst->pitch, srcPitch, keycolor, aMask);
+ } else if (inputFormat.bytesPerPixel == 4) {
+ applyColorKey<uint32, uint32>((uint32 *)dst->getPixels(), (const uint32 *)buf, w, h,
+ dst->pitch, srcPitch, keycolor, aMask);
+ }
+ }
+
+ // Flag the texture as dirty.
+ _cursor->flagDirty();
+ }
+
+ // In case we actually use a palette set that up properly.
+ if (inputFormat.bytesPerPixel == 1) {
+ updateCursorPalette();
+ }
+
+ // Update the scaling.
+ recalculateCursorScaling();
}
void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
- assert(colors);
+ // FIXME: For some reason client code assumes that usage of this function
+ // automatically enables the cursor palette.
+ _cursorPaletteEnabled = true;
- // Save the cursor palette
memcpy(_cursorPalette + start * 3, colors, num * 3);
-
- _cursorPaletteDisabled = false;
- _cursorNeedsRedraw = true;
+ updateCursorPalette();
}
-//
-// Misc
-//
-
void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
- assert(_transactionMode == kTransactionNone);
- assert(msg);
-
#ifdef USE_OSD
- // Split the message into separate lines.
- _osdLines.clear();
+ // HACK: Actually no client code should use graphics functions from
+ // another thread. But the MT-32 emulator still does, thus we need to
+ // make sure this doesn't happen while a updateScreen call is done.
+ Common::StackLock lock(_osdMutex);
+ // Slip up the lines.
+ Common::Array<Common::String> osdLines;
Common::StringTokenizer tokenizer(msg, "\n");
- while (!tokenizer.empty())
- _osdLines.push_back(tokenizer.nextToken());
+ while (!tokenizer.empty()) {
+ osdLines.push_back(tokenizer.nextToken());
+ }
- // Request update of the texture
- _requireOSDUpdate = true;
+ // Do the actual drawing like the SDL backend.
+ const Graphics::Font *font = getFontOSD();
+ Graphics::Surface *dst = _osd->getSurface();
+ _osd->fill(0);
+ _osd->flagDirty();
+
+ // Determine a rect which would contain the message string (clipped to the
+ // screen dimensions).
+ const int vOffset = 6;
+ const int lineSpacing = 1;
+ const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
+ int width = 0;
+ int height = lineHeight * osdLines.size() + 2 * vOffset;
+ for (uint i = 0; i < osdLines.size(); i++) {
+ width = MAX(width, font->getStringWidth(osdLines[i]) + 14);
+ }
+
+ // Clip the rect
+ width = MIN<int>(width, dst->w);
+ height = MIN<int>(height, dst->h);
+
+ int dstX = (dst->w - width) / 2;
+ int dstY = (dst->h - height) / 2;
+
+ // Draw a dark gray rect.
+ const uint32 color = dst->format.RGBToColor(40, 40, 40);
+ dst->fillRect(Common::Rect(dstX, dstY, dstX + width, dstY + height), color);
+
+ // Render the message, centered, and in white
+ const uint32 white = dst->format.RGBToColor(255, 255, 255);
+ for (uint i = 0; i < osdLines.size(); ++i) {
+ font->drawString(dst, osdLines[i],
+ dstX, dstY + i * lineHeight + vOffset + lineSpacing, width,
+ white, Graphics::kTextAlignCenter);
+ }
- // Init the OSD display parameters, and the fade out
+ // Init the OSD display parameters.
_osdAlpha = kOSDInitialAlpha;
_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
#endif
}
-//
-// Intern
-//
+void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
+ assert(_gameScreen->hasPalette());
-void OpenGLGraphicsManager::setFullscreenMode(bool enable) {
- assert(_transactionMode == kTransactionActive);
+ memcpy(_gamePalette + start * 3, colors, num * 3);
+ _gameScreen->setPalette(start, num, colors);
- if (_oldVideoMode.setup && _oldVideoMode.fullscreen == enable)
- return;
+ // We might need to update the cursor palette here.
+ updateCursorPalette();
+}
- if (_transactionMode == kTransactionActive) {
- _videoMode.fullscreen = enable;
- _transactionDetails.needRefresh = true;
- }
+void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) {
+ assert(_gameScreen->hasPalette());
+
+ memcpy(colors, _gamePalette + start * 3, num * 3);
}
-void OpenGLGraphicsManager::refreshGameScreen() {
- if (_screenNeedsRedraw)
- _screenDirtyRect = Common::Rect(0, 0, _screenData.w, _screenData.h);
-
- int x = _screenDirtyRect.left;
- int y = _screenDirtyRect.top;
- int w = _screenDirtyRect.width();
- int h = _screenDirtyRect.height();
-
- if (_screenData.format.bytesPerPixel == 1) {
- // Create a temporary RGB888 surface
- byte *surface = new byte[w * h * 3];
-
- // Convert the paletted buffer to RGB888
- const byte *src = (byte *)_screenData.getBasePtr(0, y);
- src += x * _screenData.format.bytesPerPixel;
- byte *dst = surface;
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- dst[0] = _gamePalette[src[j] * 3];
- dst[1] = _gamePalette[src[j] * 3 + 1];
- dst[2] = _gamePalette[src[j] * 3 + 2];
- dst += 3;
- }
- src += _screenData.pitch;
+void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
+ _outputScreenWidth = width;
+ _outputScreenHeight = height;
+
+ // Setup coordinates system.
+ GLCALL(glViewport(0, 0, _outputScreenWidth, _outputScreenHeight));
+
+ GLCALL(glMatrixMode(GL_PROJECTION));
+ GLCALL(glLoadIdentity());
+#ifdef USE_GLES
+ GLCALL(glOrthof(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1));
+#else
+ GLCALL(glOrtho(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1));
+#endif
+ GLCALL(glMatrixMode(GL_MODELVIEW));
+ GLCALL(glLoadIdentity());
+
+ uint overlayWidth = width;
+ uint overlayHeight = height;
+
+ // WORKAROUND: We can only support surfaces up to the maximum supported
+ // texture size. Thus, in case we encounter a physical size bigger than
+ // this maximum texture size we will simply use an overlay as big as
+ // possible and then scale it to the physical display size. This sounds
+ // bad but actually all recent chips should support full HD resolution
+ // anyway. Thus, it should not be a real issue for modern hardware.
+ if ( overlayWidth > (uint)Texture::getMaximumTextureSize()
+ || overlayHeight > (uint)Texture::getMaximumTextureSize()) {
+ const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight;
+
+ if (outputAspect > (frac_t)FRAC_ONE) {
+ overlayWidth = Texture::getMaximumTextureSize();
+ overlayHeight = intToFrac(overlayWidth) / outputAspect;
+ } else {
+ overlayHeight = Texture::getMaximumTextureSize();
+ overlayWidth = fracToInt(overlayHeight * outputAspect);
}
+ }
- // Update the texture
- _gameTexture->updateBuffer(surface, w * 3, x, y, w, h);
+ // HACK: We limit the minimal overlay size to 256x200, which is the
+ // minimum of the dimensions of the two resolutions 256x240 (NES) and
+ // 320x200 (many DOS games use this). This hopefully assure that our
+ // GUI has working layouts.
+ overlayWidth = MAX<uint>(overlayWidth, 256);
+ overlayHeight = MAX<uint>(overlayHeight, 200);
+
+ if (!_overlay || _overlay->getFormat() != _defaultFormatAlpha) {
+ delete _overlay;
+ _overlay = nullptr;
+
+ _overlay = createTexture(_defaultFormatAlpha);
+ assert(_overlay);
+ // We always filter the overlay with GL_LINEAR. This assures it's
+ // readable in case it needs to be scaled and does not affect it
+ // otherwise.
+ _overlay->enableLinearFiltering(true);
+ }
+ _overlay->allocate(overlayWidth, overlayHeight);
+ _overlay->fill(0);
- // Free the temp surface
- delete[] surface;
- } else {
- // Update the texture
- _gameTexture->updateBuffer((byte *)_screenData.getBasePtr(x, y), _screenData.pitch, x, y, w, h);
+#ifdef USE_OSD
+ if (!_osd || _osd->getFormat() != _defaultFormatAlpha) {
+ delete _osd;
+ _osd = nullptr;
+
+ _osd = createTexture(_defaultFormatAlpha);
+ assert(_osd);
+ // We always filter the osd with GL_LINEAR. This assures it's
+ // readable in case it needs to be scaled and does not affect it
+ // otherwise.
+ _osd->enableLinearFiltering(true);
}
+ _osd->allocate(_overlay->getWidth(), _overlay->getHeight());
+ _osd->fill(0);
+#endif
- _screenNeedsRedraw = false;
- _screenDirtyRect = Common::Rect();
+ // Re-setup the scaling for the screen and cursor
+ recalculateDisplayArea();
+ recalculateCursorScaling();
+
+ // Something changed, so update the screen change ID.
+ ++_screenChangeID;
}
-void OpenGLGraphicsManager::refreshOverlay() {
- if (_overlayNeedsRedraw)
- _overlayDirtyRect = Common::Rect(0, 0, _overlayData.w, _overlayData.h);
-
- int x = _overlayDirtyRect.left;
- int y = _overlayDirtyRect.top;
- int w = _overlayDirtyRect.width();
- int h = _overlayDirtyRect.height();
-
- if (_overlayData.format.bytesPerPixel == 1) {
- // Create a temporary RGB888 surface
- byte *surface = new byte[w * h * 3];
-
- // Convert the paletted buffer to RGB888
- const byte *src = (byte *)_overlayData.getBasePtr(0, y);
- src += x * _overlayData.format.bytesPerPixel;
- byte *dst = surface;
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- dst[0] = _gamePalette[src[j] * 3];
- dst[1] = _gamePalette[src[j] * 3 + 1];
- dst[2] = _gamePalette[src[j] * 3 + 2];
- dst += 3;
- }
- src += _screenData.pitch;
- }
+void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha) {
+ // Initialize all extensions.
+ initializeGLExtensions();
- // Update the texture
- _overlayTexture->updateBuffer(surface, w * 3, x, y, w, h);
+ // Disable 3D properties.
+ GLCALL(glDisable(GL_CULL_FACE));
+ GLCALL(glDisable(GL_DEPTH_TEST));
+ GLCALL(glDisable(GL_LIGHTING));
+ GLCALL(glDisable(GL_FOG));
+ GLCALL(glDisable(GL_DITHER));
+ GLCALL(glShadeModel(GL_FLAT));
+ GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
- // Free the temp surface
- delete[] surface;
- } else {
- // Update the texture
- _overlayTexture->updateBuffer((byte *)_overlayData.getBasePtr(x, y), _overlayData.pitch, x, y, w, h);
+ // Default to black as clear color.
+ GLCALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
+ GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
+
+ // Setup alpha blend (for overlay and cursor).
+ GLCALL(glEnable(GL_BLEND));
+ GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+
+ // Enable rendering with vertex and coord arrays.
+ GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
+ GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
+
+ GLCALL(glEnable(GL_TEXTURE_2D));
+
+ // We use a "pack" alignment (when reading from textures) to 4 here,
+ // since the only place where we really use it is the BMP screenshot
+ // code and that requires the same alignment too.
+ GLCALL(glPixelStorei(GL_PACK_ALIGNMENT, 4));
+
+ // Query information needed by textures.
+ Texture::queryTextureInformation();
+
+ // Refresh the output screen dimensions if some are set up.
+ if (_outputScreenWidth != 0 && _outputScreenHeight != 0) {
+ setActualScreenSize(_outputScreenWidth, _outputScreenHeight);
}
- _overlayNeedsRedraw = false;
- _overlayDirtyRect = Common::Rect();
-}
+ // TODO: Should we try to convert textures into one of those formats if
+ // possible? For example, when _gameScreen is CLUT8 we might want to use
+ // defaultFormat now.
+ _defaultFormat = defaultFormat;
+ _defaultFormatAlpha = defaultFormatAlpha;
-void OpenGLGraphicsManager::refreshCursor() {
- _cursorNeedsRedraw = false;
-
- // Allocate a texture big enough for cursor
- _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
-
- // Create a temporary RGBA8888 surface
- byte *surface = new byte[_cursorState.w * _cursorState.h * 4];
- memset(surface, 0, _cursorState.w * _cursorState.h * 4);
-
- byte *dst = surface;
-
- // Convert the paletted cursor to RGBA8888
- if (_cursorFormat.bytesPerPixel == 1) {
- // Select palette
- byte *palette;
- if (_cursorPaletteDisabled)
- palette = _gamePalette;
- else
- palette = _cursorPalette;
-
- // Convert the paletted cursor to RGBA8888
- const byte *src = (byte *)_cursorData.getPixels();
- for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
- // Check for keycolor
- if (src[i] != _cursorKeyColor) {
- dst[0] = palette[src[i] * 3];
- dst[1] = palette[src[i] * 3 + 1];
- dst[2] = palette[src[i] * 3 + 2];
- dst[3] = 255;
- }
- dst += 4;
- }
- } else {
- const bool gotNoAlpha = (_cursorFormat.aLoss == 8);
-
- // Convert the RGB cursor to RGBA8888
- if (_cursorFormat.bytesPerPixel == 2) {
- const uint16 *src = (uint16 *)_cursorData.getPixels();
- for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
- // Check for keycolor
- if (src[i] != _cursorKeyColor) {
- _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]);
-
- if (gotNoAlpha)
- dst[3] = 255;
- }
- dst += 4;
- }
- } else if (_cursorFormat.bytesPerPixel == 4) {
- const uint32 *src = (uint32 *)_cursorData.getPixels();
- for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
- // Check for keycolor
- if (src[i] != _cursorKeyColor) {
- _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]);
-
- if (gotNoAlpha)
- dst[3] = 255;
- }
- dst += 4;
- }
- }
+ if (_gameScreen) {
+ _gameScreen->recreateInternalTexture();
}
- // Update the texture with new cursor
- _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h);
+ if (_overlay) {
+ _overlay->recreateInternalTexture();
+ }
+
+ if (_cursor) {
+ _cursor->recreateInternalTexture();
+ }
- // Free the temp surface
- delete[] surface;
+#ifdef USE_OSD
+ if (_osd) {
+ _osd->recreateInternalTexture();
+ }
+#endif
}
-void OpenGLGraphicsManager::refreshCursorScale() {
- // Calculate the scale factors of the screen.
- // We also totally ignore the aspect of the overlay cursor, since aspect
- // ratio correction only applies to the game screen.
- // TODO: It might make sense to always ignore scaling of the mouse cursor
- // when the overlay is visible.
- uint screenScaleFactorX = _videoMode.hardwareWidth * 10000 / _videoMode.screenWidth;
- uint screenScaleFactorY = _videoMode.hardwareHeight * 10000 / _videoMode.screenHeight;
-
- // Ignore scaling when the cursor should not be scaled.
- if (_cursorDontScale) {
- screenScaleFactorX = 10000;
- screenScaleFactorY = 10000;
+void OpenGLGraphicsManager::notifyContextDestroy() {
+ if (_gameScreen) {
+ _gameScreen->releaseInternalTexture();
}
- // Apply them (without any possible) aspect ratio correction to the
- // overlay.
- _cursorState.rW = (int16)(_cursorState.w * screenScaleFactorX / 10000);
- _cursorState.rH = (int16)(_cursorState.h * screenScaleFactorY / 10000);
- _cursorState.rHotX = (int16)(_cursorState.hotX * screenScaleFactorX / 10000);
- _cursorState.rHotY = (int16)(_cursorState.hotY * screenScaleFactorY / 10000);
-
- // Only apply scaling when it's desired.
- if (_cursorDontScale) {
- screenScaleFactorX = 10000;
- screenScaleFactorY = 10000;
- } else {
- // Make sure we properly scale the cursor according to the desired aspect.
- int width, height;
- calculateDisplaySize(width, height);
- screenScaleFactorX = (width * 10000 / _videoMode.screenWidth);
- screenScaleFactorY = (height * 10000 / _videoMode.screenHeight);
+ if (_overlay) {
+ _overlay->releaseInternalTexture();
}
- // Apply the scale cursor scaling for the game screen.
- _cursorState.vW = (int16)(_cursorState.w * screenScaleFactorX / 10000);
- _cursorState.vH = (int16)(_cursorState.h * screenScaleFactorY / 10000);
- _cursorState.vHotX = (int16)(_cursorState.hotX * screenScaleFactorX / 10000);
- _cursorState.vHotY = (int16)(_cursorState.hotY * screenScaleFactorY / 10000);
+ if (_cursor) {
+ _cursor->releaseInternalTexture();
+ }
+
+#ifdef USE_OSD
+ if (_osd) {
+ _osd->releaseInternalTexture();
+ }
+#endif
}
-void OpenGLGraphicsManager::calculateDisplaySize(int &width, int &height) {
- if (_videoMode.mode == OpenGL::GFX_ORIGINAL) {
- width = _videoMode.screenWidth;
- height = _videoMode.screenHeight;
- } else {
- width = _videoMode.hardwareWidth;
- height = _videoMode.hardwareHeight;
+void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
+ if (_overlayVisible) {
+ // It might be confusing that we actually have to handle something
+ // here when the overlay is visible. This is because for very small
+ // resolutions we have a minimal overlay size and have to adjust
+ // for that.
+ // This can also happen when the overlay is smaller than the actual
+ // display size because of texture size limitations.
+ if (_overlay) {
+ x = (x * _overlay->getWidth()) / _outputScreenWidth;
+ y = (y * _overlay->getHeight()) / _outputScreenHeight;
+ }
+ } else if (_gameScreen) {
+ x -= _displayX;
+ y -= _displayY;
+
+ const int16 width = _gameScreen->getWidth();
+ const int16 height = _gameScreen->getHeight();
- uint aspectRatio = (_videoMode.hardwareWidth * 10000 + 5000) / _videoMode.hardwareHeight;
- uint desiredAspectRatio = getAspectRatio();
+ x = (x * width) / _displayWidth;
+ y = (y * height) / _displayHeight;
- // Adjust one screen dimension for mantaining the aspect ratio
- if (aspectRatio < desiredAspectRatio)
- height = (width * 10000 + 5000) / desiredAspectRatio;
- else if (aspectRatio > desiredAspectRatio)
- width = (height * desiredAspectRatio + 5000) / 10000;
+ // Make sure we only supply valid coordinates.
+ x = CLIP<int16>(x, 0, width - 1);
+ y = CLIP<int16>(y, 0, height - 1);
}
}
-void OpenGLGraphicsManager::refreshDisplaySize() {
- calculateDisplaySize(_displayWidth, _displayHeight);
-
- // Adjust x and y for centering the screen
- _displayX = (_videoMode.hardwareWidth - _displayWidth) / 2;
- _displayY = (_videoMode.hardwareHeight - _displayHeight) / 2;
+Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) {
+ GLenum glIntFormat, glFormat, glType;
+ if (format.bytesPerPixel == 1) {
+ const Graphics::PixelFormat &virtFormat = wantAlpha ? _defaultFormatAlpha : _defaultFormat;
+ const bool supported = getGLPixelFormat(virtFormat, glIntFormat, glFormat, glType);
+ if (!supported) {
+ return nullptr;
+ } else {
+ return new TextureCLUT8(glIntFormat, glFormat, glType, virtFormat);
+ }
+ } else {
+ const bool supported = getGLPixelFormat(format, glIntFormat, glFormat, glType);
+ if (!supported) {
+ return nullptr;
+ } else {
+ return new Texture(glIntFormat, glFormat, glType, format);
+ }
+ }
}
-void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &gltype) {
+bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const {
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
+#else
if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
- bpp = 4;
- intFormat = GL_RGBA;
+#endif
+ glIntFormat = GL_RGBA;
glFormat = GL_RGBA;
- gltype = GL_UNSIGNED_INT_8_8_8_8;
- } else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0)) { // RGB888
- bpp = 3;
- intFormat = GL_RGB;
- glFormat = GL_RGB;
- gltype = GL_UNSIGNED_BYTE;
+ glType = GL_UNSIGNED_BYTE;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565
- bpp = 2;
- intFormat = GL_RGB;
+ glIntFormat = GL_RGB;
glFormat = GL_RGB;
- gltype = GL_UNSIGNED_SHORT_5_6_5;
- } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGB5551
- bpp = 2;
- intFormat = GL_RGBA;
+ glType = GL_UNSIGNED_SHORT_5_6_5;
+ return true;
+ } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGBA5551
+ glIntFormat = GL_RGBA;
glFormat = GL_RGBA;
- gltype = GL_UNSIGNED_SHORT_5_5_5_1;
+ glType = GL_UNSIGNED_SHORT_5_5_5_1;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444
- bpp = 2;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_RGBA;
- gltype = GL_UNSIGNED_SHORT_4_4_4_4;
- } else if (pixelFormat.bytesPerPixel == 1) { // CLUT8
- // If uses a palette, create texture as RGB888. The pixel data will be converted
- // later.
- bpp = 3;
- intFormat = GL_RGB;
- glFormat = GL_RGB;
- gltype = GL_UNSIGNED_BYTE;
+ glType = GL_UNSIGNED_SHORT_4_4_4_4;
+ return true;
#ifndef USE_GLES
+#ifdef SCUMM_LITTLE_ENDIAN
+ } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
+ glIntFormat = GL_RGBA;
+ glFormat = GL_RGBA;
+ glType = GL_UNSIGNED_INT_8_8_8_8;
+ return true;
+#endif
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555
// GL_BGRA does not exist in every GLES implementation so should not be configured if
// USE_GLES is set.
- bpp = 2;
- intFormat = GL_RGB;
+ glIntFormat = GL_RGB;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888
- bpp = 4;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
+ glType = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444
- bpp = 2;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV;
+ glType = GL_UNSIGNED_SHORT_4_4_4_4_REV;
+ return true;
+#ifdef SCUMM_BIG_ENDIAN
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
- bpp = 4;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_RGBA;
- gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
+ glType = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return true;
+#endif
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888
- bpp = 4;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_BYTE;
- } else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0)) { // BGR888
- bpp = 3;
- intFormat = GL_RGB;
- glFormat = GL_BGR;
- gltype = GL_UNSIGNED_BYTE;
+ glType = GL_UNSIGNED_INT_8_8_8_8;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565
- bpp = 2;
- intFormat = GL_RGB;
+ glIntFormat = GL_RGB;
glFormat = GL_BGR;
- gltype = GL_UNSIGNED_SHORT_5_6_5;
+ glType = GL_UNSIGNED_SHORT_5_6_5;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551
- bpp = 2;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_SHORT_5_5_5_1;
+ glType = GL_UNSIGNED_SHORT_5_5_5_1;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444
- bpp = 2;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_RGBA;
- gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV;
+ glType = GL_UNSIGNED_SHORT_4_4_4_4_REV;
+ return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444
- bpp = 2;
- intFormat = GL_RGBA;
+ glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
- gltype = GL_UNSIGNED_SHORT_4_4_4_4;
+ glType = GL_UNSIGNED_SHORT_4_4_4_4;
+ return true;
#endif
} else {
- error("OpenGLGraphicsManager: Pixel format not supported");
+ return false;
}
}
-void OpenGLGraphicsManager::internUpdateScreen() {
- // Clear the screen buffer
- glClear(GL_COLOR_BUFFER_BIT); CHECK_GL_ERROR();
-
- if (_screenNeedsRedraw || !_screenDirtyRect.isEmpty())
- // Refresh texture if dirty
- refreshGameScreen();
-
- int scaleFactor = _videoMode.hardwareHeight / _videoMode.screenHeight;
-
- glPushMatrix();
-
- // Adjust game screen shake position
- glTranslatef(0, _shakePos * scaleFactor, 0); CHECK_GL_ERROR();
-
- // Draw the game screen
- _gameTexture->drawTexture(_displayX, _displayY, _displayWidth, _displayHeight);
-
- glPopMatrix();
-
- if (_overlayVisible) {
- if (_overlayNeedsRedraw || !_overlayDirtyRect.isEmpty())
- // Refresh texture if dirty
- refreshOverlay();
-
- // Draw the overlay
- _overlayTexture->drawTexture(0, 0, _videoMode.overlayWidth, _videoMode.overlayHeight);
- }
-
- if (_cursorVisible) {
- if (_cursorNeedsRedraw)
- // Refresh texture if dirty
- refreshCursor();
+frac_t OpenGLGraphicsManager::getDesiredGameScreenAspect() const {
+ const uint width = _currentState.gameWidth;
+ const uint height = _currentState.gameHeight;
- glPushMatrix();
-
- // Adjust mouse shake position, unless the overlay is visible
- glTranslatef(0, _overlayVisible ? 0 : _shakePos * scaleFactor, 0); CHECK_GL_ERROR();
-
- // Draw the cursor
- if (_overlayVisible)
- _cursorTexture->drawTexture(_cursorState.x - _cursorState.rHotX,
- _cursorState.y - _cursorState.rHotY, _cursorState.rW, _cursorState.rH);
- else
- _cursorTexture->drawTexture(_cursorState.x - _cursorState.vHotX,
- _cursorState.y - _cursorState.vHotY, _cursorState.vW, _cursorState.vH);
-
- glPopMatrix();
- }
-
-#ifdef USE_OSD
- if (_osdAlpha > 0) {
- if (_requireOSDUpdate) {
- updateOSD();
- _requireOSDUpdate = false;
- }
-
- // Update alpha value
- const int diff = g_system->getMillis() - _osdFadeStartTime;
- if (diff > 0) {
- if (diff >= kOSDFadeOutDuration) {
- // Back to full transparency
- _osdAlpha = 0;
- } else {
- // Do a fade out
- _osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration;
- }
+ if (_currentState.aspectRatioCorrection) {
+ // In case we enable aspect ratio correction we force a 4/3 ratio.
+ // But just for 320x200 and 640x400 games, since other games do not need
+ // this.
+ if ((width == 320 && height == 200) || (width == 640 && height == 400)) {
+ return intToFrac(4) / 3;
}
- // Set the osd transparency
- glColor4f(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f); CHECK_GL_ERROR();
-
- // Draw the osd texture
- _osdTexture->drawTexture(0, 0, _videoMode.hardwareWidth, _videoMode.hardwareHeight);
-
- // Reset color
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f); CHECK_GL_ERROR();
}
-#endif
-}
-
-void OpenGLGraphicsManager::initGL() {
- // Check available GL Extensions
- GLTexture::initGLExtensions();
-
- // Disable 3D properties
- glDisable(GL_CULL_FACE); CHECK_GL_ERROR();
- glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR();
- glDisable(GL_LIGHTING); CHECK_GL_ERROR();
- glDisable(GL_FOG); CHECK_GL_ERROR();
- glDisable(GL_DITHER); CHECK_GL_ERROR();
- glShadeModel(GL_FLAT); CHECK_GL_ERROR();
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); CHECK_GL_ERROR();
-
- // Setup alpha blend (For overlay and cursor)
- glEnable(GL_BLEND); CHECK_GL_ERROR();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CHECK_GL_ERROR();
-
- // Enable rendering with vertex and coord arrays
- glEnableClientState(GL_VERTEX_ARRAY); CHECK_GL_ERROR();
- glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR();
- glEnable(GL_TEXTURE_2D); CHECK_GL_ERROR();
-
- // Setup the GL viewport
- glViewport(0, 0, _videoMode.hardwareWidth, _videoMode.hardwareHeight); CHECK_GL_ERROR();
-
- // Setup coordinates system
- glMatrixMode(GL_PROJECTION); CHECK_GL_ERROR();
- glLoadIdentity(); CHECK_GL_ERROR();
-#ifdef USE_GLES
- glOrthof(0, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 0, -1, 1); CHECK_GL_ERROR();
-#else
- glOrtho(0, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 0, -1, 1); CHECK_GL_ERROR();
-#endif
- glMatrixMode(GL_MODELVIEW); CHECK_GL_ERROR();
- glLoadIdentity(); CHECK_GL_ERROR();
+ return intToFrac(width) / height;
}
-void OpenGLGraphicsManager::loadTextures() {
-#ifdef USE_RGB_COLOR
- if (_transactionDetails.formatChanged && _gameTexture) {
- delete _gameTexture;
- _gameTexture = 0;
+void OpenGLGraphicsManager::recalculateDisplayArea() {
+ if (!_gameScreen || _outputScreenHeight == 0) {
+ return;
}
-#endif
- if (!_gameTexture) {
- byte bpp;
- GLenum intformat;
- GLenum format;
- GLenum type;
-#ifdef USE_RGB_COLOR
- getGLPixelFormat(_screenFormat, bpp, intformat, format, type);
-#else
- getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, intformat, format, type);
-#endif
- _gameTexture = new GLTexture(bpp, intformat, format, type);
- } else
- _gameTexture->refresh();
-
- _overlayFormat = Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0);
-
- if (!_overlayTexture) {
- byte bpp;
- GLenum intformat;
- GLenum format;
- GLenum type;
- getGLPixelFormat(_overlayFormat, bpp, intformat, format, type);
- _overlayTexture = new GLTexture(bpp, intformat, format, type);
- } else
- _overlayTexture->refresh();
-
- if (!_cursorTexture)
- _cursorTexture = new GLTexture(4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
- else
- _cursorTexture->refresh();
-
- GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST;
- _gameTexture->setFilter(filter);
- _overlayTexture->setFilter(filter);
- _cursorTexture->setFilter(filter);
-
- // Allocate texture memory and finish refreshing
- _gameTexture->allocBuffer(_videoMode.screenWidth, _videoMode.screenHeight);
- _overlayTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight);
- _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
-
- if (
-#ifdef USE_RGB_COLOR
- _transactionDetails.formatChanged ||
-#endif
- _oldVideoMode.screenWidth != _videoMode.screenWidth ||
- _oldVideoMode.screenHeight != _videoMode.screenHeight)
- _screenData.create(_videoMode.screenWidth, _videoMode.screenHeight,
-#ifdef USE_RGB_COLOR
- _screenFormat
-#else
- Graphics::PixelFormat::createFormatCLUT8()
-#endif
- );
-
-
- if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth ||
- _oldVideoMode.overlayHeight != _videoMode.overlayHeight)
- _overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight,
- _overlayFormat);
+ const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight;
+ const frac_t desiredAspect = getDesiredGameScreenAspect();
- _screenNeedsRedraw = true;
- _overlayNeedsRedraw = true;
- _cursorNeedsRedraw = true;
+ _displayWidth = _outputScreenWidth;
+ _displayHeight = _outputScreenHeight;
- // We need to setup a proper unpack alignment value here, else we will
- // get problems with the texture updates, in case the surface data is
- // not properly aligned.
- // It is noteworthy this assumes the OSD uses the same BPP as the overlay
- // and that the cursor works with any alignment setting.
- int newAlignment = Common::gcd(_gameTexture->getBytesPerPixel(), _overlayTexture->getBytesPerPixel());
- assert(newAlignment == 1 || newAlignment == 2 || newAlignment == 4);
- glPixelStorei(GL_UNPACK_ALIGNMENT, newAlignment);
-
- // We use a "pack" alignment (when reading from textures) to 4 here,
- // since the only place where we really use it is the BMP screenshot
- // code and that requires the same alignment too.
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
-#ifdef USE_OSD
- if (!_osdTexture)
- _osdTexture = new GLTexture(2, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
- else
- _osdTexture->refresh();
-
- _osdTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight);
+ // Adjust one dimension for mantaining the aspect ratio.
+ if (outputAspect < desiredAspect) {
+ _displayHeight = intToFrac(_displayWidth) / desiredAspect;
+ } else if (outputAspect > desiredAspect) {
+ _displayWidth = fracToInt(_displayHeight * desiredAspect);
+ }
- // Update the OSD in case it is used right now
- _requireOSDUpdate = true;
-#endif
+ // We center the screen in the middle for now.
+ _displayX = (_outputScreenWidth - _displayWidth ) / 2;
+ _displayY = (_outputScreenHeight - _displayHeight) / 2;
}
-bool OpenGLGraphicsManager::loadGFXMode() {
- // Initialize OpenGL settings
- initGL();
-
- loadTextures();
-
- refreshCursorScale();
-
- refreshDisplaySize();
-
- internUpdateScreen();
-
- return true;
-}
+void OpenGLGraphicsManager::updateCursorPalette() {
+ if (!_cursor || !_cursor->hasPalette()) {
+ return;
+ }
-void OpenGLGraphicsManager::unloadGFXMode() {
+ if (_cursorPaletteEnabled) {
+ _cursor->setPalette(0, 256, _cursorPalette);
+ } else {
+ _cursor->setPalette(0, 256, _gamePalette);
+ }
+ // We remove all alpha bits from the palette entry of the color key.
+ // This makes sure its properly handled as color key.
+ const Graphics::PixelFormat &hardwareFormat = _cursor->getHardwareFormat();
+ const uint32 aMask = (0xFF >> hardwareFormat.aLoss) << hardwareFormat.aShift;
+
+ if (hardwareFormat.bytesPerPixel == 2) {
+ uint16 *palette = (uint16 *)_cursor->getPalette() + _cursorKeyColor;
+ *palette &= ~aMask;
+ } else if (hardwareFormat.bytesPerPixel == 4) {
+ uint32 *palette = (uint32 *)_cursor->getPalette() + _cursorKeyColor;
+ *palette &= ~aMask;
+ } else {
+ warning("OpenGLGraphicsManager::updateCursorPalette: Unsupported pixel depth %d", hardwareFormat.bytesPerPixel);
+ }
}
-void OpenGLGraphicsManager::setScale(int newScale) {
- assert(_transactionMode == kTransactionActive);
-
- if (newScale == _videoMode.scaleFactor)
+void OpenGLGraphicsManager::recalculateCursorScaling() {
+ if (!_cursor || !_gameScreen) {
return;
+ }
- _videoMode.scaleFactor = newScale;
- _transactionDetails.sizeChanged = true;
-}
+ // By default we use the unscaled versions.
+ _cursorHotspotXScaled = _cursorHotspotX;
+ _cursorHotspotYScaled = _cursorHotspotY;
+ _cursorWidthScaled = _cursor->getWidth();
+ _cursorHeightScaled = _cursor->getHeight();
-void OpenGLGraphicsManager::toggleAntialiasing() {
- assert(_transactionMode == kTransactionActive);
+ // In case scaling is actually enabled we will scale the cursor according
+ // to the game screen.
+ if (!_cursorDontScale) {
+ const frac_t screenScaleFactorX = intToFrac(_displayWidth) / _gameScreen->getWidth();
+ const frac_t screenScaleFactorY = intToFrac(_displayHeight) / _gameScreen->getHeight();
- _videoMode.antialiasing = !_videoMode.antialiasing;
- _transactionDetails.filterChanged = true;
-}
+ _cursorHotspotXScaled = fracToInt(_cursorHotspotXScaled * screenScaleFactorX);
+ _cursorWidthScaled = fracToInt(_cursorWidthScaled * screenScaleFactorX);
-uint OpenGLGraphicsManager::getAspectRatio() const {
- // In case we enable aspect ratio correction we force a 4/3 ratio.
- // But just for 320x200 and 640x400 games, since other games do not need
- // this.
- // TODO: This makes OpenGL Normal behave like OpenGL Conserve, when aspect
- // ratio correction is enabled, but it's better than the previous 4/3 mode
- // mess at least...
- if (_videoMode.aspectRatioCorrection
- && ((_videoMode.screenWidth == 320 && _videoMode.screenHeight == 200)
- || (_videoMode.screenWidth == 640 && _videoMode.screenHeight == 400)))
- return 13333;
- else if (_videoMode.mode == OpenGL::GFX_NORMAL)
- return _videoMode.hardwareWidth * 10000 / _videoMode.hardwareHeight;
- else
- return _videoMode.screenWidth * 10000 / _videoMode.screenHeight;
+ _cursorHotspotYScaled = fracToInt(_cursorHotspotYScaled * screenScaleFactorY);
+ _cursorHeightScaled = fracToInt(_cursorHeightScaled * screenScaleFactorY);
+ }
}
-void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
- if (_overlayVisible)
- return;
-
- x -= _displayX;
- y -= _displayY;
-
- if (_displayWidth != _videoMode.screenWidth)
- x = x * _videoMode.screenWidth / _displayWidth;
- if (_displayHeight != _videoMode.screenHeight)
- y = y * _videoMode.screenHeight / _displayHeight;
+#ifdef USE_OSD
+const Graphics::Font *OpenGLGraphicsManager::getFontOSD() {
+ return FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
}
+#endif
-bool OpenGLGraphicsManager::saveScreenshot(const char *filename) {
- int width = _videoMode.hardwareWidth;
- int height = _videoMode.hardwareHeight;
+void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const {
+ const uint width = _outputScreenWidth;
+ const uint height = _outputScreenHeight;
// A line of a BMP image must have a size divisible by 4.
// We calculate the padding bytes needed here.
// Since we use a 3 byte per pixel mode, we can use width % 4 here, since
// it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the
// usual way of computing the padding bytes required).
- const int linePaddingSize = width % 4;
- const int lineSize = width * 3 + linePaddingSize;
+ const uint linePaddingSize = width % 4;
+ const uint lineSize = width * 3 + linePaddingSize;
// Allocate memory for screenshot
uint8 *pixels = new uint8[lineSize * height];
// Get pixel data from OpenGL buffer
-#ifdef USE_GLES
- glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR();
-#else
- if (_formatBGR) {
- glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR();
- } else {
- glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR();
+ GLCALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels));
+
+ // BMP stores as BGR. Since we can't assume that GL_BGR is supported we
+ // will swap the components from the RGB we read to BGR on our own.
+ for (uint y = height; y-- > 0;) {
+ uint8 *line = pixels + y * lineSize;
+
+ for (uint x = width; x > 0; --x, line += 3) {
+ SWAP(line[0], line[2]);
+ }
}
-#endif
// Open file
Common::DumpFile out;
@@ -1324,73 +1161,6 @@ bool OpenGLGraphicsManager::saveScreenshot(const char *filename) {
// Free allocated memory
delete[] pixels;
-
- return true;
}
-const char *OpenGLGraphicsManager::getCurrentModeName() {
- const char *modeName = 0;
- const OSystem::GraphicsMode *g = getSupportedGraphicsModes();
- while (g->name) {
- if (g->id == _videoMode.mode) {
- modeName = g->description;
- break;
- }
- g++;
- }
- return modeName;
-}
-
-#ifdef USE_OSD
-const Graphics::Font *OpenGLGraphicsManager::getFontOSD() {
- return FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
-}
-
-void OpenGLGraphicsManager::updateOSD() {
- // The font we are going to use:
- const Graphics::Font *font = getFontOSD();
-
- if (_osdSurface.w != _osdTexture->getWidth() || _osdSurface.h != _osdTexture->getHeight())
- _osdSurface.create(_osdTexture->getWidth(), _osdTexture->getHeight(), _overlayFormat);
- else
- // Clear everything
- memset(_osdSurface.getPixels(), 0, _osdSurface.h * _osdSurface.pitch);
-
- // Determine a rect which would contain the message string (clipped to the
- // screen dimensions).
- const int vOffset = 6;
- const int lineSpacing = 1;
- const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
- int width = 0;
- int height = lineHeight * _osdLines.size() + 2 * vOffset;
- for (uint i = 0; i < _osdLines.size(); i++) {
- width = MAX(width, font->getStringWidth(_osdLines[i]) + 14);
- }
-
- // Clip the rect
- if (width > _osdSurface.w)
- width = _osdSurface.w;
- if (height > _osdSurface.h)
- height = _osdSurface.h;
-
- int dstX = (_osdSurface.w - width) / 2;
- int dstY = (_osdSurface.h - height) / 2;
-
- // Draw a dark gray rect (R = 40, G = 40, B = 40)
- const uint16 color = 0x294B;
- _osdSurface.fillRect(Common::Rect(dstX, dstY, dstX + width, dstY + height), color);
-
- // Render the message, centered, and in white
- for (uint i = 0; i < _osdLines.size(); i++) {
- font->drawString(&_osdSurface, _osdLines[i],
- dstX, dstY + i * lineHeight + vOffset + lineSpacing, width,
- 0xFFFF, Graphics::kTextAlignCenter);
- }
-
- // Update the texture
- _osdTexture->updateBuffer(_osdSurface.getPixels(), _osdSurface.pitch, 0, 0,
- _osdSurface.w, _osdSurface.h);
-}
-#endif
-
-#endif
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index 9d8d418d11..d16f92d148 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -8,338 +8,483 @@
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-#ifndef BACKENDS_GRAPHICS_OPENGL_H
-#define BACKENDS_GRAPHICS_OPENGL_H
+#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
+#define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
-#include "backends/graphics/opengl/gltexture.h"
+#include "backends/graphics/opengl/opengl-sys.h"
#include "backends/graphics/graphics.h"
-#include "common/array.h"
-#include "common/rect.h"
-#include "graphics/font.h"
-#include "graphics/pixelformat.h"
-// Uncomment this to enable the 'on screen display' code.
-#define USE_OSD 1
+#include "common/frac.h"
+#include "common/mutex.h"
+
+namespace Graphics {
+class Font;
+} // End of namespace Graphics
namespace OpenGL {
-// The OpenGL GFX modes. They have to be inside the OpenGL namespace so they
-// do not clash with the SDL GFX modes.
+
+// HACK: We use glColor in the OSD code. This might not be working on GL ES but
+// we still enable it because Tizen already shipped with it. Also, the
+// SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp.
+#define USE_OSD 1
+
+class Texture;
+
enum {
- GFX_NORMAL = 0,
- GFX_CONSERVE = 1,
- GFX_ORIGINAL = 2
+ GFX_LINEAR = 0,
+ GFX_NEAREST = 1
};
-}
-
-/**
- * OpenGL graphics manager. This is an abstract class, it does not do the
- * window and OpenGL context initialization.
- * Derived classes should at least override internUpdateScreen for doing
- * the buffers swap, and implement loadGFXMode for handling the window/context if
- * needed. If USE_RGB_COLOR is enabled, getSupportedFormats must be implemented.
- */
-class OpenGLGraphicsManager : public GraphicsManager {
+class OpenGLGraphicsManager : virtual public GraphicsManager {
public:
OpenGLGraphicsManager();
virtual ~OpenGLGraphicsManager();
+ // GraphicsManager API
virtual bool hasFeature(OSystem::Feature f);
virtual void setFeatureState(OSystem::Feature f, bool enable);
virtual bool getFeatureState(OSystem::Feature f);
- static const OSystem::GraphicsMode *supportedGraphicsModes();
virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode);
virtual int getGraphicsMode() const;
- virtual void resetGraphicsScale();
+
+ virtual void resetGraphicsScale() {}
+
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const;
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0;
#endif
- virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL);
- virtual int getScreenChangeID() const;
virtual void beginGFXTransaction();
virtual OSystem::TransactionError endGFXTransaction();
- virtual int16 getHeight();
+ virtual int getScreenChangeID() const;
+
+ virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format);
+
virtual int16 getWidth();
-protected:
- // PaletteManager API
- virtual void setPalette(const byte *colors, uint start, uint num);
- virtual void grabPalette(byte *colors, uint start, uint num);
+ virtual int16 getHeight();
-public:
virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
- virtual Graphics::Surface *lockScreen();
- virtual void unlockScreen();
virtual void fillScreen(uint32 col);
- virtual void updateScreen();
+
virtual void setShakePos(int shakeOffset);
- virtual void setFocusRectangle(const Common::Rect &rect);
+
+ virtual void updateScreen();
+
+ virtual Graphics::Surface *lockScreen();
+ virtual void unlockScreen();
+
+ virtual void setFocusRectangle(const Common::Rect& rect);
virtual void clearFocusRectangle();
+ virtual int16 getOverlayWidth();
+ virtual int16 getOverlayHeight();
+
virtual void showOverlay();
virtual void hideOverlay();
+
virtual Graphics::PixelFormat getOverlayFormat() const;
+
+ virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
virtual void clearOverlay();
virtual void grabOverlay(void *buf, int pitch);
- virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
- virtual int16 getOverlayHeight();
- virtual int16 getOverlayWidth();
virtual bool showMouse(bool visible);
virtual void warpMouse(int x, int y);
- virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
+ virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual void displayMessageOnOSD(const char *msg);
+
+ // PaletteManager interface
+ virtual void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(byte *colors, uint start, uint num);
+
protected:
/**
- * Setup OpenGL settings
+ * Set up the actual screen size available for the OpenGL code to do any
+ * drawing.
+ *
+ * @param width The width of the screen.
+ * @param height The height of the screen.
+ */
+ void setActualScreenSize(uint width, uint height);
+
+ /**
+ * Notify the manager of a OpenGL context change. This should be the first
+ * thing to call after you created an OpenGL (ES) context!
+ *
+ * @param defaultFormat The new default format for the game screen
+ * (this is used for the CLUT8 game screens).
+ * @param defaultFromatAlpha The new default format with an alpha channel
+ * (this is used for the overlay and cursor).
+ */
+ void notifyContextCreate(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha);
+
+ /**
+ * Notify the manager that the OpenGL context is about to be destroyed.
+ * This will free up/reset internal OpenGL related state and *must* be
+ * called whenever a context might be created again after destroying a
+ * context.
*/
- virtual void initGL();
+ void notifyContextDestroy();
/**
- * Creates and refreshs OpenGL textures.
+ * Adjust the physical mouse coordinates according to the currently visible screen.
*/
- virtual void loadTextures();
+ void adjustMousePosition(int16 &x, int16 &y);
+
+ /**
+ * Set up the mouse position for graphics output.
+ *
+ * @param x X coordinate in physical coordinates.
+ * @param y Y coordinate in physical coordinates.
+ */
+ void setMousePosition(int x, int y) { _cursorX = x; _cursorY = y; }
+
+ /**
+ * Query the mouse position in physical coordinates.
+ */
+ void getMousePosition(int16 &x, int16 &y) const { x = _cursorX; y = _cursorY; }
+
+ /**
+ * Set up the mouse position for the (event) system.
+ *
+ * @param x X coordinate in physical coordinates.
+ * @param y Y coordinate in physical coordinates.
+ */
+ virtual void setInternalMousePosition(int x, int y) = 0;
+
+private:
+ /**
+ * Create a texture with the specified pixel format.
+ *
+ * @param format The pixel format the Texture object should accept as
+ * input.
+ * @param wantAlpha For CLUT8 textures this marks whether an alpha
+ * channel should be used.
+ * @return A pointer to the texture or nullptr on failure.
+ */
+ Texture *createTexture(const Graphics::PixelFormat &format, bool wantAlpha = false);
//
- // GFX and video
+ // Transaction support
//
- enum {
- kTransactionNone = 0,
- kTransactionActive = 1,
- kTransactionRollback = 2
- };
-
- struct TransactionDetails {
- bool sizeChanged;
- bool needRefresh;
- bool needUpdatescreen;
- bool filterChanged;
+ struct VideoState {
+ VideoState() : valid(false), gameWidth(0), gameHeight(0),
#ifdef USE_RGB_COLOR
- bool formatChanged;
+ gameFormat(),
#endif
- };
- TransactionDetails _transactionDetails;
- int _transactionMode;
-
- struct VideoState {
- bool setup;
+ aspectRatioCorrection(false), graphicsMode(GFX_LINEAR) {
+ }
- bool fullscreen;
+ bool valid;
- int mode;
- int scaleFactor;
- bool antialiasing;
+ uint gameWidth, gameHeight;
+#ifdef USE_RGB_COLOR
+ Graphics::PixelFormat gameFormat;
+#endif
bool aspectRatioCorrection;
+ int graphicsMode;
- int screenWidth, screenHeight;
- int overlayWidth, overlayHeight;
- int hardwareWidth, hardwareHeight;
+ bool operator==(const VideoState &right) {
+ return gameWidth == right.gameWidth && gameHeight == right.gameHeight
#ifdef USE_RGB_COLOR
- Graphics::PixelFormat format;
+ && gameFormat == right.gameFormat
#endif
+ && aspectRatioCorrection == right.aspectRatioCorrection
+ && graphicsMode == right.graphicsMode;
+ }
+
+ bool operator!=(const VideoState &right) {
+ return !(*this == right);
+ }
};
- VideoState _videoMode, _oldVideoMode;
/**
- * Sets the OpenGL texture format for the given pixel format. If format is not support will raise an error.
+ * The currently setup video state.
+ */
+ VideoState _currentState;
+
+ /**
+ * The old video state used when doing a transaction rollback.
*/
- virtual void getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &type);
+ VideoState _oldState;
+
+protected:
+ enum TransactionMode {
+ kTransactionNone = 0,
+ kTransactionActive = 1,
+ kTransactionRollback = 2
+ };
- virtual void internUpdateScreen();
- virtual bool loadGFXMode();
- virtual void unloadGFXMode();
+ TransactionMode getTransactionMode() const { return _transactionMode; }
+private:
/**
- * Setup the fullscreen mode state.
+ * The current transaction mode.
*/
- void setFullscreenMode(bool enable);
+ TransactionMode _transactionMode;
/**
- * Query the fullscreen state.
+ * The current screen change ID.
*/
- inline bool getFullscreenMode() const { return _videoMode.fullscreen; }
+ int _screenChangeID;
+protected:
/**
- * Set the scale factor.
+ * Set up the requested video mode. This takes parameters which describe
+ * what resolution the game screen requests (this is possibly aspect ratio
+ * corrected!).
*
- * This can only be used in a GFX transaction.
+ * A sub-class should take these parameters as hints. It might very well
+ * set up a mode which it thinks suites the situation best.
*
- * @param newScale New scale factor.
+ * @parma requestedWidth This is the requested actual game screen width.
+ * @param requestedHeight This is the requested actual game screen height.
+ * @param format This is the requested pixel format of the virtual game screen.
+ * @return true on success, false otherwise
*/
- void setScale(int newScale);
+ virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) = 0;
/**
- * Query the scale factor.
+ * Save a screenshot of the full display as BMP to the given file. This
+ * uses Common::DumpFile for writing the screenshot.
+ *
+ * @param filename The output filename.
*/
- inline int getScale() const { return _videoMode.scaleFactor; }
+ void saveScreenshot(const Common::String &filename) const;
+
+private:
+ //
+ // OpenGL utilities
+ //
/**
- * Toggle the antialiasing state of the current video mode.
+ * Try to determine the internal parameters for a given pixel format.
*
- * This can only be used in a GFX transaction.
+ * @return true when the format can be used, false otherwise.
*/
- void toggleAntialiasing();
+ bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const;
+
+ //
+ // Actual hardware screen
+ //
/**
- * Query the antialiasing state.
+ * The width of the physical output.
*/
- inline bool getAntialiasingState() const { return _videoMode.antialiasing; }
+ uint _outputScreenWidth;
- // Drawing coordinates for the current display mode and scale
- int _displayX;
- int _displayY;
- int _displayWidth;
- int _displayHeight;
+ /**
+ * The height of the physical output.
+ */
+ uint _outputScreenHeight;
- virtual const char *getCurrentModeName();
+ /**
+ * @return The desired aspect of the game screen.
+ */
+ frac_t getDesiredGameScreenAspect() const;
- virtual void calculateDisplaySize(int &width, int &height);
- virtual void refreshDisplaySize();
+ /**
+ * Recalculates the area used to display the game screen.
+ */
+ void recalculateDisplayArea();
- uint getAspectRatio() const;
+ /**
+ * The X coordinate of the game screen.
+ */
+ uint _displayX;
- void setFormatIsBGR(bool isBGR) { _formatBGR = isBGR; }
- bool _formatBGR;
+ /**
+ * The Y coordinate of the game screen.
+ */
+ uint _displayY;
+
+ /**
+ * The width of the game screen in physical coordinates.
+ */
+ uint _displayWidth;
+
+ /**
+ * The height of the game screen in physical coordinates.
+ */
+ uint _displayHeight;
+
+ /**
+ * The default pixel format of the backend.
+ */
+ Graphics::PixelFormat _defaultFormat;
+
+ /**
+ * The default pixel format with an alpha channel.
+ */
+ Graphics::PixelFormat _defaultFormatAlpha;
//
// Game screen
//
- GLTexture *_gameTexture;
- Graphics::Surface _screenData;
- int _screenChangeCount;
- bool _screenNeedsRedraw;
- Common::Rect _screenDirtyRect;
-#ifdef USE_RGB_COLOR
- Graphics::PixelFormat _screenFormat;
-#endif
- byte *_gamePalette;
+ /**
+ * The virtual game screen.
+ */
+ Texture *_gameScreen;
- virtual void refreshGameScreen();
+ /**
+ * The game palette if in CLUT8 mode.
+ */
+ byte _gamePalette[3 * 256];
- // Shake mode
- int _shakePos;
+ /**
+ * The offset by which the screen is moved vertically.
+ */
+ int _gameScreenShakeOffset;
//
// Overlay
//
- GLTexture *_overlayTexture;
- Graphics::Surface _overlayData;
- Graphics::PixelFormat _overlayFormat;
- bool _overlayVisible;
- bool _overlayNeedsRedraw;
- Common::Rect _overlayDirtyRect;
- virtual void refreshOverlay();
+ /**
+ * The overlay screen.
+ */
+ Texture *_overlay;
+
+ /**
+ * Whether the overlay is visible or not.
+ */
+ bool _overlayVisible;
//
- // Mouse
+ // Cursor
//
- struct MousePos {
- // The mouse position in hardware screen coordinates.
- int16 x, y;
- // The size and hotspot of the original cursor image.
- int16 w, h;
- int16 hotX, hotY;
+ /**
+ * Set up the correct cursor palette.
+ */
+ void updateCursorPalette();
- // The size and hotspot of the scaled cursor, in real coordinates.
- int16 rW, rH;
- int16 rHotX, rHotY;
+ /**
+ * The cursor image.
+ */
+ Texture *_cursor;
- // The size and hotspot of the scaled cursor, in game coordinates.
- int16 vW, vH;
- int16 vHotX, vHotY;
+ /**
+ * X coordinate of the cursor in phyiscal coordinates.
+ */
+ int _cursorX;
- MousePos() : x(0), y(0), w(0), h(0), hotX(0), hotY(0),
- rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0),
- vHotX(0), vHotY(0) {}
- };
+ /**
+ * Y coordinate of the cursor in physical coordinates.
+ */
+ int _cursorY;
- GLTexture *_cursorTexture;
- Graphics::Surface _cursorData;
- Graphics::PixelFormat _cursorFormat;
- byte *_cursorPalette;
- bool _cursorPaletteDisabled;
- MousePos _cursorState;
- bool _cursorVisible;
+ /**
+ * The X offset for the cursor hotspot in unscaled coordinates.
+ */
+ int _cursorHotspotX;
+
+ /**
+ * The Y offset for the cursor hotspot in unscaled coordinates.
+ */
+ int _cursorHotspotY;
+
+ /**
+ * Recalculate the cursor scaling. Scaling is always done according to
+ * the game screen.
+ */
+ void recalculateCursorScaling();
+
+ /**
+ * The X offset for the cursor hotspot in scaled coordinates.
+ */
+ int _cursorHotspotXScaled;
+
+ /**
+ * The Y offset for the cursor hotspot in scaled coordinates.
+ */
+ int _cursorHotspotYScaled;
+
+ /**
+ * The width of the cursor scaled coordinates.
+ */
+ uint _cursorWidthScaled;
+
+ /**
+ * The height of the cursor scaled coordinates.
+ */
+ uint _cursorHeightScaled;
+
+ /**
+ * The key color.
+ */
uint32 _cursorKeyColor;
- bool _cursorDontScale;
- bool _cursorNeedsRedraw;
/**
- * Set up the mouse position for graphics output.
- *
- * @param x X coordinate in native coordinates.
- * @param y Y coordinate in native coordinates.
+ * Whether the cursor is actually visible.
*/
- void setMousePosition(int x, int y) { _cursorState.x = x; _cursorState.y = y; }
+ bool _cursorVisible;
- virtual void refreshCursor();
- virtual void refreshCursorScale();
+ /**
+ * Whether no cursor scaling should be applied.
+ */
+ bool _cursorDontScale;
/**
- * Set up the mouse position for the (event) system.
- *
- * @param x X coordinate in native coordinates.
- * @param y Y coordinate in native coordinates.
+ * Whether the special cursor palette is enabled.
*/
- virtual void setInternalMousePosition(int x, int y) = 0;
+ bool _cursorPaletteEnabled;
/**
- * Adjusts hardware screen coordinates to either overlay or game screen
- * coordinates depending on whether the overlay is visible or not.
- *
- * @param x X coordinate of the mouse position.
- * @param y Y coordinate of the mouse position.
+ * The special cursor palette in case enabled.
*/
- virtual void adjustMousePosition(int16 &x, int16 &y);
+ byte _cursorPalette[3 * 256];
+#ifdef USE_OSD
//
- // Misc
+ // OSD
//
- virtual bool saveScreenshot(const char *filename);
-
-#ifdef USE_OSD
+protected:
/**
* Returns the font used for on screen display
*/
virtual const Graphics::Font *getFontOSD();
+private:
/**
- * Update the OSD texture / surface.
+ * The OSD's contents.
*/
- void updateOSD();
+ Texture *_osd;
/**
- * The OSD contents.
+ * Current opacity level of the OSD.
*/
- Common::Array<Common::String> _osdLines;
-
- GLTexture *_osdTexture;
- Graphics::Surface _osdSurface;
uint8 _osdAlpha;
+
+ /**
+ * When fading the OSD has started.
+ */
uint32 _osdFadeStartTime;
- bool _requireOSDUpdate;
+
+ /**
+ * Mutex to allow displayMessageOnOSD to be used from the audio thread.
+ */
+ Common::Mutex _osdMutex;
+
enum {
kOSDFadeOutDelay = 2 * 1000,
kOSDFadeOutDuration = 500,
@@ -348,4 +493,6 @@ protected:
#endif
};
+} // End of namespace OpenGL
+
#endif
diff --git a/backends/graphics/opengl/glerrorcheck.cpp b/backends/graphics/opengl/opengl-sys.h
index 439593577d..a3524b28d2 100644
--- a/backends/graphics/opengl/glerrorcheck.cpp
+++ b/backends/graphics/opengl/opengl-sys.h
@@ -8,25 +8,25 @@
* 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
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-#include "common/scummsys.h"
+#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_H
+#define BACKENDS_GRAPHICS_OPENGL_OPENGL_H
-#if defined(DEBUG) && defined(USE_OPENGL)
+// The purpose of this header is to include the OpenGL headers in an uniform
+// fashion. A notable example for a non standard port is the Tizen port.
-#include "backends/graphics/opengl/glerrorcheck.h"
-#include "common/textconsole.h"
-#include "common/str.h"
+#include "common/scummsys.h"
#ifdef WIN32
#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
@@ -37,31 +37,21 @@
#undef ARRAYSIZE
#endif
-#if defined(USE_GLES)
+// HACK: In case common/util.h has been included already we need to make sure
+// to define ARRAYSIZE again in case of Windows.
+#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
+#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+#endif
+
+#if defined(TIZEN)
+#include <FGraphicsOpengl.h>
+using namespace Tizen::Graphics::Opengl;
+#elif defined(USE_GLES)
#include <GLES/gl.h>
-#elif defined(MACOSX)
-#include <OpenGL/gl.h>
+#elif defined(SDL_BACKEND)
+#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#endif
-static Common::String getGlErrStr(GLenum error) {
- switch (error) {
- case GL_NO_ERROR: return "GL_NO_ERROR";
- case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
- case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
- case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
- case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
- case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
- }
-
- return Common::String::format("(Unknown GL error code 0x%x)", error);
-}
-
-void checkGlError(const char *file, int line) {
- GLenum error = glGetError();
- if (error != GL_NO_ERROR)
- warning("%s:%d: GL error: %s", file, line, getGlErrStr(error).c_str());
-}
-
#endif
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
new file mode 100644
index 0000000000..7b0b22d630
--- /dev/null
+++ b/backends/graphics/opengl/texture.cpp
@@ -0,0 +1,374 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/texture.h"
+#include "backends/graphics/opengl/extensions.h"
+#include "backends/graphics/opengl/debug.h"
+
+#include "common/rect.h"
+#include "common/textconsole.h"
+
+namespace OpenGL {
+
+static GLuint nextHigher2(GLuint v) {
+ if (v == 0)
+ return 1;
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return ++v;
+}
+
+GLint Texture::_maxTextureSize = 0;
+
+void Texture::queryTextureInformation() {
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);
+ debug(5, "OpenGL maximum texture size: %d", _maxTextureSize);
+}
+
+Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
+ : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST),
+ _glTexture(0), _textureData(), _userPixelData(), _allDirty(false) {
+ recreateInternalTexture();
+}
+
+Texture::~Texture() {
+ releaseInternalTexture();
+ _textureData.free();
+}
+
+void Texture::releaseInternalTexture() {
+ GLCALL(glDeleteTextures(1, &_glTexture));
+ _glTexture = 0;
+}
+
+void Texture::recreateInternalTexture() {
+ // Release old texture name in case it exists.
+ releaseInternalTexture();
+
+ // Get a new texture name.
+ GLCALL(glGenTextures(1, &_glTexture));
+
+ // Set up all texture parameters.
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+ GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ // In case there is an actual texture setup we reinitialize it.
+ if (_textureData.getPixels()) {
+ // Allocate storage for OpenGL texture.
+ GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
+ _textureData.h, 0, _glFormat, _glType, NULL));
+
+ // Mark dirts such that it will be completely refreshed the next time.
+ flagDirty();
+ }
+}
+
+void Texture::enableLinearFiltering(bool enable) {
+ if (enable) {
+ _glFilter = GL_LINEAR;
+ } else {
+ _glFilter = GL_NEAREST;
+ }
+
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
+}
+
+void Texture::allocate(uint width, uint height) {
+ uint texWidth = width, texHeight = height;
+ if (!g_extNPOTSupported) {
+ texWidth = nextHigher2(texWidth);
+ texHeight = nextHigher2(texHeight);
+ }
+
+ // In case the needed texture dimension changed we will reinitialize the
+ // texture.
+ if (texWidth != _textureData.w || texHeight != _textureData.h) {
+ // Create a buffer for the texture data.
+ _textureData.create(texWidth, texHeight, _format);
+
+ // Set the texture.
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+
+ // Allocate storage for OpenGL texture.
+ GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
+ _textureData.h, 0, _glFormat, _glType, NULL));
+ }
+
+ // Create a sub-buffer for raw access.
+ _userPixelData = _textureData.getSubArea(Common::Rect(width, height));
+}
+
+void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
+ Graphics::Surface *dstSurf = getSurface();
+ assert(x + w <= dstSurf->w);
+ assert(y + h <= dstSurf->h);
+
+ // *sigh* Common::Rect::extend behaves unexpected whenever one of the two
+ // parameters is an empty rect. Thus, we check whether the current dirty
+ // area is valid. In case it is not we simply use the parameters as new
+ // dirty area. Otherwise, we simply call extend.
+ if (_dirtyArea.isEmpty()) {
+ _dirtyArea = Common::Rect(x, y, x + w, y + h);
+ } else {
+ _dirtyArea.extend(Common::Rect(x, y, x + w, y + h));
+ }
+
+ const byte *src = (const byte *)srcPtr;
+ byte *dst = (byte *)dstSurf->getBasePtr(x, y);
+ const uint pitch = dstSurf->pitch;
+ const uint bytesPerPixel = dstSurf->format.bytesPerPixel;
+
+ if (srcPitch == pitch && x == 0 && w == dstSurf->w) {
+ memcpy(dst, src, h * pitch);
+ } else {
+ while (h-- > 0) {
+ memcpy(dst, src, w * bytesPerPixel);
+ dst += pitch;
+ src += srcPitch;
+ }
+ }
+}
+
+void Texture::fill(uint32 color) {
+ Graphics::Surface *dst = getSurface();
+ dst->fillRect(Common::Rect(dst->w, dst->h), color);
+
+ flagDirty();
+}
+
+void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
+ // Only do any processing when the Texture is initialized.
+ if (!_textureData.getPixels()) {
+ return;
+ }
+
+ // First update any potentional changes.
+ updateTexture();
+
+ // Set the texture.
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+
+ // Calculate the texture rect that will be drawn.
+ const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w;
+ const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h;
+ const GLfloat texcoords[4*2] = {
+ 0, 0,
+ texWidth, 0,
+ 0, texHeight,
+ texWidth, texHeight
+ };
+ GLCALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
+
+ // Calculate the screen rect where the texture will be drawn.
+ const GLfloat vertices[4*2] = {
+ x, y,
+ x + w, y,
+ x, y + h,
+ x + w, y + h
+ };
+ GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices));
+
+ // Draw the texture to the screen buffer.
+ GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+}
+
+void Texture::updateTexture() {
+ if (!isDirty()) {
+ return;
+ }
+
+ Common::Rect dirtyArea = getDirtyArea();
+
+ // In case we use linear filtering we might need to duplicate the last
+ // pixel row/column to avoid glitches with filtering.
+ if (_glFilter == GL_LINEAR) {
+ if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
+ uint height = dirtyArea.height();
+
+ const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, dirtyArea.top);
+ byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, dirtyArea.top);
+
+ while (height-- > 0) {
+ memcpy(dst, src, _textureData.format.bytesPerPixel);
+ dst += _textureData.pitch;
+ src += _textureData.pitch;
+ }
+
+ // Extend the dirty area.
+ ++dirtyArea.right;
+ }
+
+ if (dirtyArea.bottom == _userPixelData.h && _userPixelData.h != _textureData.h) {
+ const byte *src = (const byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h - 1);
+ byte *dst = (byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h);
+ memcpy(dst, src, dirtyArea.width() * _textureData.format.bytesPerPixel);
+
+ // Extend the dirty area.
+ ++dirtyArea.bottom;
+ }
+ }
+
+ // Set the texture.
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+
+ // Update the actual texture.
+ // Although we keep track of the dirty part of the texture buffer we
+ // cannot take advantage of the left/right boundries here because it is
+ // not possible to specify a pitch to glTexSubImage2D. To be precise, with
+ // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
+ // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
+ // with the following options:
+ //
+ // 1) (As we do right now) Simply always update the whole texture lines of
+ // rect changed. This is simplest to implement. In case performance is
+ // really an issue we can think of switching to another method.
+ //
+ // 2) Copy the dirty rect to a temporary buffer and upload that by using
+ // glTexSubImage2D. This is what the Android backend does. It is more
+ // complicated though.
+ //
+ // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
+ // graphics manager did but it is much slower! Thus, we do not use it.
+ GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(),
+ _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top)));
+
+ // We should have handled everything, thus not dirty anymore.
+ clearDirty();
+}
+
+Common::Rect Texture::getDirtyArea() const {
+ if (_allDirty) {
+ return Common::Rect(_userPixelData.w, _userPixelData.h);
+ } else {
+ return _dirtyArea;
+ }
+}
+
+TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
+ : Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) {
+ memset(_palette, 0, sizeof(byte) * format.bytesPerPixel);
+}
+
+TextureCLUT8::~TextureCLUT8() {
+ delete[] _palette;
+ _palette = nullptr;
+ _clut8Data.free();
+}
+
+void TextureCLUT8::allocate(uint width, uint height) {
+ Texture::allocate(width, height);
+
+ // We only need to reinitialize our CLUT8 surface when the output size
+ // changed.
+ if (width == _clut8Data.w && height == _clut8Data.h) {
+ return;
+ }
+
+ _clut8Data.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+Graphics::PixelFormat TextureCLUT8::getFormat() const {
+ return Graphics::PixelFormat::createFormatCLUT8();
+}
+
+namespace {
+template<typename ColorType>
+inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
+ while (colors-- > 0) {
+ *dst++ = format.RGBToColor(src[0], src[1], src[2]);
+ src += 3;
+ }
+}
+} // End of anonymous namespace
+
+void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) {
+ const Graphics::PixelFormat &hardwareFormat = getHardwareFormat();
+
+ if (hardwareFormat.bytesPerPixel == 2) {
+ convertPalette<uint16>((uint16 *)_palette + start, palData, colors, hardwareFormat);
+ } else if (hardwareFormat.bytesPerPixel == 4) {
+ convertPalette<uint32>((uint32 *)_palette + start, palData, colors, hardwareFormat);
+ } else {
+ warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", hardwareFormat.bytesPerPixel);
+ }
+
+ // A palette changes means we need to refresh the whole surface.
+ flagDirty();
+}
+
+namespace {
+template<typename PixelType>
+inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint height, uint dstPitch, uint srcPitch, const PixelType *palette) {
+ uint srcAdd = srcPitch - width;
+ uint dstAdd = dstPitch - width * sizeof(PixelType);
+
+ while (height-- > 0) {
+ for (uint x = width; x > 0; --x) {
+ *dst++ = palette[*src++];
+ }
+
+ dst = (PixelType *)((byte *)dst + dstAdd);
+ src += srcAdd;
+ }
+}
+} // End of anonymous namespace
+
+void TextureCLUT8::updateTexture() {
+ if (!isDirty()) {
+ return;
+ }
+
+ // Do the palette look up
+ Graphics::Surface *outSurf = Texture::getSurface();
+
+ Common::Rect dirtyArea = getDirtyArea();
+
+ if (outSurf->format.bytesPerPixel == 2) {
+ doPaletteLookUp<uint16>((uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
+ (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
+ dirtyArea.width(), dirtyArea.height(),
+ outSurf->pitch, _clut8Data.pitch, (const uint16 *)_palette);
+ } else if (outSurf->format.bytesPerPixel == 4) {
+ doPaletteLookUp<uint32>((uint32 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
+ (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
+ dirtyArea.width(), dirtyArea.height(),
+ outSurf->pitch, _clut8Data.pitch, (const uint32 *)_palette);
+ } else {
+ warning("TextureCLUT8::updateTexture: Unsupported pixel depth: %d", outSurf->format.bytesPerPixel);
+ }
+
+ // Do generic handling of updating the texture.
+ Texture::updateTexture();
+}
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
new file mode 100644
index 0000000000..ad70833544
--- /dev/null
+++ b/backends/graphics/opengl/texture.h
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
+#define BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+
+#include "common/rect.h"
+
+namespace OpenGL {
+
+/**
+ * An OpenGL texture wrapper. It automatically takes care of all OpenGL
+ * texture handling issues and also provides access to the texture data.
+ */
+class Texture {
+public:
+ /**
+ * Create a new texture with the specific internal format.
+ *
+ * @param glIntFormat The internal format to use.
+ * @param glFormat The input format.
+ * @param glType The input type.
+ * @param format The format used for the texture input.
+ */
+ Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
+ virtual ~Texture();
+
+ /**
+ * Destroy the OpenGL texture name.
+ */
+ void releaseInternalTexture();
+
+ /**
+ * Create the OpenGL texture name and flag the whole texture as dirty.
+ */
+ void recreateInternalTexture();
+
+ /**
+ * Enable or disable linear texture filtering.
+ *
+ * @param enable true to enable and false to disable.
+ */
+ void enableLinearFiltering(bool enable);
+
+ /**
+ * Allocate texture space for the desired dimensions. This wraps any
+ * handling of requirements for POT textures.
+ *
+ * @param width The desired logical width.
+ * @param height The desired logical height.
+ */
+ virtual void allocate(uint width, uint height);
+
+ void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
+
+ void fill(uint32 color);
+
+ void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h);
+
+ void flagDirty() { _allDirty = true; }
+ bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
+
+ uint getWidth() const { return _userPixelData.w; }
+ uint getHeight() const { return _userPixelData.h; }
+
+ /**
+ * @return The hardware format of the texture data.
+ */
+ const Graphics::PixelFormat &getHardwareFormat() const { return _format; }
+
+ /**
+ * @return The logical format of the texture data.
+ */
+ virtual Graphics::PixelFormat getFormat() const { return _format; }
+
+ virtual Graphics::Surface *getSurface() { return &_userPixelData; }
+ virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+
+ /**
+ * @return Whether the texture data is using a palette.
+ */
+ virtual bool hasPalette() const { return false; }
+
+ virtual void setPalette(uint start, uint colors, const byte *palData) {}
+
+ virtual void *getPalette() { return 0; }
+ virtual const void *getPalette() const { return 0; }
+
+ /**
+ * Query texture related OpenGL information from the context. This only
+ * queries the maximum texture size for now.
+ */
+ static void queryTextureInformation();
+
+ /**
+ * @return Return the maximum texture dimensions supported.
+ */
+ static GLint getMaximumTextureSize() { return _maxTextureSize; }
+protected:
+ virtual void updateTexture();
+
+ Common::Rect getDirtyArea() const;
+private:
+ const GLenum _glIntFormat;
+ const GLenum _glFormat;
+ const GLenum _glType;
+ const Graphics::PixelFormat _format;
+
+ GLint _glFilter;
+ GLuint _glTexture;
+
+ Graphics::Surface _textureData;
+ Graphics::Surface _userPixelData;
+
+ bool _allDirty;
+ Common::Rect _dirtyArea;
+ void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
+
+ static GLint _maxTextureSize;
+};
+
+class TextureCLUT8 : public Texture {
+public:
+ TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
+ virtual ~TextureCLUT8();
+
+ virtual void allocate(uint width, uint height);
+
+ virtual Graphics::PixelFormat getFormat() const;
+
+ virtual bool hasPalette() const { return true; }
+
+ virtual void setPalette(uint start, uint colors, const byte *palData);
+
+ virtual void *getPalette() { return _palette; }
+ virtual const void *getPalette() const { return _palette; }
+
+ virtual Graphics::Surface *getSurface() { return &_clut8Data; }
+ virtual const Graphics::Surface *getSurface() const { return &_clut8Data; }
+
+protected:
+ virtual void updateTexture();
+
+private:
+ Graphics::Surface _clut8Data;
+ byte *_palette;
+};
+
+} // End of namespace OpenGL
+
+#endif