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


Generated on Sat Mar 16 2019 05:01:55 for ResidualVM by doxygen 1.7.1
curved edge   curved edge