This partly fixes the OpenGL mode mess, but now OpenGL Normal and OpenGL Conserve will feature the same semantics when aspect ratio correction is enabled... That is still something to solve.
1451 lines
41 KiB
C++
1451 lines
41 KiB
C++
/* 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#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 "common/translation.h"
|
|
#ifdef USE_OSD
|
|
#include "common/tokenizer.h"
|
|
#endif
|
|
#include "graphics/font.h"
|
|
#include "graphics/fontman.h"
|
|
|
|
OpenGLGraphicsManager::OpenGLGraphicsManager()
|
|
:
|
|
#ifdef USE_OSD
|
|
_osdTexture(0), _osdAlpha(0), _osdFadeStartTime(0), _requireOSDUpdate(false),
|
|
#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),
|
|
_cursorTargetScale(1),
|
|
_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);
|
|
}
|
|
|
|
OpenGLGraphicsManager::~OpenGLGraphicsManager() {
|
|
// Unregister the event observer
|
|
if (g_system->getEventManager()->getEventDispatcher() != NULL)
|
|
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
|
|
|
|
free(_gamePalette);
|
|
free(_cursorPalette);
|
|
|
|
delete _gameTexture;
|
|
delete _overlayTexture;
|
|
delete _cursorTexture;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::initEventObserver() {
|
|
// Register the graphics manager as a event observer
|
|
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
|
|
}
|
|
|
|
//
|
|
// Feature
|
|
//
|
|
|
|
bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) {
|
|
return
|
|
(f == OSystem::kFeatureAspectRatioCorrection) ||
|
|
(f == OSystem::kFeatureCursorHasPalette);
|
|
}
|
|
|
|
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;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) {
|
|
switch (f) {
|
|
case OSystem::kFeatureFullscreenMode:
|
|
return _videoMode.fullscreen;
|
|
|
|
case OSystem::kFeatureAspectRatioCorrection:
|
|
return _videoMode.aspectRatioCorrection;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Screen format and modes
|
|
//
|
|
|
|
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 *OpenGLGraphicsManager::supportedGraphicsModes() {
|
|
return s_supportedGraphicsModes;
|
|
}
|
|
|
|
const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedGraphicsModes() const {
|
|
return s_supportedGraphicsModes;
|
|
}
|
|
|
|
int OpenGLGraphicsManager::getDefaultGraphicsMode() const {
|
|
return OpenGL::GFX_NORMAL;
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::setGraphicsMode(int mode) {
|
|
assert(_transactionMode == kTransactionActive);
|
|
|
|
setScale(2);
|
|
|
|
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);
|
|
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);
|
|
}
|
|
|
|
#ifdef USE_RGB_COLOR
|
|
Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const {
|
|
return _screenFormat;
|
|
}
|
|
#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);
|
|
|
|
_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 != kTransactionNone);
|
|
|
|
if (_transactionMode == kTransactionRollback) {
|
|
if (_videoMode.fullscreen != _oldVideoMode.fullscreen) {
|
|
errors |= OSystem::kTransactionFullscreenFailed;
|
|
|
|
_videoMode.fullscreen = _oldVideoMode.fullscreen;
|
|
} else if (_videoMode.mode != _oldVideoMode.mode) {
|
|
errors |= OSystem::kTransactionModeSwitchFailed;
|
|
|
|
_videoMode.mode = _oldVideoMode.mode;
|
|
_videoMode.scaleFactor = _oldVideoMode.scaleFactor;
|
|
#ifdef USE_RGB_COLOR
|
|
} else if (_videoMode.format != _oldVideoMode.format) {
|
|
errors |= OSystem::kTransactionFormatNotSupported;
|
|
|
|
_videoMode.format = _oldVideoMode.format;
|
|
_screenFormat = _videoMode.format;
|
|
#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 (_videoMode.fullscreen == _oldVideoMode.fullscreen &&
|
|
_videoMode.mode == _oldVideoMode.mode &&
|
|
_videoMode.screenWidth == _oldVideoMode.screenWidth &&
|
|
_videoMode.screenHeight == _oldVideoMode.screenHeight) {
|
|
|
|
_oldVideoMode.setup = false;
|
|
}
|
|
}
|
|
|
|
if (_transactionDetails.sizeChanged || _transactionDetails.needRefresh) {
|
|
unloadGFXMode();
|
|
if (!loadGFXMode()) {
|
|
if (_oldVideoMode.setup) {
|
|
_transactionMode = kTransactionRollback;
|
|
errors |= endGFXTransaction();
|
|
}
|
|
} else {
|
|
clearOverlay();
|
|
|
|
_videoMode.setup = true;
|
|
_screenChangeCount++;
|
|
}
|
|
#ifdef USE_RGB_COLOR
|
|
} else if (_transactionDetails.filterChanged || _transactionDetails.formatChanged) {
|
|
#else
|
|
} else if (_transactionDetails.filterChanged) {
|
|
#endif
|
|
loadTextures();
|
|
internUpdateScreen();
|
|
} else if (_transactionDetails.needUpdatescreen) {
|
|
internUpdateScreen();
|
|
}
|
|
|
|
_transactionMode = kTransactionNone;
|
|
return (OSystem::TransactionError)errors;
|
|
}
|
|
|
|
//
|
|
// Screen
|
|
//
|
|
|
|
int16 OpenGLGraphicsManager::getHeight() {
|
|
return _videoMode.screenHeight;
|
|
}
|
|
|
|
int16 OpenGLGraphicsManager::getWidth() {
|
|
return _videoMode.screenWidth;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
|
|
assert(colors);
|
|
|
|
#ifdef USE_RGB_COLOR
|
|
assert(_screenFormat.bytesPerPixel == 1);
|
|
#endif
|
|
|
|
// Save the screen palette
|
|
memcpy(_gamePalette + start * 3, colors, num * 3);
|
|
|
|
_screenNeedsRedraw = true;
|
|
|
|
if (_cursorPaletteDisabled)
|
|
_cursorNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) {
|
|
assert(colors);
|
|
|
|
#ifdef USE_RGB_COLOR
|
|
assert(_screenFormat.bytesPerPixel == 1);
|
|
#endif
|
|
|
|
// Copies current palette to buffer
|
|
memcpy(colors, _gamePalette + start * 3, num * 3);
|
|
}
|
|
|
|
void OpenGLGraphicsManager::copyRectToScreen(const byte *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 = buf;
|
|
byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch + x * _screenData.bytesPerPixel;
|
|
for (int i = 0; i < h; i++) {
|
|
memcpy(dst, src, w * _screenData.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);
|
|
}
|
|
}
|
|
|
|
Graphics::Surface *OpenGLGraphicsManager::lockScreen() {
|
|
return &_screenData;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::unlockScreen() {
|
|
_screenNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::fillScreen(uint32 col) {
|
|
if (_gameTexture == NULL)
|
|
return;
|
|
|
|
#ifdef USE_RGB_COLOR
|
|
if (_screenFormat.bytesPerPixel == 1) {
|
|
memset(_screenData.pixels, col, _screenData.h * _screenData.pitch);
|
|
} else if (_screenFormat.bytesPerPixel == 2) {
|
|
uint16 *pixels = (uint16 *)_screenData.pixels;
|
|
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.pixels;
|
|
byte r = (col >> 16) & 0xFF;
|
|
byte g = (col >> 8) & 0xFF;
|
|
byte b = col & 0xFF;
|
|
for (int i = 0; i < _screenData.w * _screenData.h; i++) {
|
|
pixels[0] = r;
|
|
pixels[1] = g;
|
|
pixels[2] = b;
|
|
pixels += 3;
|
|
}
|
|
} else if (_screenFormat.bytesPerPixel == 4) {
|
|
uint32 *pixels = (uint32 *)_screenData.pixels;
|
|
for (int i = 0; i < _screenData.w * _screenData.h; i++) {
|
|
pixels[i] = col;
|
|
}
|
|
}
|
|
#else
|
|
memset(_screenData.pixels, col, _screenData.h * _screenData.pitch);
|
|
#endif
|
|
_screenNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::updateScreen() {
|
|
assert (_transactionMode == kTransactionNone);
|
|
internUpdateScreen();
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setShakePos(int shakeOffset) {
|
|
assert (_transactionMode == kTransactionNone);
|
|
_shakePos = shakeOffset;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setFocusRectangle(const Common::Rect& rect) {
|
|
}
|
|
|
|
void OpenGLGraphicsManager::clearFocusRectangle() {
|
|
}
|
|
|
|
//
|
|
// Overlay
|
|
//
|
|
|
|
void OpenGLGraphicsManager::showOverlay() {
|
|
assert (_transactionMode == kTransactionNone);
|
|
|
|
if (_overlayVisible)
|
|
return;
|
|
|
|
_overlayVisible = true;
|
|
|
|
clearOverlay();
|
|
}
|
|
|
|
void OpenGLGraphicsManager::hideOverlay() {
|
|
assert (_transactionMode == kTransactionNone);
|
|
|
|
if (!_overlayVisible)
|
|
return;
|
|
|
|
_overlayVisible = false;
|
|
|
|
clearOverlay();
|
|
}
|
|
|
|
Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
|
|
return _overlayFormat;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::clearOverlay() {
|
|
// Set all pixels to 0
|
|
memset(_overlayData.pixels, 0, _overlayData.h * _overlayData.pitch);
|
|
_overlayNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) {
|
|
assert(_overlayData.bytesPerPixel == sizeof(buf[0]));
|
|
const byte *src = (byte *)_overlayData.pixels;
|
|
for (int i = 0; i < _overlayData.h; i++) {
|
|
// Copy overlay data to buffer
|
|
memcpy(buf, src, _overlayData.pitch);
|
|
buf += pitch;
|
|
src += _overlayData.pitch;
|
|
}
|
|
}
|
|
|
|
void OpenGLGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) {
|
|
assert (_transactionMode == kTransactionNone);
|
|
|
|
if (_overlayTexture == NULL)
|
|
return;
|
|
|
|
// Clip the coordinates
|
|
if (x < 0) {
|
|
w += x;
|
|
buf -= x;
|
|
x = 0;
|
|
}
|
|
|
|
if (y < 0) {
|
|
h += y; buf -= 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;
|
|
|
|
if (_overlayFormat.aBits() == 1) {
|
|
// Copy buffer with the alpha bit on for all pixels for correct
|
|
// overlay drawing.
|
|
const uint16 *src = (const uint16 *)buf;
|
|
uint16 *dst = (uint16 *)_overlayData.pixels + y * _overlayData.w + x;
|
|
for (int i = 0; i < h; i++) {
|
|
for (int e = 0; e < w; e++)
|
|
dst[e] = src[e] | 0x1;
|
|
src += pitch;
|
|
dst += _overlayData.w;
|
|
}
|
|
} else {
|
|
// Copy buffer data to internal overlay surface
|
|
const byte *src = (const byte *)buf;
|
|
byte *dst = (byte *)_overlayData.pixels + y * _overlayData.pitch;
|
|
for (int i = 0; i < h; i++) {
|
|
memcpy(dst + x * _overlayData.bytesPerPixel, src, w * _overlayData.bytesPerPixel);
|
|
src += pitch * sizeof(buf[0]);
|
|
dst += _overlayData.pitch;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
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;
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
scaledX += _displayX;
|
|
scaledY += _displayY;
|
|
}
|
|
|
|
setInternalMousePosition(scaledX, scaledY);
|
|
|
|
_cursorState.x = scaledX;
|
|
_cursorState.y = scaledY;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) {
|
|
#ifdef USE_RGB_COLOR
|
|
if (format)
|
|
_cursorFormat = *format;
|
|
else
|
|
_cursorFormat = Graphics::PixelFormat::createFormatCLUT8();
|
|
#else
|
|
assert(keycolor <= 255);
|
|
_cursorFormat = Graphics::PixelFormat::createFormatCLUT8();
|
|
#endif
|
|
|
|
// Allocate space for cursor data
|
|
if (_cursorData.w != w || _cursorData.h != h ||
|
|
_cursorData.bytesPerPixel != _cursorFormat.bytesPerPixel)
|
|
_cursorData.create(w, h, _cursorFormat.bytesPerPixel);
|
|
|
|
// Save cursor data
|
|
memcpy(_cursorData.pixels, buf, h * _cursorData.pitch);
|
|
|
|
// Set cursor info
|
|
_cursorState.w = w;
|
|
_cursorState.h = h;
|
|
_cursorState.hotX = hotspotX;
|
|
_cursorState.hotY = hotspotY;
|
|
_cursorKeyColor = keycolor;
|
|
_cursorTargetScale = cursorTargetScale;
|
|
_cursorNeedsRedraw = true;
|
|
|
|
refreshCursorScale();
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
|
|
assert(colors);
|
|
|
|
// Save the cursor palette
|
|
memcpy(_cursorPalette + start * 3, colors, num * 3);
|
|
|
|
_cursorPaletteDisabled = false;
|
|
_cursorNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::disableCursorPalette(bool disable) {
|
|
_cursorPaletteDisabled = disable;
|
|
_cursorNeedsRedraw = true;
|
|
}
|
|
|
|
//
|
|
// Misc
|
|
//
|
|
|
|
void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
|
|
assert(_transactionMode == kTransactionNone);
|
|
assert(msg);
|
|
|
|
#ifdef USE_OSD
|
|
// Split the message into separate lines.
|
|
_osdLines.clear();
|
|
|
|
Common::StringTokenizer tokenizer(msg, "\n");
|
|
while (!tokenizer.empty())
|
|
_osdLines.push_back(tokenizer.nextToken());
|
|
|
|
// Request update of the texture
|
|
_requireOSDUpdate = true;
|
|
|
|
// Init the OSD display parameters, and the fade out
|
|
_osdAlpha = kOSDInitialAlpha;
|
|
_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Intern
|
|
//
|
|
|
|
void OpenGLGraphicsManager::setFullscreenMode(bool enable) {
|
|
assert(_transactionMode == kTransactionActive);
|
|
|
|
if (_oldVideoMode.setup && _oldVideoMode.fullscreen == enable)
|
|
return;
|
|
|
|
if (_transactionMode == kTransactionActive) {
|
|
_videoMode.fullscreen = enable;
|
|
_transactionDetails.needRefresh = true;
|
|
}
|
|
}
|
|
|
|
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.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.pixels + y * _screenData.pitch;
|
|
src += x * _screenData.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;
|
|
}
|
|
|
|
// Update the texture
|
|
_gameTexture->updateBuffer(surface, w * 3, x, y, w, h);
|
|
|
|
// Free the temp surface
|
|
delete[] surface;
|
|
} else {
|
|
// Update the texture
|
|
_gameTexture->updateBuffer((byte *)_screenData.pixels + y * _screenData.pitch +
|
|
x * _screenData.bytesPerPixel, _screenData.pitch, x, y, w, h);
|
|
}
|
|
|
|
_screenNeedsRedraw = false;
|
|
_screenDirtyRect = Common::Rect();
|
|
}
|
|
|
|
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.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.pixels + y * _overlayData.pitch;
|
|
src += x * _overlayData.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;
|
|
}
|
|
|
|
// Update the texture
|
|
_overlayTexture->updateBuffer(surface, w * 3, x, y, w, h);
|
|
|
|
// Free the temp surface
|
|
delete[] surface;
|
|
} else {
|
|
// Update the texture
|
|
_overlayTexture->updateBuffer((byte *)_overlayData.pixels + y * _overlayData.pitch +
|
|
x * _overlayData.bytesPerPixel, _overlayData.pitch, x, y, w, h);
|
|
}
|
|
|
|
_overlayNeedsRedraw = false;
|
|
_overlayDirtyRect = Common::Rect();
|
|
}
|
|
|
|
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.pixels;
|
|
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.pixels;
|
|
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.pixels;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the texture with new cursor
|
|
_cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h);
|
|
|
|
// Free the temp surface
|
|
delete[] surface;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::refreshCursorScale() {
|
|
// Get the window minimum scale factor. The cursor will mantain its original aspect
|
|
// ratio, and we do not want it to get too big if only one dimension is resized
|
|
uint screenScaleFactor = MIN(_videoMode.hardwareWidth * 10000 / _videoMode.screenWidth,
|
|
_videoMode.hardwareHeight * 10000 / _videoMode.screenHeight);
|
|
|
|
// Do not scale cursor if original size is used
|
|
if (_videoMode.mode == OpenGL::GFX_ORIGINAL)
|
|
screenScaleFactor = _videoMode.scaleFactor * 10000;
|
|
|
|
if ((uint)_cursorTargetScale * 10000 >= screenScaleFactor && (uint)_videoMode.scaleFactor * 10000 >= screenScaleFactor) {
|
|
// If the cursor target scale and the video mode scale factor are bigger than
|
|
// the current window scale, do not scale the cursor for the overlay
|
|
_cursorState.rW = _cursorState.w;
|
|
_cursorState.rH = _cursorState.h;
|
|
_cursorState.rHotX = _cursorState.hotX;
|
|
_cursorState.rHotY = _cursorState.hotY;
|
|
} else {
|
|
// Otherwise, scale the cursor for the overlay
|
|
int targetScaleFactor = MIN(_cursorTargetScale, _videoMode.scaleFactor);
|
|
// We limit the maximum scale to 3 here to avoid too big cursors, for large overlay resolutions
|
|
int actualFactor = MIN<uint>(3, screenScaleFactor - (targetScaleFactor - 1)) * 10000;
|
|
_cursorState.rW = (int16)(_cursorState.w * actualFactor / 10000);
|
|
_cursorState.rH = (int16)(_cursorState.h * actualFactor / 10000);
|
|
_cursorState.rHotX = (int16)(_cursorState.hotX * actualFactor / 10000);
|
|
_cursorState.rHotY = (int16)(_cursorState.hotY * actualFactor / 10000);
|
|
}
|
|
|
|
// Always scale the cursor for the game
|
|
_cursorState.vW = (int16)(_cursorState.w * screenScaleFactor / 10000);
|
|
_cursorState.vH = (int16)(_cursorState.h * screenScaleFactor / 10000);
|
|
_cursorState.vHotX = (int16)(_cursorState.hotX * screenScaleFactor / 10000);
|
|
_cursorState.vHotY = (int16)(_cursorState.hotY * screenScaleFactor / 10000);
|
|
}
|
|
|
|
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;
|
|
|
|
uint aspectRatio = (_videoMode.hardwareWidth * 10000 + 5000) / _videoMode.hardwareHeight;
|
|
uint desiredAspectRatio = getAspectRatio();
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
void OpenGLGraphicsManager::refreshDisplaySize() {
|
|
calculateDisplaySize(_displayWidth, _displayHeight);
|
|
|
|
// Adjust x and y for centering the screen
|
|
_displayX = (_videoMode.hardwareWidth - _displayWidth) / 2;
|
|
_displayY = (_videoMode.hardwareHeight - _displayHeight) / 2;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &gltype) {
|
|
if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
|
|
bpp = 4;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_BYTE;
|
|
} 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;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565
|
|
bpp = 2;
|
|
intFormat = 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;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_SHORT_5_5_5_1;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555
|
|
bpp = 2;
|
|
intFormat = GL_RGB;
|
|
glFormat = GL_BGRA;
|
|
gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444
|
|
bpp = 2;
|
|
intFormat = 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;
|
|
#ifndef USE_GLES
|
|
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888
|
|
bpp = 4;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_BGRA;
|
|
gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444
|
|
bpp = 2;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_BGRA;
|
|
gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
|
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
|
|
bpp = 4;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888
|
|
bpp = 4;
|
|
intFormat = 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;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565
|
|
bpp = 2;
|
|
intFormat = GL_RGB;
|
|
glFormat = GL_BGR;
|
|
gltype = GL_UNSIGNED_SHORT_5_6_5;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551
|
|
bpp = 2;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_BGRA;
|
|
gltype = GL_UNSIGNED_SHORT_5_5_5_1;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444
|
|
bpp = 2;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444
|
|
bpp = 2;
|
|
intFormat = GL_RGBA;
|
|
glFormat = GL_BGRA;
|
|
gltype = GL_UNSIGNED_SHORT_4_4_4_4;
|
|
#endif
|
|
} else {
|
|
error("OpenGLGraphicsManager: Pixel format not supported");
|
|
}
|
|
}
|
|
|
|
void OpenGLGraphicsManager::internUpdateScreen() {
|
|
// Clear the screen buffer
|
|
glClear(GL_COLOR_BUFFER_BIT); CHECK_GL_ERROR();
|
|
|
|
if (_screenNeedsRedraw || !_screenDirtyRect.isEmpty())
|
|
// Refresh texture if dirty
|
|
refreshGameScreen();
|
|
|
|
int scaleFactor = _videoMode.hardwareHeight / _videoMode.screenHeight;
|
|
|
|
glPushMatrix();
|
|
|
|
// Adjust game screen shake position
|
|
glTranslatef(0, _shakePos * scaleFactor, 0); CHECK_GL_ERROR();
|
|
|
|
// Draw the game screen
|
|
_gameTexture->drawTexture(_displayX, _displayY, _displayWidth, _displayHeight);
|
|
|
|
glPopMatrix();
|
|
|
|
if (_overlayVisible) {
|
|
if (_overlayNeedsRedraw || !_overlayDirtyRect.isEmpty())
|
|
// Refresh texture if dirty
|
|
refreshOverlay();
|
|
|
|
// Draw the overlay
|
|
_overlayTexture->drawTexture(0, 0, _videoMode.overlayWidth, _videoMode.overlayHeight);
|
|
}
|
|
|
|
if (_cursorVisible) {
|
|
if (_cursorNeedsRedraw)
|
|
// Refresh texture if dirty
|
|
refreshCursor();
|
|
|
|
glPushMatrix();
|
|
|
|
// Adjust mouse shake position, unless the overlay is visible
|
|
glTranslatef(0, _overlayVisible ? 0 : _shakePos * scaleFactor, 0); CHECK_GL_ERROR();
|
|
|
|
// Draw the cursor
|
|
if (_overlayVisible)
|
|
_cursorTexture->drawTexture(_cursorState.x - _cursorState.rHotX,
|
|
_cursorState.y - _cursorState.rHotY, _cursorState.rW, _cursorState.rH);
|
|
else
|
|
_cursorTexture->drawTexture(_cursorState.x - _cursorState.vHotX,
|
|
_cursorState.y - _cursorState.vHotY, _cursorState.vW, _cursorState.vH);
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
#ifdef USE_OSD
|
|
if (_osdAlpha > 0) {
|
|
if (_requireOSDUpdate) {
|
|
updateOSD();
|
|
_requireOSDUpdate = false;
|
|
}
|
|
|
|
// Update alpha value
|
|
const int diff = g_system->getMillis() - _osdFadeStartTime;
|
|
if (diff > 0) {
|
|
if (diff >= kOSDFadeOutDuration) {
|
|
// Back to full transparency
|
|
_osdAlpha = 0;
|
|
} else {
|
|
// Do a fade out
|
|
_osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration;
|
|
}
|
|
}
|
|
// 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();
|
|
}
|
|
|
|
void OpenGLGraphicsManager::loadTextures() {
|
|
#ifdef USE_RGB_COLOR
|
|
if (_transactionDetails.formatChanged && _gameTexture) {
|
|
delete _gameTexture;
|
|
_gameTexture = 0;
|
|
}
|
|
#endif
|
|
|
|
uint gameScreenBPP = 0;
|
|
|
|
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
|
|
gameScreenBPP = bpp;
|
|
_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.bytesPerPixel
|
|
#else
|
|
1
|
|
#endif
|
|
);
|
|
|
|
|
|
if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth ||
|
|
_oldVideoMode.overlayHeight != _videoMode.overlayHeight)
|
|
_overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight,
|
|
_overlayFormat.bytesPerPixel);
|
|
|
|
_screenNeedsRedraw = true;
|
|
_overlayNeedsRedraw = true;
|
|
_cursorNeedsRedraw = true;
|
|
|
|
// 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.
|
|
// For now we use the gcd of the game screen format and 2, since 2 is
|
|
// the BPP value for the overlay and the OSD.
|
|
if (gameScreenBPP)
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, Common::gcd<uint>(gameScreenBPP, 2));
|
|
|
|
// 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);
|
|
|
|
// Update the OSD in case it is used right now
|
|
_requireOSDUpdate = true;
|
|
#endif
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::loadGFXMode() {
|
|
// Initialize OpenGL settings
|
|
initGL();
|
|
|
|
loadTextures();
|
|
|
|
refreshCursorScale();
|
|
|
|
refreshDisplaySize();
|
|
|
|
internUpdateScreen();
|
|
|
|
return true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::unloadGFXMode() {
|
|
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setScale(int newScale) {
|
|
assert(_transactionMode == kTransactionActive);
|
|
|
|
if (newScale == _videoMode.scaleFactor)
|
|
return;
|
|
|
|
_videoMode.scaleFactor = newScale;
|
|
_transactionDetails.sizeChanged = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::toggleAntialiasing() {
|
|
assert(_transactionMode == kTransactionActive);
|
|
|
|
_videoMode.antialiasing = !_videoMode.antialiasing;
|
|
_transactionDetails.filterChanged = true;
|
|
}
|
|
|
|
uint OpenGLGraphicsManager::getAspectRatio() {
|
|
// In case we enable aspect ratio correction we force a 4/3 ratio.
|
|
// 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)
|
|
return 13333;
|
|
else if (_videoMode.mode == OpenGL::GFX_NORMAL)
|
|
return _videoMode.hardwareWidth * 10000 / _videoMode.hardwareHeight;
|
|
else
|
|
return _videoMode.screenWidth * 10000 / _videoMode.screenHeight;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
|
|
if (_overlayVisible)
|
|
return;
|
|
|
|
if (_videoMode.mode == OpenGL::GFX_NORMAL) {
|
|
x /= _videoMode.scaleFactor;
|
|
y /= _videoMode.scaleFactor;
|
|
} else if (!_overlayVisible) {
|
|
x -= _displayX;
|
|
y -= _displayY;
|
|
|
|
if (_displayWidth != _videoMode.screenWidth)
|
|
x = x * _videoMode.screenWidth / _displayWidth;
|
|
if (_displayHeight != _videoMode.screenHeight)
|
|
y = y * _videoMode.screenHeight / _displayHeight;
|
|
}
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::notifyEvent(const Common::Event &event) {
|
|
switch (event.type) {
|
|
case Common::EVENT_MOUSEMOVE:
|
|
if (!event.synthetic) {
|
|
_cursorState.x = event.mouse.x;
|
|
_cursorState.y = event.mouse.y;
|
|
}
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
case Common::EVENT_WHEELUP:
|
|
case Common::EVENT_WHEELDOWN:
|
|
case Common::EVENT_MBUTTONDOWN:
|
|
case Common::EVENT_LBUTTONUP:
|
|
case Common::EVENT_RBUTTONUP:
|
|
case Common::EVENT_MBUTTONUP:
|
|
if (!event.synthetic) {
|
|
Common::Event newEvent(event);
|
|
newEvent.synthetic = true;
|
|
adjustMousePosition(newEvent.mouse.x, newEvent.mouse.y);
|
|
g_system->getEventManager()->pushEvent(newEvent);
|
|
}
|
|
return !event.synthetic;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::saveScreenshot(const char *filename) {
|
|
int width = _videoMode.hardwareWidth;
|
|
int height = _videoMode.hardwareHeight;
|
|
|
|
// 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;
|
|
|
|
// 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();
|
|
}
|
|
#endif
|
|
|
|
// Open file
|
|
Common::DumpFile out;
|
|
out.open(filename);
|
|
|
|
// Write BMP header
|
|
out.writeByte('B');
|
|
out.writeByte('M');
|
|
out.writeUint32LE(height * lineSize + 54);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(54);
|
|
out.writeUint32LE(40);
|
|
out.writeUint32LE(width);
|
|
out.writeUint32LE(height);
|
|
out.writeUint16LE(1);
|
|
out.writeUint16LE(24);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(0);
|
|
|
|
// Write pixel data to BMP
|
|
out.write(pixels, lineSize * height);
|
|
|
|
// 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;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::switchDisplayMode(int mode) {
|
|
assert(_transactionMode == kTransactionActive);
|
|
|
|
if (_videoMode.mode == mode)
|
|
return;
|
|
|
|
if (mode == -1) // If -1, switch to next mode
|
|
_videoMode.mode = (_videoMode.mode + 1) % 4;
|
|
else if (mode == -2) // If -2, switch to previous mode
|
|
_videoMode.mode = (_videoMode.mode + 3) % 4;
|
|
else
|
|
_videoMode.mode = mode;
|
|
|
|
_transactionDetails.needRefresh = true;
|
|
}
|
|
|
|
#ifdef USE_OSD
|
|
void OpenGLGraphicsManager::updateOSD() {
|
|
// The font we are going to use:
|
|
const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kOSDFont);
|
|
|
|
if (_osdSurface.w != _osdTexture->getWidth() || _osdSurface.h != _osdTexture->getHeight())
|
|
_osdSurface.create(_osdTexture->getWidth(), _osdTexture->getHeight(), 2);
|
|
else
|
|
// Clear everything
|
|
memset(_osdSurface.pixels, 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
|
|
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.pixels, _osdSurface.pitch, 0, 0,
|
|
_osdSurface.w, _osdSurface.h);
|
|
}
|
|
#endif
|
|
|
|
#endif
|