mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-13 04:00:29 +00:00

OSystem_SDL will create a merged list of all graphics modes from SDL and OpenGL. When the user changes the graphics mode in options and restarts ScummVM should switch to that graphics mode in the corresponding graphics manager. svn-id: r51493
1257 lines
35 KiB
C++
1257 lines
35 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$
|
|
*
|
|
*/
|
|
|
|
#ifdef USE_OPENGL
|
|
|
|
#include "backends/graphics/opengl/opengl-graphics.h"
|
|
#include "backends/graphics/opengl/glerrorcheck.h"
|
|
#include "common/file.h"
|
|
#include "common/mutex.h"
|
|
#include "common/translation.h"
|
|
#include "graphics/font.h"
|
|
#include "graphics/fontman.h"
|
|
|
|
OpenGLGraphicsManager::OpenGLGraphicsManager()
|
|
:
|
|
#ifdef USE_OSD
|
|
_osdTexture(0), _osdAlpha(0), _osdFadeStartTime(0),
|
|
#endif
|
|
_gameTexture(0), _overlayTexture(0), _cursorTexture(0),
|
|
_screenChangeCount(0), _screenNeedsRedraw(false),
|
|
_shakePos(0),
|
|
_overlayVisible(false), _overlayNeedsRedraw(false),
|
|
_transactionMode(kTransactionNone),
|
|
_cursorNeedsRedraw(false), _cursorPaletteDisabled(true),
|
|
_cursorVisible(false), _cursorKeyColor(0),
|
|
_cursorTargetScale(1),
|
|
_formatBGR(false),
|
|
_aspectX(0), _aspectY(0), _aspectWidth(0), _aspectHeight(0) {
|
|
|
|
memset(&_oldVideoMode, 0, sizeof(_oldVideoMode));
|
|
memset(&_videoMode, 0, sizeof(_videoMode));
|
|
memset(&_transactionDetails, 0, sizeof(_transactionDetails));
|
|
|
|
_videoMode.mode = OpenGL::GFX_DOUBLESIZE;
|
|
_videoMode.scaleFactor = 2;
|
|
_videoMode.fullscreen = false;
|
|
_videoMode.antialiasing = false;
|
|
_videoMode.aspectRatioCorrection = 0;
|
|
|
|
_gamePalette = (byte *)calloc(sizeof(byte) * 4, 256);
|
|
_cursorPalette = (byte *)calloc(sizeof(byte) * 4, 256);
|
|
|
|
// Register the graphics manager as a event observer
|
|
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false);
|
|
}
|
|
|
|
OpenGLGraphicsManager::~OpenGLGraphicsManager() {
|
|
// Unregister the event observer
|
|
if (g_system->getEventManager()->getEventDispatcher() != NULL)
|
|
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
|
|
|
|
free(_gamePalette);
|
|
free(_cursorPalette);
|
|
|
|
if (_gameTexture != NULL)
|
|
delete _gameTexture;
|
|
if (_overlayTexture != NULL)
|
|
delete _overlayTexture;
|
|
if (_cursorTexture != NULL)
|
|
delete _cursorTexture;
|
|
}
|
|
|
|
//
|
|
// 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::kFeatureAspectRatioCorrection:
|
|
setAspectRatioCorrection(enable ? -1 : 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Screen format and modes
|
|
//
|
|
|
|
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
|
|
{"gl1x", _s("OpenGL Normal"), OpenGL::GFX_NORMAL},
|
|
#ifdef USE_SCALERS
|
|
{"gl2x", "OpenGL 2x", OpenGL::GFX_DOUBLESIZE},
|
|
{"gl3x", "OpenGL 3x", OpenGL::GFX_TRIPLESIZE},
|
|
#endif
|
|
{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);
|
|
|
|
if (_oldVideoMode.setup && _oldVideoMode.mode == mode)
|
|
return true;
|
|
|
|
int newScaleFactor = 1;
|
|
|
|
switch (mode) {
|
|
case OpenGL::GFX_NORMAL:
|
|
newScaleFactor = 1;
|
|
break;
|
|
#ifdef USE_SCALERS
|
|
case OpenGL::GFX_DOUBLESIZE:
|
|
newScaleFactor = 2;
|
|
break;
|
|
case OpenGL::GFX_TRIPLESIZE:
|
|
newScaleFactor = 3;
|
|
break;
|
|
#endif
|
|
default:
|
|
warning("unknown gfx mode %d", mode);
|
|
return false;
|
|
}
|
|
|
|
if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newScaleFactor)
|
|
_transactionDetails.needHotswap = true;
|
|
|
|
_transactionDetails.needUpdatescreen = true;
|
|
|
|
_videoMode.mode = mode;
|
|
_videoMode.scaleFactor = newScaleFactor;
|
|
|
|
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
|
|
// Avoid redundant format changes
|
|
Graphics::PixelFormat newFormat;
|
|
if (!format)
|
|
newFormat = Graphics::PixelFormat::createFormatCLUT8();
|
|
else
|
|
newFormat = *format;
|
|
|
|
assert(newFormat.bytesPerPixel > 0);
|
|
|
|
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.needHotswap = false;
|
|
_transactionDetails.needUpdatescreen = false;
|
|
_transactionDetails.newContext = 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.aspectRatioCorrection != _oldVideoMode.aspectRatioCorrection) {
|
|
errors |= OSystem::kTransactionAspectRatioFailed;
|
|
|
|
_videoMode.aspectRatioCorrection = _oldVideoMode.aspectRatioCorrection;
|
|
} 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.aspectRatioCorrection == _oldVideoMode.aspectRatioCorrection &&
|
|
_videoMode.mode == _oldVideoMode.mode &&
|
|
_videoMode.screenWidth == _oldVideoMode.screenWidth &&
|
|
_videoMode.screenHeight == _oldVideoMode.screenHeight) {
|
|
|
|
_oldVideoMode.setup = false;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_RGB_COLOR
|
|
if (_transactionDetails.sizeChanged || _transactionDetails.formatChanged || _transactionDetails.needHotswap) {
|
|
#else
|
|
if (_transactionDetails.sizeChanged || _transactionDetails.needHotswap) {
|
|
#endif
|
|
unloadGFXMode();
|
|
if (!loadGFXMode()) {
|
|
if (_oldVideoMode.setup) {
|
|
_transactionMode = kTransactionRollback;
|
|
errors |= endGFXTransaction();
|
|
}
|
|
} else {
|
|
clearOverlay();
|
|
|
|
_videoMode.setup = true;
|
|
_screenChangeCount++;
|
|
}
|
|
} else if (_transactionDetails.filterChanged) {
|
|
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 * 4, colors, num * 4);
|
|
|
|
_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 * 4, num * 4);
|
|
}
|
|
|
|
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;
|
|
for (int i = 0; i < h; i++) {
|
|
memcpy(dst + x * _screenData.bytesPerPixel, 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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
_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() {
|
|
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++) {
|
|
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::setMousePos(int x, int y) {
|
|
_cursorState.x = x;
|
|
_cursorState.y = y;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::warpMouse(int x, int y) {
|
|
setMousePos(x, y);
|
|
}
|
|
|
|
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 = Graphics::PixelFormat::createFormatCLUT8();
|
|
else
|
|
_cursorFormat = *format;
|
|
#else
|
|
assert(keycolor <= 255);
|
|
#endif
|
|
|
|
// Allocate space for cursor data
|
|
if (_cursorData.w != w || _cursorData.h != h)
|
|
_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 * 4, colors, num * 4);
|
|
|
|
_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);
|
|
|
|
// 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);
|
|
|
|
// Split the message into separate lines.
|
|
Common::Array<Common::String> lines;
|
|
const char *ptr;
|
|
for (ptr = msg; *ptr; ++ptr) {
|
|
if (*ptr == '\n') {
|
|
lines.push_back(Common::String(msg, ptr - msg));
|
|
msg = ptr + 1;
|
|
}
|
|
}
|
|
lines.push_back(Common::String(msg, ptr - msg));
|
|
|
|
// 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 * lines.size() + 2 * vOffset;
|
|
for (uint i = 0; i < lines.size(); i++) {
|
|
width = MAX(width, font->getStringWidth(lines[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
|
|
uint16 color = 0x294B;
|
|
uint16 *dst = (uint16 *)_osdSurface.pixels + dstY * _osdSurface.w + dstX;
|
|
for (int i = 0; i < height; i++) {
|
|
for (int j = 0; j < width; j++)
|
|
dst[j] = color;
|
|
dst += _osdSurface.w;
|
|
}
|
|
|
|
// Render the message, centered, and in white
|
|
for (uint i = 0; i < lines.size(); i++) {
|
|
font->drawString(&_osdSurface, lines[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);
|
|
|
|
// Init the OSD display parameters, and the fade out
|
|
_osdAlpha = kOSDInitialAlpha;
|
|
_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
|
|
}
|
|
|
|
//
|
|
// Intern
|
|
//
|
|
|
|
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] * 4];
|
|
dst[1] = _gamePalette[src[j] * 4 + 1];
|
|
dst[2] = _gamePalette[src[j] * 4 + 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] * 4];
|
|
dst[1] = _gamePalette[src[j] * 4 + 1];
|
|
dst[2] = _gamePalette[src[j] * 4 + 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;
|
|
|
|
if (_cursorFormat.bytesPerPixel == 1) {
|
|
// Create a temporary RGBA8888 surface
|
|
byte *surface = new byte[_cursorState.w * _cursorState.h * 4];
|
|
memset(surface, 0, _cursorState.w * _cursorState.h * 4);
|
|
|
|
// Select palette
|
|
byte *palette;
|
|
if (_cursorPaletteDisabled)
|
|
palette = _gamePalette;
|
|
else
|
|
palette = _cursorPalette;
|
|
|
|
// Convert the paletted cursor to RGBA8888
|
|
const byte *src = (byte *)_cursorData.pixels;
|
|
byte *dst = surface;
|
|
for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
|
|
// Check for keycolor
|
|
if (src[i] != _cursorKeyColor) {
|
|
dst[0] = palette[src[i] * 4];
|
|
dst[1] = palette[src[i] * 4 + 1];
|
|
dst[2] = palette[src[i] * 4 + 2];
|
|
dst[3] = 255;
|
|
}
|
|
dst += 4;
|
|
}
|
|
|
|
// Allocate a texture big enough for cursor
|
|
_cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
|
|
|
|
// 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
|
|
float screenScaleFactor = MIN((float)_videoMode.hardwareWidth / _videoMode.screenWidth,
|
|
(float)_videoMode.hardwareHeight / _videoMode.screenHeight);
|
|
|
|
if (_cursorTargetScale >= screenScaleFactor && _videoMode.scaleFactor >= 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
|
|
float targetScaleFactor = MIN(_cursorTargetScale, _videoMode.scaleFactor);
|
|
float actualFactor = (screenScaleFactor - targetScaleFactor + 1);
|
|
_cursorState.rW = (int16)(_cursorState.w * actualFactor);
|
|
_cursorState.rH = (int16)(_cursorState.h * actualFactor);
|
|
_cursorState.rHotX = (int16)(_cursorState.hotX * actualFactor);
|
|
_cursorState.rHotY = (int16)(_cursorState.hotY * actualFactor);
|
|
}
|
|
|
|
// Always scale the cursor for the game
|
|
_cursorState.vW = (int16)(_cursorState.w * screenScaleFactor);
|
|
_cursorState.vH = (int16)(_cursorState.h * screenScaleFactor);
|
|
_cursorState.vHotX = (int16)(_cursorState.hotX * screenScaleFactor);
|
|
_cursorState.vHotY = (int16)(_cursorState.hotY * screenScaleFactor);
|
|
}
|
|
|
|
void OpenGLGraphicsManager::refreshAspectRatio() {
|
|
_aspectWidth = _videoMode.hardwareWidth;
|
|
_aspectHeight = _videoMode.hardwareHeight;
|
|
|
|
float aspectRatio = (float)_videoMode.hardwareWidth / _videoMode.hardwareHeight;
|
|
float desiredAspectRatio = getAspectRatio();
|
|
|
|
if (aspectRatio < desiredAspectRatio)
|
|
_aspectHeight = (int)(_aspectWidth / desiredAspectRatio + 0.5f);
|
|
else if (aspectRatio > desiredAspectRatio)
|
|
_aspectWidth = (int)(_aspectHeight * desiredAspectRatio + 0.5f);
|
|
|
|
_aspectX = (_videoMode.hardwareWidth - _aspectWidth) / 2;
|
|
_aspectY = (_videoMode.hardwareHeight - _aspectHeight) / 2;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &glFormat, GLenum &gltype) {
|
|
if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
|
|
bpp = 4;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_BYTE;
|
|
} else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0)) { // RGB888
|
|
bpp = 3;
|
|
glFormat = GL_RGB;
|
|
gltype = GL_UNSIGNED_BYTE;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565
|
|
bpp = 2;
|
|
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;
|
|
glFormat = GL_RGBA;
|
|
gltype = GL_UNSIGNED_SHORT_5_5_5_1;
|
|
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444
|
|
bpp = 2;
|
|
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;
|
|
glFormat = GL_RGB;
|
|
gltype = GL_UNSIGNED_BYTE;
|
|
} 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();
|
|
|
|
glTranslatef(0, _shakePos * scaleFactor, 0); CHECK_GL_ERROR();
|
|
|
|
// Draw the game screen
|
|
_gameTexture->drawTexture(_aspectX, _aspectY, _aspectWidth, _aspectHeight);
|
|
|
|
glPopMatrix();
|
|
|
|
if (_overlayVisible) {
|
|
if (_overlayNeedsRedraw || !_overlayDirtyRect.isEmpty())
|
|
// Refresh texture if dirty
|
|
refreshOverlay();
|
|
|
|
// Draw the overlay
|
|
_overlayTexture->drawTexture(_aspectX, _aspectY, _aspectWidth, _aspectHeight);
|
|
}
|
|
|
|
if (_cursorVisible) {
|
|
if (_cursorNeedsRedraw)
|
|
// Refresh texture if dirty
|
|
refreshCursor();
|
|
|
|
glPushMatrix();
|
|
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) {
|
|
// 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();
|
|
glOrtho(0, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 0, -1, 1); CHECK_GL_ERROR();
|
|
glMatrixMode(GL_MODELVIEW); CHECK_GL_ERROR();
|
|
glLoadIdentity(); CHECK_GL_ERROR();
|
|
}
|
|
|
|
void OpenGLGraphicsManager::loadTextures() {
|
|
#ifdef USE_RGB_COLOR
|
|
if (_transactionDetails.formatChanged && _gameTexture)
|
|
delete _gameTexture;
|
|
#endif
|
|
|
|
if (!_gameTexture) {
|
|
byte bpp;
|
|
GLenum format;
|
|
GLenum type;
|
|
#ifdef USE_RGB_COLOR
|
|
getGLPixelFormat(_screenFormat, bpp, format, type);
|
|
#else
|
|
getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, format, type);
|
|
#endif
|
|
_gameTexture = new GLTexture(bpp, format, type);
|
|
}
|
|
|
|
_overlayFormat = Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0);
|
|
|
|
if (!_overlayTexture) {
|
|
byte bpp;
|
|
GLenum format;
|
|
GLenum type;
|
|
getGLPixelFormat(_overlayFormat, bpp, format, type);
|
|
_overlayTexture = new GLTexture(bpp, format, type);
|
|
}
|
|
|
|
if (!_cursorTexture)
|
|
_cursorTexture = new GLTexture(4, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
|
|
GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST;
|
|
_gameTexture->setFilter(filter);
|
|
_overlayTexture->setFilter(filter);
|
|
_cursorTexture->setFilter(filter);
|
|
|
|
if (_transactionDetails.newContext || _transactionDetails.filterChanged) {
|
|
// If the context was destroyed or it is needed to change the texture filter
|
|
// we need to recreate the textures
|
|
_gameTexture->refresh();
|
|
_overlayTexture->refresh();
|
|
_cursorTexture->refresh();
|
|
}
|
|
|
|
// 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 (_transactionDetails.formatChanged ||
|
|
_oldVideoMode.screenWidth != _videoMode.screenWidth ||
|
|
_oldVideoMode.screenHeight != _videoMode.screenHeight)
|
|
_screenData.create(_videoMode.screenWidth, _videoMode.screenHeight,
|
|
_screenFormat.bytesPerPixel);
|
|
|
|
if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth ||
|
|
_oldVideoMode.overlayHeight != _videoMode.overlayHeight)
|
|
_overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight,
|
|
_overlayFormat.bytesPerPixel);
|
|
|
|
_screenNeedsRedraw = true;
|
|
_overlayNeedsRedraw = true;
|
|
_cursorNeedsRedraw = true;
|
|
|
|
#ifdef USE_OSD
|
|
if (!_osdTexture)
|
|
_osdTexture = new GLTexture(2, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
|
|
|
|
if (_transactionDetails.newContext || _transactionDetails.filterChanged)
|
|
_osdTexture->refresh();
|
|
_osdTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight);
|
|
#endif
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::loadGFXMode() {
|
|
// Initialize OpenGL settings
|
|
initGL();
|
|
|
|
loadTextures();
|
|
|
|
refreshCursorScale();
|
|
|
|
refreshAspectRatio();
|
|
|
|
internUpdateScreen();
|
|
|
|
return true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::unloadGFXMode() {
|
|
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setScale(int newScale) {
|
|
if (newScale == _videoMode.scaleFactor)
|
|
return;
|
|
|
|
switch (newScale - 1) {
|
|
case OpenGL::GFX_NORMAL:
|
|
_videoMode.mode = OpenGL::GFX_NORMAL;
|
|
break;
|
|
case OpenGL::GFX_DOUBLESIZE:
|
|
_videoMode.mode = OpenGL::GFX_DOUBLESIZE;
|
|
break;
|
|
case OpenGL::GFX_TRIPLESIZE:
|
|
_videoMode.mode = OpenGL::GFX_TRIPLESIZE;
|
|
break;
|
|
}
|
|
|
|
_videoMode.scaleFactor = newScale;
|
|
_transactionDetails.sizeChanged = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setAspectRatioCorrection(int ratio) {
|
|
if (_oldVideoMode.setup && _oldVideoMode.aspectRatioCorrection == ratio)
|
|
return;
|
|
|
|
if (_transactionMode == kTransactionActive) {
|
|
if (ratio == -1)
|
|
_videoMode.aspectRatioCorrection = (_videoMode.aspectRatioCorrection + 1) % 5;
|
|
else
|
|
_videoMode.aspectRatioCorrection = ratio;
|
|
_transactionDetails.needHotswap = true;
|
|
}
|
|
}
|
|
|
|
Common::String OpenGLGraphicsManager::getAspectRatioName() {
|
|
switch (_videoMode.aspectRatioCorrection) {
|
|
case kAspectRatioNone:
|
|
return "None";
|
|
case kAspectRatioConserve:
|
|
return "Conserve";
|
|
case kAspectRatio4_3:
|
|
return "4/3";
|
|
case kAspectRatio16_9:
|
|
return "16/9";
|
|
case kAspectRatio16_10:
|
|
return "16/10";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
float OpenGLGraphicsManager::getAspectRatio() {
|
|
switch (_videoMode.aspectRatioCorrection) {
|
|
case kAspectRatioConserve:
|
|
return (float)_videoMode.screenWidth / _videoMode.screenHeight;
|
|
case kAspectRatio4_3:
|
|
return 4.0f / 3.0f;
|
|
case kAspectRatio16_9:
|
|
return 16.0f / 9.0f;
|
|
case kAspectRatio16_10:
|
|
return 16.0f / 10.0f;
|
|
default:
|
|
return (float)_videoMode.hardwareWidth / _videoMode.hardwareHeight;
|
|
}
|
|
}
|
|
|
|
void OpenGLGraphicsManager::adjustMouseEvent(const Common::Event &event) {
|
|
if (!event.synthetic) {
|
|
Common::Event newEvent(event);
|
|
newEvent.synthetic = true;
|
|
|
|
if (!_videoMode.aspectRatioCorrection) {
|
|
if (_videoMode.hardwareWidth != _videoMode.overlayWidth)
|
|
newEvent.mouse.x = newEvent.mouse.x * _videoMode.overlayWidth / _videoMode.hardwareWidth;
|
|
if (_videoMode.hardwareHeight != _videoMode.overlayHeight)
|
|
newEvent.mouse.y = newEvent.mouse.y * _videoMode.overlayHeight / _videoMode.hardwareHeight;
|
|
|
|
if (!_overlayVisible) {
|
|
newEvent.mouse.x /= _videoMode.scaleFactor;
|
|
newEvent.mouse.y /= _videoMode.scaleFactor;
|
|
}
|
|
|
|
} else {
|
|
newEvent.mouse.x -= _aspectX;
|
|
newEvent.mouse.y -= _aspectY;
|
|
|
|
if (_overlayVisible) {
|
|
if (_aspectWidth != _videoMode.overlayWidth)
|
|
newEvent.mouse.x = newEvent.mouse.x * _videoMode.overlayWidth / _aspectWidth;
|
|
if (_aspectHeight != _videoMode.overlayHeight)
|
|
newEvent.mouse.y = newEvent.mouse.y * _videoMode.overlayHeight / _aspectHeight;
|
|
} else {
|
|
if (_aspectWidth != _videoMode.screenWidth)
|
|
newEvent.mouse.x = newEvent.mouse.x * _videoMode.screenWidth / _aspectWidth;
|
|
if (_aspectHeight != _videoMode.screenHeight)
|
|
newEvent.mouse.y = newEvent.mouse.y * _videoMode.screenHeight / _aspectHeight;
|
|
}
|
|
}
|
|
|
|
g_system->getEventManager()->pushEvent(newEvent);
|
|
}
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::notifyEvent(const Common::Event &event) {
|
|
switch (event.type) {
|
|
case Common::EVENT_MOUSEMOVE:
|
|
if (!event.synthetic)
|
|
setMousePos(event.mouse.x, 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:
|
|
adjustMouseEvent(event);
|
|
return !event.synthetic;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::saveScreenshot(const char *filename) {
|
|
int width = _videoMode.hardwareWidth;
|
|
int height = _videoMode.hardwareHeight;
|
|
|
|
// Allocate space for screenshot
|
|
uint8 *pixels = new uint8[width * height * 3];
|
|
|
|
// Get pixel data from opengl buffer
|
|
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();
|
|
}
|
|
|
|
// Open file
|
|
Common::DumpFile out;
|
|
out.open(filename);
|
|
|
|
// Write BMP header
|
|
out.writeByte('B');
|
|
out.writeByte('M');
|
|
out.writeUint32LE(height * width * 3 + 52);
|
|
out.writeUint32LE(0);
|
|
out.writeUint32LE(52);
|
|
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, width * height * 3);
|
|
|
|
delete[] pixels;
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|