/* 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