scummvm/backends/graphics/surfacesdl/surfacesdl-graphics.cpp

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