diff options
23 files changed, 2387 insertions, 2295 deletions
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index e2ef7f6bf6..e84a8f8c01 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -391,8 +391,17 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return false; case SDL_VIDEORESIZE: - if (_graphicsManager) + if (_graphicsManager) { _graphicsManager->notifyResize(ev.resize.w, ev.resize.h); + + // If the screen changed, send an Common::EVENT_SCREEN_CHANGED + int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID(); + if (screenID != _lastScreenID) { + _lastScreenID = screenID; + event.type = Common::EVENT_SCREEN_CHANGED; + return true; + } + } return false; case SDL_QUIT: diff --git a/backends/graphics/opengl/debug.cpp b/backends/graphics/opengl/debug.cpp new file mode 100644 index 0000000000..69006bb975 --- /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 = glGetError(); + + if (error != 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..00e8dc358e 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,1020 @@ 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 } }; +} // End of anonymous namespace + const OSystem::GraphicsMode *OpenGLGraphicsManager::supportedGraphicsModes() { - return s_supportedGraphicsModes; + return glGraphicsModes; } 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); + + switch (mode) { + case GFX_LINEAR: + case GFX_NEAREST: + _currentState.graphicsMode = mode; + + if (_gameScreen) { + _gameScreen->enableLinearFiltering(mode == GFX_LINEAR); + } - setScale(2); + 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; - - _transactionDetails.sizeChanged = true; -} - -int OpenGLGraphicsManager::getScreenChangeID() const { - return _screenChangeCount; -} - -// -// GFX -// - void OpenGLGraphicsManager::beginGFXTransaction() { assert(_transactionMode == kTransactionNone); + // Start a transaction. + _oldState = _currentState; _transactionMode = kTransactionActive; - _transactionDetails.sizeChanged = false; - _transactionDetails.needRefresh = false; - _transactionDetails.needUpdatescreen = false; - _transactionDetails.filterChanged = false; -#ifdef USE_RGB_COLOR - _transactionDetails.formatChanged = false; -#endif - - _oldVideoMode = _videoMode; } OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() { - int errors = OSystem::kTransactionSuccess; + assert(_transactionMode == kTransactionActive); - assert(_transactionMode != kTransactionNone); + uint transactionError = OSystem::kTransactionSuccess; + + bool setupNewGameScreen = false; + if ( _oldState.gameWidth != _currentState.gameWidth + || _oldState.gameHeight != _currentState.gameHeight) { + setupNewGameScreen = true; + } - if (_transactionMode == kTransactionRollback) { - if (_videoMode.fullscreen != _oldVideoMode.fullscreen) { - errors |= OSystem::kTransactionFullscreenFailed; +#ifdef USE_RGB_COLOR + 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 - _videoMode.fullscreen = _oldVideoMode.fullscreen; - } else if (_videoMode.mode != _oldVideoMode.mode) { - errors |= OSystem::kTransactionModeSwitchFailed; + do { + uint requestedWidth = _currentState.gameWidth; + uint requestedHeight = _currentState.gameHeight; + const uint desiredAspect = getDesiredGameScreenAspect(); + requestedHeight = intToFrac(requestedWidth) / desiredAspect; - _videoMode.mode = _oldVideoMode.mode; - _videoMode.scaleFactor = _oldVideoMode.scaleFactor; + if (!loadVideoMode(requestedWidth, requestedHeight, #ifdef USE_RGB_COLOR - } else if (_videoMode.format != _oldVideoMode.format) { - errors |= OSystem::kTransactionFormatNotSupported; + _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; + } - _videoMode.format = _oldVideoMode.format; - _screenFormat = _videoMode.format; +#ifdef USE_RGB_COLOR + if (_oldState.gameFormat != _currentState.gameFormat) { + transactionError |= OSystem::kTransactionFormatNotSupported; + } #endif - } else if (_videoMode.screenWidth != _oldVideoMode.screenWidth || _videoMode.screenHeight != _oldVideoMode.screenHeight) { - errors |= OSystem::kTransactionSizeChangeFailed; - _videoMode.screenWidth = _oldVideoMode.screenWidth; - _videoMode.screenHeight = _oldVideoMode.screenHeight; - _videoMode.overlayWidth = _oldVideoMode.overlayWidth; - _videoMode.overlayHeight = _oldVideoMode.overlayHeight; - } + if (_oldState.aspectRatioCorrection != _currentState.aspectRatioCorrection) { + transactionError |= OSystem::kTransactionAspectRatioFailed; + } - if (_videoMode.fullscreen == _oldVideoMode.fullscreen && - _videoMode.mode == _oldVideoMode.mode && - _videoMode.screenWidth == _oldVideoMode.screenWidth && - _videoMode.screenHeight == _oldVideoMode.screenHeight) { + if (_oldState.graphicsMode != _currentState.graphicsMode) { + transactionError |= OSystem::kTransactionModeSwitchFailed; + } - _oldVideoMode.setup = false; - } - } + // Roll back to the old state. + _currentState = _oldState; + _transactionMode = kTransactionRollback; - if (_transactionDetails.sizeChanged || _transactionDetails.needRefresh) { - unloadGFXMode(); - if (!loadGFXMode()) { - if (_oldVideoMode.setup) { - _transactionMode = kTransactionRollback; - errors |= endGFXTransaction(); + // Try to set up the old state. + continue; + } } - } else { - clearOverlay(); - _videoMode.setup = true; - _screenChangeCount++; + // 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(); } + + // 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; + + GLenum glIntFormat, glFormat, glType; #ifdef USE_RGB_COLOR - } else if (_transactionDetails.filterChanged || _transactionDetails.formatChanged) { -#else - } else if (_transactionDetails.filterChanged) { + if (_currentState.gameFormat.bytesPerPixel == 1) { #endif - loadTextures(); - internUpdateScreen(); - } else if (_transactionDetails.needUpdatescreen) { - internUpdateScreen(); + const bool supported = getGLPixelFormat(_defaultFormat, glIntFormat, glFormat, glType); + assert(supported); + _gameScreen = new TextureCLUT8(glIntFormat, glFormat, glType, _defaultFormat); + _gameScreen->setPalette(0, 255, _gamePalette); +#ifdef USE_RGB_COLOR + } else { + const bool supported = getGLPixelFormat(_currentState.gameFormat, glIntFormat, glFormat, glType); + assert(supported); + _gameScreen = new Texture(glIntFormat, glFormat, glType, _currentState.gameFormat); + } +#endif + + _gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight); + _gameScreen->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR); + // We fill the screen to all black or index 0 for CLUT8. + if (_currentState.gameFormat.bytesPerPixel == 1) { + _gameScreen->fill(0); + } else { + _gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0)); + } } - _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); - - _screenNeedsRedraw = true; - - if (_cursorPaletteDisabled) - _cursorNeedsRedraw = true; + _currentState.gameWidth = width; + _currentState.gameHeight = height; } -void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) { - assert(colors); - -#ifdef USE_RGB_COLOR - assert(_screenFormat.bytesPerPixel == 1); -#endif +int16 OpenGLGraphicsManager::getWidth() { + return _currentState.gameWidth; +} - // Copies current palette to buffer - memcpy(colors, _gamePalette + start * 3, num * 3); +int16 OpenGLGraphicsManager::getHeight() { + return _currentState.gameHeight; } 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; - } - - // 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); - } + _gameScreen->copyRectToTexture(x, y, w, h, buf, pitch); } -Graphics::Surface *OpenGLGraphicsManager::lockScreen() { - return &_screenData; +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); } -void OpenGLGraphicsManager::unlockScreen() { - _screenNeedsRedraw = true; +void OpenGLGraphicsManager::setShakePos(int shakeOffset) { + _gameScreenShakeOffset = shakeOffset; } -void OpenGLGraphicsManager::fillScreen(uint32 col) { - if (_gameTexture == NULL) +void OpenGLGraphicsManager::updateScreen() { + if (!_gameScreen) { return; + } -#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; + // 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. + glPushMatrix(); + + // Adjust game screen shake position + GLCALL(glTranslatef(0, shakeOffset, 0)); + + // Draw the game screen + _gameScreen->draw(_displayX, _displayY, _displayWidth, _displayHeight); + + glPopMatrix(); + + // Second step: Draw the overlay if visible. + if (_overlayVisible) { + _overlay->draw(0, 0, _outputScreenWidth, _outputScreenHeight); + } + + // Third step: Draw the cursor if visible. + if (_cursorVisible && _cursor) { + glPushMatrix(); + + // Adjust game screen shake position, but only when the overlay is not + // visible. + if (!_overlayVisible) { + GLCALL(glTranslatef(0, shakeOffset, 0)); } - } else if (_screenFormat.bytesPerPixel == 4) { - uint32 *pixels = (uint32 *)_screenData.getPixels(); - for (int i = 0; i < _screenData.w * _screenData.h; i++) { - pixels[i] = col; + + _cursor->draw(_cursorX - _cursorHotspotXScaled, _cursorY - _cursorHotspotYScaled, + _cursorWidthScaled, _cursorHeightScaled); + + glPopMatrix(); + } + +#ifdef USE_OSD + // Fourth step: Draw the OSD. + if (_osdAlpha > 0) { + Common::StackLock lock(_osdMutex); + + // 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); + // 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; - // Save cursor data - memcpy(_cursorData.getPixels(), buf, h * _cursorData.pitch); + if (inputFormat.bytesPerPixel == 1) { + // In case this is not supported this is a serious programming + // error and the assert a bit below will trigger! + const bool supported = getGLPixelFormat(_defaultFormatAlpha, glIntFormat, glFormat, glType); + assert(supported); + _cursor = new TextureCLUT8(glIntFormat, glFormat, glType, _defaultFormatAlpha); + } else { + // Try to use the format specified as input directly. We can only + // do so when it actually has alpha bits. + if (inputFormat.aBits() != 0 && getGLPixelFormat(inputFormat, glIntFormat, glFormat, glType)) { + _cursor = new Texture(glIntFormat, glFormat, glType, inputFormat); + } + + // Otherwise fall back to the default alpha format. + if (!_cursor) { + const bool supported = getGLPixelFormat(_defaultFormatAlpha, glIntFormat, glFormat, glType); + assert(supported); + _cursor = new Texture(glIntFormat, glFormat, glType, _defaultFormatAlpha); + } + } + + 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; + + // In case the actual cursor format differs from the requested format + // we will need to convert the format. This might be the case because + // the cursor format does not have any alpha bits. + if (dst->format != inputFormat) { + 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(); - // Init the OSD display parameters, and the fade out + // 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. _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; + + GLenum glIntFormat, glFormat, glType; + const bool supported = getGLPixelFormat(_defaultFormatAlpha, glIntFormat, glFormat, glType); + assert(supported); + _overlay = new Texture(glIntFormat, glFormat, glType, _defaultFormatAlpha); + // 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; + + GLenum glIntFormat, glFormat, glType; + const bool supported = getGLPixelFormat(_defaultFormatAlpha, glIntFormat, glFormat, glType); + assert(supported); + _osd = new Texture(glIntFormat, glFormat, glType, _defaultFormatAlpha); + // 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 + + // Re-setup the scaling for the screen and cursor + recalculateDisplayArea(); + recalculateCursorScaling(); - _screenNeedsRedraw = false; - _screenDirtyRect = Common::Rect(); + // 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::notifyContextChange(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)); - _overlayNeedsRedraw = false; - _overlayDirtyRect = Common::Rect(); -} + // Setup alpha blend (for overlay and cursor). + GLCALL(glEnable(GL_BLEND)); + GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); -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; - } - } - } + // Enable rendering with vertex and coord arrays. + GLCALL(glEnableClientState(GL_VERTEX_ARRAY)); + GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - // Update the texture with new cursor - _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h); + GLCALL(glEnable(GL_TEXTURE_2D)); - // Free the temp surface - delete[] surface; -} + // 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)); -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; - } + // Query information needed by textures. + Texture::queryTextureInformation(); - // 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); + // Refresh the output screen dimensions if some are set up. + if (_outputScreenWidth != 0 && _outputScreenHeight != 0) { + setActualScreenSize(_outputScreenWidth, _outputScreenHeight); } - // 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); -} + // 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::calculateDisplaySize(int &width, int &height) { - if (_videoMode.mode == OpenGL::GFX_ORIGINAL) { - width = _videoMode.screenWidth; - height = _videoMode.screenHeight; - } else { - width = _videoMode.hardwareWidth; - height = _videoMode.hardwareHeight; + if (_gameScreen) { + _gameScreen->recreateInternalTexture(); + } + + if (_overlay) { + _overlay->recreateInternalTexture(); + } - uint aspectRatio = (_videoMode.hardwareWidth * 10000 + 5000) / _videoMode.hardwareHeight; - uint desiredAspectRatio = getAspectRatio(); + if (_cursor) { + _cursor->recreateInternalTexture(); + } - // 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; +#ifdef USE_OSD + if (_osd) { + _osd->recreateInternalTexture(); } +#endif } -void OpenGLGraphicsManager::refreshDisplaySize() { - calculateDisplaySize(_displayWidth, _displayHeight); +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(); + + x = (x * width) / _displayWidth; + y = (y * height) / _displayHeight; - // Adjust x and y for centering the screen - _displayX = (_videoMode.hardwareWidth - _displayWidth) / 2; - _displayY = (_videoMode.hardwareHeight - _displayHeight) / 2; + // Make sure we only supply valid coordinates. + x = CLIP<int16>(x, 0, width - 1); + y = CLIP<int16>(y, 0, height - 1); + } } -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 { if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 - bpp = 4; - intFormat = GL_RGBA; + 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_INT_8_8_8_8; + 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 } 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; } 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; } 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); - } +frac_t OpenGLGraphicsManager::getDesiredGameScreenAspect() const { + const uint width = _currentState.gameWidth; + const uint height = _currentState.gameHeight; - if (_cursorVisible) { - if (_cursorNeedsRedraw) - // Refresh texture if dirty - refreshCursor(); - - 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; + 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; } - - // 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; - } - } - // 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); - _screenNeedsRedraw = true; - _overlayNeedsRedraw = true; - _cursorNeedsRedraw = true; + const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; + const frac_t desiredAspect = getDesiredGameScreenAspect(); - // 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); + _displayWidth = _outputScreenWidth; + _displayHeight = _outputScreenHeight; - // 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 uint screenScaleFactorX = _displayWidth * 10000 / _gameScreen->getWidth(); + const uint screenScaleFactorY = _displayHeight * 10000 / _gameScreen->getHeight(); - _videoMode.antialiasing = !_videoMode.antialiasing; - _transactionDetails.filterChanged = true; -} + _cursorHotspotXScaled = (_cursorHotspotXScaled * screenScaleFactorX) / 10000; + _cursorWidthScaled = (_cursorWidthScaled * screenScaleFactorX) / 10000; -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 = (_cursorHotspotYScaled * screenScaleFactorY) / 10000; + _cursorHeightScaled = (_cursorHeightScaled * screenScaleFactorY) / 10000; + } } -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 +1143,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..ebfe38fb60 100644 --- a/backends/graphics/opengl/opengl-graphics.h +++ b/backends/graphics/opengl/opengl-graphics.h @@ -8,338 +8,468 @@ * 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 { 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); + // HACK: This is required for the SDL backend to switch between OpenGL SDL + // and Surface SDL. 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. */ - virtual void initGL(); + void setActualScreenSize(uint width, uint height); /** - * Creates and refreshs OpenGL textures. + * Notify the manager of a OpenGL context change. This should be the first + * thing to call when you create 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). */ - virtual void loadTextures(); + void notifyContextChange(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha); + /** + * Adjust the physical mouse coordinates according to the currently visible screen. + */ + 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: // - // 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. */ - virtual void getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &type); + VideoState _currentState; - virtual void internUpdateScreen(); - virtual bool loadGFXMode(); - virtual void unloadGFXMode(); + /** + * The old video state used when doing a transaction rollback. + */ + VideoState _oldState; +protected: + enum TransactionMode { + kTransactionNone = 0, + kTransactionActive = 1, + kTransactionRollback = 2 + }; + + 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; + + /** + * @return The desired aspect of the game screen. + */ + frac_t getDesiredGameScreenAspect() const; + + /** + * Recalculates the area used to display the game screen. + */ + void recalculateDisplayArea(); + + /** + * The X coordinate of the game screen. + */ + uint _displayX; + + /** + * The Y coordinate of the game screen. + */ + uint _displayY; - virtual const char *getCurrentModeName(); + /** + * The width of the game screen in physical coordinates. + */ + uint _displayWidth; - virtual void calculateDisplaySize(int &width, int &height); - virtual void refreshDisplaySize(); + /** + * The height of the game screen in physical coordinates. + */ + uint _displayHeight; - uint getAspectRatio() const; + /** + * The default pixel format of the backend. + */ + Graphics::PixelFormat _defaultFormat; - void setFormatIsBGR(bool isBGR) { _formatBGR = isBGR; } - bool _formatBGR; + /** + * 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. + */ + uint _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. + */ + uint _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. + */ + uint _cursorHotspotX; + + /** + * The Y offset for the cursor hotspot in unscaled coordinates. + */ + uint _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. + */ + uint _cursorHotspotXScaled; + + /** + * The Y offset for the cursor hotspot in scaled coordinates. + */ + uint _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 +478,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..917bf70534 --- /dev/null +++ b/backends/graphics/opengl/texture.cpp @@ -0,0 +1,371 @@ +/* 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() { + // 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(GLuint x, GLuint y, GLuint w, GLuint 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 GLshort vertices[4*2] = { + (GLshort)x, (GLshort)y, + (GLshort)(x + w), (GLshort)y, + (GLshort)x, (GLshort)(y + h), + (GLshort)(x + w), (GLshort)(y + h) + }; + GLCALL(glVertexPointer(2, GL_SHORT, 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 && width == _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..e28d980de4 --- /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(GLuint x, GLuint y, GLuint w, GLuint 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 diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index c5605cae87..925237b75e 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -8,45 +8,42 @@ * 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(SDL_BACKEND) && defined(USE_OPENGL) - #include "backends/graphics/openglsdl/openglsdl-graphics.h" -#include "backends/platform/sdl/sdl.h" -#include "common/config-manager.h" + #include "common/textconsole.h" +#include "common/config-manager.h" +#ifdef USE_OSD #include "common/translation.h" +#endif -OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource) - : - SdlGraphicsManager(eventSource), - _hwscreen(0), - _screenResized(false), - _activeFullscreenMode(-2), - _lastFullscreenModeWidth(0), - _lastFullscreenModeHeight(0), - _desktopWidth(0), - _desktopHeight(0), - _ignoreResizeFrames(0) { - +OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource) + : SdlGraphicsManager(eventSource), _lastVideoModeLoad(0), _hwScreen(nullptr), _lastRequestedWidth(0), _lastRequestedHeight(0), + _graphicsScale(2), _ignoreLoadVideoMode(false), _gotResize(false), _wantsFullScreen(false), _ignoreResizeEvents(0), + _desiredFullscreenWidth(0), _desiredFullscreenHeight(0) { // Initialize SDL video subsystem if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { error("Could not initialize SDL: %s", SDL_GetError()); } + // Setup OpenGL attributes for SDL + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + // This is also called in initSDL(), but initializing graphics // may reset it. SDL_EnableUNICODE(1); @@ -54,27 +51,41 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource) // Disable OS cursor SDL_ShowCursor(SDL_DISABLE); - // Get desktop resolution - // TODO: In case the OpenGL manager is created *after* a plain SDL manager - // has been used, this will return the last setup graphics mode rather - // than the desktop resolution. We should really look into a way to - // properly retrieve the desktop resolution. - const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo(); - if (videoInfo->current_w > 0 && videoInfo->current_h > 0) { - _desktopWidth = videoInfo->current_w; - _desktopHeight = videoInfo->current_h; + // Retrieve a list of working fullscreen modes + const SDL_Rect *const *availableModes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN); + if (availableModes != (void *)-1) { + for (;*availableModes; ++availableModes) { + const SDL_Rect *mode = *availableModes; + + _fullscreenVideoModes.push_back(VideoMode(mode->w, mode->h)); + } + + // Sort the modes in ascending order. + Common::sort(_fullscreenVideoModes.begin(), _fullscreenVideoModes.end()); + } + + // In case SDL is fine with every mode we will force the desktop mode. + // TODO? We could also try to add some default resolutions here. + if (_fullscreenVideoModes.empty() && desktopWidth && desktopHeight) { + _fullscreenVideoModes.push_back(VideoMode(desktopWidth, desktopHeight)); } - if (ConfMan.hasKey("last_fullscreen_mode_width") && ConfMan.hasKey("last_fullscreen_mode_height")) { - _lastFullscreenModeWidth = ConfMan.getInt("last_fullscreen_mode_width"); - _lastFullscreenModeHeight = ConfMan.getInt("last_fullscreen_mode_height"); + // Get information about display sizes from the previous runs. + if (ConfMan.hasKey("last_fullscreen_mode_width", Common::ConfigManager::kApplicationDomain) && ConfMan.hasKey("last_fullscreen_mode_height", Common::ConfigManager::kApplicationDomain)) { + _desiredFullscreenWidth = ConfMan.getInt("last_fullscreen_mode_width", Common::ConfigManager::kApplicationDomain); + _desiredFullscreenHeight = ConfMan.getInt("last_fullscreen_mode_height", Common::ConfigManager::kApplicationDomain); + } else { + // Use the desktop resolutions when no previous default has been setup. + _desiredFullscreenWidth = desktopWidth; + _desiredFullscreenHeight = desktopHeight; } } OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() { // Unregister the event observer - if (g_system->getEventManager()->getEventDispatcher() != NULL) + if (g_system->getEventManager()->getEventDispatcher()) { g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); + } } void OpenGLSdlGraphicsManager::initEventObserver() { @@ -83,421 +94,270 @@ void OpenGLSdlGraphicsManager::initEventObserver() { } bool OpenGLSdlGraphicsManager::hasFeature(OSystem::Feature f) { - return - (f == OSystem::kFeatureFullscreenMode) || - (f == OSystem::kFeatureIconifyWindow) || - OpenGLGraphicsManager::hasFeature(f); + switch (f) { + case OSystem::kFeatureFullscreenMode: + case OSystem::kFeatureIconifyWindow: + return true; + + default: + return OpenGLGraphicsManager::hasFeature(f); + } } void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { switch (f) { + case OSystem::kFeatureFullscreenMode: + assert(getTransactionMode() != kTransactionNone); + _wantsFullScreen = enable; + // When we switch to windowed mode we will ignore resize events. This + // avoids bad resizes to the (former) fullscreen resolution. + if (!enable) { + _ignoreResizeEvents = 10; + } + break; + case OSystem::kFeatureIconifyWindow: - if (enable) + if (enable) { SDL_WM_IconifyWindow(); + } break; + default: OpenGLGraphicsManager::setFeatureState(f, enable); } } -#ifdef USE_RGB_COLOR - -Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormats() const { - assert(!_supportedFormats.empty()); - return _supportedFormats; -} - -void OpenGLSdlGraphicsManager::detectSupportedFormats() { - - // Clear old list - _supportedFormats.clear(); - - // Some tables with standard formats that we always list - // as "supported". If frontend code tries to use one of - // these, we will perform the necessary format - // conversion in the background. Of course this incurs a - // performance hit, but on desktop ports this should not - // matter. We still push the currently active format to - // the front, so if frontend code just uses the first - // available format, it will get one that is "cheap" to - // use. - const Graphics::PixelFormat RGBList[] = { -#if defined(ENABLE_32BIT) - Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), // RGBA8888 -#ifndef USE_GLES - Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), // ARGB8888 -#endif - Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0), // RGB888 -#endif - Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), // RGB565 - Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), // RGB5551 - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), // RGB555 - Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), // RGBA4444 -#ifndef USE_GLES - Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12) // ARGB4444 -#endif - }; -#ifndef USE_GLES - const Graphics::PixelFormat BGRList[] = { -#ifdef ENABLE_32BIT - Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), // ABGR8888 - Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), // BGRA8888 - Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0), // BGR888 -#endif - Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0), // BGR565 - Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0), // BGRA5551 - Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12), // ABGR4444 - Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0) // BGRA4444 - }; -#endif - - Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); - if (_hwscreen) { - // Get our currently set hardware format - format = Graphics::PixelFormat(_hwscreen->format->BytesPerPixel, - 8 - _hwscreen->format->Rloss, 8 - _hwscreen->format->Gloss, - 8 - _hwscreen->format->Bloss, 8 - _hwscreen->format->Aloss, - _hwscreen->format->Rshift, _hwscreen->format->Gshift, - _hwscreen->format->Bshift, _hwscreen->format->Ashift); - - // Workaround to SDL not providing an accurate Aloss value on Mac OS X. - if (_hwscreen->format->Amask == 0) - format.aLoss = 8; - - // Push it first, as the prefered format if available - for (int i = 0; i < ARRAYSIZE(RGBList); i++) { - if (RGBList[i] == format) { - _supportedFormats.push_back(format); - break; - } - } -#ifndef USE_GLES - for (int i = 0; i < ARRAYSIZE(BGRList); i++) { - if (BGRList[i] == format) { - _supportedFormats.push_back(format); - break; - } +bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + if (_hwScreen) { + return (_hwScreen->flags & SDL_FULLSCREEN) != 0; + } else { + return _wantsFullScreen; } -#endif - } - // Push some RGB formats - for (int i = 0; i < ARRAYSIZE(RGBList); i++) { - if (_hwscreen && (RGBList[i].bytesPerPixel > format.bytesPerPixel)) - continue; - if (RGBList[i] != format) - _supportedFormats.push_back(RGBList[i]); - } -#ifndef USE_GLES - // Push some BGR formats - for (int i = 0; i < ARRAYSIZE(BGRList); i++) { - if (_hwscreen && (BGRList[i].bytesPerPixel > format.bytesPerPixel)) - continue; - if (BGRList[i] != format) - _supportedFormats.push_back(BGRList[i]); + default: + return OpenGLGraphicsManager::getFeatureState(f); } -#endif - _supportedFormats.push_back(Graphics::PixelFormat::createFormatCLUT8()); } -#endif +bool OpenGLSdlGraphicsManager::setGraphicsMode(int mode) { + // HACK: This is stupid but the SurfaceSDL backend defaults to 2x. This + // assures that the launcher (which requests 320x200) has a reasonable + // size. It also makes small games have a reasonable size (i.e. at least + // 640x400). We follow the same logic here until we have a better way to + // give hints to our backend for that. + _graphicsScale = 2; -void OpenGLSdlGraphicsManager::setInternalMousePosition(int x, int y) { - SDL_WarpMouse(x, y); + return OpenGLGraphicsManager::setGraphicsMode(mode); } -void OpenGLSdlGraphicsManager::updateScreen() { - if (_ignoreResizeFrames) - _ignoreResizeFrames -= 1; +void OpenGLSdlGraphicsManager::resetGraphicsScale() { + OpenGLGraphicsManager::resetGraphicsScale(); - OpenGLGraphicsManager::updateScreen(); + // HACK: See OpenGLSdlGraphicsManager::setGraphicsMode. + _graphicsScale = 1; } -// -// Intern -// - -bool OpenGLSdlGraphicsManager::setupFullscreenMode() { - SDL_Rect const* const*availableModes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_OPENGL); - - // SDL_ListModes() returns -1 in case any dimension is okay. In that - // case we'll reuse the current desktop resolution for fullscreen. - if (availableModes == (void *)-1) { - _videoMode.hardwareWidth = _desktopWidth; - _videoMode.hardwareHeight = _desktopHeight; - _activeFullscreenMode = -2; - return true; - } - - // If -2, autodetect the fullscreen mode - // The last used fullscreen mode will be prioritized, if there is no last fullscreen - // mode, the desktop resolution will be used, and in case the desktop resolution - // is not available as a fullscreen mode, the one with smallest metric will be selected. - if (_activeFullscreenMode == -2) { - // Desktop resolution - int desktopModeIndex = -1; - - // Best metric mode - const SDL_Rect *bestMode = availableModes[0]; - int bestModeIndex = 0; - uint bestMetric = (uint)-1; - - // Iterate over all available fullscreen modes - for (int i = 0; const SDL_Rect *mode = availableModes[i]; i++) { - // Try to setup the last used fullscreen mode - if (mode->w == _lastFullscreenModeWidth && mode->h == _lastFullscreenModeHeight) { - _videoMode.hardwareWidth = _lastFullscreenModeWidth; - _videoMode.hardwareHeight = _lastFullscreenModeHeight; - _activeFullscreenMode = i; - return true; - } +#ifdef USE_RGB_COLOR +Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormats() const { + Common::List<Graphics::PixelFormat> formats; - if (mode->w == _desktopWidth && mode->h == _desktopHeight) - desktopModeIndex = i; + // RGBA8888 + formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + // RGB565 + formats.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + // RGBA5551 + formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); + // RGBA4444 + formats.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)); - if (mode->w < _videoMode.overlayWidth) - continue; - if (mode->h < _videoMode.overlayHeight) - continue; +#ifndef USE_GLES + // ARGB8888, this should not be here, but Sword25 requires it. :-/ + formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); - uint metric = mode->w * mode->h - _videoMode.overlayWidth * _videoMode.overlayHeight; - if (metric < bestMetric) { - bestMode = mode; - bestMetric = metric; - bestModeIndex = i; - } - } + // RGB555, this is used by SCUMM HE 16 bit games. + formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); +#endif - if (desktopModeIndex >= 0) { - _videoMode.hardwareWidth = _desktopWidth; - _videoMode.hardwareHeight = _desktopHeight; + formats.push_back(Graphics::PixelFormat::createFormatCLUT8()); - _activeFullscreenMode = desktopModeIndex; - return true; - } else if (bestMode) { - _videoMode.hardwareWidth = bestMode->w; - _videoMode.hardwareHeight = bestMode->h; + return formats; +} +#endif - _activeFullscreenMode = bestModeIndex; - return true; - } - } else { - // Use last fullscreen mode if looping backwards from the first mode - if (_activeFullscreenMode == -1) { - do { - _activeFullscreenMode++; - } while(availableModes[_activeFullscreenMode]); - _activeFullscreenMode--; - } +void OpenGLSdlGraphicsManager::updateScreen() { + if (_ignoreResizeEvents) { + --_ignoreResizeEvents; + } - // Use first fullscreen mode if looping from last mode - if (!availableModes[_activeFullscreenMode]) - _activeFullscreenMode = 0; + OpenGLGraphicsManager::updateScreen(); - // Check if the fullscreen mode is valid - if (availableModes[_activeFullscreenMode]) { - _videoMode.hardwareWidth = availableModes[_activeFullscreenMode]->w; - _videoMode.hardwareHeight = availableModes[_activeFullscreenMode]->h; - return true; - } - } + // Swap OpenGL buffers + SDL_GL_SwapBuffers(); +} - // Could not find any suiting fullscreen mode, return false. - return false; +void OpenGLSdlGraphicsManager::notifyVideoExpose() { } -bool OpenGLSdlGraphicsManager::loadGFXMode() { - // If the screen was resized, do not change its size - if (!_screenResized) { - const int scaleFactor = getScale(); - _videoMode.overlayWidth = _videoMode.hardwareWidth = _videoMode.screenWidth * scaleFactor; - _videoMode.overlayHeight = _videoMode.hardwareHeight = _videoMode.screenHeight * scaleFactor; - - // The only modes where we need to adapt the aspect ratio are 320x200 - // and 640x400. That is since our aspect ratio correction in fact is - // only used to ensure that the original pixel size aspect for these - // modes is used. - // (Non-square pixels on old monitors vs square pixel on new ones). - if (_videoMode.aspectRatioCorrection) { - if (_videoMode.screenWidth == 320 && _videoMode.screenHeight == 200) - _videoMode.overlayHeight = _videoMode.hardwareHeight = 240 * scaleFactor; - else if (_videoMode.screenWidth == 640 && _videoMode.screenHeight == 400) - _videoMode.overlayHeight = _videoMode.hardwareHeight = 480 * scaleFactor; +void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) { + if (!_ignoreResizeEvents && _hwScreen && !(_hwScreen->flags & SDL_FULLSCREEN)) { + // We save that we handled a resize event here. We need to know this + // so we do not overwrite the users requested window size whenever we + // switch aspect ratio or similar. + _gotResize = true; + if (!setupMode(width, height)) { + warning("OpenGLSdlGraphicsManager::notifyResize: Resize failed ('%s')", SDL_GetError()); + g_system->quit(); } } +} - _screenResized = false; +void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) { + adjustMousePosition(point.x, point.y); +} - // Setup OpenGL attributes for SDL - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) { + setMousePosition(mouse.x, mouse.y); +} - const bool isFullscreen = getFullscreenMode(); +void OpenGLSdlGraphicsManager::setInternalMousePosition(int x, int y) { + SDL_WarpMouse(x, y); +} - // In case we have an fullscreen mode and we are not in a rollback, detect - // a proper mode to use. In case we are in a rollback, we already detected - // a proper mode when setting up that mode, thus there is no need to run - // the detection again. - if (isFullscreen && _transactionMode != kTransactionRollback) { - if (!setupFullscreenMode()) - // Failed setuping a fullscreen mode - return false; +bool OpenGLSdlGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) { + // In some cases we might not want to load the requested video mode. This + // will assure that the window size is not altered. + if (_ignoreLoadVideoMode) { + _ignoreLoadVideoMode = false; + return true; } - _videoMode.overlayWidth = _videoMode.hardwareWidth; - _videoMode.overlayHeight = _videoMode.hardwareHeight; - - uint32 flags = SDL_OPENGL; - - if (isFullscreen) - flags |= SDL_FULLSCREEN; - else - flags |= SDL_RESIZABLE; + // This function should never be called from notifyResize thus we know + // that the requested size came from somewhere else. + _gotResize = false; - // Create our window - _hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 32, flags); -#ifdef USE_RGB_COLOR - detectSupportedFormats(); -#endif + // Save the requested dimensions. + _lastRequestedWidth = requestedWidth; + _lastRequestedHeight = requestedHeight; - if (_hwscreen == NULL) { - // DON'T use error(), as this tries to bring up the debug - // console, which WON'T WORK now that _hwscreen is hosed. + // Apply the currently saved scale setting. + requestedWidth *= _graphicsScale; + requestedHeight *= _graphicsScale; - if (!_oldVideoMode.setup) { - warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); - g_system->quit(); - } else - // Cancel GFX load, and go back to last mode - return false; - } + // Set up the mode. + return setupMode(requestedWidth, requestedHeight); +} - // Check if the screen is BGR format - setFormatIsBGR(_hwscreen->format->Rshift != 0); +bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { + // In case we request a fullscreen mode we will use the mode the user + // has chosen last time or the biggest mode available. + if (_wantsFullScreen) { + if (_desiredFullscreenWidth && _desiredFullscreenHeight) { + // In case only a distinct set of modes is available we check + // whether the requested mode is actually available. + if (!_fullscreenVideoModes.empty()) { + VideoModeArray::const_iterator i = Common::find(_fullscreenVideoModes.begin(), + _fullscreenVideoModes.end(), + VideoMode(_desiredFullscreenWidth, _desiredFullscreenHeight)); + // It's not available fall back to default. + if (i == _fullscreenVideoModes.end()) { + _desiredFullscreenWidth = 0; + _desiredFullscreenHeight = 0; + } + } + } - if (isFullscreen) { - _lastFullscreenModeWidth = _videoMode.hardwareWidth; - _lastFullscreenModeHeight = _videoMode.hardwareHeight; - ConfMan.setInt("last_fullscreen_mode_width", _lastFullscreenModeWidth); - ConfMan.setInt("last_fullscreen_mode_height", _lastFullscreenModeHeight); - } + // In case no desired mode has been set we default to the biggest mode + // available or the requested mode in case we don't know any + // any fullscreen modes. + if (!_desiredFullscreenWidth || !_desiredFullscreenHeight) { + if (!_fullscreenVideoModes.empty()) { + VideoModeArray::const_iterator i = _fullscreenVideoModes.end(); + --i; + + _desiredFullscreenWidth = i->width; + _desiredFullscreenHeight = i->height; + } else { + _desiredFullscreenWidth = width; + _desiredFullscreenHeight = height; + } + } - // Call and return parent implementation of this method - return OpenGLGraphicsManager::loadGFXMode(); -} + // Remember our choice. + ConfMan.setInt("last_fullscreen_mode_width", _desiredFullscreenWidth, Common::ConfigManager::kApplicationDomain); + ConfMan.setInt("last_fullscreen_mode_height", _desiredFullscreenHeight, Common::ConfigManager::kApplicationDomain); -void OpenGLSdlGraphicsManager::unloadGFXMode() { - if (_hwscreen) { - SDL_FreeSurface(_hwscreen); - _hwscreen = NULL; + // Use our choice. + width = _desiredFullscreenWidth; + height = _desiredFullscreenHeight; } -} -void OpenGLSdlGraphicsManager::internUpdateScreen() { - // Call to parent implementation of this method - OpenGLGraphicsManager::internUpdateScreen(); - - // Swap OpenGL buffers - SDL_GL_SwapBuffers(); -} - -#ifdef USE_OSD -void OpenGLSdlGraphicsManager::displayModeChangedMsg() { - const char *newModeName = getCurrentModeName(); - if (newModeName) { - const int scaleFactor = getScale(); - - Common::String osdMessage = Common::String::format( - "%s: %s\n%d x %d -> %d x %d", - _("Current display mode"), - newModeName, - _videoMode.screenWidth * scaleFactor, - _videoMode.screenHeight * scaleFactor, - _hwscreen->w, _hwscreen->h - ); - displayMessageOnOSD(osdMessage.c_str()); + // WORKAROUND: Working around infamous SDL bugs when switching + // resolutions too fast. This might cause the event system to supply + // incorrect mouse position events otherwise. + // Reference: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665779 + const uint32 curTime = SDL_GetTicks(); + if (_hwScreen && (curTime < _lastVideoModeLoad || curTime - _lastVideoModeLoad < 100)) { + for (int i = 10; i > 0; --i) { + SDL_PumpEvents(); + SDL_Delay(10); + } } -} -void OpenGLSdlGraphicsManager::displayScaleChangedMsg() { - const int scaleFactor = getScale(); - Common::String osdMessage = Common::String::format( - "%s: x%d\n%d x %d -> %d x %d", - _("Current scale"), - scaleFactor, - _videoMode.screenWidth, _videoMode.screenHeight, - _videoMode.overlayWidth, _videoMode.overlayHeight - ); - displayMessageOnOSD(osdMessage.c_str()); -} -#endif + _lastVideoModeLoad = curTime; -bool OpenGLSdlGraphicsManager::isHotkey(const Common::Event &event) { - if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { - if (event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS || - event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS || - event.kbd.keycode == 'a' || event.kbd.keycode == 'f') - return true; - } else if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_SHIFT)) == (Common::KBD_CTRL|Common::KBD_SHIFT)) { - if (event.kbd.keycode == 'a' || event.kbd.keycode == 'f') - return true; - } else if ((event.kbd.flags & (Common::KBD_ALT)) == (Common::KBD_ALT) && event.kbd.keycode == 's') { - return true; + uint32 flags = SDL_OPENGL; + if (_wantsFullScreen) { + flags |= SDL_FULLSCREEN; + } else { + flags |= SDL_RESIZABLE; } - return false; -} -void OpenGLSdlGraphicsManager::toggleFullScreen(int loop) { - beginGFXTransaction(); - const bool isFullscreen = getFullscreenMode(); + _hwScreen = SDL_SetVideoMode(width, height, 32, flags); - if (isFullscreen && loop) { - _activeFullscreenMode += loop; - setFullscreenMode(true); - } else { - _activeFullscreenMode = -2; - setFullscreenMode(!isFullscreen); + if (!_hwScreen) { + // We treat fullscreen requests as a "hint" for now. This means in + // case it is not available we simply ignore it. + if (_wantsFullScreen) { + _hwScreen = SDL_SetVideoMode(width, height, 32, SDL_OPENGL | SDL_RESIZABLE); } + } - // HACK: We need to force a refresh here, since we change the - // fullscreen mode. - _transactionDetails.needRefresh = true; - endGFXTransaction(); - - // Ignore resize events for the next 10 frames - _ignoreResizeFrames = 10; + if (_hwScreen) { + const Graphics::PixelFormat rgba8888 = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + notifyContextChange(rgba8888, rgba8888); + setActualScreenSize(_hwScreen->w, _hwScreen->h); + } -#ifdef USE_OSD - Common::String osdMessage; - if (getFullscreenMode()) - osdMessage = Common::String::format("%s\n%d x %d", - _("Fullscreen mode"), - _hwscreen->w, _hwscreen->h - ); - else - osdMessage = Common::String::format("%s\n%d x %d", - _("Windowed mode"), - _hwscreen->w, _hwscreen->h - ); - displayMessageOnOSD(osdMessage.c_str()); -#endif + return _hwScreen != nullptr; } bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) { switch (event.type) { + case Common::EVENT_KEYUP: + return isHotkey(event); + case Common::EVENT_KEYDOWN: if (event.kbd.hasFlags(Common::KBD_ALT)) { - // Alt-Return and Alt-Enter toggle full screen mode - if (event.kbd.keycode == Common::KEYCODE_RETURN || - event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER) { - toggleFullScreen(0); + if ( event.kbd.keycode == Common::KEYCODE_RETURN + || event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER) { + // Alt-Return and Alt-Enter toggle full screen mode + beginGFXTransaction(); + setFeatureState(OSystem::kFeatureFullscreenMode, !getFeatureState(OSystem::kFeatureFullscreenMode)); + endGFXTransaction(); + +#ifdef USE_OSD + if (getFeatureState(OSystem::kFeatureFullscreenMode)) { + displayMessageOnOSD("Fullscreen mode"); + } else { + displayMessageOnOSD("Windowed mode"); + } +#endif return true; } - // Alt-S create a screenshot - if (event.kbd.keycode == 's') { + if (event.kbd.keycode == Common::KEYCODE_s) { + // Alt-s creates a screenshot Common::String filename; for (int n = 0;; n++) { @@ -509,169 +369,167 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) { break; SDL_RWclose(file); } - if (saveScreenshot(filename.c_str())) - debug("Saved screenshot '%s'", filename.c_str()); - else - warning("Could not save screenshot"); - return true; - } - } - if (event.kbd.hasFlags(Common::KBD_CTRL|Common::KBD_ALT)) { - // Ctrl-Alt-Return and Ctrl-Alt-Enter switch between full screen modes - if (event.kbd.keycode == Common::KEYCODE_RETURN || - event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER) { - toggleFullScreen(1); + saveScreenshot(filename.c_str()); + debug("Saved screenshot '%s'", filename.c_str()); + return true; } + } else if (event.kbd.hasFlags(Common::KBD_CTRL | Common::KBD_ALT)) { + if ( event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS + || event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS) { + // Ctrl+Alt+Plus/Minus Increase/decrease the size + const int direction = (event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_KP_PLUS) ? +1 : -1; + + if (getFeatureState(OSystem::kFeatureFullscreenMode)) { + // In case we are in fullscreen we will choose the previous + // or next mode. + + // In case no modes are available we do nothing. + if (_fullscreenVideoModes.empty()) { + return true; + } + + // Look for the current mode. + VideoModeArray::const_iterator i = Common::find(_fullscreenVideoModes.begin(), + _fullscreenVideoModes.end(), + VideoMode(_desiredFullscreenWidth, _desiredFullscreenHeight)); + if (i == _fullscreenVideoModes.end()) { + return true; + } + + // Cycle through the modes in the specified direction. + if (direction > 0) { + ++i; + if (i == _fullscreenVideoModes.end()) { + i = _fullscreenVideoModes.begin(); + } + } else { + if (i == _fullscreenVideoModes.begin()) { + i = _fullscreenVideoModes.end(); + } + --i; + } + + _desiredFullscreenWidth = i->width; + _desiredFullscreenHeight = i->height; + + // Try to setup the mode. + if (!setupMode(_lastRequestedWidth, _lastRequestedHeight)) { + warning("OpenGLSdlGraphicsManager::notifyEvent: Fullscreen resize failed ('%s')", SDL_GetError()); + g_system->quit(); + } + } else { + // Calculate the next scaling setting. We approximate the + // current scale setting in case the user resized the + // window. Then we apply the direction change. + _graphicsScale = MAX<int>(_hwScreen->w / _lastRequestedWidth, _hwScreen->h / _lastRequestedHeight); + _graphicsScale = MAX<int>(_graphicsScale + direction, 1); + + // Since we overwrite a user resize here we reset its + // flag here. This makes enabling AR smoother because it + // will change the window size like in surface SDL. + _gotResize = false; + + // Try to setup the mode. + if (!setupMode(_lastRequestedWidth * _graphicsScale, _lastRequestedHeight * _graphicsScale)) { + warning("OpenGLSdlGraphicsManager::notifyEvent: Window resize failed ('%s')", SDL_GetError()); + g_system->quit(); + } + } - // Ctrl-Alt-a switch between display modes - if (event.kbd.keycode == 'a') { - beginGFXTransaction(); - setFeatureState(OSystem::kFeatureAspectRatioCorrection, !getFeatureState(OSystem::kFeatureAspectRatioCorrection)); - endGFXTransaction(); #ifdef USE_OSD - Common::String osdMessage; - if (getFeatureState(OSystem::kFeatureAspectRatioCorrection)) - osdMessage = Common::String::format("%s\n%d x %d -> %d x %d", - _("Enabled aspect ratio correction"), - _videoMode.screenWidth, _videoMode.screenHeight, - _hwscreen->w, _hwscreen->h); - else - osdMessage = Common::String::format("%s\n%d x %d -> %d x %d", - _("Disabled aspect ratio correction"), - _videoMode.screenWidth, _videoMode.screenHeight, - _hwscreen->w, _hwscreen->h); - displayMessageOnOSD(osdMessage.c_str()); + const Common::String osdMsg = Common::String::format("Resolution: %dx%d", _hwScreen->w, _hwScreen->h); + displayMessageOnOSD(osdMsg.c_str()); #endif - internUpdateScreen(); + return true; - } + } else if (event.kbd.keycode == Common::KEYCODE_a) { + // In case the user changed the window size manually we will + // not change the window size again here. + _ignoreLoadVideoMode = _gotResize; - // Ctrl-Alt-f toggles antialiasing - if (event.kbd.keycode == 'f') { + // Ctrl+Alt+a toggles the aspect ratio correction state. beginGFXTransaction(); - toggleAntialiasing(); + setFeatureState(OSystem::kFeatureAspectRatioCorrection, !getFeatureState(OSystem::kFeatureAspectRatioCorrection)); endGFXTransaction(); + // Make sure we do not ignore the next resize. This + // effectively checks whether loadVideoMode has been called. + assert(!_ignoreLoadVideoMode); + #ifdef USE_OSD - // TODO: This makes guesses about what internal antialiasing - // modes we use, we might want to consider a better way of - // displaying information to the user. - if (getAntialiasingState()) - displayMessageOnOSD(_("Active filter mode: Linear")); - else - displayMessageOnOSD(_("Active filter mode: Nearest")); + Common::String osdMsg = "Aspect ratio correction: "; + osdMsg += getFeatureState(OSystem::kFeatureAspectRatioCorrection) ? "enabled" : "disabled"; + displayMessageOnOSD(osdMsg.c_str()); #endif + return true; - } + } else if (event.kbd.keycode == Common::KEYCODE_f) { + // Ctrl+Alt+f toggles the graphics modes. + + // We are crazy we will allow the OpenGL base class to + // introduce new graphics modes like shaders for special + // filtering. If some other OpenGL subclass needs this, + // we can think of refactoring this. + int mode = getGraphicsMode(); + const OSystem::GraphicsMode *supportedModes = getSupportedGraphicsModes(); + const OSystem::GraphicsMode *modeDesc = nullptr; + + // Search the current mode. + for (; supportedModes->name; ++supportedModes) { + if (supportedModes->id == mode) { + modeDesc = supportedModes; + break; + } + } + assert(modeDesc); - SDLKey sdlKey = (SDLKey)event.kbd.keycode; - - // Ctrl+Alt+Plus/Minus Increase/decrease the scale factor - if ((sdlKey == SDLK_EQUALS || sdlKey == SDLK_PLUS || sdlKey == SDLK_MINUS || - sdlKey == SDLK_KP_PLUS || sdlKey == SDLK_KP_MINUS)) { - int factor = getScale(); - factor += (sdlKey == SDLK_MINUS || sdlKey == SDLK_KP_MINUS) ? -1 : +1; - if (0 < factor && factor < 4) { - // Check if the desktop resolution has been detected - if (_desktopWidth > 0 && _desktopHeight > 0) - // If the new scale factor is too big, do not scale - if (_videoMode.screenWidth * factor > _desktopWidth || - _videoMode.screenHeight * factor > _desktopHeight) - return false; - - beginGFXTransaction(); - setScale(factor); - endGFXTransaction(); -#ifdef USE_OSD - displayScaleChangedMsg(); -#endif - return true; + // Try to use the next mode in the list. + ++modeDesc; + if (!modeDesc->name) { + modeDesc = getSupportedGraphicsModes(); } - } - const bool isNormalNumber = (SDLK_1 <= sdlKey && sdlKey <= SDLK_3); - const bool isKeypadNumber = (SDLK_KP1 <= sdlKey && sdlKey <= SDLK_KP3); + // Never ever try to resize the window when we simply want to + // switch the graphics mode. This assures that the window size + // does not change. + _ignoreLoadVideoMode = true; + + beginGFXTransaction(); + setGraphicsMode(modeDesc->id); + endGFXTransaction(); + + // Make sure we do not ignore the next resize. This + // effectively checks whether loadVideoMode has been called. + assert(!_ignoreLoadVideoMode); - // Ctrl-Alt-<number key> will change the GFX mode - if (isNormalNumber || isKeypadNumber) { - if (sdlKey - (isNormalNumber ? SDLK_1 : SDLK_KP1) <= 3) { -#ifdef USE_OSD - int lastMode = _videoMode.mode; -#endif - // We need to query the scale and set it up, because - // setGraphicsMode sets the default scale to 2 - int oldScale = getScale(); - beginGFXTransaction(); - setGraphicsMode(sdlKey - (isNormalNumber ? SDLK_1 : SDLK_KP1)); - setScale(oldScale); - endGFXTransaction(); #ifdef USE_OSD - if (lastMode != _videoMode.mode) - displayModeChangedMsg(); + const Common::String osdMsg = Common::String::format("Graphics mode: %s", _(modeDesc->description)); + displayMessageOnOSD(osdMsg.c_str()); #endif - internUpdateScreen(); - } - } - } - if (event.kbd.hasFlags(Common::KBD_CTRL|Common::KBD_SHIFT)) { - // Ctrl-Shift-Return and Ctrl-Shift-Enter switch backwards between full screen modes - if (event.kbd.keycode == Common::KEYCODE_RETURN || - event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER) { - toggleFullScreen(-1); return true; } } - break; - - case Common::EVENT_KEYUP: - return isHotkey(event); + // Fall through default: - break; + return false; } - - return false; -} - -void OpenGLSdlGraphicsManager::notifyVideoExpose() { } -void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) { - // Do not resize if ignoring resize events. - if (!_ignoreResizeFrames && !getFullscreenMode()) { - bool scaleChanged = false; - beginGFXTransaction(); - _videoMode.hardwareWidth = width; - _videoMode.hardwareHeight = height; - - _screenResized = true; - - int scale = MIN(_videoMode.hardwareWidth / _videoMode.screenWidth, - _videoMode.hardwareHeight / _videoMode.screenHeight); - - if (getScale() != scale) { - scaleChanged = true; - setScale(MAX(MIN(scale, 3), 1)); - } - - _transactionDetails.sizeChanged = true; - endGFXTransaction(); -#ifdef USE_OSD - if (scaleChanged) - displayScaleChangedMsg(); -#endif +bool OpenGLSdlGraphicsManager::isHotkey(const Common::Event &event) { + if (event.kbd.hasFlags(Common::KBD_ALT)) { + return event.kbd.keycode == Common::KEYCODE_RETURN + || event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER + || event.kbd.keycode == Common::KEYCODE_s; + } else if (event.kbd.hasFlags(Common::KBD_CTRL | Common::KBD_ALT)) { + return event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS + || event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS + || event.kbd.keycode == Common::KEYCODE_a + || event.kbd.keycode == Common::KEYCODE_f; } -} -void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) { - adjustMousePosition(point.x, point.y); -} - -void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) { - setMousePosition(mouse.x, mouse.y); + return false; } - -#endif diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h index 1587183328..0644f27602 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.h +++ b/backends/graphics/openglsdl/openglsdl-graphics.h @@ -8,117 +8,108 @@ * 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_OPENGLSDL_H -#define BACKENDS_GRAPHICS_OPENGLSDL_H +#ifndef BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H +#define BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H -#include "backends/platform/sdl/sdl-sys.h" -#if defined(ARRAYSIZE) && !defined(_WINDOWS_) -#undef ARRAYSIZE -#endif -#include "backends/graphics/sdl/sdl-graphics.h" #include "backends/graphics/opengl/opengl-graphics.h" +#include "backends/graphics/sdl/sdl-graphics.h" +#include "backends/platform/sdl/sdl-sys.h" +#include "common/array.h" #include "common/events.h" -/** - * SDL OpenGL graphics manager - */ -class OpenGLSdlGraphicsManager : public OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver { +class OpenGLSdlGraphicsManager : public OpenGL::OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver { public: - OpenGLSdlGraphicsManager(SdlEventSource *eventSource); + OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource); virtual ~OpenGLSdlGraphicsManager(); + void initEventObserver(); + + // GraphicsManager API virtual bool hasFeature(OSystem::Feature f); virtual void setFeatureState(OSystem::Feature f, bool enable); + virtual bool getFeatureState(OSystem::Feature f); + + virtual bool setGraphicsMode(int mode); + virtual void resetGraphicsScale(); #ifdef USE_RGB_COLOR virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; #endif - virtual void initEventObserver(); - virtual bool notifyEvent(const Common::Event &event); - virtual void updateScreen(); - // SdlGraphicsManager interface + // EventObserver API + virtual bool notifyEvent(const Common::Event &event); + + // SdlGraphicsManager API virtual void notifyVideoExpose(); virtual void notifyResize(const uint width, const uint height); virtual void transformMouseCoordinates(Common::Point &point); virtual void notifyMousePos(Common::Point mouse); protected: - virtual void internUpdateScreen(); + virtual void setInternalMousePosition(int x, int y); - virtual bool loadGFXMode(); - virtual void unloadGFXMode(); - virtual bool isHotkey(const Common::Event &event); + virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format); +private: + bool setupMode(uint width, uint height); -#ifdef USE_RGB_COLOR - Common::List<Graphics::PixelFormat> _supportedFormats; + uint32 _lastVideoModeLoad; + SDL_Surface *_hwScreen; - /** - * Update the list of supported pixel formats. - * This method is invoked by loadGFXMode(). - */ - void detectSupportedFormats(); -#endif + uint _lastRequestedWidth; + uint _lastRequestedHeight; + uint _graphicsScale; + bool _ignoreLoadVideoMode; + bool _gotResize; - /** - * Toggles fullscreen. - * @loop loop direction for switching fullscreen mode, if 0 toggles it. - */ - virtual void toggleFullScreen(int loop); + bool _wantsFullScreen; + uint _ignoreResizeEvents; - int _activeFullscreenMode; + struct VideoMode { + VideoMode() : width(0), height(0) {} + VideoMode(uint w, uint h) : width(w), height(h) {} - /** - * Setup the fullscreen mode. - * @return false if failed finding a mode, true otherwise. - */ - virtual bool setupFullscreenMode(); + bool operator<(const VideoMode &right) const { + if (width < right.width) { + return true; + } else if (width == right.width && height < right.height) { + return true; + } else { + return false; + } + } - virtual void setInternalMousePosition(int x, int y); + bool operator==(const VideoMode &right) const { + return width == right.width && height == right.height; + } - int _lastFullscreenModeWidth; - int _lastFullscreenModeHeight; - int _desktopWidth; - int _desktopHeight; - - // Hardware screen - SDL_Surface *_hwscreen; - - // If screen was resized by the user - bool _screenResized; - - // Ignore resize events for the number of updateScreen() calls. - // Normaly resize events are user generated when resizing the window - // from its borders, but in some cases a resize event can be generated - // after a fullscreen change. - int _ignoreResizeFrames; - -#ifdef USE_OSD - /** - * Displays a mode change message in OSD - */ - void displayModeChangedMsg(); - - /** - * Displays a scale change message in OSD - */ - void displayScaleChangedMsg(); -#endif + bool operator!=(const VideoMode &right) const { + return !(*this == right); + } + + uint width, height; + }; + typedef Common::Array<VideoMode> VideoModeArray; + VideoModeArray _fullscreenVideoModes; + + uint _desiredFullscreenWidth; + uint _desiredFullscreenHeight; + + virtual bool isHotkey(const Common::Event &event); }; #endif diff --git a/backends/module.mk b/backends/module.mk index 31ac444750..1222d9a363 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -40,14 +40,6 @@ MODULE_OBJS += \ keymapper/remap-dialog.o endif -ifdef USE_OPENGL -MODULE_OBJS += \ - graphics/opengl/glerrorcheck.o \ - graphics/opengl/gltexture.o \ - graphics/opengl/opengl-graphics.o \ - graphics/openglsdl/openglsdl-graphics.o -endif - ifdef ENABLE_VKEYBD MODULE_OBJS += \ vkeybd/image-map.o \ @@ -57,6 +49,15 @@ MODULE_OBJS += \ vkeybd/virtual-keyboard-parser.o endif +# OpenGL specific source files. +ifdef USE_OPENGL +MODULE_OBJS += \ + graphics/opengl/debug.o \ + graphics/opengl/extensions.o \ + graphics/opengl/opengl-graphics.o \ + graphics/opengl/texture.o +endif + # SDL specific source files. # We cannot just check $BACKEND = sdl, as various other backends # derive from the SDL backend, and they all need the following files. @@ -76,6 +77,11 @@ ifndef USE_SDL13 MODULE_OBJS += \ audiocd/sdl/sdl-audiocd.o endif + +ifdef USE_OPENGL +MODULE_OBJS += \ + graphics/openglsdl/openglsdl-graphics.o +endif endif ifdef POSIX diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 7ab367d4a4..1431e1fc5e 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -65,6 +65,8 @@ OSystem_SDL::OSystem_SDL() : #ifdef USE_OPENGL + _desktopWidth(0), + _desktopHeight(0), _graphicsModes(0), _graphicsMode(0), _sdlModesCount(0), @@ -161,6 +163,16 @@ void OSystem_SDL::initBackend() { int graphicsManagerType = 0; +#ifdef USE_OPENGL + // Query the desktop resolution. We simply hope nothing tried to change + // the resolution so far. + const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo(); + if (videoInfo && videoInfo->current_w > 0 && videoInfo->current_h > 0) { + _desktopWidth = videoInfo->current_w; + _desktopHeight = videoInfo->current_h; + } +#endif + if (_graphicsManager == 0) { #ifdef USE_OPENGL if (ConfMan.hasKey("gfx_mode")) { @@ -180,7 +192,7 @@ void OSystem_SDL::initBackend() { // If the gfx_mode is from OpenGL, create the OpenGL graphics manager if (use_opengl) { - _graphicsManager = new OpenGLSdlGraphicsManager(_eventSource); + _graphicsManager = new OpenGLSdlGraphicsManager(_desktopWidth, _desktopHeight, _eventSource); graphicsManagerType = 1; } } @@ -261,8 +273,9 @@ void OSystem_SDL::initSDL() { if (ConfMan.hasKey("disable_sdl_parachute")) sdlFlags |= SDL_INIT_NOPARACHUTE; -#ifdef WEBOS - // WebOS needs this flag or otherwise the application won't start +#if defined(WEBOS) || defined(USE_OPENGL) + // WebOS needs this flag or otherwise the application won't start. + // OpenGL SDL needs this to query the desktop resolution on startup. sdlFlags |= SDL_INIT_VIDEO; #endif @@ -591,7 +604,7 @@ bool OSystem_SDL::setGraphicsMode(int mode) { } else if (_graphicsMode < _sdlModesCount && mode >= _sdlModesCount) { debug(1, "switching to OpenGL graphics"); delete _graphicsManager; - _graphicsManager = new OpenGLSdlGraphicsManager(_eventSource); + _graphicsManager = new OpenGLSdlGraphicsManager(_desktopWidth, _desktopHeight, _eventSource); ((OpenGLSdlGraphicsManager *)_graphicsManager)->initEventObserver(); _graphicsManager->beginGFXTransaction(); diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 840e73ff09..590354b699 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -106,6 +106,8 @@ protected: Backends::Log::Log *_logger; #ifdef USE_OPENGL + int _desktopWidth, _desktopHeight; + OSystem::GraphicsMode *_graphicsModes; int _graphicsMode; int _sdlModesCount; diff --git a/backends/platform/tizen/graphics.cpp b/backends/platform/tizen/graphics.cpp index 2cafb9f781..390796dc0e 100644 --- a/backends/platform/tizen/graphics.cpp +++ b/backends/platform/tizen/graphics.cpp @@ -37,7 +37,6 @@ TizenGraphicsManager::TizenGraphicsManager(TizenAppForm *appForm) : _eglContext(EGL_NO_CONTEXT), _initState(true) { assert(appForm != NULL); - _videoMode.fullscreen = true; } TizenGraphicsManager::~TizenGraphicsManager() { @@ -51,17 +50,34 @@ TizenGraphicsManager::~TizenGraphicsManager() { } } +result TizenGraphicsManager::Construct() { + // Initialize our OpenGL ES context. + loadEgl(); + + // Notify the OpenGL code about our context. + + // We default to RGB565 and RGBA5551 which is closest to the actual output + // mode we setup. + notifyContextChange(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); + + // Tell our size. + int x, y, width, height; + _appForm->GetBounds(x, y, width, height); + AppLog("screen size: %dx%d", width, height); + setActualScreenSize(width, height); + return E_SUCCESS; +} + const Graphics::Font *TizenGraphicsManager::getFontOSD() { return FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); } bool TizenGraphicsManager::moveMouse(int16 &x, int16 &y) { - int16 currentX = _cursorState.x; - int16 currentY = _cursorState.y; + int16 currentX, currentY; + getMousePosition(currentX, currentY); // save the current hardware coordinates - _cursorState.x = x; - _cursorState.y = y; + setMousePosition(x, y); // return x/y as game coordinates adjustMousePosition(x, y); @@ -85,15 +101,17 @@ Common::List<Graphics::PixelFormat> TizenGraphicsManager::getSupportedFormats() } bool TizenGraphicsManager::hasFeature(OSystem::Feature f) { - bool result = (f == OSystem::kFeatureFullscreenMode || - f == OSystem::kFeatureVirtualKeyboard || + bool result = + (f == OSystem::kFeatureVirtualKeyboard || OpenGLGraphicsManager::hasFeature(f)); return result; } void TizenGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { - if (f == OSystem::kFeatureVirtualKeyboard && enable) { - _appForm->showKeypad(); + if (f == OSystem::kFeatureVirtualKeyboard) { + if (enable) { + _appForm->showKeypad(); + } } else { OpenGLGraphicsManager::setFeatureState(f, enable); } @@ -106,8 +124,9 @@ void TizenGraphicsManager::setReady() { } void TizenGraphicsManager::updateScreen() { - if (_transactionMode == kTransactionNone) { - internUpdateScreen(); + if (!_initState) { + OpenGLGraphicsManager::updateScreen(); + eglSwapBuffers(_eglDisplay, _eglSurface); } } @@ -133,10 +152,6 @@ bool TizenGraphicsManager::loadEgl() { eglBindAPI(EGL_OPENGL_ES_API); - if (_eglDisplay) { - unloadGFXMode(); - } - _eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY); if (EGL_NO_DISPLAY == _eglDisplay) { systemError("eglGetDisplay() failed"); @@ -178,65 +193,12 @@ bool TizenGraphicsManager::loadEgl() { systemError("eglMakeCurrent() failed"); return false; } - if (!_initState) { - _appForm->GetVisualElement()->SetShowState(true); - } logLeaving(); return true; } -bool TizenGraphicsManager::loadGFXMode() { - logEntered(); - - if (!loadEgl()) { - unloadGFXMode(); - return false; - } - - int x, y, width, height; - _appForm->GetBounds(x, y, width, height); - _videoMode.overlayWidth = _videoMode.hardwareWidth = width; - _videoMode.overlayHeight = _videoMode.hardwareHeight = height; - _videoMode.scaleFactor = 4; // for proportional sized cursor in the launcher - - AppLog("screen size: %dx%d", _videoMode.hardwareWidth, _videoMode.hardwareHeight); - return OpenGLGraphicsManager::loadGFXMode(); -} - -void TizenGraphicsManager::loadTextures() { - logEntered(); - OpenGLGraphicsManager::loadTextures(); -} - -void TizenGraphicsManager::internUpdateScreen() { - if (!_initState) { - OpenGLGraphicsManager::internUpdateScreen(); - eglSwapBuffers(_eglDisplay, _eglSurface); - } -} - -void TizenGraphicsManager::unloadGFXMode() { - logEntered(); - _appForm->GetVisualElement()->SetShowState(false); - - if (_eglDisplay != EGL_NO_DISPLAY) { - eglMakeCurrent(_eglDisplay, NULL, NULL, NULL); - - if (_eglContext != EGL_NO_CONTEXT) { - eglDestroyContext(_eglDisplay, _eglContext); - _eglContext = EGL_NO_CONTEXT; - } - - if (_eglSurface != EGL_NO_SURFACE) { - eglDestroySurface(_eglDisplay, _eglSurface); - _eglSurface = EGL_NO_SURFACE; - } - - eglTerminate(_eglDisplay); - _eglDisplay = EGL_NO_DISPLAY; - } - - _eglConfig = NULL; - OpenGLGraphicsManager::unloadGFXMode(); - logLeaving(); +bool TizenGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) { + // We get this whenever a new resolution is requested. Since Tizen is + // using a fixed output size we do nothing like that here. + return true; } diff --git a/backends/platform/tizen/graphics.h b/backends/platform/tizen/graphics.h index 27e5a6aaeb..29ba86a3c4 100644 --- a/backends/platform/tizen/graphics.h +++ b/backends/platform/tizen/graphics.h @@ -39,28 +39,31 @@ using namespace Tizen::Graphics; using namespace Tizen::Graphics::Opengl; using namespace Tizen::App; -class TizenGraphicsManager : public OpenGLGraphicsManager { +class TizenGraphicsManager : public OpenGL::OpenGLGraphicsManager { public: TizenGraphicsManager(TizenAppForm *appForm); virtual ~TizenGraphicsManager(); + result Construct(); + Common::List<Graphics::PixelFormat> getSupportedFormats() const; bool hasFeature(OSystem::Feature f); - void updateScreen(); void setFeatureState(OSystem::Feature f, bool enable); + void updateScreen(); + void setReady(); bool isReady() { return !_initState; } - const Graphics::Font *getFontOSD(); + bool moveMouse(int16 &x, int16 &y); -private: - void internUpdateScreen(); - bool loadGFXMode(); - void loadTextures(); - void unloadGFXMode(); +protected: void setInternalMousePosition(int x, int y) {} - void showSplash(); + bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format); + + const Graphics::Font *getFontOSD(); + +private: bool loadEgl(); TizenAppForm *_appForm; EGLDisplay _eglDisplay; diff --git a/backends/platform/tizen/system.cpp b/backends/platform/tizen/system.cpp index 3448dc1421..f7ebc46719 100644 --- a/backends/platform/tizen/system.cpp +++ b/backends/platform/tizen/system.cpp @@ -267,7 +267,7 @@ result TizenSystem::initModules() { } _graphicsManager = (GraphicsManager *)new TizenGraphicsManager(_appForm); - if (!_graphicsManager) { + if (!_graphicsManager || graphicsManager->Construct() != E_SUCCESS) { return E_OUT_OF_MEMORY; } diff --git a/base/main.cpp b/base/main.cpp index 103d743bbc..c993dfa57a 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -134,6 +134,19 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const Common::Error err = Common::kNoError; Engine *engine = 0; +#if defined(SDL_BACKEND) && defined(USE_OPENGL) && defined(USE_RGB_COLOR) + // HACK: We set up the requested graphics mode setting here to allow the + // backend to switch from Surface SDL to OpenGL if necessary. This is + // needed because otherwise the g_system->getSupportedFormats might return + // bad values. + g_system->beginGFXTransaction(); + g_system->setGraphicsMode(ConfMan.get("gfx_mode").c_str()); + if (g_system->endGFXTransaction() != OSystem::kTransactionSuccess) { + warning("Switching graphics mode to '%s' failed", ConfMan.get("gfx_mode").c_str()); + return Common::kUnknownError; + } +#endif + // Verify that the game path refers to an actual directory if (!(dir.exists() && dir.isDirectory())) err = Common::kPathNotDirectory; diff --git a/engines/engine.cpp b/engines/engine.cpp index c84404cc68..52020c772e 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -183,7 +183,7 @@ void initCommonGFX(bool defaultTo1XScaler) { g_system->setGraphicsMode(gfxMode.c_str()); // HACK: For OpenGL modes, we will still honor the graphics scale override - if (defaultTo1XScaler && (gfxMode.equalsIgnoreCase("gl1") || gfxMode.equalsIgnoreCase("gl2") || gfxMode.equalsIgnoreCase("gl4"))) + if (defaultTo1XScaler && (gfxMode.equalsIgnoreCase("opengl_linear") || gfxMode.equalsIgnoreCase("opengl_nearest"))) g_system->resetGraphicsScale(); } } diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 1505c8c707..999d61e854 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -309,6 +309,19 @@ void GuiManager::runLoop() { Common::Event event; while (eventMan->pollEvent(event)) { + // We will need to check whether the screen changed while polling + // for an event here. While we do send EVENT_SCREEN_CHANGED + // whenever this happens we still cannot be sure that we get such + // an event immediately. For example, we might have an mouse move + // event queued before an screen changed event. In some rare cases + // this would make the GUI redraw (with the code a few lines + // below) when it is not yet updated for new overlay dimensions. + // As a result ScummVM would crash because it tries to copy data + // outside the actual overlay screen. + if (event.type != Common::EVENT_SCREEN_CHANGED) { + checkScreenChange(); + } + // The top dialog can change during the event loop. In that case, flush all the // dialog-related events since they were probably generated while the old dialog // was still visible, and therefore not intended for the new one. |