diff options
Diffstat (limited to 'backends/graphics/opengl')
| -rw-r--r-- | backends/graphics/opengl/debug.cpp | 65 | ||||
| -rw-r--r-- | backends/graphics/opengl/debug.h | 39 | ||||
| -rw-r--r-- | backends/graphics/opengl/extensions.cpp | 48 | ||||
| -rw-r--r-- | backends/graphics/opengl/extensions.h (renamed from backends/graphics/opengl/glerrorcheck.h) | 26 | ||||
| -rw-r--r-- | backends/graphics/opengl/gltexture.cpp | 225 | ||||
| -rw-r--r-- | backends/graphics/opengl/gltexture.h | 131 | ||||
| -rw-r--r-- | backends/graphics/opengl/opengl-graphics.cpp | 1830 | ||||
| -rw-r--r-- | backends/graphics/opengl/opengl-graphics.h | 490 | ||||
| -rw-r--r-- | backends/graphics/opengl/opengl-sys.h (renamed from backends/graphics/opengl/glerrorcheck.cpp) | 50 | ||||
| -rw-r--r-- | backends/graphics/opengl/texture.cpp | 371 | ||||
| -rw-r--r-- | backends/graphics/opengl/texture.h | 175 | 
11 files changed, 1832 insertions, 1618 deletions
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..9ad0e62f37 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,1016 @@ void OpenGLGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {  bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) {  	switch (f) { -	case OSystem::kFeatureFullscreenMode: -		return _videoMode.fullscreen; -  	case OSystem::kFeatureAspectRatioCorrection: -		return _videoMode.aspectRatioCorrection; +		return _currentState.aspectRatioCorrection;  	case OSystem::kFeatureCursorPalette: -		return !_cursorPaletteDisabled; +		return _cursorPaletteEnabled;  	default:  		return false;  	}  } -// -// Screen format and modes -// +namespace { -static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { -	{"gl1", _s("OpenGL Normal"), OpenGL::GFX_NORMAL}, -	{"gl2", _s("OpenGL Conserve"), OpenGL::GFX_CONSERVE}, -	{"gl4", _s("OpenGL Original"), OpenGL::GFX_ORIGINAL}, -	{0, 0, 0} +const OSystem::GraphicsMode glGraphicsModes[] = { +	{ "opengl_linear",  _s("OpenGL"),                GFX_LINEAR  }, +	{ "opengl_nearest", _s("OpenGL (No filtering)"), GFX_NEAREST }, +	{ nullptr, nullptr, 0 }  }; -const OSystem::GraphicsMode *OpenGLGraphicsManager::supportedGraphicsModes() { -	return s_supportedGraphicsModes; -} +} // End of anonymous namespace  const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedGraphicsModes() const { -	return s_supportedGraphicsModes; +	return glGraphicsModes;  }  int OpenGLGraphicsManager::getDefaultGraphicsMode() const { -	return OpenGL::GFX_NORMAL; +	return GFX_LINEAR;  }  bool OpenGLGraphicsManager::setGraphicsMode(int mode) { -	assert(_transactionMode == kTransactionActive); +	assert(_transactionMode != kTransactionNone); + +	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 +1139,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..d2d0358407 100644 --- a/backends/graphics/opengl/opengl-graphics.h +++ b/backends/graphics/opengl/opengl-graphics.h @@ -8,338 +8,464 @@   * 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); -	static const OSystem::GraphicsMode *supportedGraphicsModes();  	virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;  	virtual int getDefaultGraphicsMode() const;  	virtual bool setGraphicsMode(int mode);  	virtual int getGraphicsMode() const; -	virtual void resetGraphicsScale(); + +	virtual void resetGraphicsScale() {} +  #ifdef USE_RGB_COLOR  	virtual Graphics::PixelFormat getScreenFormat() const;  	virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0;  #endif -	virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL); -	virtual int getScreenChangeID() const;  	virtual void beginGFXTransaction();  	virtual OSystem::TransactionError endGFXTransaction(); -	virtual int16 getHeight(); +	virtual int getScreenChangeID() const; + +	virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format); +  	virtual int16 getWidth(); -protected: -	// PaletteManager API -	virtual void setPalette(const byte *colors, uint start, uint num); -	virtual void grabPalette(byte *colors, uint start, uint num); +	virtual int16 getHeight(); -public:  	virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h); -	virtual Graphics::Surface *lockScreen(); -	virtual void unlockScreen();  	virtual void fillScreen(uint32 col); -	virtual void updateScreen(); +  	virtual void setShakePos(int shakeOffset); -	virtual void setFocusRectangle(const Common::Rect &rect); + +	virtual void updateScreen(); + +	virtual Graphics::Surface *lockScreen(); +	virtual void unlockScreen(); + +	virtual void setFocusRectangle(const Common::Rect& rect);  	virtual void clearFocusRectangle(); +	virtual int16 getOverlayWidth(); +	virtual int16 getOverlayHeight(); +  	virtual void showOverlay();  	virtual void hideOverlay(); +  	virtual Graphics::PixelFormat getOverlayFormat() const; + +	virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);  	virtual void clearOverlay();  	virtual void grabOverlay(void *buf, int pitch); -	virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h); -	virtual int16 getOverlayHeight(); -	virtual int16 getOverlayWidth();  	virtual bool showMouse(bool visible);  	virtual void warpMouse(int x, int y); -	virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); +	virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);  	virtual void setCursorPalette(const byte *colors, uint start, uint num);  	virtual void displayMessageOnOSD(const char *msg); + +	// PaletteManager interface +	virtual void setPalette(const byte *colors, uint start, uint num); +	virtual void grabPalette(byte *colors, uint start, uint num); +  protected:  	/** -	 * Setup OpenGL settings +	 * Set up the actual screen size available for the OpenGL code to do any +	 * drawing. +	 * +	 * @param width  The width of the screen. +	 * @param height The height of the screen. +	 */ +	void setActualScreenSize(uint width, uint height); + +	/** +	 * Notify the manager of a OpenGL context change. This should be the first +	 * thing to call 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). +	 */ +	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.  	 */ -	virtual void initGL(); +	void getMousePosition(int16 &x, int16 &y) const { x = _cursorX; y = _cursorY; }  	/** -	 * Creates and refreshs OpenGL textures. +	 * 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 loadTextures(); +	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. +	 */ +	bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const; + +	// +	// Actual hardware screen +	// + +	/** +	 * The width of the physical output. +	 */ +	uint _outputScreenWidth; + +	/** +	 * 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 toggleAntialiasing(); +	void recalculateDisplayArea();  	/** -	 * Query the antialiasing state. +	 * The X coordinate of the game screen.  	 */ -	inline bool getAntialiasingState() const { return _videoMode.antialiasing; } +	uint _displayX; -	// Drawing coordinates for the current display mode and scale -	int _displayX; -	int _displayY; -	int _displayWidth; -	int _displayHeight; +	/** +	 * 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 +474,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  | 
