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


Generated on Sat Dec 7 2019 05:00:25 for ResidualVM by doxygen 1.7.1
curved edge   curved edge