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 
24 #include "backends/graphics/opengl/opengl-graphics.h"
25 #include "backends/graphics/opengl/texture.h"
26 #include "backends/graphics/opengl/pipelines/pipeline.h"
27 #include "backends/graphics/opengl/pipelines/fixed.h"
28 #include "backends/graphics/opengl/pipelines/shader.h"
29 #include "backends/graphics/opengl/shader.h"
30 
31 #include "common/array.h"
32 #include "common/textconsole.h"
33 #include "common/translation.h"
34 #include "common/algorithm.h"
35 #include "common/file.h"
36 #ifdef USE_OSD
37 #include "common/tokenizer.h"
38 #include "common/rect.h"
39 #endif
40 
41 #include "graphics/conversion.h"
42 #ifdef USE_OSD
43 #include "graphics/fontman.h"
44 #include "graphics/font.h"
45 #endif
46 
47 #ifdef USE_PNG
48 #include "image/png.h"
49 #else
50 #include "image/bmp.h"
51 #endif
52 
53 #ifdef USE_TTS
54 #include "common/text-to-speech.h"
55 #endif
56 
57 namespace OpenGL {
58 
OpenGLGraphicsManager()59 OpenGLGraphicsManager::OpenGLGraphicsManager()
60     : _currentState(), _oldState(), _transactionMode(kTransactionNone), _screenChangeID(1 << (sizeof(int) * 8 - 2)),
61       _pipeline(nullptr), _stretchMode(STRETCH_FIT),
62       _defaultFormat(), _defaultFormatAlpha(),
63       _gameScreen(nullptr), _overlay(nullptr),
64       _cursor(nullptr),
65       _cursorHotspotX(0), _cursorHotspotY(0),
66       _cursorHotspotXScaled(0), _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0),
67       _cursorKeyColor(0), _cursorDontScale(false), _cursorPaletteEnabled(false)
68 #ifdef USE_OSD
69       , _osdMessageChangeRequest(false), _osdMessageAlpha(0), _osdMessageFadeStartTime(0), _osdMessageSurface(nullptr),
70       _osdIconSurface(nullptr)
71 #endif
72     {
73 	memset(_gamePalette, 0, sizeof(_gamePalette));
74 	g_context.reset();
75 }
76 
~OpenGLGraphicsManager()77 OpenGLGraphicsManager::~OpenGLGraphicsManager() {
78 	delete _gameScreen;
79 	delete _overlay;
80 	delete _cursor;
81 #ifdef USE_OSD
82 	delete _osdMessageSurface;
83 	delete _osdIconSurface;
84 #endif
85 #if !USE_FORCED_GLES
86 	ShaderManager::destroy();
87 #endif
88 }
89 
hasFeature(OSystem::Feature f) const90 bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) const {
91 	switch (f) {
92 	case OSystem::kFeatureAspectRatioCorrection:
93 	case OSystem::kFeatureCursorPalette:
94 	case OSystem::kFeatureFilteringMode:
95 	case OSystem::kFeatureStretchMode:
96 		return true;
97 
98 	case OSystem::kFeatureOverlaySupportsAlpha:
99 		return _defaultFormatAlpha.aBits() > 3;
100 
101 	default:
102 		return false;
103 	}
104 }
105 
setFeatureState(OSystem::Feature f,bool enable)106 void OpenGLGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
107 	switch (f) {
108 	case OSystem::kFeatureAspectRatioCorrection:
109 		assert(_transactionMode != kTransactionNone);
110 		_currentState.aspectRatioCorrection = enable;
111 		break;
112 
113 	case OSystem::kFeatureFilteringMode:
114 		assert(_transactionMode != kTransactionNone);
115 		_currentState.filtering = enable;
116 
117 		if (_gameScreen) {
118 			_gameScreen->enableLinearFiltering(enable);
119 		}
120 
121 		if (_cursor) {
122 			_cursor->enableLinearFiltering(enable);
123 		}
124 
125 		break;
126 
127 	case OSystem::kFeatureCursorPalette:
128 		_cursorPaletteEnabled = enable;
129 		updateCursorPalette();
130 		break;
131 
132 	default:
133 		break;
134 	}
135 }
136 
getFeatureState(OSystem::Feature f) const137 bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) const {
138 	switch (f) {
139 	case OSystem::kFeatureAspectRatioCorrection:
140 		return _currentState.aspectRatioCorrection;
141 
142 	case OSystem::kFeatureFilteringMode:
143 		return _currentState.filtering;
144 
145 	case OSystem::kFeatureCursorPalette:
146 		return _cursorPaletteEnabled;
147 
148 	default:
149 		return false;
150 	}
151 }
152 
153 namespace {
154 
155 const OSystem::GraphicsMode glGraphicsModes[] = {
156 	{ "opengl",  _s("OpenGL"),                GFX_OPENGL  },
157 	{ nullptr, nullptr, 0 }
158 };
159 
160 } // End of anonymous namespace
161 
getSupportedGraphicsModes() const162 const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedGraphicsModes() const {
163 	return glGraphicsModes;
164 }
165 
getDefaultGraphicsMode() const166 int OpenGLGraphicsManager::getDefaultGraphicsMode() const {
167 	return GFX_OPENGL;
168 }
169 
setGraphicsMode(int mode)170 bool OpenGLGraphicsManager::setGraphicsMode(int mode) {
171 	assert(_transactionMode != kTransactionNone);
172 
173 	switch (mode) {
174 	case GFX_OPENGL:
175 		_currentState.graphicsMode = mode;
176 		return true;
177 
178 	default:
179 		warning("OpenGLGraphicsManager::setGraphicsMode(%d): Unknown graphics mode", mode);
180 		return false;
181 	}
182 }
183 
getGraphicsMode() const184 int OpenGLGraphicsManager::getGraphicsMode() const {
185 	return _currentState.graphicsMode;
186 }
187 
188 #ifdef USE_RGB_COLOR
getScreenFormat() const189 Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const {
190 	return _currentState.gameFormat;
191 }
192 
getSupportedFormats() const193 Common::List<Graphics::PixelFormat> OpenGLGraphicsManager::getSupportedFormats() const {
194 	Common::List<Graphics::PixelFormat> formats;
195 
196 	// Our default mode is (memory layout wise) RGBA8888 which is a different
197 	// logical layout depending on the endianness. We chose this mode because
198 	// it is the only 32bit color mode we can safely assume to be present in
199 	// OpenGL and OpenGL ES implementations. Thus, we need to supply different
200 	// logical formats based on endianness.
201 #ifdef SCUMM_LITTLE_ENDIAN
202 	// ABGR8888
203 	formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
204 #else
205 	// RGBA8888
206 	formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
207 #endif
208 	// RGB565
209 	formats.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
210 	// RGBA5551
211 	formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0));
212 	// RGBA4444
213 	formats.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0));
214 
215 	// These formats are not natively supported by OpenGL ES implementations,
216 	// we convert the pixel format internally.
217 #ifdef SCUMM_LITTLE_ENDIAN
218 	// RGBA8888
219 	formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
220 #else
221 	// ABGR8888
222 	formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
223 #endif
224 	// RGB555, this is used by SCUMM HE 16 bit games.
225 	formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
226 
227 	formats.push_back(Graphics::PixelFormat::createFormatCLUT8());
228 
229 	return formats;
230 }
231 #endif
232 
233 namespace {
234 const OSystem::GraphicsMode glStretchModes[] = {
235 	{"center", _s("Center"), STRETCH_CENTER},
236 	{"pixel-perfect", _s("Pixel-perfect scaling"), STRETCH_INTEGRAL},
237 	{"fit", _s("Fit to window"), STRETCH_FIT},
238 	{"stretch", _s("Stretch to window"), STRETCH_STRETCH},
239 	{nullptr, nullptr, 0}
240 };
241 
242 } // End of anonymous namespace
243 
getSupportedStretchModes() const244 const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedStretchModes() const {
245 	return glStretchModes;
246 }
247 
getDefaultStretchMode() const248 int OpenGLGraphicsManager::getDefaultStretchMode() const {
249 	return STRETCH_FIT;
250 }
251 
setStretchMode(int mode)252 bool OpenGLGraphicsManager::setStretchMode(int mode) {
253 	assert(getTransactionMode() != kTransactionNone);
254 
255 	if (mode == _stretchMode)
256 		return true;
257 
258 	// Check this is a valid mode
259 	const OSystem::GraphicsMode *sm = getSupportedStretchModes();
260 	bool found = false;
261 	while (sm->name) {
262 		if (sm->id == mode) {
263 			found = true;
264 			break;
265 		}
266 		sm++;
267 	}
268 	if (!found) {
269 		warning("unknown stretch mode %d", mode);
270 		return false;
271 	}
272 
273 	_stretchMode = mode;
274 	return true;
275 }
276 
getStretchMode() const277 int OpenGLGraphicsManager::getStretchMode() const {
278 	return _stretchMode;
279 }
280 
beginGFXTransaction()281 void OpenGLGraphicsManager::beginGFXTransaction() {
282 	assert(_transactionMode == kTransactionNone);
283 
284 	// Start a transaction.
285 	_oldState = _currentState;
286 	_transactionMode = kTransactionActive;
287 }
288 
endGFXTransaction()289 OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
290 	assert(_transactionMode == kTransactionActive);
291 
292 	uint transactionError = OSystem::kTransactionSuccess;
293 
294 	bool setupNewGameScreen = false;
295 	if (   _oldState.gameWidth  != _currentState.gameWidth
296 	    || _oldState.gameHeight != _currentState.gameHeight) {
297 		setupNewGameScreen = true;
298 	}
299 
300 #ifdef USE_RGB_COLOR
301 	if (_oldState.gameFormat != _currentState.gameFormat) {
302 		setupNewGameScreen = true;
303 	}
304 
305 	// Check whether the requested format can actually be used.
306 	Common::List<Graphics::PixelFormat> supportedFormats = getSupportedFormats();
307 	// In case the requested format is not usable we will fall back to CLUT8.
308 	if (Common::find(supportedFormats.begin(), supportedFormats.end(), _currentState.gameFormat) == supportedFormats.end()) {
309 		_currentState.gameFormat = Graphics::PixelFormat::createFormatCLUT8();
310 		transactionError |= OSystem::kTransactionFormatNotSupported;
311 	}
312 #endif
313 
314 	do {
315 		const uint desiredAspect = getDesiredGameAspectRatio();
316 		const uint requestedWidth  = _currentState.gameWidth;
317 		const uint requestedHeight = intToFrac(requestedWidth) / desiredAspect;
318 
319 		if (!loadVideoMode(requestedWidth, requestedHeight,
320 #ifdef USE_RGB_COLOR
321 		                   _currentState.gameFormat
322 #else
323 		                   Graphics::PixelFormat::createFormatCLUT8()
324 #endif
325 		                  )
326 		   // HACK: This is really nasty but we don't have any guarantees of
327 		   // a context existing before, which means we don't know the maximum
328 		   // supported texture size before this. Thus, we check whether the
329 		   // requested game resolution is supported over here.
330 		   || (   _currentState.gameWidth  > (uint)g_context.maxTextureSize
331 		       || _currentState.gameHeight > (uint)g_context.maxTextureSize)) {
332 			if (_transactionMode == kTransactionActive) {
333 				// Try to setup the old state in case its valid and is
334 				// actually different from the new one.
335 				if (_oldState.valid && _oldState != _currentState) {
336 					// Give some hints on what failed to set up.
337 					if (   _oldState.gameWidth  != _currentState.gameWidth
338 					    || _oldState.gameHeight != _currentState.gameHeight) {
339 						transactionError |= OSystem::kTransactionSizeChangeFailed;
340 					}
341 
342 #ifdef USE_RGB_COLOR
343 					if (_oldState.gameFormat != _currentState.gameFormat) {
344 						transactionError |= OSystem::kTransactionFormatNotSupported;
345 					}
346 #endif
347 
348 					if (_oldState.aspectRatioCorrection != _currentState.aspectRatioCorrection) {
349 						transactionError |= OSystem::kTransactionAspectRatioFailed;
350 					}
351 
352 					if (_oldState.graphicsMode != _currentState.graphicsMode) {
353 						transactionError |= OSystem::kTransactionModeSwitchFailed;
354 					}
355 
356 					if (_oldState.filtering != _currentState.filtering) {
357 						transactionError |= OSystem::kTransactionFilteringFailed;
358 					}
359 
360 					// Roll back to the old state.
361 					_currentState = _oldState;
362 					_transactionMode = kTransactionRollback;
363 
364 					// Try to set up the old state.
365 					continue;
366 				}
367 			}
368 
369 			// DON'T use error(), as this tries to bring up the debug
370 			// console, which WON'T WORK now that we might no have a
371 			// proper screen.
372 			warning("OpenGLGraphicsManager::endGFXTransaction: Could not load any graphics mode!");
373 			g_system->quit();
374 		}
375 
376 		// In case we reach this we have a valid state, yay.
377 		_transactionMode = kTransactionNone;
378 		_currentState.valid = true;
379 	} while (_transactionMode == kTransactionRollback);
380 
381 	if (setupNewGameScreen) {
382 		delete _gameScreen;
383 		_gameScreen = nullptr;
384 
385 #ifdef USE_RGB_COLOR
386 		_gameScreen = createSurface(_currentState.gameFormat);
387 #else
388 		_gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8());
389 #endif
390 		assert(_gameScreen);
391 		if (_gameScreen->hasPalette()) {
392 			_gameScreen->setPalette(0, 256, _gamePalette);
393 		}
394 
395 		_gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight);
396 		_gameScreen->enableLinearFiltering(_currentState.filtering);
397 		// We fill the screen to all black or index 0 for CLUT8.
398 #ifdef USE_RGB_COLOR
399 		if (_currentState.gameFormat.bytesPerPixel == 1) {
400 			_gameScreen->fill(0);
401 		} else {
402 			_gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0));
403 		}
404 #else
405 		_gameScreen->fill(0);
406 #endif
407 	}
408 
409 	// Update our display area and cursor scaling. This makes sure we pick up
410 	// aspect ratio correction and game screen changes correctly.
411 	recalculateDisplayAreas();
412 	recalculateCursorScaling();
413 
414 	// Something changed, so update the screen change ID.
415 	++_screenChangeID;
416 
417 	// Since transactionError is a ORd list of TransactionErrors this is
418 	// clearly wrong. But our API is simply broken.
419 	return (OSystem::TransactionError)transactionError;
420 }
421 
getScreenChangeID() const422 int OpenGLGraphicsManager::getScreenChangeID() const {
423 	return _screenChangeID;
424 }
425 
initSize(uint width,uint height,const Graphics::PixelFormat * format)426 void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
427 	Graphics::PixelFormat requestedFormat;
428 #ifdef USE_RGB_COLOR
429 	if (!format) {
430 		requestedFormat = Graphics::PixelFormat::createFormatCLUT8();
431 	} else {
432 		requestedFormat = *format;
433 	}
434 	_currentState.gameFormat = requestedFormat;
435 #endif
436 
437 	_currentState.gameWidth = width;
438 	_currentState.gameHeight = height;
439 	_gameScreenShakeXOffset = 0;
440 	_gameScreenShakeYOffset = 0;
441 }
442 
getWidth() const443 int16 OpenGLGraphicsManager::getWidth() const {
444 	return _currentState.gameWidth;
445 }
446 
getHeight() const447 int16 OpenGLGraphicsManager::getHeight() const {
448 	return _currentState.gameHeight;
449 }
450 
copyRectToScreen(const void * buf,int pitch,int x,int y,int w,int h)451 void OpenGLGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
452 	_gameScreen->copyRectToTexture(x, y, w, h, buf, pitch);
453 }
454 
fillScreen(uint32 col)455 void OpenGLGraphicsManager::fillScreen(uint32 col) {
456 	_gameScreen->fill(col);
457 }
458 
updateScreen()459 void OpenGLGraphicsManager::updateScreen() {
460 	if (!_gameScreen) {
461 		return;
462 	}
463 
464 #ifdef USE_OSD
465 	if (_osdMessageChangeRequest) {
466 		osdMessageUpdateSurface();
467 	}
468 
469 	if (_osdIconSurface) {
470 		_osdIconSurface->updateGLTexture();
471 	}
472 #endif
473 
474 	// We only update the screen when there actually have been any changes.
475 	if (   !_forceRedraw
476 		&& !_cursorNeedsRedraw
477 	    && !_gameScreen->isDirty()
478 	    && !(_overlayVisible && _overlay->isDirty())
479 	    && !(_cursorVisible && _cursor && _cursor->isDirty())
480 #ifdef USE_OSD
481 	    && !_osdMessageSurface && !_osdIconSurface
482 #endif
483 	    ) {
484 		return;
485 	}
486 
487 	// Update changes to textures.
488 	_gameScreen->updateGLTexture();
489 	if (_cursorVisible && _cursor) {
490 		_cursor->updateGLTexture();
491 	}
492 	_overlay->updateGLTexture();
493 
494 	// Clear the screen buffer.
495 	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
496 
497 	if (!_overlayVisible) {
498 		// The scissor test is enabled to:
499 		// - Clip the cursor to the game screen
500 		// - Clip the game screen when the shake offset is non-zero
501 		_backBuffer.enableScissorTest(true);
502 	}
503 
504 	// Alpha blending is disabled when drawing the screen
505 	_backBuffer.enableBlend(Framebuffer::kBlendModeDisabled);
506 
507 	// First step: Draw the (virtual) game screen.
508 	g_context.getActivePipeline()->drawTexture(_gameScreen->getGLTexture(), _gameDrawRect.left, _gameDrawRect.top, _gameDrawRect.width(), _gameDrawRect.height());
509 
510 	// Second step: Draw the overlay if visible.
511 	if (_overlayVisible) {
512 		_backBuffer.enableBlend(Framebuffer::kBlendModeTraditionalTransparency);
513 		g_context.getActivePipeline()->drawTexture(_overlay->getGLTexture(), 0, 0, _overlayDrawRect.width(), _overlayDrawRect.height());
514 	}
515 
516 	// Third step: Draw the cursor if visible.
517 	if (_cursorVisible && _cursor) {
518 		_backBuffer.enableBlend(Framebuffer::kBlendModePremultipliedTransparency);
519 
520 		g_context.getActivePipeline()->drawTexture(_cursor->getGLTexture(),
521 		                         _cursorX - _cursorHotspotXScaled,
522 		                         _cursorY - _cursorHotspotYScaled,
523 		                         _cursorWidthScaled, _cursorHeightScaled);
524 	}
525 
526 	if (!_overlayVisible) {
527 		_backBuffer.enableScissorTest(false);
528 	}
529 
530 #ifdef USE_OSD
531 	// Fourth step: Draw the OSD.
532 	if (_osdMessageSurface || _osdIconSurface) {
533 		_backBuffer.enableBlend(Framebuffer::kBlendModeTraditionalTransparency);
534 	}
535 
536 	if (_osdMessageSurface) {
537 		// Update alpha value.
538 		const int diff = g_system->getMillis(false) - _osdMessageFadeStartTime;
539 		if (diff > 0) {
540 			if (diff >= kOSDMessageFadeOutDuration) {
541 				// Back to full transparency.
542 				_osdMessageAlpha = 0;
543 			} else {
544 				// Do a fade out.
545 				_osdMessageAlpha = kOSDMessageInitialAlpha - diff * kOSDMessageInitialAlpha / kOSDMessageFadeOutDuration;
546 			}
547 		}
548 
549 		// Set the OSD transparency.
550 		g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdMessageAlpha / 100.0f);
551 
552 		int dstX = (_windowWidth - _osdMessageSurface->getWidth()) / 2;
553 		int dstY = (_windowHeight - _osdMessageSurface->getHeight()) / 2;
554 
555 		// Draw the OSD texture.
556 		g_context.getActivePipeline()->drawTexture(_osdMessageSurface->getGLTexture(),
557 		                                           dstX, dstY, _osdMessageSurface->getWidth(), _osdMessageSurface->getHeight());
558 
559 		// Reset color.
560 		g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
561 
562 		if (_osdMessageAlpha <= 0) {
563 			delete _osdMessageSurface;
564 			_osdMessageSurface = nullptr;
565 		}
566 	}
567 
568 	if (_osdIconSurface) {
569 		int dstX = _windowWidth - _osdIconSurface->getWidth() - kOSDIconRightMargin;
570 		int dstY = kOSDIconTopMargin;
571 
572 		// Draw the OSD icon texture.
573 		g_context.getActivePipeline()->drawTexture(_osdIconSurface->getGLTexture(),
574 		                                           dstX, dstY, _osdIconSurface->getWidth(), _osdIconSurface->getHeight());
575 	}
576 #endif
577 
578 	_cursorNeedsRedraw = false;
579 	_forceRedraw = false;
580 	refreshScreen();
581 }
582 
lockScreen()583 Graphics::Surface *OpenGLGraphicsManager::lockScreen() {
584 	return _gameScreen->getSurface();
585 }
586 
unlockScreen()587 void OpenGLGraphicsManager::unlockScreen() {
588 	_gameScreen->flagDirty();
589 }
590 
setFocusRectangle(const Common::Rect & rect)591 void OpenGLGraphicsManager::setFocusRectangle(const Common::Rect& rect) {
592 }
593 
clearFocusRectangle()594 void OpenGLGraphicsManager::clearFocusRectangle() {
595 }
596 
getOverlayWidth() const597 int16 OpenGLGraphicsManager::getOverlayWidth() const {
598 	if (_overlay) {
599 		return _overlay->getWidth();
600 	} else {
601 		return 0;
602 	}
603 }
604 
getOverlayHeight() const605 int16 OpenGLGraphicsManager::getOverlayHeight() const {
606 	if (_overlay) {
607 		return _overlay->getHeight();
608 	} else {
609 		return 0;
610 	}
611 }
612 
getOverlayFormat() const613 Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
614 	return _overlay->getFormat();
615 }
616 
copyRectToOverlay(const void * buf,int pitch,int x,int y,int w,int h)617 void OpenGLGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
618 	_overlay->copyRectToTexture(x, y, w, h, buf, pitch);
619 }
620 
clearOverlay()621 void OpenGLGraphicsManager::clearOverlay() {
622 	_overlay->fill(0);
623 }
624 
grabOverlay(void * buf,int pitch) const625 void OpenGLGraphicsManager::grabOverlay(void *buf, int pitch) const {
626 	const Graphics::Surface *overlayData = _overlay->getSurface();
627 
628 	const byte *src = (const byte *)overlayData->getPixels();
629 	byte *dst = (byte *)buf;
630 
631 	for (uint h = overlayData->h; h > 0; --h) {
632 		memcpy(dst, src, overlayData->w * overlayData->format.bytesPerPixel);
633 		dst += pitch;
634 		src += overlayData->pitch;
635 	}
636 }
637 
638 namespace {
639 template<typename SrcColor, typename DstColor>
multiplyColorWithAlpha(const byte * src,byte * dst,const uint w,const uint h,const Graphics::PixelFormat & srcFmt,const Graphics::PixelFormat & dstFmt,const uint srcPitch,const uint dstPitch,const SrcColor keyColor)640 void multiplyColorWithAlpha(const byte *src, byte *dst, const uint w, const uint h,
641                             const Graphics::PixelFormat &srcFmt, const Graphics::PixelFormat &dstFmt,
642                             const uint srcPitch, const uint dstPitch, const SrcColor keyColor) {
643 	for (uint y = 0; y < h; ++y) {
644 		for (uint x = 0; x < w; ++x) {
645 			const uint32 color = *(const SrcColor *)src;
646 
647 			if (color == keyColor) {
648 				*(DstColor *)dst = 0;
649 			} else {
650 				byte a, r, g, b;
651 				srcFmt.colorToARGB(color, a, r, g, b);
652 
653 				if (a != 0xFF) {
654 					r = (int) r * a / 255;
655 					g = (int) g * a / 255;
656 					b = (int) b * a / 255;
657 				}
658 
659 				*(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b);
660 			}
661 
662 			src += sizeof(SrcColor);
663 			dst += sizeof(DstColor);
664 		}
665 
666 		src += srcPitch - w * srcFmt.bytesPerPixel;
667 		dst += dstPitch - w * dstFmt.bytesPerPixel;
668 	}
669 }
670 } // End of anonymous namespace
671 
setMouseCursor(const void * buf,uint w,uint h,int hotspotX,int hotspotY,uint32 keycolor,bool dontScale,const Graphics::PixelFormat * format)672 void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
673 
674 	_cursorKeyColor = keycolor;
675 	_cursorHotspotX = hotspotX;
676 	_cursorHotspotY = hotspotY;
677 	_cursorDontScale = dontScale;
678 
679 	if (!w || !h) {
680 		delete _cursor;
681 		_cursor = nullptr;
682 		return;
683 	}
684 
685 	Graphics::PixelFormat inputFormat;
686 #ifdef USE_RGB_COLOR
687 	if (format) {
688 		inputFormat = *format;
689 	} else {
690 		inputFormat = Graphics::PixelFormat::createFormatCLUT8();
691 	}
692 #else
693 	inputFormat = Graphics::PixelFormat::createFormatCLUT8();
694 #endif
695 
696 	// In case the color format has changed we will need to create the texture.
697 	if (!_cursor || _cursor->getFormat() != inputFormat) {
698 		delete _cursor;
699 		_cursor = nullptr;
700 
701 		GLenum glIntFormat, glFormat, glType;
702 
703 		Graphics::PixelFormat textureFormat;
704 		if (inputFormat.bytesPerPixel == 1 || (inputFormat.aBits() && getGLPixelFormat(inputFormat, glIntFormat, glFormat, glType))) {
705 			// There is two cases when we can use the cursor format directly.
706 			// The first is when it's CLUT8, here color key handling can
707 			// always be applied because we use the alpha channel of
708 			// _defaultFormatAlpha for that.
709 			// The other is when the input format has alpha bits and
710 			// furthermore is directly supported.
711 			textureFormat = inputFormat;
712 		} else {
713 			textureFormat = _defaultFormatAlpha;
714 		}
715 		_cursor = createSurface(textureFormat, true);
716 		assert(_cursor);
717 		_cursor->enableLinearFiltering(_currentState.filtering);
718 	}
719 
720 	_cursor->allocate(w, h);
721 	if (inputFormat.bytesPerPixel == 1) {
722 		// For CLUT8 cursors we can simply copy the input data into the
723 		// texture.
724 		_cursor->copyRectToTexture(0, 0, w, h, buf, w * inputFormat.bytesPerPixel);
725 	} else {
726 		// Otherwise it is a bit more ugly because we have to handle a key
727 		// color properly.
728 
729 		Graphics::Surface *dst = _cursor->getSurface();
730 		const uint srcPitch = w * inputFormat.bytesPerPixel;
731 
732 		// Copy the cursor data to the actual texture surface. This will make
733 		// sure that the data is also converted to the expected format.
734 
735 		// Also multiply the color values with the alpha channel.
736 		// The pre-multiplication allows using a blend mode that prevents
737 		// color fringes due to filtering.
738 
739 		if (dst->format.bytesPerPixel == 2) {
740 			if (inputFormat.bytesPerPixel == 2) {
741 				multiplyColorWithAlpha<uint16, uint16>((const byte *) buf, (byte *) dst->getPixels(), w, h,
742 				                                       inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
743 			} else if (inputFormat.bytesPerPixel == 4) {
744 				multiplyColorWithAlpha<uint32, uint16>((const byte *) buf, (byte *) dst->getPixels(), w, h,
745 				                                       inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
746 			}
747 		} else {
748 			if (inputFormat.bytesPerPixel == 2) {
749 				multiplyColorWithAlpha<uint16, uint32>((const byte *) buf, (byte *) dst->getPixels(), w, h,
750 				                                       inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
751 			} else if (inputFormat.bytesPerPixel == 4) {
752 				multiplyColorWithAlpha<uint32, uint32>((const byte *) buf, (byte *) dst->getPixels(), w, h,
753 				                                       inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
754 			}
755 		}
756 
757 		// Flag the texture as dirty.
758 		_cursor->flagDirty();
759 	}
760 
761 	// In case we actually use a palette set that up properly.
762 	if (inputFormat.bytesPerPixel == 1) {
763 		updateCursorPalette();
764 	}
765 
766 	recalculateCursorScaling();
767 }
768 
setCursorPalette(const byte * colors,uint start,uint num)769 void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
770 	// FIXME: For some reason client code assumes that usage of this function
771 	// automatically enables the cursor palette.
772 	_cursorPaletteEnabled = true;
773 
774 	memcpy(_cursorPalette + start * 3, colors, num * 3);
775 	updateCursorPalette();
776 }
777 
displayMessageOnOSD(const char * msg)778 void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
779 #ifdef USE_OSD
780 	_osdMessageChangeRequest = true;
781 
782 	_osdMessageNextData = msg;
783 #endif // USE_OSD
784 }
785 
786 #ifdef USE_OSD
osdMessageUpdateSurface()787 void OpenGLGraphicsManager::osdMessageUpdateSurface() {
788 	// Split up the lines.
789 	Common::Array<Common::String> osdLines;
790 	Common::StringTokenizer tokenizer(_osdMessageNextData, "\n");
791 	while (!tokenizer.empty()) {
792 		osdLines.push_back(tokenizer.nextToken());
793 	}
794 
795 	// Do the actual drawing like the SDL backend.
796 	const Graphics::Font *font = getFontOSD();
797 
798 	// Determine a rect which would contain the message string (clipped to the
799 	// screen dimensions).
800 	const int vOffset = 6;
801 	const int lineSpacing = 1;
802 	const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
803 	uint width = 0;
804 	uint height = lineHeight * osdLines.size() + 2 * vOffset;
805 	for (uint i = 0; i < osdLines.size(); i++) {
806 		width = MAX<uint>(width, font->getStringWidth(osdLines[i]) + 14);
807 	}
808 
809 	// Clip the rect
810 	width  = MIN<uint>(width,  _gameDrawRect.width());
811 	height = MIN<uint>(height, _gameDrawRect.height());
812 
813 	delete _osdMessageSurface;
814 	_osdMessageSurface = nullptr;
815 
816 	_osdMessageSurface = createSurface(_defaultFormatAlpha);
817 	assert(_osdMessageSurface);
818 	// We always filter the osd with GL_LINEAR. This assures it's
819 	// readable in case it needs to be scaled and does not affect it
820 	// otherwise.
821 	_osdMessageSurface->enableLinearFiltering(true);
822 
823 	_osdMessageSurface->allocate(width, height);
824 
825 	Graphics::Surface *dst = _osdMessageSurface->getSurface();
826 
827 	// Draw a dark gray rect.
828 	const uint32 color = dst->format.RGBToColor(40, 40, 40);
829 	dst->fillRect(Common::Rect(0, 0, width, height), color);
830 
831 	// Render the message in white
832 	const uint32 white = dst->format.RGBToColor(255, 255, 255);
833 	for (uint i = 0; i < osdLines.size(); ++i) {
834 		font->drawString(dst, osdLines[i],
835 		                 0, i * lineHeight + vOffset + lineSpacing, width,
836 		                 white, Graphics::kTextAlignCenter);
837 	}
838 
839 	_osdMessageSurface->updateGLTexture();
840 
841 	// Init the OSD display parameters.
842 	_osdMessageAlpha = kOSDMessageInitialAlpha;
843 	_osdMessageFadeStartTime = g_system->getMillis() + kOSDMessageFadeOutDelay;
844 
845 #ifdef USE_TTS
846 	if (ConfMan.hasKey("tts_enabled", "scummvm") &&
847 			ConfMan.getBool("tts_enabled", "scummvm")) {
848 		Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
849 		if (ttsMan)
850 			ttsMan->say(_osdMessageNextData);
851 	}
852 #endif // USE_TTS
853 	// Clear the text update request
854 	_osdMessageNextData.clear();
855 	_osdMessageChangeRequest = false;
856 }
857 #endif
858 
displayActivityIconOnOSD(const Graphics::Surface * icon)859 void OpenGLGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface *icon) {
860 #ifdef USE_OSD
861 	if (_osdIconSurface) {
862 		delete _osdIconSurface;
863 		_osdIconSurface = nullptr;
864 
865 		// Make sure the icon is cleared on the next update
866 		_forceRedraw = true;
867 	}
868 
869 	if (icon) {
870 		Graphics::Surface *converted = icon->convertTo(_defaultFormatAlpha);
871 
872 		_osdIconSurface = createSurface(_defaultFormatAlpha);
873 		assert(_osdIconSurface);
874 		// We always filter the osd with GL_LINEAR. This assures it's
875 		// readable in case it needs to be scaled and does not affect it
876 		// otherwise.
877 		_osdIconSurface->enableLinearFiltering(true);
878 
879 		_osdIconSurface->allocate(converted->w, converted->h);
880 
881 		Graphics::Surface *dst = _osdIconSurface->getSurface();
882 
883 		// Copy the icon to the texture
884 		dst->copyRectToSurface(*converted, 0, 0, Common::Rect(0, 0, converted->w, converted->h));
885 
886 		converted->free();
887 		delete converted;
888 	}
889 #endif
890 }
891 
setPalette(const byte * colors,uint start,uint num)892 void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
893 	assert(_gameScreen->hasPalette());
894 
895 	memcpy(_gamePalette + start * 3, colors, num * 3);
896 	_gameScreen->setPalette(start, num, colors);
897 
898 	// We might need to update the cursor palette here.
899 	updateCursorPalette();
900 }
901 
grabPalette(byte * colors,uint start,uint num) const902 void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) const {
903 	assert(_gameScreen->hasPalette());
904 
905 	memcpy(colors, _gamePalette + start * 3, num * 3);
906 }
907 
handleResizeImpl(const int width,const int height,const int xdpi,const int ydpi)908 void OpenGLGraphicsManager::handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) {
909 	// Setup backbuffer size.
910 	_backBuffer.setDimensions(width, height);
911 
912 	uint overlayWidth = width;
913 	uint overlayHeight = height;
914 
915 	// WORKAROUND: We can only support surfaces up to the maximum supported
916 	// texture size. Thus, in case we encounter a physical size bigger than
917 	// this maximum texture size we will simply use an overlay as big as
918 	// possible and then scale it to the physical display size. This sounds
919 	// bad but actually all recent chips should support full HD resolution
920 	// anyway. Thus, it should not be a real issue for modern hardware.
921 	if (   overlayWidth  > (uint)g_context.maxTextureSize
922 	    || overlayHeight > (uint)g_context.maxTextureSize) {
923 		const frac_t outputAspect = intToFrac(_windowWidth) / _windowHeight;
924 
925 		if (outputAspect > (frac_t)FRAC_ONE) {
926 			overlayWidth  = g_context.maxTextureSize;
927 			overlayHeight = intToFrac(overlayWidth) / outputAspect;
928 		} else {
929 			overlayHeight = g_context.maxTextureSize;
930 			overlayWidth  = fracToInt(overlayHeight * outputAspect);
931 		}
932 	}
933 
934 	// HACK: We limit the minimal overlay size to 256x200, which is the
935 	// minimum of the dimensions of the two resolutions 256x240 (NES) and
936 	// 320x200 (many DOS games use this). This hopefully assure that our
937 	// GUI has working layouts.
938 	overlayWidth = MAX<uint>(overlayWidth, 256);
939 	overlayHeight = MAX<uint>(overlayHeight, 200);
940 
941 	// HACK: Reduce the size of the overlay on high DPI screens.
942 	overlayWidth = fracToInt(overlayWidth * (intToFrac(90) / xdpi));
943 	overlayHeight = fracToInt(overlayHeight * (intToFrac(90) / ydpi));
944 
945 	if (!_overlay || _overlay->getFormat() != _defaultFormatAlpha) {
946 		delete _overlay;
947 		_overlay = nullptr;
948 
949 		_overlay = createSurface(_defaultFormatAlpha);
950 		assert(_overlay);
951 		// We always filter the overlay with GL_LINEAR. This assures it's
952 		// readable in case it needs to be scaled and does not affect it
953 		// otherwise.
954 		_overlay->enableLinearFiltering(true);
955 	}
956 	_overlay->allocate(overlayWidth, overlayHeight);
957 	_overlay->fill(0);
958 
959 	// Re-setup the scaling for the screen and cursor
960 	recalculateDisplayAreas();
961 	recalculateCursorScaling();
962 
963 	// Something changed, so update the screen change ID.
964 	++_screenChangeID;
965 }
966 
notifyContextCreate(const Graphics::PixelFormat & defaultFormat,const Graphics::PixelFormat & defaultFormatAlpha)967 void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha) {
968 	// Initialize context for use.
969 	initializeGLContext();
970 
971 	// Initialize pipeline.
972 	delete _pipeline;
973 	_pipeline = nullptr;
974 
975 #if !USE_FORCED_GLES
976 	if (g_context.shadersSupported) {
977 		ShaderMan.notifyCreate();
978 		_pipeline = new ShaderPipeline(ShaderMan.query(ShaderManager::kDefault));
979 	}
980 #endif
981 
982 #if !USE_FORCED_GLES2
983 	if (_pipeline == nullptr) {
984 		_pipeline = new FixedPipeline();
985 	}
986 #endif
987 
988 	g_context.setPipeline(_pipeline);
989 
990 	// Disable 3D properties.
991 	GL_CALL(glDisable(GL_CULL_FACE));
992 	GL_CALL(glDisable(GL_DEPTH_TEST));
993 	GL_CALL(glDisable(GL_DITHER));
994 
995 	g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
996 
997 	// Setup backbuffer state.
998 
999 	// Default to black as clear color.
1000 	_backBuffer.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1001 
1002 	g_context.getActivePipeline()->setFramebuffer(&_backBuffer);
1003 
1004 	// We use a "pack" alignment (when reading from textures) to 4 here,
1005 	// since the only place where we really use it is the BMP screenshot
1006 	// code and that requires the same alignment too.
1007 	GL_CALL(glPixelStorei(GL_PACK_ALIGNMENT, 4));
1008 
1009 	// Refresh the output screen dimensions if some are set up.
1010 	if (_windowWidth != 0 && _windowHeight != 0) {
1011 		handleResize(_windowWidth, _windowHeight, _xdpi, _ydpi);
1012 	}
1013 
1014 	// TODO: Should we try to convert textures into one of those formats if
1015 	// possible? For example, when _gameScreen is CLUT8 we might want to use
1016 	// defaultFormat now.
1017 	_defaultFormat = defaultFormat;
1018 	_defaultFormatAlpha = defaultFormatAlpha;
1019 
1020 	if (_gameScreen) {
1021 		_gameScreen->recreate();
1022 	}
1023 
1024 	if (_overlay) {
1025 		_overlay->recreate();
1026 	}
1027 
1028 	if (_cursor) {
1029 		_cursor->recreate();
1030 	}
1031 
1032 #ifdef USE_OSD
1033 	if (_osdMessageSurface) {
1034 		_osdMessageSurface->recreate();
1035 	}
1036 
1037 	if (_osdIconSurface) {
1038 		_osdIconSurface->recreate();
1039 	}
1040 #endif
1041 }
1042 
notifyContextDestroy()1043 void OpenGLGraphicsManager::notifyContextDestroy() {
1044 	if (_gameScreen) {
1045 		_gameScreen->destroy();
1046 	}
1047 
1048 	if (_overlay) {
1049 		_overlay->destroy();
1050 	}
1051 
1052 	if (_cursor) {
1053 		_cursor->destroy();
1054 	}
1055 
1056 #ifdef USE_OSD
1057 	if (_osdMessageSurface) {
1058 		_osdMessageSurface->destroy();
1059 	}
1060 
1061 	if (_osdIconSurface) {
1062 		_osdIconSurface->destroy();
1063 	}
1064 #endif
1065 
1066 #if !USE_FORCED_GLES
1067 	if (g_context.shadersSupported) {
1068 		ShaderMan.notifyDestroy();
1069 	}
1070 #endif
1071 
1072 	// Destroy rendering pipeline.
1073 	g_context.setPipeline(nullptr);
1074 	delete _pipeline;
1075 	_pipeline = nullptr;
1076 
1077 	// Rest our context description since the context is gone soon.
1078 	g_context.reset();
1079 }
1080 
createSurface(const Graphics::PixelFormat & format,bool wantAlpha)1081 Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha) {
1082 	GLenum glIntFormat, glFormat, glType;
1083 	if (format.bytesPerPixel == 1) {
1084 #if !USE_FORCED_GLES
1085 		if (TextureCLUT8GPU::isSupportedByContext()) {
1086 			return new TextureCLUT8GPU();
1087 		}
1088 #endif
1089 
1090 		const Graphics::PixelFormat &virtFormat = wantAlpha ? _defaultFormatAlpha : _defaultFormat;
1091 		const bool supported = getGLPixelFormat(virtFormat, glIntFormat, glFormat, glType);
1092 		if (!supported) {
1093 			return nullptr;
1094 		} else {
1095 			return new TextureCLUT8(glIntFormat, glFormat, glType, virtFormat);
1096 		}
1097 #if !USE_FORCED_GL
1098 	} else if (isGLESContext() && format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
1099 		// OpenGL ES does not support a texture format usable for RGB555.
1100 		// Since SCUMM uses this pixel format for some games (and there is no
1101 		// hope for this to change anytime soon) we use pixel format
1102 		// conversion to a supported texture format.
1103 		return new TextureRGB555();
1104 #ifdef SCUMM_LITTLE_ENDIAN
1105 	} else if (isGLESContext() && format == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
1106 #else
1107 	} else if (isGLESContext() && format == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
1108 #endif
1109 		return new TextureRGBA8888Swap();
1110 #endif // !USE_FORCED_GL
1111 	} else {
1112 		const bool supported = getGLPixelFormat(format, glIntFormat, glFormat, glType);
1113 		if (!supported) {
1114 			return nullptr;
1115 		} else {
1116 			return new Texture(glIntFormat, glFormat, glType, format);
1117 		}
1118 	}
1119 }
1120 
getGLPixelFormat(const Graphics::PixelFormat & pixelFormat,GLenum & glIntFormat,GLenum & glFormat,GLenum & glType) const1121 bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const {
1122 #ifdef SCUMM_LITTLE_ENDIAN
1123 	if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
1124 #else
1125 	if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
1126 #endif
1127 		glIntFormat = GL_RGBA;
1128 		glFormat = GL_RGBA;
1129 		glType = GL_UNSIGNED_BYTE;
1130 		return true;
1131 	} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565
1132 		glIntFormat = GL_RGB;
1133 		glFormat = GL_RGB;
1134 		glType = GL_UNSIGNED_SHORT_5_6_5;
1135 		return true;
1136 	} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGBA5551
1137 		glIntFormat = GL_RGBA;
1138 		glFormat = GL_RGBA;
1139 		glType = GL_UNSIGNED_SHORT_5_5_5_1;
1140 		return true;
1141 	} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444
1142 		glIntFormat = GL_RGBA;
1143 		glFormat = GL_RGBA;
1144 		glType = GL_UNSIGNED_SHORT_4_4_4_4;
1145 		return true;
1146 #if !USE_FORCED_GLES && !USE_FORCED_GLES2
1147 	// The formats below are not supported by every GLES implementation.
1148 	// Thus, we do not mark them as supported when a GLES context is setup.
1149 	} else if (isGLESContext()) {
1150 		return false;
1151 #ifdef SCUMM_LITTLE_ENDIAN
1152 	} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
1153 		glIntFormat = GL_RGBA;
1154 		glFormat = GL_RGBA;
1155 		glType = GL_UNSIGNED_INT_8_8_8_8;
1156 		return true;
1157 #endif
1158 	} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555
1159 		glIntFormat = GL_RGB;
1160 		glFormat = GL_BGRA;
1161 		glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1162 		return true;
1163 	} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444
1164 		glIntFormat = GL_RGBA;
1165 		glFormat = GL_BGRA;
1166 		glType = GL_UNSIGNED_SHORT_4_4_4_4_REV;
1167 		return true;
1168 #ifdef SCUMM_BIG_ENDIAN
1169 	} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888
1170 		glIntFormat = GL_RGBA;
1171 		glFormat = GL_RGBA;
1172 		glType = GL_UNSIGNED_INT_8_8_8_8_REV;
1173 		return true;
1174 #endif
1175 	} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888
1176 		glIntFormat = GL_RGBA;
1177 		glFormat = GL_BGRA;
1178 		glType = GL_UNSIGNED_INT_8_8_8_8;
1179 		return true;
1180 	} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565
1181 		glIntFormat = GL_RGB;
1182 		glFormat = GL_RGB;
1183 		glType = GL_UNSIGNED_SHORT_5_6_5_REV;
1184 		return true;
1185 	} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551
1186 		glIntFormat = GL_RGBA;
1187 		glFormat = GL_BGRA;
1188 		glType = GL_UNSIGNED_SHORT_5_5_5_1;
1189 		return true;
1190 	} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444
1191 		glIntFormat = GL_RGBA;
1192 		glFormat = GL_RGBA;
1193 		glType = GL_UNSIGNED_SHORT_4_4_4_4_REV;
1194 		return true;
1195 	} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444
1196 		glIntFormat = GL_RGBA;
1197 		glFormat = GL_BGRA;
1198 		glType = GL_UNSIGNED_SHORT_4_4_4_4;
1199 		return true;
1200 #endif // !USE_FORCED_GLES && !USE_FORCED_GLES2
1201 	} else {
1202 		return false;
1203 	}
1204 }
1205 
1206 bool OpenGLGraphicsManager::gameNeedsAspectRatioCorrection() const {
1207 	if (_currentState.aspectRatioCorrection) {
1208 		const uint width = getWidth();
1209 		const uint height = getHeight();
1210 
1211 		// In case we enable aspect ratio correction we force a 4/3 ratio.
1212 		// But just for 320x200 and 640x400 games, since other games do not need
1213 		// this.
1214 		return (width == 320 && height == 200) || (width == 640 && height == 400);
1215 	}
1216 
1217 	return false;
1218 }
1219 
1220 void OpenGLGraphicsManager::recalculateDisplayAreas() {
1221 	if (!_gameScreen) {
1222 		return;
1223 	}
1224 
1225 	WindowedGraphicsManager::recalculateDisplayAreas();
1226 
1227 	// Setup drawing limitation for game graphics.
1228 	// This involves some trickery because OpenGL's viewport coordinate system
1229 	// is upside down compared to ours.
1230 	_backBuffer.setScissorBox(_gameDrawRect.left,
1231 	                          _windowHeight - _gameDrawRect.height() - _gameDrawRect.top,
1232 	                          _gameDrawRect.width(),
1233 	                          _gameDrawRect.height());
1234 
1235 	// Update the cursor position to adjust for new display area.
1236 	setMousePosition(_cursorX, _cursorY);
1237 
1238 	// Force a redraw to assure screen is properly redrawn.
1239 	_forceRedraw = true;
1240 }
1241 
1242 void OpenGLGraphicsManager::updateCursorPalette() {
1243 	if (!_cursor || !_cursor->hasPalette()) {
1244 		return;
1245 	}
1246 
1247 	if (_cursorPaletteEnabled) {
1248 		_cursor->setPalette(0, 256, _cursorPalette);
1249 	} else {
1250 		_cursor->setPalette(0, 256, _gamePalette);
1251 	}
1252 
1253 	_cursor->setColorKey(_cursorKeyColor);
1254 }
1255 
1256 void OpenGLGraphicsManager::recalculateCursorScaling() {
1257 	if (!_cursor || !_gameScreen) {
1258 		return;
1259 	}
1260 
1261 	// By default we use the unscaled versions.
1262 	_cursorHotspotXScaled = _cursorHotspotX;
1263 	_cursorHotspotYScaled = _cursorHotspotY;
1264 	_cursorWidthScaled = _cursor->getWidth();
1265 	_cursorHeightScaled = _cursor->getHeight();
1266 
1267 	// In case scaling is actually enabled we will scale the cursor according
1268 	// to the game screen.
1269 	if (!_cursorDontScale) {
1270 		const frac_t screenScaleFactorX = intToFrac(_gameDrawRect.width()) / _gameScreen->getWidth();
1271 		const frac_t screenScaleFactorY = intToFrac(_gameDrawRect.height()) / _gameScreen->getHeight();
1272 
1273 		_cursorHotspotXScaled = fracToInt(_cursorHotspotXScaled * screenScaleFactorX);
1274 		_cursorWidthScaled    = fracToInt(_cursorWidthScaled    * screenScaleFactorX);
1275 
1276 		_cursorHotspotYScaled = fracToInt(_cursorHotspotYScaled * screenScaleFactorY);
1277 		_cursorHeightScaled   = fracToInt(_cursorHeightScaled   * screenScaleFactorY);
1278 	} else {
1279 		const frac_t screenScaleFactorX = intToFrac(90) / _xdpi;
1280 		const frac_t screenScaleFactorY = intToFrac(90) / _ydpi;
1281 
1282 		// FIXME: Replace this with integer maths
1283 		_cursorHotspotXScaled /= fracToDouble(screenScaleFactorX);
1284 		_cursorWidthScaled    /= fracToDouble(screenScaleFactorX);
1285 
1286 		_cursorHotspotYScaled /= fracToDouble(screenScaleFactorY);
1287 		_cursorHeightScaled   /= fracToDouble(screenScaleFactorY);
1288 	}
1289 }
1290 
1291 #ifdef USE_OSD
1292 const Graphics::Font *OpenGLGraphicsManager::getFontOSD() const {
1293 	return FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
1294 }
1295 #endif
1296 
1297 bool OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const {
1298 	const uint width  = _windowWidth;
1299 	const uint height = _windowHeight;
1300 
1301 	// A line of a BMP image must have a size divisible by 4.
1302 	// We calculate the padding bytes needed here.
1303 	// Since we use a 3 byte per pixel mode, we can use width % 4 here, since
1304 	// it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the
1305 	// usual way of computing the padding bytes required).
1306 	// GL_PACK_ALIGNMENT is 4, so this line padding is required for PNG too
1307 	const uint linePaddingSize = width % 4;
1308 	const uint lineSize        = width * 3 + linePaddingSize;
1309 
1310 	Common::DumpFile out;
1311 	if (!out.open(filename)) {
1312 		return false;
1313 	}
1314 
1315 	Common::Array<uint8> pixels;
1316 	pixels.resize(lineSize * height);
1317 	GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front()));
1318 
1319 #ifdef SCUMM_LITTLE_ENDIAN
1320 	const Graphics::PixelFormat format(3, 8, 8, 8, 0, 0, 8, 16, 0);
1321 #else
1322 	const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
1323 #endif
1324 	Graphics::Surface data;
1325 	data.init(width, height, lineSize, &pixels.front(), format);
1326 #ifdef USE_PNG
1327 	return Image::writePNG(out, data, true);
1328 #else
1329 	return Image::writeBMP(out, data, true);
1330 #endif
1331 }
1332 
1333 } // End of namespace OpenGL
1334