mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-15 05:00:47 +00:00

I added an comment explaining why OSD is broken currently. The basic problem is that OpenGL is not thread safe, thus in case we try to display any message on the OSD from the sound thread for example it will result in crashes. svn-id: r54829
1321 lines
37 KiB
C++
1321 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),
|
|
#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) * 4, 256);
|
|
_cursorPalette = (byte *)calloc(sizeof(byte) * 4, 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 * 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() {
|
|
// 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 * 4, colors, num * 4);
|
|
|
|
_cursorPaletteDisabled = false;
|
|
_cursorNeedsRedraw = true;
|
|
}
|
|
|
|
void OpenGLGraphicsManager::disableCursorPalette(bool disable) {
|
|
_cursorPaletteDisabled = disable;
|
|
_cursorNeedsRedraw = true;
|
|
}
|
|
|
|
//
|
|
// Misc
|
|
//
|
|
|
|
void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
|
|
// TODO: Fix OSD support.
|
|
//
|
|
// Actually we have nice OSD support for the OpenGL backend, but
|
|
// since the OpenGL context/resources are not shared between threads we
|
|
// can not use this here.
|
|
//
|
|
// A possible way of making the code crash hard is to use the MT-32 emu.
|
|
// Whenever there is some message send to the OSD from the emulator's
|
|
// sound thread the glBindTexture call inside _osdTexture->updateBuffer
|
|
// will result in a crash.
|
|
//
|
|
// To try it out just uncomment the below code and start up any game which
|
|
// prints some info on the MT-32 display. Whenever the message should be
|
|
// displayed it will crash hard.
|
|
//
|
|
// We can not even use a GUI dialog here, since that would also result in
|
|
// an change of the overlay surface and thus result in the same problem
|
|
// just in another place.
|
|
warning("OpenGL: OSD messsage \"%s\" suppressed", 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
|
|
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) {
|
|
// 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
|
|
|
|
if (!_gameTexture) {
|
|
byte bpp;
|
|
GLenum intformat;
|
|
GLenum format;
|
|
GLenum type;
|
|
#ifdef USE_RGB_COLOR
|
|
getGLPixelFormat(_screenFormat, bpp, intformat, format, type);
|
|
#else
|
|
getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, intformat, format, type);
|
|
#endif
|
|
_gameTexture = new GLTexture(bpp, intformat, format, type);
|
|
} else
|
|
_gameTexture->refresh();
|
|
|
|
_overlayFormat = Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0);
|
|
|
|
if (!_overlayTexture) {
|
|
byte bpp;
|
|
GLenum intformat;
|
|
GLenum format;
|
|
GLenum type;
|
|
getGLPixelFormat(_overlayFormat, bpp, intformat, format, type);
|
|
_overlayTexture = new GLTexture(bpp, intformat, format, type);
|
|
} else
|
|
_overlayTexture->refresh();
|
|
|
|
if (!_cursorTexture)
|
|
_cursorTexture = new GLTexture(4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
else
|
|
_cursorTexture->refresh();
|
|
|
|
GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST;
|
|
_gameTexture->setFilter(filter);
|
|
_overlayTexture->setFilter(filter);
|
|
_cursorTexture->setFilter(filter);
|
|
|
|
// Allocate texture memory and finish refreshing
|
|
_gameTexture->allocBuffer(_videoMode.screenWidth, _videoMode.screenHeight);
|
|
_overlayTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight);
|
|
_cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
|
|
|
|
if (_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_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::adjustMouseEvent(const Common::Event &event) {
|
|
if (!event.synthetic) {
|
|
Common::Event newEvent(event);
|
|
newEvent.synthetic = true;
|
|
|
|
if (_videoMode.mode == OpenGL::GFX_NORMAL) {
|
|
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 -= _displayX;
|
|
newEvent.mouse.y -= _displayY;
|
|
|
|
if (_overlayVisible) {
|
|
if (_displayWidth != _videoMode.overlayWidth)
|
|
newEvent.mouse.x = newEvent.mouse.x * _videoMode.overlayWidth / _displayWidth;
|
|
if (_displayHeight != _videoMode.overlayHeight)
|
|
newEvent.mouse.y = newEvent.mouse.y * _videoMode.overlayHeight / _displayHeight;
|
|
} else {
|
|
if (_displayWidth != _videoMode.screenWidth)
|
|
newEvent.mouse.x = newEvent.mouse.x * _videoMode.screenWidth / _displayWidth;
|
|
if (_displayHeight != _videoMode.screenHeight)
|
|
newEvent.mouse.y = newEvent.mouse.y * _videoMode.screenHeight / _displayHeight;
|
|
}
|
|
}
|
|
|
|
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 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
|