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


Generated on Sat May 23 2020 05:00:28 for ResidualVM by doxygen 1.7.1
curved edge   curved edge