mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-13 04:00:29 +00:00
517 lines
14 KiB
C++
517 lines
14 KiB
C++
/* Residual - A 3D game interpreter
|
|
*
|
|
* Residual is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/scummsys.h"
|
|
|
|
#if defined(SDL_BACKEND)
|
|
|
|
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
|
|
#include "backends/events/sdl/sdl-events.h"
|
|
#include "backends/platform/sdl/sdl.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/mutex.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/translation.h"
|
|
#include "common/util.h"
|
|
#ifdef USE_RGB_COLOR
|
|
#include "common/list.h"
|
|
#endif
|
|
#include "graphics/font.h"
|
|
#include "graphics/fontman.h"
|
|
#include "graphics/scaler.h"
|
|
#include "graphics/surface.h"
|
|
|
|
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
|
:
|
|
_sdlEventSource(sdlEventSource),
|
|
_screen(0),
|
|
_overlayVisible(false),
|
|
_overlayscreen(0),
|
|
_overlayWidth(0), _overlayHeight(0),
|
|
_overlayDirty(true),
|
|
_screenChangeCount(0)
|
|
#ifdef USE_OPENGL
|
|
, _overlayNumTex(0), _overlayTexIds(0)
|
|
#endif
|
|
{
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) {
|
|
error("Could not initialize SDL: %s", SDL_GetError());
|
|
}
|
|
|
|
// This is also called in initSDL(), but initializing graphics
|
|
// may reset it.
|
|
SDL_EnableUNICODE(1);
|
|
|
|
#ifdef _WIN32_WCE
|
|
if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) {
|
|
SDL_VideoInit("windib", 0);
|
|
sdlFlags ^= SDL_INIT_VIDEO;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
|
|
// Unregister the event observer
|
|
if (g_system->getEventManager()->getEventDispatcher() != NULL)
|
|
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::initEventObserver() {
|
|
// Register the graphics manager as a event observer
|
|
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
|
|
}
|
|
|
|
bool SurfaceSdlGraphicsManager::hasFeature(OSystem::Feature f) {
|
|
return
|
|
#ifdef USE_OPENGL
|
|
(f == OSystem::kFeatureOpenGL);
|
|
#else
|
|
false;
|
|
#endif
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
|
|
}
|
|
|
|
bool SurfaceSdlGraphicsManager::getFeatureState(OSystem::Feature f) {
|
|
return false;
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::launcherInitSize(uint w, uint h) {
|
|
closeOverlay();
|
|
setupScreen(w, h, false, false);
|
|
}
|
|
|
|
byte *SurfaceSdlGraphicsManager::setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d) {
|
|
uint32 sdlflags;
|
|
int bpp;
|
|
|
|
closeOverlay();
|
|
|
|
#ifdef USE_OPENGL
|
|
_opengl = accel3d;
|
|
#endif
|
|
_fullscreen = fullscreen;
|
|
|
|
#ifdef USE_OPENGL
|
|
if (_opengl) {
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
|
sdlflags = SDL_OPENGL;
|
|
bpp = 24;
|
|
} else
|
|
#endif
|
|
{
|
|
bpp = 16;
|
|
sdlflags = SDL_HWSURFACE;
|
|
}
|
|
|
|
if (_fullscreen)
|
|
sdlflags |= SDL_FULLSCREEN;
|
|
|
|
_screen = SDL_SetVideoMode(screenW, screenH, bpp, sdlflags);
|
|
|
|
#ifdef USE_OPENGL
|
|
if (!_screen && _opengl) {
|
|
warning("Couldn't create 32-bit visual, trying 16-bit");
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 1);
|
|
_screen = SDL_SetVideoMode(screenW, screenH, 0, sdlflags);
|
|
}
|
|
#endif
|
|
|
|
if (!_screen)
|
|
error("Could not initialize video: %s", SDL_GetError());
|
|
|
|
#ifdef USE_OPENGL
|
|
if (_opengl) {
|
|
int glflag;
|
|
|
|
// apply atribute again for sure based on SDL docs
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
|
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &glflag);
|
|
warning("INFO: GL RED bits: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &glflag);
|
|
warning("INFO: GL GREEN bits: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &glflag);
|
|
warning("INFO: GL BLUE bits: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &glflag);
|
|
warning("INFO: GL APLHA bits: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &glflag);
|
|
warning("INFO: GL Z buffer depth bits: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &glflag);
|
|
warning("INFO: GL Double Buffer: %d", glflag);
|
|
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &glflag);
|
|
warning("INFO: GL Stencil buffer bits: %d", glflag);
|
|
}
|
|
#endif
|
|
|
|
_overlayWidth = screenW;
|
|
_overlayHeight = screenH;
|
|
|
|
#ifdef USE_OPENGL
|
|
if (_opengl) {
|
|
uint32 rmask, gmask, bmask, amask;
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
rmask = 0x00001f00;
|
|
gmask = 0x000007e0;
|
|
bmask = 0x000000f8;
|
|
amask = 0x00000000;
|
|
#else
|
|
rmask = 0x0000f800;
|
|
gmask = 0x000007e0;
|
|
bmask = 0x0000001f;
|
|
amask = 0x00000000;
|
|
#endif
|
|
_overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _overlayWidth, _overlayHeight, 16,
|
|
rmask, gmask, bmask, amask);
|
|
} else
|
|
#endif
|
|
{
|
|
_overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _overlayWidth, _overlayHeight, 16,
|
|
_screen->format->Rmask, _screen->format->Gmask, _screen->format->Bmask, _screen->format->Amask);
|
|
}
|
|
|
|
if (!_overlayscreen)
|
|
error("allocating _overlayscreen failed");
|
|
|
|
_overlayFormat.bytesPerPixel = _overlayscreen->format->BytesPerPixel;
|
|
|
|
_overlayFormat.rLoss = _overlayscreen->format->Rloss;
|
|
_overlayFormat.gLoss = _overlayscreen->format->Gloss;
|
|
_overlayFormat.bLoss = _overlayscreen->format->Bloss;
|
|
_overlayFormat.aLoss = _overlayscreen->format->Aloss;
|
|
|
|
_overlayFormat.rShift = _overlayscreen->format->Rshift;
|
|
_overlayFormat.gShift = _overlayscreen->format->Gshift;
|
|
_overlayFormat.bShift = _overlayscreen->format->Bshift;
|
|
_overlayFormat.aShift = _overlayscreen->format->Ashift;
|
|
|
|
_screenChangeCount++;
|
|
|
|
return (byte *)_screen->pixels;
|
|
}
|
|
|
|
#define BITMAP_TEXTURE_SIZE 256
|
|
|
|
void SurfaceSdlGraphicsManager::updateScreen() {
|
|
#ifdef USE_OPENGL
|
|
if (_opengl) {
|
|
if (_overlayVisible) {
|
|
if (_overlayDirty) {
|
|
// remove if already exist
|
|
if (_overlayNumTex > 0) {
|
|
glDeleteTextures(_overlayNumTex, _overlayTexIds);
|
|
delete[] _overlayTexIds;
|
|
_overlayNumTex = 0;
|
|
}
|
|
|
|
_overlayNumTex = ((_overlayWidth + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE) *
|
|
((_overlayHeight + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE);
|
|
_overlayTexIds = new GLuint[_overlayNumTex];
|
|
glGenTextures(_overlayNumTex, _overlayTexIds);
|
|
for (int i = 0; i < _overlayNumTex; i++) {
|
|
glBindTexture(GL_TEXTURE_2D, _overlayTexIds[i]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, BITMAP_TEXTURE_SIZE, BITMAP_TEXTURE_SIZE, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, _overlayWidth);
|
|
|
|
int curTexIdx = 0;
|
|
for (int y = 0; y < _overlayHeight; y += BITMAP_TEXTURE_SIZE) {
|
|
for (int x = 0; x < _overlayWidth; x += BITMAP_TEXTURE_SIZE) {
|
|
int t_width = (x + BITMAP_TEXTURE_SIZE >= _overlayWidth) ? (_overlayWidth - x) : BITMAP_TEXTURE_SIZE;
|
|
int t_height = (y + BITMAP_TEXTURE_SIZE >= _overlayHeight) ? (_overlayHeight - y) : BITMAP_TEXTURE_SIZE;
|
|
glBindTexture(GL_TEXTURE_2D, _overlayTexIds[curTexIdx]);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, t_width, t_height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (byte *)_overlayscreen->pixels + (y * 2 * _overlayWidth) + (2 * x));
|
|
curTexIdx++;
|
|
}
|
|
}
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
}
|
|
|
|
// Save current state
|
|
glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT);
|
|
|
|
// prepare view
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0, _overlayWidth, _overlayHeight, 0, 0, 1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glDisable(GL_LIGHTING);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glScissor(0, 0, _overlayWidth, _overlayHeight);
|
|
|
|
int curTexIdx = 0;
|
|
for (int y = 0; y < _overlayHeight; y += BITMAP_TEXTURE_SIZE) {
|
|
for (int x = 0; x < _overlayWidth; x += BITMAP_TEXTURE_SIZE) {
|
|
glBindTexture(GL_TEXTURE_2D, _overlayTexIds[curTexIdx]);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0, 0);
|
|
glVertex2i(x, y);
|
|
glTexCoord2f(1.0f, 0.0f);
|
|
glVertex2i(x + BITMAP_TEXTURE_SIZE, y);
|
|
glTexCoord2f(1.0f, 1.0f);
|
|
glVertex2i(x + BITMAP_TEXTURE_SIZE, y + BITMAP_TEXTURE_SIZE);
|
|
glTexCoord2f(0.0f, 1.0f);
|
|
glVertex2i(x, y + BITMAP_TEXTURE_SIZE);
|
|
glEnd();
|
|
curTexIdx++;
|
|
}
|
|
}
|
|
|
|
// Restore previous state
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPopMatrix();
|
|
|
|
glPopAttrib();
|
|
}
|
|
SDL_GL_SwapBuffers();
|
|
} else
|
|
#endif
|
|
{
|
|
if (_overlayVisible) {
|
|
SDL_LockSurface(_screen);
|
|
SDL_LockSurface(_overlayscreen);
|
|
byte *src = (byte *)_overlayscreen->pixels;
|
|
byte *buf = (byte *)_screen->pixels;
|
|
int h = _overlayHeight;
|
|
do {
|
|
memcpy(buf, src, _overlayWidth * _overlayscreen->format->BytesPerPixel);
|
|
src += _overlayscreen->pitch;
|
|
buf += _screen->pitch;
|
|
} while (--h);
|
|
SDL_UnlockSurface(_screen);
|
|
SDL_UnlockSurface(_overlayscreen);
|
|
}
|
|
SDL_Flip(_screen);
|
|
}
|
|
}
|
|
|
|
int16 SurfaceSdlGraphicsManager::getHeight() {
|
|
return _screen->h;
|
|
}
|
|
|
|
int16 SurfaceSdlGraphicsManager::getWidth() {
|
|
return _screen->w;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Overlays ---
|
|
#pragma mark -
|
|
|
|
void SurfaceSdlGraphicsManager::showOverlay() {
|
|
if (_overlayVisible)
|
|
return;
|
|
|
|
_overlayVisible = true;
|
|
|
|
clearOverlay();
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::hideOverlay() {
|
|
|
|
if (!_overlayVisible)
|
|
return;
|
|
|
|
_overlayVisible = false;
|
|
|
|
clearOverlay();
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::clearOverlay() {
|
|
|
|
if (!_overlayVisible)
|
|
return;
|
|
|
|
#ifdef USE_OPENGL
|
|
if (_opengl) {
|
|
SDL_Surface *tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, _overlayWidth, _overlayHeight, 16,
|
|
_overlayscreen->format->Rmask, _overlayscreen->format->Gmask,
|
|
_overlayscreen->format->Bmask, _overlayscreen->format->Amask);
|
|
|
|
SDL_LockSurface(tmp);
|
|
SDL_LockSurface(_overlayscreen);
|
|
|
|
glReadPixels(0, 0, _overlayWidth, _overlayHeight, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, tmp->pixels);
|
|
|
|
// Flip pixels vertically
|
|
byte *src = (byte *)tmp->pixels;
|
|
byte *buf = (byte *)_overlayscreen->pixels + (_overlayHeight - 1) * _overlayscreen->pitch;
|
|
int h = _overlayHeight;
|
|
do {
|
|
memcpy(buf, src, _overlayWidth * _overlayscreen->format->BytesPerPixel);
|
|
src += tmp->pitch;
|
|
buf -= _overlayscreen->pitch;
|
|
} while (--h);
|
|
|
|
SDL_UnlockSurface(_overlayscreen);
|
|
SDL_UnlockSurface(tmp);
|
|
|
|
SDL_FreeSurface(tmp);
|
|
} else
|
|
#endif
|
|
{
|
|
SDL_LockSurface(_screen);
|
|
SDL_LockSurface(_overlayscreen);
|
|
byte *src = (byte *)_screen->pixels;
|
|
byte *buf = (byte *)_overlayscreen->pixels;
|
|
int h = _overlayHeight;
|
|
do {
|
|
memcpy(buf, src, _overlayWidth * _overlayscreen->format->BytesPerPixel);
|
|
src += _screen->pitch;
|
|
buf += _overlayscreen->pitch;
|
|
} while (--h);
|
|
SDL_UnlockSurface(_screen);
|
|
SDL_UnlockSurface(_overlayscreen);
|
|
}
|
|
_overlayDirty = true;
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) {
|
|
if (_overlayscreen == NULL)
|
|
return;
|
|
|
|
if (SDL_LockSurface(_overlayscreen) == -1)
|
|
error("SDL_LockSurface failed: %s", SDL_GetError());
|
|
|
|
byte *src = (byte *)_overlayscreen->pixels;
|
|
int h = _overlayHeight;
|
|
do {
|
|
memcpy(buf, src, _overlayWidth * _overlayscreen->format->BytesPerPixel);
|
|
src += _overlayscreen->pitch;
|
|
buf += pitch;
|
|
} while (--h);
|
|
|
|
SDL_UnlockSurface(_overlayscreen);
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) {
|
|
if (_overlayscreen == 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 > _overlayWidth - x) {
|
|
w = _overlayWidth - x;
|
|
}
|
|
|
|
if (h > _overlayHeight - y) {
|
|
h = _overlayHeight - y;
|
|
}
|
|
|
|
if (w <= 0 || h <= 0)
|
|
return;
|
|
|
|
if (SDL_LockSurface(_overlayscreen) == -1)
|
|
error("SDL_LockSurface failed: %s", SDL_GetError());
|
|
|
|
byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * _overlayscreen->format->BytesPerPixel;
|
|
do {
|
|
memcpy(dst, buf, w * _overlayscreen->format->BytesPerPixel);
|
|
dst += _overlayscreen->pitch;
|
|
buf += pitch;
|
|
} while (--h);
|
|
|
|
SDL_UnlockSurface(_overlayscreen);
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::closeOverlay() {
|
|
if (_overlayscreen) {
|
|
SDL_FreeSurface(_overlayscreen);
|
|
_overlayscreen = NULL;
|
|
#ifdef USE_OPENGL
|
|
if (_overlayNumTex > 0) {
|
|
glDeleteTextures(_overlayNumTex, _overlayTexIds);
|
|
delete[] _overlayTexIds;
|
|
_overlayNumTex = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Mouse ---
|
|
#pragma mark -
|
|
|
|
bool SurfaceSdlGraphicsManager::showMouse(bool visible) {
|
|
SDL_ShowCursor(visible);
|
|
return true;
|
|
}
|
|
|
|
void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
|
|
SDL_WarpMouse(x, y);
|
|
}
|
|
|
|
bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) {
|
|
return false;
|
|
}
|
|
|
|
#endif
|