ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

openglsdl-graphics.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 
00025 #if defined(SDL_BACKEND)
00026 
00027 #include "backends/graphics/openglsdl/openglsdl-graphics.h"
00028 
00029 #include "backends/events/sdl/sdl-events.h"
00030 #include "common/config-manager.h"
00031 #include "common/file.h"
00032 #include "engines/engine.h"
00033 #include "graphics/pixelbuffer.h"
00034 #include "graphics/opengl/context.h"
00035 #include "graphics/opengl/framebuffer.h"
00036 #include "graphics/opengl/surfacerenderer.h"
00037 #include "graphics/opengl/system_headers.h"
00038 #include "graphics/opengl/texture.h"
00039 #include "graphics/opengl/tiledsurface.h"
00040 #include "image/png.h"
00041 
00042 OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window, const Capabilities &capabilities)
00043     :
00044     ResVmSdlGraphicsManager(sdlEventSource, window, capabilities),
00045 #if SDL_VERSION_ATLEAST(2, 0, 0)
00046     _glContext(nullptr),
00047 #endif
00048     _overlayScreen(nullptr),
00049     _overlayBackground(nullptr),
00050     _gameRect(),
00051     _frameBuffer(nullptr),
00052     _surfaceRenderer(nullptr) {
00053     ConfMan.registerDefault("antialiasing", 0);
00054 
00055     _sideTextures[0] = _sideTextures[1] = nullptr;
00056 
00057     // Don't start at zero so that the value is never the same as the surface graphics manager
00058     _screenChangeCount = 1 << (sizeof(int) * 8 - 2);
00059 }
00060 
00061 OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() {
00062     closeOverlay();
00063 #if SDL_VERSION_ATLEAST(2, 0, 0)
00064     deinitializeRenderer();
00065 #endif
00066 }
00067 
00068 bool OpenGLSdlGraphicsManager::hasFeature(OSystem::Feature f) const {
00069     return
00070         (f == OSystem::kFeatureFullscreenMode) ||
00071         (f == OSystem::kFeatureOpenGL) ||
00072 #if SDL_VERSION_ATLEAST(2, 0, 0)
00073         (f == OSystem::kFeatureFullscreenToggleKeepsContext) ||
00074 #endif
00075         (f == OSystem::kFeatureVSync) ||
00076         (f == OSystem::kFeatureAspectRatioCorrection) ||
00077         (f == OSystem::kFeatureOverlaySupportsAlpha && _overlayFormat.aBits() > 3);
00078 }
00079 
00080 bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) const {
00081     switch (f) {
00082         case OSystem::kFeatureVSync:
00083             return isVSyncEnabled();
00084         default:
00085             return ResVmSdlGraphicsManager::getFeatureState(f);
00086     }
00087 }
00088 
00089 void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
00090     switch (f) {
00091         case OSystem::kFeatureFullscreenMode:
00092             if (_fullscreen != enable) {
00093                 _fullscreen = enable;
00094                 createOrUpdateScreen();
00095             }
00096             break;
00097         default:
00098             ResVmSdlGraphicsManager::setFeatureState(f, enable);
00099             break;
00100     }
00101 }
00102 
00103 void OpenGLSdlGraphicsManager::setupScreen(uint gameWidth, uint gameHeight, bool fullscreen, bool accel3d) {
00104     assert(accel3d);
00105     closeOverlay();
00106 
00107 #if SDL_VERSION_ATLEAST(2, 0, 0)
00108     // Clear the GL context when going from / to the launcher
00109     SDL_GL_DeleteContext(_glContext);
00110     _glContext = nullptr;
00111 #endif
00112 
00113     _engineRequestedWidth = gameWidth;
00114     _engineRequestedHeight = gameHeight;
00115     _antialiasing = ConfMan.getInt("antialiasing");
00116     _fullscreen = fullscreen;
00117     _lockAspectRatio = ConfMan.getBool("aspect_ratio");
00118     _vsync = ConfMan.getBool("vsync");
00119 
00120     createOrUpdateScreen();
00121 
00122     int glflag;
00123     const GLubyte *str;
00124 
00125     str = glGetString(GL_VENDOR);
00126     debug("INFO: OpenGL Vendor: %s", str);
00127     str = glGetString(GL_RENDERER);
00128     debug("INFO: OpenGL Renderer: %s", str);
00129     str = glGetString(GL_VERSION);
00130     debug("INFO: OpenGL Version: %s", str);
00131     SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &glflag);
00132     debug("INFO: OpenGL Red bits: %d", glflag);
00133     SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &glflag);
00134     debug("INFO: OpenGL Green bits: %d", glflag);
00135     SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &glflag);
00136     debug("INFO: OpenGL Blue bits: %d", glflag);
00137     SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &glflag);
00138     debug("INFO: OpenGL Alpha bits: %d", glflag);
00139     SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &glflag);
00140     debug("INFO: OpenGL Z buffer depth bits: %d", glflag);
00141     SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &glflag);
00142     debug("INFO: OpenGL Double Buffer: %d", glflag);
00143     SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &glflag);
00144     debug("INFO: OpenGL Stencil buffer bits: %d", glflag);
00145 #ifdef USE_GLEW
00146     debug("INFO: GLEW Version: %s", glewGetString(GLEW_VERSION));
00147 #endif
00148 #ifdef USE_OPENGL_SHADERS
00149     debug("INFO: GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
00150 #endif
00151 }
00152 
00153 void OpenGLSdlGraphicsManager::createOrUpdateScreen() {
00154     closeOverlay();
00155 
00156     // If the game can't adapt to any resolution, render it to a framebuffer
00157     // so it can be scaled to fill the available space.
00158     bool engineSupportsArbitraryResolutions = !g_engine || g_engine->hasFeature(Engine::kSupportsArbitraryResolutions);
00159     bool renderToFrameBuffer = !engineSupportsArbitraryResolutions && _capabilities.openGLFrameBuffer;
00160 
00161     // Choose the effective window size or fullscreen mode
00162     uint effectiveWidth;
00163     uint effectiveHeight;
00164     if (_fullscreen && (engineSupportsArbitraryResolutions || renderToFrameBuffer)) {
00165         Common::Rect fullscreenResolution = getPreferredFullscreenResolution();
00166         effectiveWidth = fullscreenResolution.width();
00167         effectiveHeight = fullscreenResolution.height();
00168     } else {
00169         effectiveWidth = _engineRequestedWidth;
00170         effectiveHeight = _engineRequestedHeight;
00171     }
00172 
00173     if (!createOrUpdateGLContext(_engineRequestedWidth, _engineRequestedHeight,
00174                                  effectiveWidth, effectiveHeight, renderToFrameBuffer)) {
00175         warning("SDL Error: %s", SDL_GetError());
00176         g_system->quit();
00177     }
00178 
00179 #ifdef USE_GLEW
00180     GLenum err = glewInit();
00181     if (err != GLEW_OK) {
00182         warning("Error: %s", glewGetErrorString(err));
00183         g_system->quit();
00184     }
00185 #endif
00186 
00187 #if SDL_VERSION_ATLEAST(2, 0, 1)
00188     int obtainedWidth = 0, obtainedHeight = 0;
00189     SDL_GL_GetDrawableSize(_window->getSDLWindow(), &obtainedWidth, &obtainedHeight);
00190 #else
00191     int obtainedWidth = effectiveWidth;
00192     int obtainedHeight = effectiveHeight;
00193 #endif
00194 
00195     // Compute the rectangle where to draw the game inside the effective screen
00196     _gameRect = computeGameRect(renderToFrameBuffer, _engineRequestedWidth, _engineRequestedHeight,
00197                                 obtainedWidth, obtainedHeight);
00198 
00199     initializeOpenGLContext();
00200     _surfaceRenderer = OpenGL::createBestSurfaceRenderer();
00201 
00202     _overlayFormat = OpenGL::Texture::getRGBAPixelFormat();
00203     _overlayScreen = new OpenGL::TiledSurface(obtainedWidth, obtainedHeight, _overlayFormat);
00204 
00205     _screenFormat = _overlayFormat;
00206 
00207     _screenChangeCount++;
00208 
00209     _eventSource->resetKeyboardEmulation(obtainedWidth - 1, obtainedHeight - 1);
00210 
00211 #if !defined(AMIGAOS)
00212     if (renderToFrameBuffer) {
00213         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00214         _frameBuffer = createFramebuffer(_engineRequestedWidth, _engineRequestedHeight);
00215         _frameBuffer->attach();
00216     }
00217 #endif
00218 }
00219 
00220 Math::Rect2d OpenGLSdlGraphicsManager::computeGameRect(bool renderToFrameBuffer, uint gameWidth, uint gameHeight,
00221                                                       uint screenWidth, uint screenHeight) {
00222     if (renderToFrameBuffer) {
00223         if (_lockAspectRatio) {
00224             // The game is scaled to fit the screen, keeping the same aspect ratio
00225             float scale = MIN(screenHeight / float(gameHeight), screenWidth / float(gameWidth));
00226             float scaledW = scale * (gameWidth / float(screenWidth));
00227             float scaledH = scale * (gameHeight / float(screenHeight));
00228             return Math::Rect2d(
00229                     Math::Vector2d(0.5 - (0.5 * scaledW), 0.5 - (0.5 * scaledH)),
00230                     Math::Vector2d(0.5 + (0.5 * scaledW), 0.5 + (0.5 * scaledH))
00231             );
00232         } else {
00233             // The game occupies the whole screen
00234             return Math::Rect2d(Math::Vector2d(0, 0), Math::Vector2d(1, 1));
00235         }
00236     } else {
00237         return Math::Rect2d(Math::Vector2d(0, 0), Math::Vector2d(1, 1));
00238     }
00239 }
00240 
00241 void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) {
00242 #if SDL_VERSION_ATLEAST(2, 0, 0)
00243     // Get the updated size directly from SDL, in case there are multiple
00244     // resize events in the message queue.
00245     int newWidth = 0, newHeight = 0;
00246     SDL_GL_GetDrawableSize(_window->getSDLWindow(), &newWidth, &newHeight);
00247 
00248     if (newWidth == _overlayScreen->getWidth() && newHeight == _overlayScreen->getHeight()) {
00249         return; // nothing to do
00250     }
00251 
00252     // Compute the rectangle where to draw the game inside the effective screen
00253     _gameRect = computeGameRect(_frameBuffer != nullptr,
00254                                 _engineRequestedWidth, _engineRequestedHeight,
00255                                 newWidth, newHeight);
00256 
00257     // Update the overlay
00258     delete _overlayScreen;
00259     _overlayScreen = new OpenGL::TiledSurface(newWidth, newHeight, _overlayFormat);
00260 
00261     // Clear the overlay background so it is not displayed distorted while resizing
00262     delete _overlayBackground;
00263     _overlayBackground = nullptr;
00264 
00265     _screenChangeCount++;
00266 
00267     _eventSource->resetKeyboardEmulation(newWidth - 1, newHeight- 1);
00268 #endif
00269 }
00270 
00271 Graphics::PixelBuffer OpenGLSdlGraphicsManager::getScreenPixelBuffer() {
00272     error("Direct screen buffer access is not allowed when using OpenGL");
00273 }
00274 
00275 void OpenGLSdlGraphicsManager::initializeOpenGLContext() const {
00276     OpenGL::ContextType type;
00277 
00278 #ifdef USE_GLES2
00279     type = OpenGL::kContextGLES2;
00280 #else
00281     type = OpenGL::kContextGL;
00282 #endif
00283 
00284     OpenGLContext.initialize(type);
00285 
00286 #if SDL_VERSION_ATLEAST(2, 0, 0)
00287     if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) {
00288         warning("Unable to %s VSync: %s", _vsync ? "enable" : "disable", SDL_GetError());
00289     }
00290 #endif
00291 }
00292 
00293 OpenGLSdlGraphicsManager::OpenGLPixelFormat::OpenGLPixelFormat(uint screenBytesPerPixel, uint red, uint blue, uint green, uint alpha, int samples) :
00294         bytesPerPixel(screenBytesPerPixel),
00295         redSize(red),
00296         blueSize(blue),
00297         greenSize(green),
00298         alphaSize(alpha),
00299         multisampleSamples(samples) {
00300 
00301 }
00302 
00303 bool OpenGLSdlGraphicsManager::createOrUpdateGLContext(uint gameWidth, uint gameHeight,
00304                                                        uint effectiveWidth, uint effectiveHeight,
00305                                                        bool renderToFramebuffer) {
00306     // Build a list of OpenGL pixel formats usable by ResidualVM
00307     Common::Array<OpenGLPixelFormat> pixelFormats;
00308     if (_antialiasing > 0 && !renderToFramebuffer) {
00309         // Don't enable screen level multisampling when rendering to a framebuffer
00310         pixelFormats.push_back(OpenGLPixelFormat(32, 8, 8, 8, 8, _antialiasing));
00311         pixelFormats.push_back(OpenGLPixelFormat(16, 5, 5, 5, 1, _antialiasing));
00312         pixelFormats.push_back(OpenGLPixelFormat(16, 5, 6, 5, 0, _antialiasing));
00313     }
00314     pixelFormats.push_back(OpenGLPixelFormat(32, 8, 8, 8, 8, 0));
00315     pixelFormats.push_back(OpenGLPixelFormat(16, 5, 5, 5, 1, 0));
00316     pixelFormats.push_back(OpenGLPixelFormat(16, 5, 6, 5, 0, 0));
00317 
00318     // Unfortunatly, SDL does not provide a list of valid pixel formats
00319     // for the current OpenGL implementation and hardware.
00320     // SDL may not be able to create a screen with the preferred pixel format.
00321     // Try all the pixel formats in the list until SDL returns a valid screen.
00322     Common::Array<OpenGLPixelFormat>::const_iterator it = pixelFormats.begin();
00323     for (; it != pixelFormats.end(); it++) {
00324         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, it->redSize);
00325         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, it->greenSize);
00326         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, it->blueSize);
00327         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, it->alphaSize);
00328         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
00329         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
00330         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
00331         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, it->multisampleSamples > 0);
00332         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, it->multisampleSamples);
00333 #if !SDL_VERSION_ATLEAST(2, 0, 0)
00334         SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync ? 1 : 0);
00335 #endif
00336 #if SDL_VERSION_ATLEAST(2, 0, 0)
00337 #ifdef USE_GLES2
00338         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
00339         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
00340         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
00341 #else
00342         // AmigaOS4's OpenGL implementation is close to 1.3. Until that changes we need
00343         // to use 1.3 as version or residualvm will cease working at all on that platform.
00344         // Profile Mask has to be 0 as well.
00345         // This will be revised and removed once AmigaOS4 supports 2.x or OpenGLES2.
00346         #ifdef __amigaos4__
00347             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
00348             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
00349         #else
00350             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
00351             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
00352         #endif
00353 #endif
00354 #endif
00355 
00356 #if SDL_VERSION_ATLEAST(2, 0, 0)
00357         uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
00358         if (_fullscreen) {
00359             // On Linux/X11, when toggling to fullscreen, the window manager saves
00360             // the window size to be able to restore it when going back to windowed mode.
00361             // If the user configured ResidualVM to start in fullscreen mode, we first
00362             // create a window and then toggle it to fullscreen to give the window manager
00363             // a chance to save the window size. That way if the user switches back
00364             // to windowed mode, the window manager has a window size to apply instead
00365             // of leaving the window at the fullscreen resolution size.
00366             if (!_window->getSDLWindow()) {
00367                 _window->createOrUpdateWindow(gameWidth, gameHeight, sdlflags);
00368             }
00369 
00370             sdlflags |= SDL_WINDOW_FULLSCREEN;
00371         }
00372 
00373         if (_window->createOrUpdateWindow(effectiveWidth, effectiveHeight, sdlflags)) {
00374             // Get the current GL context from SDL in case the previous one
00375             // was destroyed because the window was recreated.
00376             _glContext = SDL_GL_GetCurrentContext();
00377             if (!_glContext) {
00378                 _glContext = SDL_GL_CreateContext(_window->getSDLWindow());
00379             }
00380 
00381             if (_glContext) {
00382                 assert(SDL_GL_GetCurrentWindow() == _window->getSDLWindow());
00383                 break;
00384             }
00385         }
00386 
00387         _window->destroyWindow();
00388 #else
00389         uint32 sdlflags = SDL_OPENGL;
00390         if (_fullscreen)
00391             sdlflags |= SDL_FULLSCREEN;
00392 
00393         SDL_Surface *screen = SDL_SetVideoMode(effectiveWidth, effectiveHeight, it->bytesPerPixel, sdlflags);
00394         if (screen) {
00395             break;
00396         }
00397 #endif
00398     }
00399 
00400     // Display a warning if the effective pixel format is not the preferred one
00401     if (it != pixelFormats.begin() && it != pixelFormats.end()) {
00402         bool wantsAA = pixelFormats.front().multisampleSamples > 0;
00403         bool gotAA = it->multisampleSamples > 0;
00404 
00405         warning("Couldn't create a %d-bit visual%s, using to %d-bit%s instead",
00406                 pixelFormats.front().bytesPerPixel,
00407                 wantsAA && !gotAA ? " with AA" : "",
00408                 it->bytesPerPixel,
00409                 wantsAA && !gotAA ? " without AA" : "");
00410     }
00411 
00412     return it != pixelFormats.end();
00413 }
00414 
00415 bool OpenGLSdlGraphicsManager::isVSyncEnabled() const {
00416 #if SDL_VERSION_ATLEAST(2, 0, 0)
00417     return SDL_GL_GetSwapInterval() != 0;
00418 #else
00419     int swapControl = 0;
00420     SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &swapControl);
00421     return swapControl != 0;
00422 #endif
00423 }
00424 
00425 void OpenGLSdlGraphicsManager::drawOverlay() {
00426     glViewport(0, 0, _overlayScreen->getWidth(), _overlayScreen->getHeight());
00427     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00428 
00429     _surfaceRenderer->prepareState();
00430 
00431     if (_overlayBackground) {
00432         _overlayBackground->draw(_surfaceRenderer);
00433     }
00434 
00435     _surfaceRenderer->enableAlphaBlending(true);
00436     _surfaceRenderer->setFlipY(true);
00437     _overlayScreen->draw(_surfaceRenderer);
00438 
00439     _surfaceRenderer->restorePreviousState();
00440 }
00441 
00442 void OpenGLSdlGraphicsManager::drawSideTextures() {
00443     if (_fullscreen && _lockAspectRatio) {
00444         _surfaceRenderer->setFlipY(true);
00445 
00446         const Math::Vector2d nudge(1.0 / float(_overlayScreen->getWidth()), 0);
00447         if (_sideTextures[0] != nullptr) {
00448             float left = _gameRect.getBottomLeft().getX() - (float(_overlayScreen->getHeight()) / float(_sideTextures[0]->getHeight())) * _sideTextures[0]->getWidth() / float(_overlayScreen->getWidth());
00449             Math::Rect2d leftRect(Math::Vector2d(left, 0.0), _gameRect.getBottomLeft() + nudge);
00450             _surfaceRenderer->render(_sideTextures[0], leftRect);
00451         }
00452 
00453         if (_sideTextures[1] != nullptr) {
00454             float right = _gameRect.getTopRight().getX() + (float(_overlayScreen->getHeight()) / float(_sideTextures[1]->getHeight())) * _sideTextures[1]->getWidth() / float(_overlayScreen->getWidth());
00455             Math::Rect2d rightRect(_gameRect.getTopRight() - nudge, Math::Vector2d(right, 1.0));
00456             _surfaceRenderer->render(_sideTextures[1], rightRect);
00457         }
00458 
00459         _surfaceRenderer->setFlipY(false);
00460     }
00461 }
00462 
00463 #ifndef AMIGAOS
00464 OpenGL::FrameBuffer *OpenGLSdlGraphicsManager::createFramebuffer(uint width, uint height) {
00465 #if !defined(USE_GLES2)
00466     if (_antialiasing && OpenGLContext.framebufferObjectMultisampleSupported) {
00467         return new OpenGL::MultiSampleFrameBuffer(width, height, _antialiasing);
00468     } else
00469 #endif
00470     {
00471         return new OpenGL::FrameBuffer(width, height);
00472     }
00473 }
00474 #endif // AMIGAOS
00475 
00476 void OpenGLSdlGraphicsManager::updateScreen() {
00477     if (_frameBuffer) {
00478         _frameBuffer->detach();
00479         glViewport(0, 0, _overlayScreen->getWidth(), _overlayScreen->getHeight());
00480         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00481         _surfaceRenderer->prepareState();
00482         drawSideTextures();
00483         _surfaceRenderer->render(_frameBuffer, _gameRect);
00484         _surfaceRenderer->restorePreviousState();
00485     }
00486 
00487     if (_overlayVisible) {
00488         _overlayScreen->update();
00489 
00490         if (_overlayBackground) {
00491             _overlayBackground->update();
00492         }
00493 
00494         drawOverlay();
00495     }
00496 
00497 #if SDL_VERSION_ATLEAST(2, 0, 0)
00498     SDL_GL_SwapWindow(_window->getSDLWindow());
00499 #else
00500     SDL_GL_SwapBuffers();
00501 #endif
00502 
00503     if (_frameBuffer) {
00504         _frameBuffer->attach();
00505     }
00506 }
00507 
00508 int16 OpenGLSdlGraphicsManager::getHeight() const {
00509     // ResidualVM specific
00510     if (_frameBuffer)
00511         return _frameBuffer->getHeight();
00512     else
00513         return _overlayScreen->getHeight();
00514 }
00515 
00516 int16 OpenGLSdlGraphicsManager::getWidth() const {
00517     // ResidualVM specific
00518     if (_frameBuffer)
00519         return _frameBuffer->getWidth();
00520     else
00521         return _overlayScreen->getWidth();
00522 }
00523 
00524 #pragma mark -
00525 #pragma mark --- Overlays ---
00526 #pragma mark -
00527 
00528 void OpenGLSdlGraphicsManager::suggestSideTextures(Graphics::Surface *left, Graphics::Surface *right) {
00529     delete _sideTextures[0];
00530     _sideTextures[0] = nullptr;
00531     delete _sideTextures[1];
00532     _sideTextures[1] = nullptr;
00533     if (left) {
00534         _sideTextures[0] = new OpenGL::Texture(*left);
00535     }
00536     if (right) {
00537         _sideTextures[1] = new OpenGL::Texture(*right);
00538     }
00539 }
00540 
00541 void OpenGLSdlGraphicsManager::showOverlay() {
00542     if (_overlayVisible) {
00543         return;
00544     }
00545     _overlayVisible = true;
00546 
00547     delete _overlayBackground;
00548     _overlayBackground = nullptr;
00549 
00550     if (g_engine) {
00551         if (_frameBuffer)
00552             _frameBuffer->detach();
00553         // If there is a game running capture the screen, so that it can be shown "below" the overlay.
00554         _overlayBackground = new OpenGL::TiledSurface(_overlayScreen->getWidth(), _overlayScreen->getHeight(), _overlayFormat);
00555         Graphics::Surface *background = _overlayBackground->getBackingSurface();
00556         glReadPixels(0, 0, background->w, background->h, GL_RGBA, GL_UNSIGNED_BYTE, background->getPixels());
00557         if (_frameBuffer)
00558             _frameBuffer->attach();
00559     }
00560 }
00561 
00562 void OpenGLSdlGraphicsManager::hideOverlay() {
00563     if (!_overlayVisible) {
00564         return;
00565     }
00566     _overlayVisible = false;
00567 
00568     delete _overlayBackground;
00569     _overlayBackground = nullptr;
00570 }
00571 
00572 void OpenGLSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
00573     _overlayScreen->copyRectToSurface(buf, pitch, x, y, w, h);
00574 }
00575 
00576 void OpenGLSdlGraphicsManager::clearOverlay() {
00577     _overlayScreen->fill(0);
00578 }
00579 
00580 void OpenGLSdlGraphicsManager::grabOverlay(void *buf, int pitch) const {
00581     const Graphics::Surface *overlayData = _overlayScreen->getBackingSurface();
00582 
00583     const byte *src = (const byte *)overlayData->getPixels();
00584     byte *dst = (byte *)buf;
00585 
00586     for (uint h = overlayData->h; h > 0; --h) {
00587         memcpy(dst, src, overlayData->w * overlayData->format.bytesPerPixel);
00588         dst += pitch;
00589         src += overlayData->pitch;
00590     }
00591 }
00592 
00593 void OpenGLSdlGraphicsManager::closeOverlay() {
00594     delete _sideTextures[0];
00595     delete _sideTextures[1];
00596     _sideTextures[0] = _sideTextures[1] = nullptr;
00597 
00598     if (_overlayScreen) {
00599         delete _overlayScreen;
00600         _overlayScreen = nullptr;
00601     }
00602 
00603     delete _surfaceRenderer;
00604     _surfaceRenderer = nullptr;
00605 
00606     delete _frameBuffer;
00607     _frameBuffer = nullptr;
00608 
00609     OpenGL::Context::destroy();
00610 }
00611 
00612 int16 OpenGLSdlGraphicsManager::getOverlayHeight() const {
00613     return _overlayScreen->getHeight();
00614 }
00615 
00616 int16 OpenGLSdlGraphicsManager::getOverlayWidth() const {
00617     return _overlayScreen->getWidth();
00618 }
00619 
00620 void OpenGLSdlGraphicsManager::warpMouse(int x, int y) {
00621     //ResidualVM specific
00622     if (_frameBuffer) {
00623         // Scale from game coordinates to screen coordinates
00624         x = (x * _gameRect.getWidth() * _overlayScreen->getWidth()) / _frameBuffer->getWidth();
00625         y = (y * _gameRect.getHeight() * _overlayScreen->getHeight()) / _frameBuffer->getHeight();
00626 
00627         x += _gameRect.getTopLeft().getX() * _overlayScreen->getWidth();
00628         y += _gameRect.getTopLeft().getY() * _overlayScreen->getHeight();
00629     }
00630 
00631     _window->warpMouseInWindow(x, y);
00632 }
00633 
00634 void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
00635     if (_overlayVisible || !_frameBuffer)
00636         return;
00637 
00638     // Scale from screen coordinates to game coordinates
00639     point.x -= _gameRect.getTopLeft().getX() * _overlayScreen->getWidth();
00640     point.y -= _gameRect.getTopLeft().getY() * _overlayScreen->getHeight();
00641 
00642     point.x = (point.x * _frameBuffer->getWidth())  / (_gameRect.getWidth() * _overlayScreen->getWidth());
00643     point.y = (point.y * _frameBuffer->getHeight()) / (_gameRect.getHeight() * _overlayScreen->getHeight());
00644 
00645     // Make sure we only supply valid coordinates.
00646     point.x = CLIP<int16>(point.x, 0, _frameBuffer->getWidth() - 1);
00647     point.y = CLIP<int16>(point.y, 0, _frameBuffer->getHeight() - 1);
00648 }
00649 
00650 #if SDL_VERSION_ATLEAST(2, 0, 0)
00651 void OpenGLSdlGraphicsManager::deinitializeRenderer() {
00652     SDL_GL_DeleteContext(_glContext);
00653     _glContext = nullptr;
00654 }
00655 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
00656 
00657 bool OpenGLSdlGraphicsManager::saveScreenshot(const Common::String &file) const {
00658     // Largely based on the implementation from ScummVM
00659     uint width = _overlayScreen->getWidth();
00660     uint height = _overlayScreen->getHeight();
00661 
00662     uint linePaddingSize = width % 4;
00663     uint lineSize = width * 3 + linePaddingSize;
00664 
00665     Common::DumpFile out;
00666     if (!out.open(file)) {
00667         return false;
00668     }
00669 
00670     Common::Array<uint8> pixels;
00671     pixels.resize(lineSize * height);
00672 
00673     if (_frameBuffer) {
00674         _frameBuffer->detach();
00675     }
00676     glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front());
00677     if (_frameBuffer) {
00678         _frameBuffer->attach();
00679     }
00680 
00681 #ifdef USE_PNG
00682     Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
00683     Graphics::Surface data;
00684     data.init(width, height, lineSize, &pixels.front(), format);
00685     return Image::writePNG(out, data, true);
00686 #else
00687     for (uint y = height; y-- > 0;) {
00688         uint8 *line = &pixels.front() + y * lineSize;
00689 
00690         for (uint x = width; x > 0; --x, line += 3) {
00691             SWAP(line[0], line[2]);
00692         }
00693     }
00694 
00695     out.writeByte('B');
00696     out.writeByte('M');
00697     out.writeUint32LE(height * lineSize + 54);
00698     out.writeUint32LE(0);
00699     out.writeUint32LE(54);
00700     out.writeUint32LE(40);
00701     out.writeUint32LE(width);
00702     out.writeUint32LE(height);
00703     out.writeUint16LE(1);
00704     out.writeUint16LE(24);
00705     out.writeUint32LE(0);
00706     out.writeUint32LE(0);
00707     out.writeUint32LE(0);
00708     out.writeUint32LE(0);
00709     out.writeUint32LE(0);
00710     out.writeUint32LE(0);
00711     out.write(&pixels.front(), pixels.size());
00712 
00713     return true;
00714 #endif
00715 }
00716 
00717 #endif


Generated on Sat May 18 2019 05:01:11 for ResidualVM by doxygen 1.7.1
curved edge   curved edge