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

surfacesdl-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/surfacesdl/surfacesdl-graphics.h"
00028 #include "backends/events/sdl/sdl-events.h"
00029 #include "common/config-manager.h"
00030 #include "engines/engine.h"
00031 #include "graphics/pixelbuffer.h"
00032 #include "graphics/surface.h"
00033 
00034 SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window, const Capabilities &capabilities)
00035     :
00036     ResVmSdlGraphicsManager(sdlEventSource, window, capabilities),
00037 #if SDL_VERSION_ATLEAST(2, 0, 0)
00038     _renderer(nullptr), _screenTexture(nullptr),
00039 #endif
00040     _screen(0),
00041     _subScreen(0),
00042     _overlayscreen(0),
00043     _overlayDirty(true),
00044     _gameRect()
00045     {
00046         _sideSurfaces[0] = _sideSurfaces[1] = nullptr;
00047 }
00048 
00049 SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
00050     closeOverlay();
00051 
00052     if (_subScreen) {
00053         SDL_FreeSurface(_subScreen);
00054         _subScreen = nullptr;
00055     }
00056 
00057 #if SDL_VERSION_ATLEAST(2, 0, 0)
00058     deinitializeRenderer();
00059 #endif
00060 }
00061 
00062 bool SurfaceSdlGraphicsManager::hasFeature(OSystem::Feature f) const {
00063     return
00064 #if SDL_VERSION_ATLEAST(2, 0, 0)
00065         (f == OSystem::kFeatureFullscreenToggleKeepsContext) ||
00066 #endif
00067         (f == OSystem::kFeatureFullscreenMode);
00068 }
00069 
00070 void SurfaceSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
00071     switch (f) {
00072         case OSystem::kFeatureFullscreenMode:
00073             if (_fullscreen != enable) {
00074                 _fullscreen = enable;
00075                 createOrUpdateScreen();
00076             }
00077             break;
00078         default:
00079             ResVmSdlGraphicsManager::setFeatureState(f, enable);
00080             break;
00081     }
00082 }
00083 
00084 void SurfaceSdlGraphicsManager::setupScreen(uint gameWidth, uint gameHeight, bool fullscreen, bool accel3d) {
00085     assert(!accel3d);
00086 
00087     if (_subScreen) {
00088         SDL_FreeSurface(_subScreen);
00089         _subScreen = nullptr;
00090     }
00091 
00092 #if SDL_VERSION_ATLEAST(2, 0, 0)
00093     deinitializeRenderer();
00094 #endif
00095 
00096     _engineRequestedWidth = gameWidth;
00097     _engineRequestedHeight = gameHeight;
00098     _fullscreen = fullscreen;
00099     _lockAspectRatio = ConfMan.getBool("aspect_ratio");
00100 
00101     createOrUpdateScreen();
00102 
00103     SDL_PixelFormat *f = _screen->format;
00104     _subScreen = SDL_CreateRGBSurface(SDL_SWSURFACE, gameWidth, gameHeight, f->BitsPerPixel, f->Rmask, f->Gmask, f->Bmask, f->Amask);
00105 
00106 #if SDL_VERSION_ATLEAST(2, 0, 0)
00107     SDL_SetSurfaceBlendMode(_subScreen, SDL_BLENDMODE_NONE);
00108 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
00109 }
00110 
00111 void SurfaceSdlGraphicsManager::createOrUpdateScreen() {
00112     closeOverlay();
00113 
00114     // Choose the effective window size or fullscreen mode
00115     uint effectiveWidth;
00116     uint effectiveHeight;
00117     if (_fullscreen && _lockAspectRatio) {
00118         Common::Rect fullscreenResolution = getPreferredFullscreenResolution();
00119         effectiveWidth = fullscreenResolution.width();
00120         effectiveHeight = fullscreenResolution.height();
00121     } else {
00122         effectiveWidth = _engineRequestedWidth;
00123         effectiveHeight = _engineRequestedHeight;
00124     }
00125 
00126     // The game is centered inside the effective screen
00127     _gameRect = Math::Rect2d(
00128             Math::Vector2d((effectiveWidth - _engineRequestedWidth) / 2, (effectiveHeight - _engineRequestedHeight) / 2),
00129             Math::Vector2d((effectiveWidth + _engineRequestedWidth) / 2, (effectiveHeight + _engineRequestedHeight) / 2)
00130     );
00131 
00132     uint32 sdlflags = SDL_SWSURFACE;
00133     if (_fullscreen)
00134         sdlflags |= SDL_FULLSCREEN;
00135 
00136     _screen = SDL_SetVideoMode(effectiveWidth, effectiveHeight, ConfMan.getInt("bpp"), sdlflags);
00137     if (!_screen) {
00138         warning("SDL_SetVideoMode failed: %s", SDL_GetError());
00139         g_system->quit();
00140     }
00141 
00142     SDL_PixelFormat *f = _screen->format;
00143     _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, effectiveWidth, effectiveHeight, f->BitsPerPixel,
00144                                           f->Rmask, f->Gmask, f->Bmask, f->Amask);
00145 
00146     _overlayFormat = Graphics::PixelFormat(f->BytesPerPixel, 8 - f->Rloss, 8 - f->Gloss, 8 - f->Bloss, 0,
00147                                            f->Rshift, f->Gshift, f->Bshift, f->Ashift);
00148 
00149     if (!_overlayscreen) {
00150         warning("SDL_RGBSurface failed: %s", SDL_GetError());
00151         g_system->quit();
00152     }
00153 
00154 #if SDL_VERSION_ATLEAST(2, 0, 0)
00155     SDL_SetSurfaceBlendMode(_overlayscreen, SDL_BLENDMODE_NONE);
00156 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
00157 
00158     _screenFormat = _overlayFormat;
00159 
00160     _screenChangeCount++;
00161 
00162     _eventSource->resetKeyboardEmulation(_gameRect.getWidth() - 1, _gameRect.getHeight() - 1);
00163 }
00164 
00165 Graphics::PixelBuffer SurfaceSdlGraphicsManager::getScreenPixelBuffer() {
00166     return Graphics::PixelBuffer(_screenFormat, (byte *)_subScreen->pixels);
00167 }
00168 
00169 void SurfaceSdlGraphicsManager::drawSideTextures() {
00170     if (_fullscreen && _lockAspectRatio) {
00171         if (_sideSurfaces[0]) {
00172             SDL_Rect dstrect;
00173             dstrect.x = _gameRect.getTopLeft().getX() - _sideSurfaces[0]->w;
00174             dstrect.y = _gameRect.getTopLeft().getY();
00175             dstrect.w = _sideSurfaces[0]->w;
00176             dstrect.h = _sideSurfaces[0]->h;
00177             SDL_BlitSurface(_sideSurfaces[0], NULL, _screen, &dstrect);
00178         }
00179         if (_sideSurfaces[1]) {
00180             SDL_Rect dstrect;
00181             dstrect.x = _gameRect.getTopRight().getX();
00182             dstrect.y = _gameRect.getTopLeft().getY();
00183             dstrect.w = _sideSurfaces[1]->w;
00184             dstrect.h = _sideSurfaces[1]->h;
00185             SDL_BlitSurface(_sideSurfaces[1], NULL, _screen, &dstrect);
00186         }
00187     }
00188 }
00189 
00190 void SurfaceSdlGraphicsManager::drawOverlay() {
00191     if (!_overlayscreen)
00192         return;
00193 
00194     SDL_BlitSurface(_overlayscreen, NULL, _screen, NULL);
00195 }
00196 
00197 void SurfaceSdlGraphicsManager::updateScreen() {
00198     SDL_Rect dstrect;
00199     dstrect.x = _gameRect.getTopLeft().getX();
00200     dstrect.y = _gameRect.getTopLeft().getY();
00201     dstrect.w = _gameRect.getWidth();
00202     dstrect.h = _gameRect.getHeight();
00203     SDL_BlitSurface(_subScreen, NULL, _screen, &dstrect);
00204 
00205     if (_overlayVisible) {
00206         drawOverlay();
00207     }
00208     drawSideTextures();
00209 
00210 #if SDL_VERSION_ATLEAST(2, 0, 0)
00211     SDL_UpdateTexture(_screenTexture, nullptr, _screen->pixels, _screen->pitch);
00212 
00213     SDL_RenderClear(_renderer);
00214     SDL_RenderCopy(_renderer, _screenTexture, nullptr, nullptr);
00215     SDL_RenderPresent(_renderer);
00216 #else
00217     SDL_Flip(_screen);
00218 #endif
00219 }
00220 
00221 int16 SurfaceSdlGraphicsManager::getHeight() const {
00222     // ResidualVM specific
00223     return _subScreen->h;
00224 }
00225 
00226 int16 SurfaceSdlGraphicsManager::getWidth() const {
00227     // ResidualVM specific
00228     return _subScreen->w;
00229 }
00230 
00231 #pragma mark -
00232 #pragma mark --- Overlays ---
00233 #pragma mark -
00234 
00235 void SurfaceSdlGraphicsManager::clearOverlay() {
00236     if (!_overlayscreen)
00237         return;
00238 
00239     if (!_overlayVisible)
00240         return;
00241 
00242     SDL_BlitSurface(_screen, NULL, _overlayscreen, NULL);
00243 
00244     _overlayDirty = true;
00245 }
00246 
00247 void SurfaceSdlGraphicsManager::suggestSideTextures(Graphics::Surface *left, Graphics::Surface *right) {
00248     delete _sideSurfaces[0];
00249     _sideSurfaces[0] = nullptr;
00250     delete _sideSurfaces[1];
00251     _sideSurfaces[1] = nullptr;
00252     if (left) {
00253         _sideSurfaces[0] = SDL_CreateRGBSurface(SDL_SWSURFACE, left->w, left->h, 32, 0xff << left->format.rShift, 0xff << left->format.gShift, 0xff << left->format.bShift, 0xff << left->format.aShift);
00254         memcpy(_sideSurfaces[0]->pixels, left->getPixels(), left->w * left->h * 4);
00255     }
00256     if (right) {
00257         _sideSurfaces[1] = SDL_CreateRGBSurface(SDL_SWSURFACE, right->w, right->h, 32, 0xff << right->format.rShift, 0xff << right->format.gShift, 0xff << right->format.bShift, 0xff << right->format.aShift);
00258         memcpy(_sideSurfaces[1]->pixels, right->getPixels(), right->w * right->h * 4);
00259     }
00260 }
00261 
00262 void SurfaceSdlGraphicsManager::showOverlay() {
00263     if (_overlayVisible)
00264         return;
00265 
00266     _overlayVisible = true;
00267 
00268     clearOverlay();
00269 
00270     _eventSource->resetKeyboardEmulation(getOverlayWidth() - 1, getOverlayHeight() - 1);
00271 }
00272 
00273 void SurfaceSdlGraphicsManager::hideOverlay() {
00274     if (!_overlayVisible)
00275         return;
00276 
00277     _overlayVisible = false;
00278 
00279     clearOverlay();
00280 
00281     _eventSource->resetKeyboardEmulation(_gameRect.getWidth() - 1, _gameRect.getHeight() - 1);
00282 }
00283 
00284 void SurfaceSdlGraphicsManager::grabOverlay(void *buf, int pitch) const {
00285     if (_overlayscreen == NULL)
00286         return;
00287 
00288     if (SDL_LockSurface(_overlayscreen) == -1)
00289         error("SDL_LockSurface failed: %s", SDL_GetError());
00290 
00291     byte *src = (byte *)_overlayscreen->pixels;
00292     byte *dst = (byte *)buf;
00293     int h = _overlayscreen->h;
00294     do {
00295         memcpy(dst, src, _overlayscreen->w * _overlayscreen->format->BytesPerPixel);
00296         src += _overlayscreen->pitch;
00297         dst += pitch;
00298     } while (--h);
00299 
00300     SDL_UnlockSurface(_overlayscreen);
00301 }
00302 
00303 void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
00304     if (_overlayscreen == NULL)
00305         return;
00306 
00307     const byte *src = (const byte *)buf;
00308 
00309     // Clip the coordinates
00310     if (x < 0) {
00311         w += x;
00312         src -= x * _overlayscreen->format->BytesPerPixel;
00313         x = 0;
00314     }
00315 
00316     if (y < 0) {
00317         h += y;
00318         src -= y * pitch;
00319         y = 0;
00320     }
00321 
00322     if (w > _overlayscreen->w - x) {
00323         w = _overlayscreen->w - x;
00324     }
00325 
00326     if (h > _overlayscreen->h - y) {
00327         h = _overlayscreen->h - y;
00328     }
00329 
00330     if (w <= 0 || h <= 0)
00331         return;
00332 
00333     if (SDL_LockSurface(_overlayscreen) == -1)
00334         error("SDL_LockSurface failed: %s", SDL_GetError());
00335 
00336     byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * _overlayscreen->format->BytesPerPixel;
00337     do {
00338         memcpy(dst, src, w * _overlayscreen->format->BytesPerPixel);
00339         dst += _overlayscreen->pitch;
00340         src += pitch;
00341     } while (--h);
00342 
00343     SDL_UnlockSurface(_overlayscreen);
00344 }
00345 
00346 void SurfaceSdlGraphicsManager::closeOverlay() {
00347     SDL_FreeSurface(_sideSurfaces[0]);
00348     SDL_FreeSurface(_sideSurfaces[1]);
00349     _sideSurfaces[0] = _sideSurfaces[1] = nullptr;
00350     if (_overlayscreen) {
00351         SDL_FreeSurface(_overlayscreen);
00352         _overlayscreen = nullptr;
00353     }
00354     if (_screen) {
00355 #if SDL_VERSION_ATLEAST(2, 0, 0)
00356         SDL_FreeSurface(_screen);
00357 #endif
00358         _screen = nullptr;
00359     }
00360 }
00361 
00362 void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
00363     //ResidualVM specific
00364     // Scale from game coordinates to screen coordinates
00365     x = (x * _gameRect.getWidth()) / _subScreen->w;
00366     y = (y * _gameRect.getHeight()) / _subScreen->h;
00367 
00368     x += _gameRect.getTopLeft().getX();
00369     y += _gameRect.getTopLeft().getY();
00370 
00371     _window->warpMouseInWindow(x, y);
00372 }
00373 
00374 void SurfaceSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
00375     if (_overlayVisible)
00376         return;
00377 
00378     // Scale from screen coordinates to game coordinates
00379     point.x -= _gameRect.getTopLeft().getX();
00380     point.y -= _gameRect.getTopLeft().getY();
00381 
00382     point.x = (point.x * _subScreen->w) / _gameRect.getWidth();
00383     point.y = (point.y * _subScreen->h) / _gameRect.getHeight();
00384 
00385     // Make sure we only supply valid coordinates.
00386     point.x = CLIP<int16>(point.x, 0, _subScreen->w - 1);
00387     point.y = CLIP<int16>(point.y, 0, _subScreen->h - 1);
00388 }
00389 
00390 #if SDL_VERSION_ATLEAST(2, 0, 0)
00391 void SurfaceSdlGraphicsManager::deinitializeRenderer() {
00392     SDL_DestroyTexture(_screenTexture);
00393     _screenTexture = nullptr;
00394 
00395     SDL_DestroyRenderer(_renderer);
00396     _renderer = nullptr;
00397 }
00398 
00399 SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) {
00400     deinitializeRenderer();
00401 
00402     uint32 createWindowFlags = 0;
00403     Uint32 rmask, gmask, bmask, amask, format;
00404     int depth;
00405 #ifdef USE_SDL_RESIZABLE_WINDOW
00406     createWindowFlags |= SDL_WINDOW_RESIZABLE;
00407 #endif
00408     if ((flags & SDL_FULLSCREEN) != 0) {
00409         // On Linux/X11, when toggling to fullscreen, the window manager saves
00410         // the window size to be able to restore it when going back to windowed mode.
00411         // If the user configured ResidualVM to start in fullscreen mode, we first
00412         // create a window and then toggle it to fullscreen to give the window manager
00413         // a chance to save the window size. That way if the user switches back
00414         // to windowed mode, the window manager has a window size to apply instead
00415         // of leaving the window at the fullscreen resolution size.
00416         if (!_window->getSDLWindow()) {
00417             _window->createOrUpdateWindow(width, height, createWindowFlags);
00418         }
00419 
00420         createWindowFlags |= SDL_WINDOW_FULLSCREEN;
00421     }
00422 
00423     if (!_window->createOrUpdateWindow(width, height, createWindowFlags)) {
00424         return nullptr;
00425     }
00426 
00427     _renderer = SDL_CreateRenderer(_window->getSDLWindow(), -1, 0);
00428     if (!_renderer) {
00429         deinitializeRenderer();
00430         return nullptr;
00431     }
00432 
00433     SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_NONE);
00434 
00435     switch (bpp) {
00436     case 0:
00437     case 32:
00438         format = SDL_PIXELFORMAT_ARGB8888;
00439         break;
00440     case 16:
00441         format = SDL_PIXELFORMAT_RGB565;
00442         break;
00443     default:
00444         warning("Unsupported bpp value: %i", bpp);
00445         deinitializeRenderer();
00446         return nullptr;
00447     }
00448 
00449     _screenTexture = SDL_CreateTexture(_renderer, format, SDL_TEXTUREACCESS_STREAMING, width, height);
00450     if (!_screenTexture) {
00451         deinitializeRenderer();
00452         return nullptr;
00453     }
00454 
00455     if (!SDL_PixelFormatEnumToMasks(format, &depth, &rmask, &gmask, &bmask, &amask)) {
00456         deinitializeRenderer();
00457         return nullptr;
00458     }
00459 
00460     SDL_Surface *screen = SDL_CreateRGBSurface(0, width, height, depth, rmask, gmask, bmask, amask);
00461     if (!screen) {
00462         deinitializeRenderer();
00463         return nullptr;
00464     } else {
00465         SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
00466         return screen;
00467     }
00468 }
00469 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
00470 
00471 #endif


Generated on Sat Feb 16 2019 05:01:08 for ResidualVM by doxygen 1.7.1
curved edge   curved edge