1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
24 #define BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
25 
26 #include "backends/graphics/graphics.h"
27 #include "backends/graphics/sdl/sdl-graphics.h"
28 #include "graphics/pixelformat.h"
29 #include "graphics/scaler.h"
30 #include "common/events.h"
31 #include "common/system.h"
32 
33 #include "backends/events/sdl/sdl-events.h"
34 
35 #include "backends/platform/sdl/sdl-sys.h"
36 
37 #ifndef RELEASE_BUILD
38 // Define this to allow for focus rectangle debugging
39 #define USE_SDL_DEBUG_FOCUSRECT
40 #endif
41 
42 enum {
43 	GFX_NORMAL = 0,
44 	GFX_DOUBLESIZE = 1,
45 	GFX_TRIPLESIZE = 2,
46 	GFX_2XSAI = 3,
47 	GFX_SUPER2XSAI = 4,
48 	GFX_SUPEREAGLE = 5,
49 	GFX_ADVMAME2X = 6,
50 	GFX_ADVMAME3X = 7,
51 	GFX_HQ2X = 8,
52 	GFX_HQ3X = 9,
53 	GFX_TV2X = 10,
54 	GFX_DOTMATRIX = 11
55 };
56 
57 
58 class AspectRatio {
59 	int _kw, _kh;
60 public:
AspectRatio()61 	AspectRatio() { _kw = _kh = 0; }
62 	AspectRatio(int w, int h);
63 
isAuto()64 	bool isAuto() const { return (_kw | _kh) == 0; }
65 
kw()66 	int kw() const { return _kw; }
kh()67 	int kh() const { return _kh; }
68 };
69 
70 /**
71  * SDL graphics manager
72  */
73 class SurfaceSdlGraphicsManager : public SdlGraphicsManager {
74 public:
75 	SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
76 	virtual ~SurfaceSdlGraphicsManager();
77 
78 	virtual void activateManager() override;
79 	virtual void deactivateManager() override;
80 
81 	virtual bool hasFeature(OSystem::Feature f) const override;
82 	virtual void setFeatureState(OSystem::Feature f, bool enable) override;
83 	virtual bool getFeatureState(OSystem::Feature f) const override;
84 
85 	virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
86 	virtual int getDefaultGraphicsMode() const override;
87 	virtual bool setGraphicsMode(int mode) override;
88 	virtual int getGraphicsMode() const override;
89 	virtual void resetGraphicsScale() override;
90 #ifdef USE_RGB_COLOR
getScreenFormat()91 	virtual Graphics::PixelFormat getScreenFormat() const override { return _screenFormat; }
92 	virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
93 #endif
94 	virtual const OSystem::GraphicsMode *getSupportedShaders() const override;
95 	virtual int getShader() const override;
96 	virtual bool setShader(int id) override;
97 #if SDL_VERSION_ATLEAST(2, 0, 0)
98 	virtual const OSystem::GraphicsMode *getSupportedStretchModes() const override;
99 	virtual int getDefaultStretchMode() const override;
100 	virtual bool setStretchMode(int mode) override;
101 	virtual int getStretchMode() const override;
102 #endif
103 	virtual void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL) override;
getScreenChangeID()104 	virtual int getScreenChangeID() const override { return _screenChangeCount; }
105 
106 	virtual void beginGFXTransaction() override;
107 	virtual OSystem::TransactionError endGFXTransaction() override;
108 
109 	virtual int16 getHeight() const override;
110 	virtual int16 getWidth() const override;
111 
112 protected:
113 	// PaletteManager API
114 	virtual void setPalette(const byte *colors, uint start, uint num) override;
115 	virtual void grabPalette(byte *colors, uint start, uint num) const override;
116 
117 	/**
118 	 * Convert from the SDL pixel format to Graphics::PixelFormat
119 	 * @param in    The SDL pixel format to convert
120 	 * @param out   A pixel format to be written to
121 	 */
122 	Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat *in) const;
123 public:
124 	virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
125 	virtual Graphics::Surface *lockScreen() override;
126 	virtual void unlockScreen() override;
127 	virtual void fillScreen(uint32 col) override;
128 	virtual void updateScreen() override;
129 	virtual void setFocusRectangle(const Common::Rect& rect) override;
130 	virtual void clearFocusRectangle() override;
131 
getOverlayFormat()132 	virtual Graphics::PixelFormat getOverlayFormat() const override { return _overlayFormat; }
133 	virtual void clearOverlay() override;
134 	virtual void grabOverlay(void *buf, int pitch) const override;
135 	virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
getOverlayHeight()136 	virtual int16 getOverlayHeight() const override { return _videoMode.overlayHeight; }
getOverlayWidth()137 	virtual int16 getOverlayWidth() const override { return _videoMode.overlayWidth; }
138 
139 	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;
140 	virtual void setCursorPalette(const byte *colors, uint start, uint num) override;
141 
142 #ifdef USE_OSD
143 	virtual void displayMessageOnOSD(const char *msg) override;
144 	virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
145 #endif
146 
147 	// Override from Common::EventObserver
148 	virtual bool notifyEvent(const Common::Event &event) override;
149 
150 	// SdlGraphicsManager interface
151 	virtual void notifyVideoExpose() override;
152 	virtual void notifyResize(const int width, const int height) override;
153 
154 protected:
155 #ifdef USE_OSD
156 	/** Surface containing the OSD message */
157 	SDL_Surface *_osdMessageSurface;
158 	/** Transparency level of the OSD message */
159 	uint8 _osdMessageAlpha;
160 	/** When to start the fade out */
161 	uint32 _osdMessageFadeStartTime;
162 	/** Enum with OSD options */
163 	enum {
164 		kOSDFadeOutDelay = 2 * 1000,	/** < Delay before the OSD is faded out (in milliseconds) */
165 		kOSDFadeOutDuration = 500,		/** < Duration of the OSD fade out (in milliseconds) */
166 		kOSDInitialAlpha = 80			/** < Initial alpha level, in percent */
167 	};
168 	/** Screen rectangle where the OSD message is drawn */
169 	SDL_Rect getOSDMessageRect() const;
170 	/** Clear the currently displayed OSD message if any */
171 	void removeOSDMessage();
172 	/** Surface containing the OSD background activity icon */
173 	SDL_Surface *_osdIconSurface;
174 	/** Screen rectangle where the OSD background activity icon is drawn */
175 	SDL_Rect getOSDIconRect() const;
176 
177 	void updateOSD();
178 	void drawOSD();
179 #endif
180 
gameNeedsAspectRatioCorrection()181 	virtual bool gameNeedsAspectRatioCorrection() const override {
182 		return _videoMode.aspectRatioCorrection;
183 	}
getGameRenderScale()184 	virtual int getGameRenderScale() const override {
185 		return _videoMode.scaleFactor;
186 	}
187 
188 	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) override;
189 
190 	virtual int getGraphicsModeScale(int mode) const override;
191 	virtual ScalerProc *getGraphicsScalerProc(int mode) const;
192 
193 #if SDL_VERSION_ATLEAST(2, 0, 0)
194 	/* SDL2 features a different API for 2D graphics. We create a wrapper
195 	 * around this API to keep the code paths as close as possible. */
196 	SDL_Renderer *_renderer;
197 	SDL_Texture *_screenTexture;
198 	void deinitializeRenderer();
199 	void recreateScreenTexture();
200 
201 	virtual SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
202 	virtual void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
203 #endif
204 
205 	/** Unseen game screen */
206 	SDL_Surface *_screen;
207 	Graphics::PixelFormat _screenFormat;
208 	Graphics::PixelFormat _cursorFormat;
209 #ifdef USE_RGB_COLOR
210 	Common::List<Graphics::PixelFormat> _supportedFormats;
211 
212 	/**
213 	 * Update the list of supported pixel formats.
214 	 * This method is invoked by loadGFXMode().
215 	 */
216 	void detectSupportedFormats();
217 #endif
218 
219 	/** Temporary screen (for scalers) */
220 	SDL_Surface *_tmpscreen;
221 	/** Temporary screen (for scalers) */
222 	SDL_Surface *_tmpscreen2;
223 
224 	SDL_Surface *_overlayscreen;
225 	Graphics::PixelFormat _overlayFormat;
226 
227 	enum {
228 		kTransactionNone = 0,
229 		kTransactionActive = 1,
230 		kTransactionRollback = 2
231 	};
232 
233 	struct TransactionDetails {
234 		bool sizeChanged;
235 		bool needHotswap;
236 		bool needUpdatescreen;
237 #if SDL_VERSION_ATLEAST(2, 0, 0)
238 		bool needTextureUpdate;
239 		bool needDisplayResize;
240 #endif
241 #ifdef USE_RGB_COLOR
242 		bool formatChanged;
243 #endif
244 
TransactionDetailsTransactionDetails245 		TransactionDetails() {
246 			sizeChanged = false;
247 			needHotswap = false;
248 			needUpdatescreen = false;
249 
250 #if SDL_VERSION_ATLEAST(2, 0, 0)
251 			needTextureUpdate = false;
252 			needDisplayResize = false;
253 #endif
254 #ifdef USE_RGB_COLOR
255 			formatChanged = false;
256 #endif
257 		}
258 	};
259 	TransactionDetails _transactionDetails;
260 
261 	struct VideoState {
262 		bool setup;
263 
264 		bool fullscreen;
265 		bool aspectRatioCorrection;
266 		AspectRatio desiredAspectRatio;
267 		bool filtering;
268 
269 #if SDL_VERSION_ATLEAST(2, 0, 0)
270 		int stretchMode;
271 #endif
272 
273 		int mode;
274 		int scaleFactor;
275 
276 		int screenWidth, screenHeight;
277 		int overlayWidth, overlayHeight;
278 		int hardwareWidth, hardwareHeight;
279 #ifdef USE_RGB_COLOR
280 		Graphics::PixelFormat format;
281 #endif
282 
VideoStateVideoState283 		VideoState() {
284 			setup = false;
285 			fullscreen = false;
286 			aspectRatioCorrection = false;
287 			// desiredAspectRatio set to (0, 0) by AspectRatio constructor
288 			filtering = false;
289 
290 #if SDL_VERSION_ATLEAST(2, 0, 0)
291 			stretchMode = 0;
292 #endif
293 
294 			mode = 0;
295 			scaleFactor = 0;
296 
297 			screenWidth = 0;
298 			screenHeight = 0;
299 			overlayWidth = 0;
300 			overlayHeight = 0;
301 			hardwareWidth = 0;
302 			hardwareHeight = 0;
303 #ifdef USE_RGB_COLOR
304 			// format set to 0 values by Graphics::PixelFormat constructor
305 #endif
306 		}
307 	};
308 	VideoState _videoMode, _oldVideoMode;
309 
310 #if defined(WIN32) && !SDL_VERSION_ATLEAST(2, 0, 0)
311 	/**
312 	 * Original BPP to restore the video mode on unload.
313 	 *
314 	 * This is required to make listing video modes for the OpenGL output work
315 	 * on Windows 8+. On these systems OpenGL modes are only available for
316 	 * 32bit formats. However, we setup a 16bit format and thus mode listings
317 	 * for OpenGL will return an empty list afterwards.
318 	 *
319 	 * In theory we might require this behavior on non-Win32 platforms too.
320 	 * However, SDL sometimes gives us invalid pixel formats for X11 outputs
321 	 * causing crashes when trying to setup the original pixel format.
322 	 * See bug #7038 "IRIX: X BadMatch when trying to start any 640x480 game".
323 	 */
324 	uint8 _originalBitsPerPixel;
325 #endif
326 
327 	ScalerProc *_scalerProc;
328 	int _scalerType;
329 	int _transactionMode;
330 
331 	// Indicates whether it is needed to free _hwSurface in destructor
332 	bool _displayDisabled;
333 
334 	bool _screenIsLocked;
335 	Graphics::Surface _framebuffer;
336 
337 	int _screenChangeCount;
338 
339 	int _currentShader;
340 	int _numShaders;
341 
342 	enum {
343 		NUM_DIRTY_RECT = 100,
344 		MAX_SCALING = 3
345 	};
346 
347 	// Dirty rect management
348 	SDL_Rect _dirtyRectList[NUM_DIRTY_RECT];
349 	int _numDirtyRects;
350 
351 	struct MousePos {
352 		// The size and hotspot of the original cursor image.
353 		int16 w, h;
354 		int16 hotX, hotY;
355 
356 		// The size and hotspot of the pre-scaled cursor image, in real
357 		// coordinates.
358 		int16 rW, rH;
359 		int16 rHotX, rHotY;
360 
361 		// The size and hotspot of the pre-scaled cursor image, in game
362 		// coordinates.
363 		int16 vW, vH;
364 		int16 vHotX, vHotY;
365 
MousePosMousePos366 		MousePos() : w(0), h(0), hotX(0), hotY(0),
367 					rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0),
368 					vHotX(0), vHotY(0)
369 			{ }
370 	};
371 
372 	byte *_mouseData;
373 	SDL_Rect _mouseBackup;
374 	MousePos _mouseCurState;
375 #ifdef USE_RGB_COLOR
376 	uint32 _mouseKeyColor;
377 #else
378 	byte _mouseKeyColor;
379 #endif
380 	bool _cursorDontScale;
381 	bool _cursorPaletteDisabled;
382 	SDL_Surface *_mouseOrigSurface;
383 	SDL_Surface *_mouseSurface;
384 	enum {
385 		kMouseColorKey = 1
386 	};
387 
388 	// Shake mode
389 	// This is always set to 0 when building with SDL2.
390 	int _currentShakeXOffset;
391 	int _currentShakeYOffset;
392 
393 	// Palette data
394 	SDL_Color *_currentPalette;
395 	uint _paletteDirtyStart, _paletteDirtyEnd;
396 
397 	// Cursor palette data
398 	SDL_Color *_cursorPalette;
399 
400 	/**
401 	 * Mutex which prevents multiple threads from interfering with each other
402 	 * when accessing the screen.
403 	 */
404 	OSystem::MutexRef _graphicsMutex;
405 
406 #ifdef USE_SDL_DEBUG_FOCUSRECT
407 	bool _enableFocusRectDebugCode;
408 	bool _enableFocusRect;
409 	Common::Rect _focusRect;
410 #endif
411 
412 	virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false);
413 
414 	virtual void drawMouse();
415 	virtual void undrawMouse();
416 	virtual void blitCursor();
417 
418 	virtual void internUpdateScreen();
419 	virtual void updateShader();
420 
421 	virtual bool loadGFXMode();
422 	virtual void unloadGFXMode();
423 	virtual bool hotswapGFXMode();
424 
425 	virtual void setAspectRatioCorrection(bool enable);
426 	void setFilteringMode(bool enable);
427 
428 	virtual bool saveScreenshot(const Common::String &filename) const;
429 	virtual void setGraphicsModeIntern();
430 
431 private:
432 	void setFullscreenMode(bool enable);
433 	bool handleScalerHotkeys(Common::KeyCode key);
434 	bool isScalerHotkey(const Common::Event &event);
435 
436 	/**
437 	 * Converts the given point from the overlay's coordinate space to the
438 	 * game's coordinate space.
439 	 */
convertOverlayToGame(const int x,const int y)440 	Common::Point convertOverlayToGame(const int x, const int y) const {
441 		if (getOverlayWidth() == 0 || getOverlayHeight() == 0) {
442 			error("convertOverlayToGame called without a valid overlay");
443 		}
444 
445 		return Common::Point(x * getWidth() / getOverlayWidth(),
446 							 y * getHeight() / getOverlayHeight());
447 	}
448 
449 	/**
450 	 * Converts the given point from the game's coordinate space to the
451 	 * overlay's coordinate space.
452 	 */
convertGameToOverlay(const int x,const int y)453 	Common::Point convertGameToOverlay(const int x, const int y) const {
454 		if (getWidth() == 0 || getHeight() == 0) {
455 			error("convertGameToOverlay called without a valid overlay");
456 		}
457 
458 		return Common::Point(x * getOverlayWidth() / getWidth(),
459 							 y * getOverlayHeight() / getHeight());
460 	}
461 };
462 
463 #endif
464