scummvm/backends/graphics/surfacesdl/surfacesdl-graphics.h
2021-12-09 22:00:32 +01:00

465 lines
14 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
#define BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
#include "backends/graphics/graphics.h"
#include "backends/graphics/sdl/sdl-graphics.h"
#include "graphics/pixelformat.h"
#include "graphics/scaler.h"
#include "graphics/scalerplugin.h"
#include "common/events.h"
#include "common/mutex.h"
#include "backends/events/sdl/sdl-events.h"
#include "backends/platform/sdl/sdl-sys.h"
#ifndef RELEASE_BUILD
// Define this to allow for focus rectangle debugging
#define USE_SDL_DEBUG_FOCUSRECT
#endif
enum {
GFX_SURFACESDL = 0
};
class AspectRatio {
int _kw, _kh;
public:
AspectRatio() { _kw = _kh = 0; }
AspectRatio(int w, int h);
bool isAuto() const { return (_kw | _kh) == 0; }
int kw() const { return _kw; }
int kh() const { return _kh; }
};
/**
* SDL graphics manager
*/
class SurfaceSdlGraphicsManager : public SdlGraphicsManager {
public:
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
virtual ~SurfaceSdlGraphicsManager();
virtual bool hasFeature(OSystem::Feature f) const override;
virtual void setFeatureState(OSystem::Feature f, bool enable) override;
virtual bool getFeatureState(OSystem::Feature f) const override;
virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
virtual int getDefaultGraphicsMode() const override;
virtual bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
virtual int getGraphicsMode() const override;
virtual uint getDefaultScaler() const override;
virtual uint getDefaultScaleFactor() const override;
virtual bool setScaler(uint mode, int factor) override;
virtual uint getScaler() const override;
virtual uint getScaleFactor() const override;
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const override { return _screenFormat; }
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
virtual const OSystem::GraphicsMode *getSupportedStretchModes() const override;
virtual int getDefaultStretchMode() const override;
virtual bool setStretchMode(int mode) override;
virtual int getStretchMode() const override;
#endif
virtual void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL) override;
virtual int getScreenChangeID() const override { return _screenChangeCount; }
virtual void beginGFXTransaction() override;
virtual OSystem::TransactionError endGFXTransaction() override;
virtual int16 getHeight() const override;
virtual int16 getWidth() const override;
protected:
// PaletteManager API
virtual void setPalette(const byte *colors, uint start, uint num) override;
virtual void grabPalette(byte *colors, uint start, uint num) const override;
/**
* Convert from the SDL pixel format to Graphics::PixelFormat
* @param in The SDL pixel format to convert
* @param out A pixel format to be written to
*/
Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat *in) const;
public:
virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
virtual Graphics::Surface *lockScreen() override;
virtual void unlockScreen() override;
virtual void fillScreen(uint32 col) override;
virtual void updateScreen() override;
virtual void setFocusRectangle(const Common::Rect& rect) override;
virtual void clearFocusRectangle() override;
virtual Graphics::PixelFormat getOverlayFormat() const override { return _overlayFormat; }
virtual void clearOverlay() override;
virtual void grabOverlay(Graphics::Surface &surface) const override;
virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
virtual int16 getOverlayHeight() const override { return _videoMode.overlayHeight; }
virtual int16 getOverlayWidth() const override { return _videoMode.overlayWidth; }
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override;
virtual void setCursorPalette(const byte *colors, uint start, uint num) override;
#ifdef USE_OSD
virtual void displayMessageOnOSD(const Common::U32String &msg) override;
virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
#endif
// Override from Common::EventObserver
virtual bool notifyEvent(const Common::Event &event) override;
// SdlGraphicsManager interface
virtual void notifyVideoExpose() override;
virtual void notifyResize(const int width, const int height) override;
protected:
#ifdef USE_OSD
/** Surface containing the OSD message */
SDL_Surface *_osdMessageSurface;
/** Transparency level of the OSD message */
uint8 _osdMessageAlpha;
/** When to start the fade out */
uint32 _osdMessageFadeStartTime;
/** Enum with OSD options */
enum {
kOSDFadeOutDelay = 2 * 1000, /** < Delay before the OSD is faded out (in milliseconds) */
kOSDFadeOutDuration = 500, /** < Duration of the OSD fade out (in milliseconds) */
kOSDInitialAlpha = 80 /** < Initial alpha level, in percent */
};
/** Screen rectangle where the OSD message is drawn */
SDL_Rect getOSDMessageRect() const;
/** Clear the currently displayed OSD message if any */
void removeOSDMessage();
/** Surface containing the OSD background activity icon */
SDL_Surface *_osdIconSurface;
/** Screen rectangle where the OSD background activity icon is drawn */
SDL_Rect getOSDIconRect() const;
void updateOSD();
void drawOSD();
#endif
virtual bool gameNeedsAspectRatioCorrection() const override {
return _videoMode.aspectRatioCorrection;
}
virtual int getGameRenderScale() const override {
return _videoMode.scaleFactor;
}
virtual void handleResizeImpl(const int width, const int height) override;
virtual int getGraphicsModeScale(int mode) const override;
virtual void setupHardwareSize();
void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &width, int &height) const;
#if SDL_VERSION_ATLEAST(2, 0, 0)
/* SDL2 features a different API for 2D graphics. We create a wrapper
* around this API to keep the code paths as close as possible. */
SDL_Renderer *_renderer;
SDL_Texture *_screenTexture;
void deinitializeRenderer();
void recreateScreenTexture();
virtual SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
virtual void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);
int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);
#endif
/** Unseen game screen */
SDL_Surface *_screen;
Graphics::PixelFormat _screenFormat;
Graphics::PixelFormat _cursorFormat;
#ifdef USE_RGB_COLOR
Common::List<Graphics::PixelFormat> _supportedFormats;
/**
* Update the list of supported pixel formats.
* This method is invoked by loadGFXMode().
*/
void detectSupportedFormats();
#endif
/** Temporary screen (for scalers) */
SDL_Surface *_tmpscreen;
/** Temporary screen (for scalers) */
SDL_Surface *_tmpscreen2;
SDL_Surface *_overlayscreen;
bool _useOldSrc;
Graphics::PixelFormat _overlayFormat;
enum {
kTransactionNone = 0,
kTransactionActive = 1,
kTransactionRollback = 2
};
struct TransactionDetails {
bool sizeChanged;
bool needHotswap;
bool needUpdatescreen;
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool needTextureUpdate;
bool needDisplayResize;
#endif
#ifdef USE_RGB_COLOR
bool formatChanged;
#endif
TransactionDetails() {
sizeChanged = false;
needHotswap = false;
needUpdatescreen = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
needTextureUpdate = false;
needDisplayResize = false;
#endif
#ifdef USE_RGB_COLOR
formatChanged = false;
#endif
}
};
TransactionDetails _transactionDetails;
struct VideoState {
bool setup;
bool fullscreen;
bool aspectRatioCorrection;
AspectRatio desiredAspectRatio;
bool filtering;
#if SDL_VERSION_ATLEAST(2, 0, 0)
int stretchMode;
#endif
uint scalerIndex;
int scaleFactor;
int screenWidth, screenHeight;
int overlayWidth, overlayHeight;
int hardwareWidth, hardwareHeight;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat format;
#endif
VideoState() {
setup = false;
fullscreen = false;
aspectRatioCorrection = false;
// desiredAspectRatio set to (0, 0) by AspectRatio constructor
filtering = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
stretchMode = 0;
#endif
scalerIndex = 0;
scaleFactor = 0;
screenWidth = 0;
screenHeight = 0;
overlayWidth = 0;
overlayHeight = 0;
hardwareWidth = 0;
hardwareHeight = 0;
#ifdef USE_RGB_COLOR
// format set to 0 values by Graphics::PixelFormat constructor
#endif
}
};
VideoState _videoMode, _oldVideoMode;
#if defined(WIN32) && !SDL_VERSION_ATLEAST(2, 0, 0)
/**
* Original BPP to restore the video mode on unload.
*
* This is required to make listing video modes for the OpenGL output work
* on Windows 8+. On these systems OpenGL modes are only available for
* 32bit formats. However, we setup a 16bit format and thus mode listings
* for OpenGL will return an empty list afterwards.
*
* In theory we might require this behavior on non-Win32 platforms too.
* However, SDL sometimes gives us invalid pixel formats for X11 outputs
* causing crashes when trying to setup the original pixel format.
* See bug #7038 "IRIX: X BadMatch when trying to start any 640x480 game".
*/
uint8 _originalBitsPerPixel;
#endif
int _transactionMode;
// Indicates whether it is needed to free _hwSurface in destructor
bool _displayDisabled;
const PluginList &_scalerPlugins;
ScalerPluginObject *_scalerPlugin;
Scaler *_scaler;
uint _maxExtraPixels;
uint _extraPixels;
bool _screenIsLocked;
Graphics::Surface _framebuffer;
int _screenChangeCount;
enum {
NUM_DIRTY_RECT = 100,
MAX_SCALING = 3
};
// Dirty rect management
SDL_Rect _dirtyRectList[NUM_DIRTY_RECT];
int _numDirtyRects;
struct MousePos {
// The size and hotspot of the original cursor image.
int16 w, h;
int16 hotX, hotY;
// The size and hotspot of the pre-scaled cursor image, in real
// coordinates.
int16 rW, rH;
int16 rHotX, rHotY;
// The size and hotspot of the pre-scaled cursor image, in game
// coordinates.
int16 vW, vH;
int16 vHotX, vHotY;
MousePos() : w(0), h(0), hotX(0), hotY(0),
rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0),
vHotX(0), vHotY(0)
{ }
};
byte *_mouseData;
SDL_Rect _mouseBackup;
MousePos _mouseCurState;
#ifdef USE_RGB_COLOR
uint32 _mouseKeyColor;
#else
byte _mouseKeyColor;
#endif
bool _cursorDontScale;
bool _cursorPaletteDisabled;
SDL_Surface *_mouseOrigSurface;
SDL_Surface *_mouseSurface;
enum {
kMouseColorKey = 1
};
// Shake mode
// This is always set to 0 when building with SDL2.
int _currentShakeXOffset;
int _currentShakeYOffset;
// Palette data
SDL_Color *_currentPalette;
uint _paletteDirtyStart, _paletteDirtyEnd;
// Cursor palette data
SDL_Color *_cursorPalette;
/**
* Mutex which prevents multiple threads from interfering with each other
* when accessing the screen.
*/
Common::Mutex _graphicsMutex;
#ifdef USE_SDL_DEBUG_FOCUSRECT
bool _enableFocusRectDebugCode;
bool _enableFocusRect;
Common::Rect _focusRect;
#endif
virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false);
virtual void drawMouse();
virtual void undrawMouse();
virtual void blitCursor();
virtual void internUpdateScreen();
virtual bool loadGFXMode();
virtual void unloadGFXMode();
virtual bool hotswapGFXMode();
virtual void setAspectRatioCorrection(bool enable);
void setFilteringMode(bool enable);
virtual bool saveScreenshot(const Common::String &filename) const override;
virtual void setGraphicsModeIntern();
private:
void setFullscreenMode(bool enable);
void handleScalerHotkeys(uint mode, int factor);
/**
* Converts the given point from the overlay's coordinate space to the
* game's coordinate space.
*/
Common::Point convertOverlayToGame(const int x, const int y) const {
if (getOverlayWidth() == 0 || getOverlayHeight() == 0) {
error("convertOverlayToGame called without a valid overlay");
}
return Common::Point(x * getWidth() / getOverlayWidth(),
y * getHeight() / getOverlayHeight());
}
/**
* Converts the given point from the game's coordinate space to the
* overlay's coordinate space.
*/
Common::Point convertGameToOverlay(const int x, const int y) const {
if (getWidth() == 0 || getHeight() == 0) {
error("convertGameToOverlay called without a valid overlay");
}
return Common::Point(x * getOverlayWidth() / getWidth(),
y * getOverlayHeight() / getHeight());
}
/**
* Special case for scalers that use the useOldSrc feature (currently
* only the Edge scalers). The variable is checked after closing the
* overlay, so that the creation of a new output buffer for the scaler
* can be triggered.
*/
bool _needRestoreAfterOverlay;
};
#endif