mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-09 02:00:46 +00:00

If we do not do this, we might end up with a default alignment of 4, which will fail (as in the graphics will be messed up) in case the screen resolution is not divisible by 4. Thanks to digitall for noticing this problem and finding out about GL_UNPACK_ALIGNMENT.
1317 lines
37 KiB
C++
1317 lines
37 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"
|
|
#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),
|
|
_aspectRatioCorrection(false) {
|
|
|
|
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::kFeatureAspectRatioCorrection:
|
|
_videoMode.mode = OpenGL::GFX_4_3;
|
|
_aspectRatioCorrection = enable;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) {
|
|
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},
|
|
{"gl3", _s("OpenGL 4/3"), OpenGL::GFX_4_3},
|
|
{"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);
|
|
|
|
if (_oldVideoMode.setup && _oldVideoMode.mode == mode)
|
|
return true;
|
|
|
|
switch (mode) {
|
|
case OpenGL::GFX_NORMAL:
|
|
case OpenGL::GFX_CONSERVE:
|
|
case OpenGL::GFX_4_3:
|
|
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;
|
|
|
|
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() {
|
|
// 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::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 = *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.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);
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Request update of the texture
|
|
_requireOSDUpdate = true;
|
|
|
|
// 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] * 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;
|
|
|
|
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] * 3];
|
|
dst[1] = palette[src[i] * 3 + 1];
|
|
dst[2] = palette[src[i] * 3 + 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
|
|
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);
|
|
int actualFactor = 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.overlayWidth;
|
|
height = _videoMode.overlayHeight;
|
|
} 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, 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(_displayX, _displayY, _displayWidth, _displayHeight);
|
|
}
|
|
|
|
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) {
|
|
// Update the texture
|
|
_osdTexture->updateBuffer(_osdSurface.pixels, _osdSurface.pitch, 0, 0,
|
|
_osdSurface.w, _osdSurface.h);
|
|
_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;
|
|
#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 (_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;
|
|
|
|
// 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));
|
|
|
|
#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);
|
|
#endif
|
|
}
|
|
|
|
bool OpenGLGraphicsManager::loadGFXMode() {
|
|
// Initialize OpenGL settings
|
|
initGL();
|
|
|
|
loadTextures();
|
|
|
|
refreshCursorScale();
|
|
|
|
refreshDisplaySize();
|
|
|
|
internUpdateScreen();
|
|
|
|
return true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::unloadGFXMode() {
|
|
|
|
}
|
|
|
|
void OpenGLGraphicsManager::setScale(int newScale) {
|
|
if (newScale == _videoMode.scaleFactor)
|
|
return;
|
|
|
|
_videoMode.scaleFactor = newScale;
|
|
_transactionDetails.sizeChanged = true;
|
|
}
|
|
|
|
uint OpenGLGraphicsManager::getAspectRatio() {
|
|
if (_videoMode.mode == OpenGL::GFX_NORMAL)
|
|
return _videoMode.hardwareWidth * 10000 / _videoMode.hardwareHeight;
|
|
else if (_videoMode.mode == OpenGL::GFX_4_3)
|
|
return 13333;
|
|
else
|
|
return _videoMode.screenWidth * 10000 / _videoMode.screenHeight;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
|
|
if (_videoMode.mode == OpenGL::GFX_NORMAL) {
|
|
if (_videoMode.hardwareWidth != _videoMode.overlayWidth)
|
|
x = x * _videoMode.overlayWidth / _videoMode.hardwareWidth;
|
|
if (_videoMode.hardwareHeight != _videoMode.overlayHeight)
|
|
y = y * _videoMode.overlayHeight / _videoMode.hardwareHeight;
|
|
|
|
if (!_overlayVisible) {
|
|
x /= _videoMode.scaleFactor;
|
|
y /= _videoMode.scaleFactor;
|
|
}
|
|
|
|
} else {
|
|
x -= _displayX;
|
|
y -= _displayY;
|
|
|
|
if (_overlayVisible) {
|
|
if (_displayWidth != _videoMode.overlayWidth)
|
|
x = x * _videoMode.overlayWidth / _displayWidth;
|
|
if (_displayHeight != _videoMode.overlayHeight)
|
|
y = y * _videoMode.overlayHeight / _displayHeight;
|
|
} else {
|
|
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)
|
|
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:
|
|
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;
|
|
|
|
// Allocate memory for screenshot
|
|
uint8 *pixels = new uint8[width * height * 3];
|
|
|
|
// 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 * 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);
|
|
|
|
// 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) {
|
|
if (_oldVideoMode.setup && _oldVideoMode.mode == mode)
|
|
return;
|
|
|
|
if (_transactionMode == kTransactionActive) {
|
|
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;
|
|
_aspectRatioCorrection = false;
|
|
}
|
|
}
|
|
|
|
#endif
|