SDL: Switch the OpenGL renderer to use small textures to draw the OSD

This commit is contained in:
Bastien Bouclet 2016-09-13 20:35:05 +02:00
parent 05bc82b622
commit 9cbaad6140
2 changed files with 201 additions and 85 deletions

View file

@ -57,7 +57,8 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
_cursorKeyColor(0), _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false),
_forceRedraw(false), _scissorOverride(3)
#ifdef USE_OSD
, _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr)
, _osdMessageChangeRequest(false), _osdMessageAlpha(0), _osdMessageFadeStartTime(0), _osdMessageSurface(nullptr),
_osdIconChangeRequest(false), _osdIconSurface(nullptr)
#endif
{
memset(_gamePalette, 0, sizeof(_gamePalette));
@ -69,7 +70,9 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() {
delete _overlay;
delete _cursor;
#ifdef USE_OSD
delete _osd;
delete _osdMessageSurface;
delete _osdIconSurface;
_osdIconNextData.free();
#endif
#if !USE_FORCED_GLES
ShaderManager::destroy();
@ -362,13 +365,27 @@ void OpenGLGraphicsManager::updateScreen() {
return;
}
#ifdef USE_OSD
{
Common::StackLock lock(_osdMutex);
if (_osdMessageChangeRequest) {
osdMessageUpdateSurface();
}
if (_osdIconChangeRequest) {
osdIconUpdateSurface();
}
}
#endif
// We only update the screen when there actually have been any changes.
if ( !_forceRedraw
&& !_gameScreen->isDirty()
&& !(_overlayVisible && _overlay->isDirty())
&& !(_cursorVisible && _cursor && _cursor->isDirty())
#ifdef USE_OSD
&& _osdAlpha == 0
&& !_osdMessageSurface && !_osdIconSurface
#endif
) {
return;
@ -381,9 +398,6 @@ void OpenGLGraphicsManager::updateScreen() {
_cursor->updateGLTexture();
}
_overlay->updateGLTexture();
#ifdef USE_OSD
_osd->updateGLTexture();
#endif
// Clear the screen buffer.
if (_scissorOverride && !_overlayVisible) {
@ -424,29 +438,45 @@ void OpenGLGraphicsManager::updateScreen() {
#ifdef USE_OSD
// Fourth step: Draw the OSD.
if (_osdAlpha > 0) {
Common::StackLock lock(_osdMutex);
if (_osdMessageSurface) {
// Update alpha value.
const int diff = g_system->getMillis(false) - _osdFadeStartTime;
const int diff = g_system->getMillis(false) - _osdMessageFadeStartTime;
if (diff > 0) {
if (diff >= kOSDFadeOutDuration) {
if (diff >= kOSDMessageFadeOutDuration) {
// Back to full transparency.
_osdAlpha = 0;
_osdMessageAlpha = 0;
} else {
// Do a fade out.
_osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration;
_osdMessageAlpha = kOSDMessageInitialAlpha - diff * kOSDMessageInitialAlpha / kOSDMessageFadeOutDuration;
}
}
// Set the OSD transparency.
g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f);
g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdMessageAlpha / 100.0f);
int dstX = (_outputScreenWidth - _osdMessageSurface->getWidth()) / 2;
int dstY = (_outputScreenHeight - _osdMessageSurface->getHeight()) / 2;
// Draw the OSD texture.
g_context.getActivePipeline()->drawTexture(_osd->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight);
g_context.getActivePipeline()->drawTexture(_osdMessageSurface->getGLTexture(),
dstX, dstY, _osdMessageSurface->getWidth(), _osdMessageSurface->getHeight());
// Reset color.
g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
if (_osdMessageAlpha <= 0) {
delete _osdMessageSurface;
_osdMessageSurface = nullptr;
}
}
if (_osdIconSurface) {
int dstX = _outputScreenWidth - _osdIconSurface->getWidth() - kOSDIconRightMargin;
int dstY = kOSDIconTopMargin;
// Draw the OSD icon texture.
g_context.getActivePipeline()->drawTexture(_osdIconSurface->getGLTexture(),
dstX, dstY, _osdIconSurface->getWidth(), _osdIconSurface->getHeight());
}
#endif
@ -703,85 +733,130 @@ void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uin
void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
#ifdef USE_OSD
// HACK: Actually no client code should use graphics functions from
// another thread. But the MT-32 emulator still does, thus we need to
// make sure this doesn't happen while a updateScreen call is done.
// another thread. But the MT-32 emulator and network synchronization still do,
// thus we need to make sure this doesn't happen while a updateScreen call is done.
Common::StackLock lock(_osdMutex);
// Slip up the lines.
_osdMessageChangeRequest = true;
_osdMessageNextData = msg;
#endif
}
#ifdef USE_OSD
void OpenGLGraphicsManager::osdMessageUpdateSurface() {
// Split up the lines.
Common::Array<Common::String> osdLines;
Common::StringTokenizer tokenizer(msg, "\n");
Common::StringTokenizer tokenizer(_osdMessageNextData, "\n");
while (!tokenizer.empty()) {
osdLines.push_back(tokenizer.nextToken());
}
// Do the actual drawing like the SDL backend.
const Graphics::Font *font = getFontOSD();
Graphics::Surface *dst = _osd->getSurface();
_osd->fill(0);
_osd->flagDirty();
// Determine a rect which would contain the message string (clipped to the
// screen dimensions).
const int vOffset = 6;
const int lineSpacing = 1;
const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
int width = 0;
int height = lineHeight * osdLines.size() + 2 * vOffset;
uint width = 0;
uint height = lineHeight * osdLines.size() + 2 * vOffset;
for (uint i = 0; i < osdLines.size(); i++) {
width = MAX(width, font->getStringWidth(osdLines[i]) + 14);
width = MAX<uint>(width, font->getStringWidth(osdLines[i]) + 14);
}
// Clip the rect
width = MIN<int>(width, dst->w);
height = MIN<int>(height, dst->h);
width = MIN<uint>(width, _displayWidth);
height = MIN<uint>(height, _displayHeight);
int dstX = (dst->w - width) / 2;
int dstY = (dst->h - height) / 2;
delete _osdMessageSurface;
_osdMessageSurface = nullptr;
_osdMessageSurface = createSurface(_defaultFormatAlpha);
assert(_osdMessageSurface);
// We always filter the osd with GL_LINEAR. This assures it's
// readable in case it needs to be scaled and does not affect it
// otherwise.
_osdMessageSurface->enableLinearFiltering(true);
_osdMessageSurface->allocate(width, height);
Graphics::Surface *dst = _osdMessageSurface->getSurface();
// Draw a dark gray rect.
const uint32 color = dst->format.RGBToColor(40, 40, 40);
dst->fillRect(Common::Rect(dstX, dstY, dstX + width, dstY + height), color);
dst->fillRect(Common::Rect(0, 0, width, height), color);
// Render the message, centered, and in white
// Render the message in white
const uint32 white = dst->format.RGBToColor(255, 255, 255);
for (uint i = 0; i < osdLines.size(); ++i) {
font->drawString(dst, osdLines[i],
dstX, dstY + i * lineHeight + vOffset + lineSpacing, width,
0, i * lineHeight + vOffset + lineSpacing, width,
white, Graphics::kTextAlignCenter);
}
_osdMessageSurface->updateGLTexture();
// Init the OSD display parameters.
_osdAlpha = kOSDInitialAlpha;
_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
#endif
}
_osdMessageAlpha = kOSDMessageInitialAlpha;
_osdMessageFadeStartTime = g_system->getMillis() + kOSDMessageFadeOutDelay;
void OpenGLGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {
#ifdef USE_OSD
_osd->copyRectToTexture(x, y, w, h, buf, pitch);
#endif
// Clear the text update request
_osdMessageNextData.clear();
_osdMessageChangeRequest = false;
}
#endif
void OpenGLGraphicsManager::clearOSD() {
void OpenGLGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface *icon) {
#ifdef USE_OSD
// HACK: Actually no client code should use graphics functions from
// another thread. But the MT-32 emulator still does, thus we need to
// make sure this doesn't happen while a updateScreen call is done.
// another thread. But the MT-32 emulator and network synchronization still do,
// thus we need to make sure this doesn't happen while a updateScreen call is done.
// HACK: We can't make OpenGL calls outside of the main thread. This method
// stores a copy of the icon. The main thread will pick up the changed icon,
// and copy it to an OpenGL texture.
Common::StackLock lock(_osdMutex);
Graphics::Surface *dst = _osd->getSurface();
_osd->fill(0);
_osd->flagDirty();
_osdIconChangeRequest = true;
// Init the OSD display parameters.
_osdAlpha = kOSDInitialAlpha;
_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
_osdIconNextData.free();
_osdIconNextData.copyFrom(*icon);
#endif
}
Graphics::PixelFormat OpenGLGraphicsManager::getOSDFormat() {
return _defaultFormatAlpha;
#ifdef USE_OSD
void OpenGLGraphicsManager::osdIconUpdateSurface() {
delete _osdIconSurface;
_osdIconSurface = nullptr;
if (_osdIconNextData.getPixels()) {
Graphics::Surface *converted = _osdIconNextData.convertTo(_defaultFormatAlpha);
_osdIconNextData.free();
_osdIconSurface = createSurface(_defaultFormatAlpha);
assert(_osdIconSurface);
// We always filter the osd with GL_LINEAR. This assures it's
// readable in case it needs to be scaled and does not affect it
// otherwise.
_osdIconSurface->enableLinearFiltering(true);
_osdIconSurface->allocate(converted->w, converted->h);
Graphics::Surface *dst = _osdIconSurface->getSurface();
// Copy the icon to the texture
dst->copyRectToSurface(*converted, 0, 0, Common::Rect(0, 0, converted->w, converted->h));
converted->free();
delete converted;
_osdIconSurface->updateGLTexture();
}
_osdIconChangeRequest = false;
}
#endif
void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
assert(_gameScreen->hasPalette());
@ -849,22 +924,6 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
_overlay->allocate(overlayWidth, overlayHeight);
_overlay->fill(0);
#ifdef USE_OSD
if (!_osd || _osd->getFormat() != _defaultFormatAlpha) {
delete _osd;
_osd = nullptr;
_osd = createSurface(_defaultFormatAlpha);
assert(_osd);
// We always filter the osd with GL_LINEAR. This assures it's
// readable in case it needs to be scaled and does not affect it
// otherwise.
_osd->enableLinearFiltering(true);
}
_osd->allocate(_overlay->getWidth(), _overlay->getHeight());
_osd->fill(0);
#endif
// Re-setup the scaling for the screen and cursor
recalculateDisplayArea();
recalculateCursorScaling();
@ -949,8 +1008,12 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def
}
#ifdef USE_OSD
if (_osd) {
_osd->recreate();
if (_osdMessageSurface) {
_osdMessageSurface->recreate();
}
if (_osdIconSurface) {
_osdIconSurface->recreate();
}
#endif
}
@ -969,8 +1032,12 @@ void OpenGLGraphicsManager::notifyContextDestroy() {
}
#ifdef USE_OSD
if (_osd) {
_osd->destroy();
if (_osdMessageSurface) {
_osdMessageSurface->destroy();
}
if (_osdIconSurface) {
_osdIconSurface->destroy();
}
#endif