diff options
Diffstat (limited to 'backends/graphics')
20 files changed, 6806 insertions, 0 deletions
diff --git a/backends/graphics/gp2xsdl/gp2xsdl-graphics.cpp b/backends/graphics/gp2xsdl/gp2xsdl-graphics.cpp new file mode 100644 index 0000000000..a1e351251a --- /dev/null +++ b/backends/graphics/gp2xsdl/gp2xsdl-graphics.cpp @@ -0,0 +1,183 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef GP2X + +#include "backends/graphics/gp2xsdl/gp2xsdl-graphics.h" +#include "graphics/scaler/aspect.h" +#include <SDL_gp2x.h> + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"Fullscreen", "1x", GFX_NORMAL}, + {0, 0, 0} +}; + +GP2XSdlGraphicsManager::GP2XSdlGraphicsManager() + : + _adjustZoomOnMouse(false) { + +} + +const OSystem::GraphicsMode *GP2XSdlGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int GP2XSdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_NORMAL; +} + + +bool GP2XSdlGraphicsManager::hasFeature(OSystem::Feature f) { + if (f == OSystem::kFeatureIconifyWindow) + return false; + + return SdlGraphicsManager::hasFeature(f); +} + +void GP2XSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + if (f != OSystem::kFeatureIconifyWindow) + SdlGraphicsManager::setFeatureState(f, enable); +} + +void GP2XSdlGraphicsManager::drawMouse() { + if (!_mouseVisible || !_mouseSurface) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect zoomdst; + SDL_Rect dst; + int scale; + int hotX, hotY; + int tmpScreenWidth, tmpScreenHeight; + + // Temp vars to ensure we zoom to the LCD resolution or greater. + tmpScreenWidth = _videoMode.screenWidth; + tmpScreenHeight = _videoMode.screenHeight; + + if (_videoMode.screenHeight <= 240) { + tmpScreenHeight = 240; + } + + if (_videoMode.screenWidth <= 320) { + tmpScreenWidth = 320; + } + + dst.x = _mouseCurState.x; + dst.y = _mouseCurState.y; + + if (!_overlayVisible) { + scale = _videoMode.scaleFactor; + dst.w = _mouseCurState.vW; + dst.h = _mouseCurState.vH; + hotX = _mouseCurState.vHotX; + hotY = _mouseCurState.vHotY; + } else { + scale = 1; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + hotX = _mouseCurState.rHotX; + hotY = _mouseCurState.rHotY; + } + + // The mouse is undrawn using virtual coordinates, i.e. they may be + // scaled and aspect-ratio corrected. + + _mouseBackup.x = dst.x - hotX; + _mouseBackup.y = dst.y - hotY; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + // We draw the pre-scaled cursor image, so now we need to adjust for + // scaling, shake position and aspect ratio correction manually. + + if (!_overlayVisible) { + dst.y += _currentShakePos; + } + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst.y = real2Aspect(dst.y); + + dst.x = scale * dst.x - _mouseCurState.rHotX; + dst.y = scale * dst.y - _mouseCurState.rHotY; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + + // Hacking about with the zoom around mouse pointer stuff. + if (_adjustZoomOnMouse){ + + zoomdst.w = (tmpScreenWidth / 2); + zoomdst.h = (tmpScreenHeight / 2); + + // Create a zoomed rect centered on the mouse pointer. + // Will pan 1/4 of the screen. + + if (dst.x > ((tmpScreenWidth / 4) * 3)) { + zoomdst.x = (tmpScreenWidth / 2); + } else { + zoomdst.x = (dst.x - (tmpScreenWidth / 4)); + if (zoomdst.x < 0) { + zoomdst.x = 0; + } + } + + if (dst.y > ((tmpScreenHeight / 4) * 3)) { + zoomdst.y = (tmpScreenHeight / 2); + } else { + zoomdst.y = (dst.y - (tmpScreenHeight / 4)); + if (zoomdst.y < 0) { + zoomdst.y = 0; + } + } + SDL_GP2X_Display(&zoomdst); + } else { + + // Make sure we are looking at the whole screen otherwise. + + zoomdst.x = 0; + zoomdst.y = 0; + zoomdst.w = (tmpScreenWidth); + zoomdst.h = (tmpScreenHeight); + + SDL_GP2X_Display(&zoomdst); + }; + + // Note that SDL_BlitSurface() and addDirtyRect() will both perform any + // clipping necessary + + if (SDL_BlitSurface(_mouseSurface, NULL, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + // The screen will be updated using real surface coordinates, i.e. + // they will not be scaled or aspect-ratio corrected. + + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} + +void GP2XSdlGraphicsManager::toggleZoomOnMouse() { + _adjustZoomOnMouse = !_adjustZoomOnMouse; +} + +#endif diff --git a/backends/graphics/gp2xsdl/gp2xsdl-graphics.h b/backends/graphics/gp2xsdl/gp2xsdl-graphics.h new file mode 100644 index 0000000000..776a9b1a0f --- /dev/null +++ b/backends/graphics/gp2xsdl/gp2xsdl-graphics.h @@ -0,0 +1,48 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_GP2X_H +#define BACKENDS_GRAPHICS_SDL_GP2X_H + +#include "backends/graphics/sdl/sdl-graphics.h" + +class GP2XSdlGraphicsManager : public SdlGraphicsManager { +public: + + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual void drawMouse(); + + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + + // Toggles zoom adjust on mouse + void toggleZoomOnMouse(); + +protected: + bool _adjustZoomOnMouse; +}; + +#endif diff --git a/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.cpp b/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.cpp new file mode 100644 index 0000000000..8cb3eed083 --- /dev/null +++ b/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.cpp @@ -0,0 +1,445 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef GP2XWIZ + +#include "backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.h" +#include "backends/events/gp2xsdl/gp2xsdl-events.h" + +#include "common/mutex.h" +#include "graphics/scaler/aspect.h" +#include "graphics/scaler/downscaler.h" + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"1x", "Fullscreen", GFX_NORMAL}, +// {"½x", "Downscale", GFX_HALF}, + {0, 0, 0} +}; + +const OSystem::GraphicsMode *GP2XWIZSdlGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int GP2XWIZSdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_NORMAL; +} + +bool GP2XWIZSdlGraphicsManager::setGraphicsMode(int mode) { + Common::StackLock lock(_graphicsMutex); + + assert(_transactionMode == kTransactionActive); + + if (_oldVideoMode.setup && _oldVideoMode.mode == mode) + return true; + + int newScaleFactor = 1; + + switch (mode) { + case GFX_NORMAL: + newScaleFactor = 1; + break; + case GFX_HALF: + newScaleFactor = 1; + break; + default: + warning("unknown gfx mode %d", mode); + return false; + } + + _transactionDetails.normal1xScaler = (mode == GFX_NORMAL); + if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newScaleFactor) + _transactionDetails.needHotswap = true; + + _transactionDetails.needUpdatescreen = true; + + _videoMode.mode = mode; + _videoMode.scaleFactor = newScaleFactor; + + return true; +} + +void GP2XWIZSdlGraphicsManager::setGraphicsModeIntern() { + Common::StackLock lock(_graphicsMutex); + ScalerProc *newScalerProc = 0; + + switch (_videoMode.mode) { + case GFX_NORMAL: + newScalerProc = Normal1x; + break; + case GFX_HALF: + newScalerProc = DownscaleAllByHalf; + break; + + default: + error("Unknown gfx mode %d", _videoMode.mode); + } + + _scalerProc = newScalerProc; + + if (!_screen || !_hwscreen) + return; + + // Blit everything to the screen + _forceFull = true; + + // Even if the old and new scale factors are the same, we may have a + // different scaler for the cursor now. + blitCursor(); +} + +void GP2XWIZSdlGraphicsManager::initSize(uint w, uint h) { + assert(_transactionMode == kTransactionActive); + + // Avoid redundant res changes + if ((int)w == _videoMode.screenWidth && (int)h == _videoMode.screenHeight) + return; + + _videoMode.screenWidth = w; + _videoMode.screenHeight = h; + if (w > 320 || h > 240){ + setGraphicsMode(GFX_HALF); + setGraphicsModeIntern(); + ((GP2XSdlEventManager *)g_system->getEventManager())->toggleMouseGrab(); + } + + _transactionDetails.sizeChanged = true; +} + +bool GP2XWIZSdlGraphicsManager::loadGFXMode() { + _videoMode.overlayWidth = 320; + _videoMode.overlayHeight = 240; + _videoMode.fullscreen = true; + + if (_videoMode.screenHeight != 200 && _videoMode.screenHeight != 400) + _videoMode.aspectRatioCorrection = false; + + return SdlGraphicsManager::loadGFXMode(); +} + +void GP2XWIZSdlGraphicsManager::drawMouse() { + if (!_mouseVisible || !_mouseSurface) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect dst; + int scale; + int hotX, hotY; + + if (_videoMode.mode == GFX_HALF && !_overlayVisible){ + dst.x = _mouseCurState.x/2; + dst.y = _mouseCurState.y/2; + } else { + dst.x = _mouseCurState.x; + dst.y = _mouseCurState.y; + } + + if (!_overlayVisible) { + scale = _videoMode.scaleFactor; + dst.w = _mouseCurState.vW; + dst.h = _mouseCurState.vH; + hotX = _mouseCurState.vHotX; + hotY = _mouseCurState.vHotY; + } else { + scale = 1; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + hotX = _mouseCurState.rHotX; + hotY = _mouseCurState.rHotY; + } + + // The mouse is undrawn using virtual coordinates, i.e. they may be + // scaled and aspect-ratio corrected. + + _mouseBackup.x = dst.x - hotX; + _mouseBackup.y = dst.y - hotY; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + // We draw the pre-scaled cursor image, so now we need to adjust for + // scaling, shake position and aspect ratio correction manually. + + if (!_overlayVisible) { + dst.y += _currentShakePos; + } + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst.y = real2Aspect(dst.y); + + dst.x = scale * dst.x - _mouseCurState.rHotX; + dst.y = scale * dst.y - _mouseCurState.rHotY; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + + // Note that SDL_BlitSurface() and addDirtyRect() will both perform any + // clipping necessary + + if (SDL_BlitSurface(_mouseSurface, NULL, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + // The screen will be updated using real surface coordinates, i.e. + // they will not be scaled or aspect-ratio corrected. + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} + +void GP2XWIZSdlGraphicsManager::undrawMouse() { + const int x = _mouseBackup.x; + const int y = _mouseBackup.y; + + // When we switch bigger overlay off mouse jumps. Argh! + // This is intended to prevent undrawing offscreen mouse + if (!_overlayVisible && (x >= _videoMode.screenWidth || y >= _videoMode.screenHeight)) + return; + + if (_mouseBackup.w != 0 && _mouseBackup.h != 0){ + if (_videoMode.mode == GFX_HALF && !_overlayVisible){ + addDirtyRect(x*2, y*2, _mouseBackup.w*2, _mouseBackup.h*2); + } else { + addDirtyRect(x, y, _mouseBackup.w, _mouseBackup.h); + } + } +} + +void GP2XWIZSdlGraphicsManager::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + int height, width; + ScalerProc *scalerProc; + int scale1; + +#if defined (DEBUG) && ! defined(_WIN32_WCE) // definitions not available for non-DEBUG here. (needed this to compile in SYMBIAN32 & linux?) + assert(_hwscreen != NULL); + assert(_hwscreen->map->sw_data != NULL); +#endif + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos) { + SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor}; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + + SDL_FillRect(_hwscreen, &blackrect, 0); + + _currentShakePos = _newShakePos; + + _forceFull = true; + } + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_screen && _paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, + _paletteDirtyStart, + _paletteDirtyEnd - _paletteDirtyStart); + + _paletteDirtyEnd = 0; + + _forceFull = true; + } + +#ifdef USE_OSD + // OSD visible (i.e. non-transparent)? + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + // Updated alpha value + const int diff = SDL_GetTicks() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = SDL_ALPHA_TRANSPARENT; + } else { + // Do a linear fade out... + const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; + } + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + _forceFull = true; + } + } +#endif + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _tmpscreen; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + scalerProc = _scalerProc; + scale1 = _videoMode.scaleFactor; + } else { + origSurf = _overlayscreen; + srcSurf = _tmpscreen2; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + scalerProc = Normal1x; + + scale1 = 1; + } + + // Add the area covered by the mouse cursor to the list of dirty rects if + // we have to redraw the mouse. + if (_mouseNeedsRedraw) + undrawMouse(); + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + _dirtyRectList[0].x = 0; + _dirtyRectList[0].y = 0; + _dirtyRectList[0].w = width; + _dirtyRectList[0].h = height; + } + + // Only draw anything if necessary + if (_numDirtyRects > 0 || _mouseNeedsRedraw) { + SDL_Rect *r; + SDL_Rect dst; + uint32 srcPitch, dstPitch; + SDL_Rect *lastRect = _dirtyRectList + _numDirtyRects; + + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + dst.x++; // Shift rect by one since 2xSai needs to access the data around + dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. + + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + SDL_LockSurface(srcSurf); + SDL_LockSurface(_hwscreen); + + srcPitch = srcSurf->pitch; + dstPitch = _hwscreen->pitch; + + for (r = _dirtyRectList; r != lastRect; ++r) { + register int dst_y = r->y + _currentShakePos; + register int dst_h = 0; + register int dst_w = r->w; + register int orig_dst_y = 0; + register int dst_x = r->x; + register int src_y; + register int src_x; + + if (dst_y < height) { + dst_h = r->h; + if (dst_h > height - dst_y) + dst_h = height - dst_y; + + orig_dst_y = dst_y; + src_x = dst_x; + src_y = dst_y; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst_y = real2Aspect(dst_y); + + assert(scalerProc != NULL); + + if (_videoMode.mode == GFX_HALF && scalerProc == DownscaleAllByHalf){ + if (dst_x%2==1){ + dst_x--; + dst_w++; + } + if (dst_y%2==1){ + dst_y--; + dst_h++; + } + src_x = dst_x; + src_y = dst_y; + dst_x = dst_x / 2; + dst_y = dst_y / 2; + } + scalerProc((byte *)srcSurf->pixels + (src_x * 2 + 2) + (src_y + 1) * srcPitch, srcPitch, + (byte *)_hwscreen->pixels + dst_x * 2 + dst_y * dstPitch, dstPitch, dst_w, dst_h); + } + + if (_videoMode.mode == GFX_HALF && scalerProc == DownscaleAllByHalf){ + r->w = r->w / 2; + r->h = dst_h / 2; + } else { + r->w = r->w; + r->h = dst_h; + } + + r->x = dst_x; + r->y = dst_y; + + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection && orig_dst_y < height && !_overlayVisible) + r->h = stretch200To240((uint8 *) _hwscreen->pixels, dstPitch, r->w, r->h, r->x, r->y, orig_dst_y * scale1); +#endif + } + SDL_UnlockSurface(srcSurf); + SDL_UnlockSurface(_hwscreen); + + // Readjust the dirty rect list in case we are doing a full update. + // This is necessary if shaking is active. + if (_forceFull) { + _dirtyRectList[0].y = 0; + _dirtyRectList[0].h = (_videoMode.mode == GFX_HALF) ? effectiveScreenHeight()/2 : effectiveScreenHeight(); + } + + drawMouse(); + +#ifdef USE_OSD + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0); + } +#endif + // Finally, blit all our changes to the screen + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } + + _numDirtyRects = 0; + _forceFull = false; + _mouseNeedsRedraw = false; +} + +void GP2XWIZSdlGraphicsManager::showOverlay() { + if (_videoMode.mode == GFX_HALF){ + _mouseCurState.x = _mouseCurState.x / 2; + _mouseCurState.y = _mouseCurState.y / 2; + } + SdlGraphicsManager::showOverlay(); +} + +void GP2XWIZSdlGraphicsManager::hideOverlay() { + if (_videoMode.mode == GFX_HALF){ + _mouseCurState.x = _mouseCurState.x * 2; + _mouseCurState.y = _mouseCurState.y * 2; + } + SdlGraphicsManager::hideOverlay(); +} + +void GP2XWIZSdlGraphicsManager::warpMouse(int x, int y) { + if (_mouseCurState.x != x || _mouseCurState.y != y) { + if (_videoMode.mode == GFX_HALF && !_overlayVisible){ + x = x / 2; + y = y / 2; + } + } + SdlGraphicsManager::warpMouse(x, y); +} + +#endif diff --git a/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.h b/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.h new file mode 100644 index 0000000000..5f0c739379 --- /dev/null +++ b/backends/graphics/gp2xwizsdl/gp2xwizsdl-graphics.h @@ -0,0 +1,58 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_GP2XWIZ_H +#define BACKENDS_GRAPHICS_SDL_GP2XWIZ_H + +#include "backends/graphics/sdl/sdl-graphics.h" + +// FIXME: For now keep hacks in this header to save polluting the SDL backend. +enum { + GFX_HALF = 12 +}; + +class GP2XWIZSdlGraphicsManager : public SdlGraphicsManager { +public: + virtual void setGraphicsModeIntern(); + virtual bool setGraphicsMode(int mode); + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + + virtual void initSize(uint w, uint h); + + virtual void internUpdateScreen(); + + virtual bool loadGFXMode(); + + virtual void drawMouse(); + virtual void undrawMouse(); + + virtual void showOverlay(); + virtual void hideOverlay(); + + virtual void warpMouse(int x, int y); +}; + +#endif diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h new file mode 100644 index 0000000000..d2ce6534e1 --- /dev/null +++ b/backends/graphics/graphics.h @@ -0,0 +1,91 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_ABSTRACT_H +#define BACKENDS_GRAPHICS_ABSTRACT_H + +#include "common/system.h" +#include "common/noncopyable.h" +#include "common/keyboard.h" + +/** + * Abstract class for graphics manager. Subclasses + * implement the real functionality. + */ +class GraphicsManager : Common::NonCopyable { +public: + virtual ~GraphicsManager() {} + + virtual bool hasFeature(OSystem::Feature f) = 0; + virtual void setFeatureState(OSystem::Feature f, bool enable) = 0; + virtual bool getFeatureState(OSystem::Feature f) = 0; + + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const = 0; + virtual int getDefaultGraphicsMode() const = 0; + virtual bool setGraphicsMode(int mode) = 0; + virtual void resetGraphicsScale() = 0; + virtual int getGraphicsMode() const = 0; +#ifdef USE_RGB_COLOR + virtual Graphics::PixelFormat getScreenFormat() const = 0; + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0; +#endif + virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) = 0; + virtual int getScreenChangeID() const = 0; + + virtual void beginGFXTransaction() = 0; + virtual OSystem::TransactionError endGFXTransaction() = 0; + + virtual int16 getHeight() = 0; + virtual int16 getWidth() = 0; + virtual void setPalette(const byte *colors, uint start, uint num) = 0; + virtual void grabPalette(byte *colors, uint start, uint num) = 0; + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) = 0; + virtual Graphics::Surface *lockScreen() = 0; + virtual void unlockScreen() = 0; + virtual void fillScreen(uint32 col) = 0; + virtual void updateScreen() = 0; + virtual void setShakePos(int shakeOffset) = 0; + virtual void setFocusRectangle(const Common::Rect& rect) = 0; + virtual void clearFocusRectangle() = 0; + + virtual void showOverlay() = 0; + virtual void hideOverlay() = 0; + virtual Graphics::PixelFormat getOverlayFormat() const = 0; + virtual void clearOverlay() = 0; + virtual void grabOverlay(OverlayColor *buf, int pitch) = 0; + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h)= 0; + virtual int16 getOverlayHeight() = 0; + virtual int16 getOverlayWidth() = 0; + + virtual bool showMouse(bool visible) = 0; + virtual void warpMouse(int x, int y) = 0; + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) = 0; + virtual void setCursorPalette(const byte *colors, uint start, uint num) = 0; + virtual void disableCursorPalette(bool disable) = 0; + + virtual void displayMessageOnOSD(const char *msg) {} +}; + +#endif diff --git a/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.cpp b/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.cpp new file mode 100644 index 0000000000..dbedb2e1a7 --- /dev/null +++ b/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.cpp @@ -0,0 +1,495 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef LINUXMOTO + +#include "backends/graphics/linuxmotosdl/linuxmotosdl-graphics.h" +#include "backends/events/linuxmotosdl/linuxmotosdl-events.h" +#include "common/mutex.h" +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/scaler.h" +#include "graphics/scaler/aspect.h" +#include "graphics/scaler/downscaler.h" +#include "graphics/surface.h" + +enum { + GFX_HALF = 12 +}; + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"1x", "Fullscreen", GFX_NORMAL}, + {"½x", "Downscale", GFX_HALF}, + {0, 0, 0} +}; + +const OSystem::GraphicsMode *LinuxmotoSdlGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int LinuxmotoSdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_NORMAL; +} + +bool LinuxmotoSdlGraphicsManager::setGraphicsMode(int mode) { + Common::StackLock lock(_graphicsMutex); + + assert(_transactionMode == kTransactionActive); + + if (_oldVideoMode.setup && _oldVideoMode.mode == mode) + return true; + + int newScaleFactor = 1; + + switch (mode) { + case GFX_NORMAL: + newScaleFactor = 1; + break; + case GFX_HALF: + newScaleFactor = 1; + break; + default: + warning("unknown gfx mode %d", mode); + return false; + } + + _transactionDetails.normal1xScaler = (mode == GFX_NORMAL); + if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newScaleFactor) + _transactionDetails.needHotswap = true; + + _transactionDetails.needUpdatescreen = true; + + _videoMode.mode = mode; + _videoMode.scaleFactor = newScaleFactor; + + return true; +} + +void LinuxmotoSdlGraphicsManager::setGraphicsModeIntern() { + Common::StackLock lock(_graphicsMutex); + ScalerProc *newScalerProc = 0; + + switch (_videoMode.mode) { + case GFX_NORMAL: + newScalerProc = Normal1x; + break; + case GFX_HALF: + newScalerProc = DownscaleAllByHalf; + break; + + default: + error("Unknown gfx mode %d", _videoMode.mode); + } + + _scalerProc = newScalerProc; + + if (!_screen || !_hwscreen) + return; + + // Blit everything to the screen + _forceFull = true; + + // Even if the old and new scale factors are the same, we may have a + // different scaler for the cursor now. + blitCursor(); +} + + +void LinuxmotoSdlGraphicsManager::initSize(uint w, uint h) { + assert(_transactionMode == kTransactionActive); + + // Avoid redundant res changes + if ((int)w == _videoMode.screenWidth && (int)h == _videoMode.screenHeight) + return; + + _videoMode.screenWidth = w; + _videoMode.screenHeight = h; + + if (w > 320 || h > 240) { + setGraphicsMode(GFX_HALF); + setGraphicsModeIntern(); + ((LinuxmotoSdlEventManager *)g_system->getEventManager())->toggleMouseGrab(); + } + + _transactionDetails.sizeChanged = true; +} + +bool LinuxmotoSdlGraphicsManager::loadGFXMode() { + printf("Game ScreenMode = %d*%d\n",_videoMode.screenWidth, _videoMode.screenHeight); + if (_videoMode.screenWidth > 320 || _videoMode.screenHeight > 240) { + _videoMode.aspectRatioCorrection = false; + setGraphicsMode(GFX_HALF); + printf("GraphicsMode set to HALF\n"); + } else { + setGraphicsMode(GFX_NORMAL); + printf("GraphicsMode set to NORMAL\n"); + } + if (_videoMode.mode == GFX_HALF && !_overlayVisible) { + _videoMode.overlayWidth = 320; + _videoMode.overlayHeight = 240; + _videoMode.fullscreen = true; + } else { + + _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; + + if (_videoMode.aspectRatioCorrection) + _videoMode.overlayHeight = real2Aspect(_videoMode.overlayHeight); + + _videoMode.hardwareWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.hardwareHeight = effectiveScreenHeight(); + } + + return SdlGraphicsManager::loadGFXMode(); +} + +void LinuxmotoSdlGraphicsManager::drawMouse() { + if (!_mouseVisible || !_mouseSurface) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect dst; + int scale; + int hotX, hotY; + + if (_videoMode.mode == GFX_HALF && !_overlayVisible) { + dst.x = _mouseCurState.x/2; + dst.y = _mouseCurState.y/2; + } else { + dst.x = _mouseCurState.x; + dst.y = _mouseCurState.y; + } + + if (!_overlayVisible) { + scale = _videoMode.scaleFactor; + dst.w = _mouseCurState.vW; + dst.h = _mouseCurState.vH; + hotX = _mouseCurState.vHotX; + hotY = _mouseCurState.vHotY; + } else { + scale = 1; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + hotX = _mouseCurState.rHotX; + hotY = _mouseCurState.rHotY; + } + + // The mouse is undrawn using virtual coordinates, i.e. they may be + // scaled and aspect-ratio corrected. + + _mouseBackup.x = dst.x - hotX; + _mouseBackup.y = dst.y - hotY; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + // We draw the pre-scaled cursor image, so now we need to adjust for + // scaling, shake position and aspect ratio correction manually. + + if (!_overlayVisible) { + dst.y += _currentShakePos; + } + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst.y = real2Aspect(dst.y); + + dst.x = scale * dst.x - _mouseCurState.rHotX; + dst.y = scale * dst.y - _mouseCurState.rHotY; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + + // Note that SDL_BlitSurface() and addDirtyRect() will both perform any + // clipping necessary + + if (SDL_BlitSurface(_mouseSurface, NULL, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + // The screen will be updated using real surface coordinates, i.e. + // they will not be scaled or aspect-ratio corrected. + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} + +void LinuxmotoSdlGraphicsManager::undrawMouse() { + const int x = _mouseBackup.x; + const int y = _mouseBackup.y; + + // When we switch bigger overlay off mouse jumps. Argh! + // This is intended to prevent undrawing offscreen mouse + if (!_overlayVisible && (x >= _videoMode.screenWidth || y >= _videoMode.screenHeight)) + return; + + if (_mouseBackup.w != 0 && _mouseBackup.h != 0) { + if (_videoMode.mode == GFX_HALF && !_overlayVisible) { + addDirtyRect(x*2, y*2, _mouseBackup.w*2, _mouseBackup.h*2); + } else { + addDirtyRect(x, y, _mouseBackup.w, _mouseBackup.h); + } + } +} + +void LinuxmotoSdlGraphicsManager::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + int height, width; + ScalerProc *scalerProc; + int scale1; + +#if defined (DEBUG) // definitions not available for non-DEBUG here. (needed this to compile in SYMBIAN32 & linux?) + assert(_hwscreen != NULL); + assert(_hwscreen->map->sw_data != NULL); +#endif + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos) { + SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor}; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + + SDL_FillRect(_hwscreen, &blackrect, 0); + + _currentShakePos = _newShakePos; + + _forceFull = true; + } + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_screen && _paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, + _paletteDirtyStart, + _paletteDirtyEnd - _paletteDirtyStart); + + _paletteDirtyEnd = 0; + + _forceFull = true; + } + +#ifdef USE_OSD + // OSD visible (i.e. non-transparent)? + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + // Updated alpha value + const int diff = SDL_GetTicks() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = SDL_ALPHA_TRANSPARENT; + } else { + // Do a linear fade out... + const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; + } + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + _forceFull = true; + } + } +#endif + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _tmpscreen; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + scalerProc = _scalerProc; + scale1 = _videoMode.scaleFactor; + } else { + origSurf = _overlayscreen; + srcSurf = _tmpscreen2; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + scalerProc = Normal1x; + scale1 = 1; + } + + // Add the area covered by the mouse cursor to the list of dirty rects if + // we have to redraw the mouse. + if (_mouseNeedsRedraw) + undrawMouse(); + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + _dirtyRectList[0].x = 0; + _dirtyRectList[0].y = 0; + _dirtyRectList[0].w = width; + _dirtyRectList[0].h = height; + } + + // Only draw anything if necessary + if (_numDirtyRects > 0 || _mouseNeedsRedraw) { + SDL_Rect *r; + SDL_Rect dst; + uint32 srcPitch, dstPitch; + SDL_Rect *lastRect = _dirtyRectList + _numDirtyRects; + + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + dst.x++; // Shift rect by one since 2xSai needs to access the data around + dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. + + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + SDL_LockSurface(srcSurf); + SDL_LockSurface(_hwscreen); + + srcPitch = srcSurf->pitch; + dstPitch = _hwscreen->pitch; + + for (r = _dirtyRectList; r != lastRect; ++r) { + register int dst_y = r->y + _currentShakePos; + register int dst_h = 0; + register int dst_w = r->w; + register int orig_dst_y = 0; + register int dst_x = r->x; + register int src_y; + register int src_x; + + if (dst_y < height) { + dst_h = r->h; + if (dst_h > height - dst_y) + dst_h = height - dst_y; + + orig_dst_y = dst_y; + src_x = dst_x; + src_y = dst_y; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst_y = real2Aspect(dst_y); + + assert(scalerProc != NULL); + + if (_videoMode.mode == GFX_HALF && scalerProc == DownscaleAllByHalf) { + + if (dst_x%2==1) { + dst_x--; + dst_w++; + } + if (dst_y%2==1) { + dst_y--; + dst_h++; + } + + if (dst_w&1) + dst_w++; + if (dst_h&1) + dst_h++; + + src_x = dst_x; + src_y = dst_y; + dst_x = dst_x / 2; + dst_y = dst_y / 2; + } + scalerProc((byte *)srcSurf->pixels + (src_x * 2 + 2) + (src_y + 1) * srcPitch, srcPitch, + (byte *)_hwscreen->pixels + dst_x * 2 + dst_y * dstPitch, dstPitch, dst_w, dst_h); + } + + if (_videoMode.mode == GFX_HALF && scalerProc == DownscaleAllByHalf) { + r->w = r->w / 2; + r->h = dst_h / 2; + } else { + r->w = r->w; + r->h = dst_h; + } + + r->x = dst_x; + r->y = dst_y; + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection && orig_dst_y < height && !_overlayVisible) + r->h = stretch200To240((uint8 *) _hwscreen->pixels, dstPitch, r->w, r->h, r->x, r->y, orig_dst_y * scale1); +#endif + } + SDL_UnlockSurface(srcSurf); + SDL_UnlockSurface(_hwscreen); + + // Readjust the dirty rect list in case we are doing a full update. + // This is necessary if shaking is active. + if (_forceFull) { + _dirtyRectList[0].y = 0; + _dirtyRectList[0].h = (_videoMode.mode == GFX_HALF) ? effectiveScreenHeight()/2 : effectiveScreenHeight(); + } + + drawMouse(); + +#ifdef USE_OSD + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0); + } +#endif + // Finally, blit all our changes to the screen + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } + + _numDirtyRects = 0; + _forceFull = false; + _mouseNeedsRedraw = false; +} + +void LinuxmotoSdlGraphicsManager::showOverlay() { + if (_videoMode.mode == GFX_HALF) { + _mouseCurState.x = _mouseCurState.x / 2; + _mouseCurState.y = _mouseCurState.y / 2; + } + SdlGraphicsManager::showOverlay(); +} + +void LinuxmotoSdlGraphicsManager::hideOverlay() { + if (_videoMode.mode == GFX_HALF) { + _mouseCurState.x = _mouseCurState.x * 2; + _mouseCurState.y = _mouseCurState.y * 2; + } + SdlGraphicsManager::hideOverlay(); +} + +void LinuxmotoSdlGraphicsManager::warpMouse(int x, int y) { + if (_mouseCurState.x != x || _mouseCurState.y != y) { + if (_videoMode.mode == GFX_HALF && !_overlayVisible) { + x = x / 2; + y = y / 2; + } + } + SdlGraphicsManager::warpMouse(x, y); +} + +void LinuxmotoSdlGraphicsManager::adjustMouseEvent(const Common::Event &event) { + if (!event.synthetic) { + Common::Event newEvent(event); + newEvent.synthetic = true; + if (!_overlayVisible) { + if (_videoMode.mode == GFX_HALF) { + event.mouse.x *= 2; + event.mouse.y *= 2; + } + newEvent.mouse.x /= _videoMode.scaleFactor; + newEvent.mouse.y /= _videoMode.scaleFactor; + if (_videoMode.aspectRatioCorrection) + newEvent.mouse.y = aspect2Real(newEvent.mouse.y); + } + g_system->getEventManager()->pushEvent(newEvent); + } +} + +#endif diff --git a/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.h b/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.h new file mode 100644 index 0000000000..1b387ca189 --- /dev/null +++ b/backends/graphics/linuxmotosdl/linuxmotosdl-graphics.h @@ -0,0 +1,50 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_LINUXMOTO_H +#define BACKENDS_GRAPHICS_SDL_LINUXMOTO_H + +#include "backends/graphics/sdl/sdl-graphics.h" + +class LinuxmotoSdlGraphicsManager : public SdlGraphicsManager { +public: + virtual void initSize(uint w, uint h); + virtual void setGraphicsModeIntern(); + virtual bool setGraphicsMode(int mode); + virtual void internUpdateScreen(); + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool loadGFXMode(); + virtual void drawMouse(); + virtual void undrawMouse(); + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void warpMouse(int x, int y); + +protected: + virtual void adjustMouseEvent(const Common::Event &event); +}; + +#endif diff --git a/backends/graphics/null/null-graphics.h b/backends/graphics/null/null-graphics.h new file mode 100644 index 0000000000..4c75a9faba --- /dev/null +++ b/backends/graphics/null/null-graphics.h @@ -0,0 +1,88 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_NULL_H +#define BACKENDS_GRAPHICS_NULL_H + +#include "backends/graphics/graphics.h" + +static const OSystem::GraphicsMode s_noGraphicsModes[] = { {0, 0, 0} }; + +class NullGraphicsManager : GraphicsManager { +public: + virtual ~NullGraphicsManager() {} + + bool hasFeature(OSystem::Feature f) { return false; } + void setFeatureState(OSystem::Feature f, bool enable) {} + bool getFeatureState(OSystem::Feature f) { return false; } + + const OSystem::GraphicsMode *getSupportedGraphicsModes() const { return s_noGraphicsModes; } + int getDefaultGraphicsMode() const { return 0; } + bool setGraphicsMode(int mode) { return true; } + int getGraphicsMode() const { return 0; } + inline Graphics::PixelFormat getScreenFormat() const { + return Graphics::PixelFormat::createFormatCLUT8(); + }; + inline Common::List<Graphics::PixelFormat> getSupportedFormats() { + Common::List<Graphics::PixelFormat> list; + list.push_back(Graphics::PixelFormat::createFormatCLUT8()); + return list; + }; + void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) {} + virtual int getScreenChangeID() const { return 0; } + + void beginGFXTransaction() {} + OSystem::TransactionError endGFXTransaction() { return OSystem::kTransactionSuccess; } + + int16 getHeight() { return 0; } + int16 getWidth() { return 0; } + void setPalette(const byte *colors, uint start, uint num) {} + void grabPalette(byte *colors, uint start, uint num) {} + void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {} + Graphics::Surface *lockScreen() { return NULL; } + void unlockScreen() {} + void fillScreen(uint32 col) {} + void updateScreen() {} + void setShakePos(int shakeOffset) {} + void setFocusRectangle(const Common::Rect& rect) {} + void clearFocusRectangle() {} + + void showOverlay() {} + void hideOverlay() {} + Graphics::PixelFormat getOverlayFormat() const { return Graphics::PixelFormat(); } + void clearOverlay() {} + void grabOverlay(OverlayColor *buf, int pitch) {} + void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) {} + int16 getOverlayHeight() { return 0; } + int16 getOverlayWidth() { return 0; } + + bool showMouse(bool visible) { return !visible; } + void warpMouse(int x, int y) {} + void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) {} + void setCursorPalette(const byte *colors, uint start, uint num) {} + void disableCursorPalette(bool disable) {} +}; + +#endif diff --git a/backends/graphics/opengl/glerrorcheck.cpp b/backends/graphics/opengl/glerrorcheck.cpp new file mode 100644 index 0000000000..ee177f0ac5 --- /dev/null +++ b/backends/graphics/opengl/glerrorcheck.cpp @@ -0,0 +1,69 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DEBUG) && defined(USE_OPENGL) + +#include "backends/graphics/opengl/glerrorcheck.h" +#include "common/debug.h" + +#ifdef WIN32 +#if defined(ARRAYSIZE) && !defined(_WINDOWS_) +#undef ARRAYSIZE +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ARRAYSIZE +#endif + +#ifdef MACOSX +#include <gl.h> +#elif defined(USE_GLES) +#include <GLES/gl.h> +#else +#include <GL/gl.h> +#endif + +static const char *getGlErrStr(GLenum error) { + switch (error) { + case GL_NO_ERROR: return "GL_NO_ERROR"; + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + } + + static char buf[40]; + snprintf(buf, sizeof(buf), "(Unknown GL error code 0x%x)", error); + return buf; +} + +void checkGlError(const char *file, int line) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + warning("%s:%d: GL error: %s", file, line, getGlErrStr(error)); +} + +#endif diff --git a/backends/graphics/opengl/glerrorcheck.h b/backends/graphics/opengl/glerrorcheck.h new file mode 100644 index 0000000000..a94699ce1d --- /dev/null +++ b/backends/graphics/opengl/glerrorcheck.h @@ -0,0 +1,38 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if !defined(DEBUG) + +// If not in debug, do nothing +#define CHECK_GL_ERROR() do {} while (false) + +#else + +// If in debug, check for an error after a GL call +#define CHECK_GL_ERROR() checkGlError(__FILE__, __LINE__) + +void checkGlError(const char *file, int line); + +#endif diff --git a/backends/graphics/opengl/gltexture.cpp b/backends/graphics/opengl/gltexture.cpp new file mode 100644 index 0000000000..e43cbe2266 --- /dev/null +++ b/backends/graphics/opengl/gltexture.cpp @@ -0,0 +1,189 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if defined(USE_OPENGL) + +#include "backends/graphics/opengl/gltexture.h" +#include "backends/graphics/opengl/glerrorcheck.h" + +#include "common/rect.h" +#include "common/array.h" +#include "common/util.h" +#include "common/tokenizer.h" + +// Supported GL extensions +static bool npot_supported = false; + +/*static inline GLint xdiv(int numerator, int denominator) { + assert(numerator < (1 << 16)); + return (numerator << 16) / denominator; +}*/ + +static GLuint nextHigher2(GLuint v) { + if (v == 0) + return 1; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return ++v; +} + +void GLTexture::initGLExtensions() { + static bool inited = false; + + // Return if extensions were already checked + if (inited) + return; + + // Get a string with all extensions + const char* ext_string = + reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + CHECK_GL_ERROR(); + Common::StringTokenizer tokenizer(ext_string, " "); + // Iterate all string tokens + while (!tokenizer.empty()) { + Common::String token = tokenizer.nextToken(); + if (token == "GL_ARB_texture_non_power_of_two") + npot_supported = true; + } + + inited = true; +} + +GLTexture::GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type) + : + _bytesPerPixel(bpp), + _internalFormat(internalFormat), + _glFormat(format), + _glType(type), + _textureWidth(0), + _textureHeight(0), + _realWidth(0), + _realHeight(0), + _refresh(false), + _filter(GL_NEAREST) { + + // Generate the texture ID + glGenTextures(1, &_textureName); CHECK_GL_ERROR(); +} + +GLTexture::~GLTexture() { + // Delete the texture + glDeleteTextures(1, &_textureName); CHECK_GL_ERROR(); +} + +void GLTexture::refresh() { + // Delete previous texture + glDeleteTextures(1, &_textureName); CHECK_GL_ERROR(); + + // Generate the texture ID + glGenTextures(1, &_textureName); CHECK_GL_ERROR(); + _refresh = true; +} + +void GLTexture::allocBuffer(GLuint w, GLuint h) { + _realWidth = w; + _realHeight = h; + + if (w <= _textureWidth && h <= _textureHeight && !_refresh) + // Already allocated a sufficiently large buffer + return; + + if (npot_supported) { + _textureWidth = w; + _textureHeight = h; + } else { + _textureWidth = nextHigher2(w); + _textureHeight = nextHigher2(h); + } + + // Select this OpenGL texture + glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR(); + + // Set the texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _filter); CHECK_GL_ERROR(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _filter); CHECK_GL_ERROR(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); + + // Allocate room for the texture + glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat, + _textureWidth, _textureHeight, 0, _glFormat, _glType, NULL); CHECK_GL_ERROR(); + + _refresh = false; +} + +void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLuint w, GLuint h) { + // Select this OpenGL texture + glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR(); + + // Check if the buffer has its data contiguously + if (static_cast<int>(w) * _bytesPerPixel == pitch && w == _textureWidth) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, + _glFormat, _glType, buf); CHECK_GL_ERROR(); + } else { + // Update the texture row by row + const byte *src = static_cast<const byte *>(buf); + do { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, + w, 1, _glFormat, _glType, src); CHECK_GL_ERROR(); + ++y; + src += pitch; + } while (--h); + } +} + +void GLTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { + // Select this OpenGL texture + glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR(); + + // Calculate the texture rect that will be drawn + const GLfloat texWidth = (GLfloat)_realWidth / _textureWidth;//xdiv(_surface.w, _textureWidth); + const GLfloat texHeight = (GLfloat)_realHeight / _textureHeight;//xdiv(_surface.h, _textureHeight); + const GLfloat texcoords[] = { + 0, 0, + texWidth, 0, + 0, texHeight, + texWidth, texHeight, + }; + glTexCoordPointer(2, GL_FLOAT, 0, texcoords); CHECK_GL_ERROR(); + + // Calculate the screen rect where the texture will be drawn + const GLshort vertices[] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h, + }; + glVertexPointer(2, GL_SHORT, 0, vertices); CHECK_GL_ERROR(); + + // Draw the texture to the screen buffer + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); CHECK_GL_ERROR(); +} + +#endif diff --git a/backends/graphics/opengl/gltexture.h b/backends/graphics/opengl/gltexture.h new file mode 100644 index 0000000000..9864f6816c --- /dev/null +++ b/backends/graphics/opengl/gltexture.h @@ -0,0 +1,115 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef WIN32 +#if defined(ARRAYSIZE) && !defined(_WINDOWS_) +#undef ARRAYSIZE +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ARRAYSIZE +#endif + +#if defined(USE_GLES) +#include <GLES/gl.h> +#include <GLES/glext.h> +#elif defined(MACOSX) +#include <gl.h> +#include <glext.h> +#else +#include <GL/gl.h> +#include <GL/glext.h> +#endif + +#include "graphics/surface.h" + +#include "common/rect.h" +#include "common/array.h" + +/** + * OpenGL texture manager class + */ +class GLTexture { +public: + /** + * Initialize OpenGL Extensions + */ + static void initGLExtensions(); + + GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type); + virtual ~GLTexture(); + + /** + * Refresh the texture after a context change. The + * process will be completed on next allocBuffer call. + */ + virtual void refresh(); + + /** + * Allocates memory needed for the given size. + */ + virtual void allocBuffer(GLuint width, GLuint height); + + /** + * Updates the texture pixels. + */ + virtual void updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, + GLuint w, GLuint h); + + /** + * Draws the texture to the screen buffer. + */ + virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h); + + /** + * Get the texture width. + */ + GLuint getWidth() const { return _realWidth; } + + /** + * Get the texture height. + */ + GLuint getHeight() const { return _realHeight; } + + /** + * Set the texture filter. + * @filter the filter type, GL_NEAREST or GL_LINEAR + */ + void setFilter(GLint filter) { _filter = filter; } + +protected: + const byte _bytesPerPixel; + const GLenum _internalFormat; + const GLenum _glFormat; + const GLenum _glType; + + GLuint _realWidth; + GLuint _realHeight; + GLuint _textureName; + GLuint _textureWidth; + GLuint _textureHeight; + GLint _filter; + bool _refresh; +}; diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp new file mode 100644 index 0000000000..f1b6cdd410 --- /dev/null +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -0,0 +1,1323 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if defined(USE_OPENGL) + +#include "backends/graphics/opengl/opengl-graphics.h" +#include "backends/graphics/opengl/glerrorcheck.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/mutex.h" +#include "common/translation.h" +#include "graphics/font.h" +#include "graphics/fontman.h" + +OpenGLGraphicsManager::OpenGLGraphicsManager() + : +#ifdef USE_OSD + _osdTexture(0), _osdAlpha(0), _osdFadeStartTime(0), +#endif + _gameTexture(0), _overlayTexture(0), _cursorTexture(0), + _screenChangeCount(1 << (sizeof(int) * 8 - 2)), _screenNeedsRedraw(false), + _shakePos(0), + _overlayVisible(false), _overlayNeedsRedraw(false), + _transactionMode(kTransactionNone), + _cursorNeedsRedraw(false), _cursorPaletteDisabled(true), + _cursorVisible(false), _cursorKeyColor(0), + _cursorTargetScale(1), + _formatBGR(false), + _aspectX(0), _aspectY(0), _aspectWidth(0), _aspectHeight(0) { + + memset(&_oldVideoMode, 0, sizeof(_oldVideoMode)); + memset(&_videoMode, 0, sizeof(_videoMode)); + memset(&_transactionDetails, 0, sizeof(_transactionDetails)); + + _videoMode.mode = OpenGL::GFX_DOUBLESIZE; + _videoMode.scaleFactor = 2; + _videoMode.fullscreen = ConfMan.getBool("fullscreen"); + _videoMode.antialiasing = false; + _videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio") ? kAspectRatioConserve : kAspectRatioNone; + + _gamePalette = (byte *)calloc(sizeof(byte) * 4, 256); + _cursorPalette = (byte *)calloc(sizeof(byte) * 4, 256); + + // Register the graphics manager as a event observer + g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false); +} + +OpenGLGraphicsManager::~OpenGLGraphicsManager() { + // Unregister the event observer + if (g_system->getEventManager()->getEventDispatcher() != NULL) + g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); + + free(_gamePalette); + free(_cursorPalette); + + if (_gameTexture != NULL) + delete _gameTexture; + if (_overlayTexture != NULL) + delete _overlayTexture; + if (_cursorTexture != NULL) + delete _cursorTexture; +} + +// +// Feature +// + +bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) { + return + (f == OSystem::kFeatureAspectRatioCorrection) || + (f == OSystem::kFeatureCursorHasPalette); +} + +void OpenGLGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureAspectRatioCorrection: + setAspectRatioCorrection(enable ? -1 : 0); + break; + default: + break; + } +} + +bool OpenGLGraphicsManager::getFeatureState(OSystem::Feature f) { + return false; +} + +// +// Screen format and modes +// + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"gl1x", _s("OpenGL Normal"), OpenGL::GFX_NORMAL}, +#ifdef USE_SCALERS + {"gl2x", "OpenGL 2x", OpenGL::GFX_DOUBLESIZE}, + {"gl3x", "OpenGL 3x", OpenGL::GFX_TRIPLESIZE}, +#endif + {0, 0, 0} +}; + +const OSystem::GraphicsMode *OpenGLGraphicsManager::supportedGraphicsModes() { + return s_supportedGraphicsModes; +} + +const OSystem::GraphicsMode *OpenGLGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int OpenGLGraphicsManager::getDefaultGraphicsMode() const { + return OpenGL::GFX_NORMAL; +} + +bool OpenGLGraphicsManager::setGraphicsMode(int mode) { + assert(_transactionMode == kTransactionActive); + + if (_oldVideoMode.setup && _oldVideoMode.mode == mode) + return true; + + int newScaleFactor = 1; + + switch (mode) { + case OpenGL::GFX_NORMAL: + newScaleFactor = 1; + break; +#ifdef USE_SCALERS + case OpenGL::GFX_DOUBLESIZE: + newScaleFactor = 2; + break; + case OpenGL::GFX_TRIPLESIZE: + newScaleFactor = 3; + break; +#endif + default: + warning("unknown gfx mode %d", mode); + return false; + } + + if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newScaleFactor) + _transactionDetails.needRefresh = true; + + _transactionDetails.needUpdatescreen = true; + + _videoMode.mode = mode; + _videoMode.scaleFactor = newScaleFactor; + + return true; +} + +int OpenGLGraphicsManager::getGraphicsMode() const { + assert (_transactionMode == kTransactionNone); + return _videoMode.mode; +} + +void OpenGLGraphicsManager::resetGraphicsScale() { + setScale(1); +} + +#ifdef USE_RGB_COLOR + +Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const { + return _screenFormat; +} + +#endif + +void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) { + assert(_transactionMode == kTransactionActive); + +#ifdef USE_RGB_COLOR + Graphics::PixelFormat newFormat; + if (!format) + newFormat = Graphics::PixelFormat::createFormatCLUT8(); + else + newFormat = *format; + + assert(newFormat.bytesPerPixel > 0); + + // Avoid redundant format changes + if (newFormat != _videoMode.format) { + _videoMode.format = newFormat; + _transactionDetails.formatChanged = true; + _screenFormat = newFormat; + } +#endif + + // Avoid redundant res changes + if ((int)width == _videoMode.screenWidth && (int)height == _videoMode.screenHeight) + return; + + _videoMode.screenWidth = width; + _videoMode.screenHeight = height; + + _transactionDetails.sizeChanged = true; +} + +int OpenGLGraphicsManager::getScreenChangeID() const { + return _screenChangeCount; +} + +// +// GFX +// + +void OpenGLGraphicsManager::beginGFXTransaction() { + assert(_transactionMode == kTransactionNone); + + _transactionMode = kTransactionActive; + _transactionDetails.sizeChanged = false; + _transactionDetails.needRefresh = false; + _transactionDetails.needUpdatescreen = false; + _transactionDetails.filterChanged = false; +#ifdef USE_RGB_COLOR + _transactionDetails.formatChanged = false; +#endif + + _oldVideoMode = _videoMode; +} + +OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() { + int errors = OSystem::kTransactionSuccess; + + assert(_transactionMode != kTransactionNone); + + if (_transactionMode == kTransactionRollback) { + if (_videoMode.fullscreen != _oldVideoMode.fullscreen) { + errors |= OSystem::kTransactionFullscreenFailed; + + _videoMode.fullscreen = _oldVideoMode.fullscreen; + } else if (_videoMode.aspectRatioCorrection != _oldVideoMode.aspectRatioCorrection) { + errors |= OSystem::kTransactionAspectRatioFailed; + + _videoMode.aspectRatioCorrection = _oldVideoMode.aspectRatioCorrection; + } else if (_videoMode.mode != _oldVideoMode.mode) { + errors |= OSystem::kTransactionModeSwitchFailed; + + _videoMode.mode = _oldVideoMode.mode; + _videoMode.scaleFactor = _oldVideoMode.scaleFactor; +#ifdef USE_RGB_COLOR + } else if (_videoMode.format != _oldVideoMode.format) { + errors |= OSystem::kTransactionFormatNotSupported; + + _videoMode.format = _oldVideoMode.format; + _screenFormat = _videoMode.format; +#endif + } else if (_videoMode.screenWidth != _oldVideoMode.screenWidth || _videoMode.screenHeight != _oldVideoMode.screenHeight) { + errors |= OSystem::kTransactionSizeChangeFailed; + + _videoMode.screenWidth = _oldVideoMode.screenWidth; + _videoMode.screenHeight = _oldVideoMode.screenHeight; + _videoMode.overlayWidth = _oldVideoMode.overlayWidth; + _videoMode.overlayHeight = _oldVideoMode.overlayHeight; + } + + if (_videoMode.fullscreen == _oldVideoMode.fullscreen && + _videoMode.aspectRatioCorrection == _oldVideoMode.aspectRatioCorrection && + _videoMode.mode == _oldVideoMode.mode && + _videoMode.screenWidth == _oldVideoMode.screenWidth && + _videoMode.screenHeight == _oldVideoMode.screenHeight) { + + _oldVideoMode.setup = false; + } + } + + if (_transactionDetails.sizeChanged || _transactionDetails.needRefresh) { + unloadGFXMode(); + if (!loadGFXMode()) { + if (_oldVideoMode.setup) { + _transactionMode = kTransactionRollback; + errors |= endGFXTransaction(); + } + } else { + clearOverlay(); + + _videoMode.setup = true; + _screenChangeCount++; + } +#ifdef USE_RGB_COLOR + } else if (_transactionDetails.filterChanged || _transactionDetails.formatChanged) { +#else + } else if (_transactionDetails.filterChanged) { +#endif + loadTextures(); + internUpdateScreen(); + } else if (_transactionDetails.needUpdatescreen) { + internUpdateScreen(); + } + + _transactionMode = kTransactionNone; + return (OSystem::TransactionError)errors; +} + +// +// Screen +// + +int16 OpenGLGraphicsManager::getHeight() { + return _videoMode.screenHeight; +} + +int16 OpenGLGraphicsManager::getWidth() { + return _videoMode.screenWidth; +} + +void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) { + assert(colors); + +#ifdef USE_RGB_COLOR + assert(_screenFormat.bytesPerPixel == 1); +#endif + + // Save the screen palette + memcpy(_gamePalette + start * 4, colors, num * 4); + + _screenNeedsRedraw = true; + + if (_cursorPaletteDisabled) + _cursorNeedsRedraw = true; +} + +void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) { + assert(colors); + +#ifdef USE_RGB_COLOR + assert(_screenFormat.bytesPerPixel == 1); +#endif + + // Copies current palette to buffer + memcpy(colors, _gamePalette + start * 4, num * 4); +} + +void OpenGLGraphicsManager::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { + assert(x >= 0 && x < _screenData.w); + assert(y >= 0 && y < _screenData.h); + assert(h > 0 && y + h <= _screenData.h); + assert(w > 0 && x + w <= _screenData.w); + + // Copy buffer data to game screen internal buffer + const byte *src = buf; + byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch; + for (int i = 0; i < h; i++) { + memcpy(dst + x * _screenData.bytesPerPixel, src, w * _screenData.bytesPerPixel); + src += pitch; + dst += _screenData.pitch; + } + + // Extend dirty area if not full screen redraw is flagged + if (!_screenNeedsRedraw) { + const Common::Rect dirtyRect(x, y, x + w, y + h); + _screenDirtyRect.extend(dirtyRect); + } +} + +Graphics::Surface *OpenGLGraphicsManager::lockScreen() { + return &_screenData; +} + +void OpenGLGraphicsManager::unlockScreen() { + _screenNeedsRedraw = true; +} + +void OpenGLGraphicsManager::fillScreen(uint32 col) { + if (_gameTexture == NULL) + return; + + if (_screenFormat.bytesPerPixel == 1) { + memset(_screenData.pixels, col, _screenData.h * _screenData.pitch); + } else if (_screenFormat.bytesPerPixel == 2) { + uint16 *pixels = (uint16 *)_screenData.pixels; + uint16 col16 = (uint16)col; + for (int i = 0; i < _screenData.w * _screenData.h; i++) { + pixels[i] = col16; + } + } else if (_screenFormat.bytesPerPixel == 3) { + uint8 *pixels = (uint8 *)_screenData.pixels; + byte r = (col >> 16) & 0xFF; + byte g = (col >> 8) & 0xFF; + byte b = col & 0xFF; + for (int i = 0; i < _screenData.w * _screenData.h; i++) { + pixels[0] = r; + pixels[1] = g; + pixels[2] = b; + pixels += 3; + } + } else if (_screenFormat.bytesPerPixel == 4) { + uint32 *pixels = (uint32 *)_screenData.pixels; + for (int i = 0; i < _screenData.w * _screenData.h; i++) { + pixels[i] = col; + } + } + + _screenNeedsRedraw = true; +} + +void OpenGLGraphicsManager::updateScreen() { + assert (_transactionMode == kTransactionNone); + internUpdateScreen(); +} + +void OpenGLGraphicsManager::setShakePos(int shakeOffset) { + assert (_transactionMode == kTransactionNone); + _shakePos = shakeOffset; +} + +void OpenGLGraphicsManager::setFocusRectangle(const Common::Rect& rect) { + +} + +void OpenGLGraphicsManager::clearFocusRectangle() { + +} + +// +// Overlay +// + +void OpenGLGraphicsManager::showOverlay() { + assert (_transactionMode == kTransactionNone); + + if (_overlayVisible) + return; + + _overlayVisible = true; + + clearOverlay(); +} + +void OpenGLGraphicsManager::hideOverlay() { + assert (_transactionMode == kTransactionNone); + + if (!_overlayVisible) + return; + + _overlayVisible = false; + + clearOverlay(); +} + +Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const { + return _overlayFormat; +} + +void OpenGLGraphicsManager::clearOverlay() { + // Set all pixels to 0 + memset(_overlayData.pixels, 0, _overlayData.h * _overlayData.pitch); + _overlayNeedsRedraw = true; +} + +void OpenGLGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) { + assert(_overlayData.bytesPerPixel == sizeof(buf[0])); + const byte *src = (byte *)_overlayData.pixels; + for (int i = 0; i < _overlayData.h; i++) { + // Copy overlay data to buffer + memcpy(buf, src, _overlayData.pitch); + buf += pitch; + src += _overlayData.pitch; + } +} + +void OpenGLGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { + assert (_transactionMode == kTransactionNone); + + if (_overlayTexture == NULL) + return; + + // Clip the coordinates + if (x < 0) { + w += x; + buf -= x; + x = 0; + } + + if (y < 0) { + h += y; buf -= y * pitch; + y = 0; + } + + if (w > _overlayData.w - x) + w = _overlayData.w - x; + + if (h > _overlayData.h - y) + h = _overlayData.h - y; + + if (w <= 0 || h <= 0) + return; + + if (_overlayFormat.aBits() == 1) { + // Copy buffer with the alpha bit on for all pixels for correct + // overlay drawing. + const uint16 *src = (const uint16 *)buf; + uint16 *dst = (uint16 *)_overlayData.pixels + y * _overlayData.w + x; + for (int i = 0; i < h; i++) { + for (int e = 0; e < w; e++) + dst[e] = src[e] | 0x1; + src += pitch; + dst += _overlayData.w; + } + } else { + // Copy buffer data to internal overlay surface + const byte *src = (const byte *)buf; + byte *dst = (byte *)_overlayData.pixels + y * _overlayData.pitch; + for (int i = 0; i < h; i++) { + memcpy(dst + x * _overlayData.bytesPerPixel, src, w * _overlayData.bytesPerPixel); + src += pitch * sizeof(buf[0]); + dst += _overlayData.pitch; + } + } + + // Extend dirty area if not full screen redraw is flagged + if (!_overlayNeedsRedraw) { + const Common::Rect dirtyRect(x, y, x + w, y + h); + _overlayDirtyRect.extend(dirtyRect); + } +} + +int16 OpenGLGraphicsManager::getOverlayHeight() { + return _videoMode.overlayHeight; +} + +int16 OpenGLGraphicsManager::getOverlayWidth() { + return _videoMode.overlayWidth; +} + +// +// Cursor +// + +bool OpenGLGraphicsManager::showMouse(bool visible) { + if (_cursorVisible == visible) + return visible; + + bool last = _cursorVisible; + _cursorVisible = visible; + + return last; +} + +void OpenGLGraphicsManager::setMousePos(int x, int y) { + _cursorState.x = x; + _cursorState.y = y; +} + +void OpenGLGraphicsManager::warpMouse(int x, int y) { + setMousePos(x, y); +} + +void OpenGLGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) { +#ifdef USE_RGB_COLOR + if (format) + _cursorFormat = *format; + else + _cursorFormat = Graphics::PixelFormat::createFormatCLUT8(); +#else + assert(keycolor <= 255); + _cursorFormat = Graphics::PixelFormat::createFormatCLUT8(); +#endif + + // Allocate space for cursor data + if (_cursorData.w != w || _cursorData.h != h) + _cursorData.create(w, h, _cursorFormat.bytesPerPixel); + + // Save cursor data + memcpy(_cursorData.pixels, buf, h * _cursorData.pitch); + + // Set cursor info + _cursorState.w = w; + _cursorState.h = h; + _cursorState.hotX = hotspotX; + _cursorState.hotY = hotspotY; + _cursorKeyColor = keycolor; + _cursorTargetScale = cursorTargetScale; + _cursorNeedsRedraw = true; + + refreshCursorScale(); +} + +void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) { + assert(colors); + + // Save the cursor palette + memcpy(_cursorPalette + start * 4, colors, num * 4); + + _cursorPaletteDisabled = false; + _cursorNeedsRedraw = true; +} + +void OpenGLGraphicsManager::disableCursorPalette(bool disable) { + _cursorPaletteDisabled = disable; + _cursorNeedsRedraw = true; +} + +// +// Misc +// + +void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) { + assert (_transactionMode == kTransactionNone); + assert(msg); + + // The font we are going to use: + const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kOSDFont); + + if (_osdSurface.w != _osdTexture->getWidth() || _osdSurface.h != _osdTexture->getHeight()) + _osdSurface.create(_osdTexture->getWidth(), _osdTexture->getHeight(), 2); + else + // Clear everything + memset(_osdSurface.pixels, 0, _osdSurface.h * _osdSurface.pitch); + + // Split the message into separate lines. + Common::Array<Common::String> lines; + const char *ptr; + for (ptr = msg; *ptr; ++ptr) { + if (*ptr == '\n') { + lines.push_back(Common::String(msg, ptr - msg)); + msg = ptr + 1; + } + } + lines.push_back(Common::String(msg, ptr - msg)); + + // 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 * lines.size() + 2 * vOffset; + for (uint i = 0; i < lines.size(); i++) { + width = MAX(width, font->getStringWidth(lines[i]) + 14); + } + + // Clip the rect + if (width > _osdSurface.w) + width = _osdSurface.w; + if (height > _osdSurface.h) + height = _osdSurface.h; + + int dstX = (_osdSurface.w - width) / 2; + int dstY = (_osdSurface.h - height) / 2; + + // Draw a dark gray rect + uint16 color = 0x294B; + uint16 *dst = (uint16 *)_osdSurface.pixels + dstY * _osdSurface.w + dstX; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) + dst[j] = color; + dst += _osdSurface.w; + } + + // Render the message, centered, and in white + for (uint i = 0; i < lines.size(); i++) { + font->drawString(&_osdSurface, lines[i], + dstX, dstY + i * lineHeight + vOffset + lineSpacing, width, + 0xFFFF, Graphics::kTextAlignCenter); + } + + // Update the texture + _osdTexture->updateBuffer(_osdSurface.pixels, _osdSurface.pitch, 0, 0, + _osdSurface.w, _osdSurface.h); + + // Init the OSD display parameters, and the fade out + _osdAlpha = kOSDInitialAlpha; + _osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay; +} + +// +// Intern +// + +void OpenGLGraphicsManager::refreshGameScreen() { + if (_screenNeedsRedraw) + _screenDirtyRect = Common::Rect(0, 0, _screenData.w, _screenData.h); + + int x = _screenDirtyRect.left; + int y = _screenDirtyRect.top; + int w = _screenDirtyRect.width(); + int h = _screenDirtyRect.height(); + + if (_screenData.bytesPerPixel == 1) { + // Create a temporary RGB888 surface + byte *surface = new byte[w * h * 3]; + + // Convert the paletted buffer to RGB888 + const byte *src = (byte *)_screenData.pixels + y * _screenData.pitch; + src += x * _screenData.bytesPerPixel; + byte *dst = surface; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + dst[0] = _gamePalette[src[j] * 4]; + dst[1] = _gamePalette[src[j] * 4 + 1]; + dst[2] = _gamePalette[src[j] * 4 + 2]; + dst += 3; + } + src += _screenData.pitch; + } + + // Update the texture + _gameTexture->updateBuffer(surface, w * 3, x, y, w, h); + + // Free the temp surface + delete[] surface; + } else { + // Update the texture + _gameTexture->updateBuffer((byte *)_screenData.pixels + y * _screenData.pitch + + x * _screenData.bytesPerPixel, _screenData.pitch, x, y, w, h); + } + + _screenNeedsRedraw = false; + _screenDirtyRect = Common::Rect(); +} + +void OpenGLGraphicsManager::refreshOverlay() { + if (_overlayNeedsRedraw) + _overlayDirtyRect = Common::Rect(0, 0, _overlayData.w, _overlayData.h); + + int x = _overlayDirtyRect.left; + int y = _overlayDirtyRect.top; + int w = _overlayDirtyRect.width(); + int h = _overlayDirtyRect.height(); + + if (_overlayData.bytesPerPixel == 1) { + // Create a temporary RGB888 surface + byte *surface = new byte[w * h * 3]; + + // Convert the paletted buffer to RGB888 + const byte *src = (byte *)_overlayData.pixels + y * _overlayData.pitch; + src += x * _overlayData.bytesPerPixel; + byte *dst = surface; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + dst[0] = _gamePalette[src[j] * 4]; + dst[1] = _gamePalette[src[j] * 4 + 1]; + dst[2] = _gamePalette[src[j] * 4 + 2]; + dst += 3; + } + src += _screenData.pitch; + } + + // Update the texture + _overlayTexture->updateBuffer(surface, w * 3, x, y, w, h); + + // Free the temp surface + delete[] surface; + } else { + // Update the texture + _overlayTexture->updateBuffer((byte *)_overlayData.pixels + y * _overlayData.pitch + + x * _overlayData.bytesPerPixel, _overlayData.pitch, x, y, w, h); + } + + _overlayNeedsRedraw = false; + _overlayDirtyRect = Common::Rect(); +} + +void OpenGLGraphicsManager::refreshCursor() { + _cursorNeedsRedraw = false; + + if (_cursorFormat.bytesPerPixel == 1) { + // Create a temporary RGBA8888 surface + byte *surface = new byte[_cursorState.w * _cursorState.h * 4]; + memset(surface, 0, _cursorState.w * _cursorState.h * 4); + + // Select palette + byte *palette; + if (_cursorPaletteDisabled) + palette = _gamePalette; + else + palette = _cursorPalette; + + // Convert the paletted cursor to RGBA8888 + const byte *src = (byte *)_cursorData.pixels; + byte *dst = surface; + for (int i = 0; i < _cursorState.w * _cursorState.h; i++) { + // Check for keycolor + if (src[i] != _cursorKeyColor) { + dst[0] = palette[src[i] * 4]; + dst[1] = palette[src[i] * 4 + 1]; + dst[2] = palette[src[i] * 4 + 2]; + dst[3] = 255; + } + dst += 4; + } + + // Allocate a texture big enough for cursor + _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h); + + // Update the texture with new cursor + _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h); + + // Free the temp surface + delete[] surface; + } +} + +void OpenGLGraphicsManager::refreshCursorScale() { + // Get the window minimum scale factor. The cursor will mantain its original aspect + // ratio, and we do not want it to get too big if only one dimension is resized + uint screenScaleFactor = MIN(_videoMode.hardwareWidth * 10000 / _videoMode.screenWidth, + _videoMode.hardwareHeight * 10000 / _videoMode.screenHeight); + + // Do not scale cursor if original size is used + if (_videoMode.aspectRatioCorrection == kAspectRatioOriginal) + screenScaleFactor = _videoMode.scaleFactor * 10000; + + if ((uint)_cursorTargetScale * 10000 >= screenScaleFactor && (uint)_videoMode.scaleFactor * 10000 >= screenScaleFactor) { + // If the cursor target scale and the video mode scale factor are bigger than + // the current window scale, do not scale the cursor for the overlay + _cursorState.rW = _cursorState.w; + _cursorState.rH = _cursorState.h; + _cursorState.rHotX = _cursorState.hotX; + _cursorState.rHotY = _cursorState.hotY; + } else { + // Otherwise, scale the cursor for the overlay + int targetScaleFactor = MIN(_cursorTargetScale, _videoMode.scaleFactor); + int actualFactor = screenScaleFactor - (targetScaleFactor - 1) * 10000; + _cursorState.rW = (int16)(_cursorState.w * actualFactor / 10000); + _cursorState.rH = (int16)(_cursorState.h * actualFactor / 10000); + _cursorState.rHotX = (int16)(_cursorState.hotX * actualFactor / 10000); + _cursorState.rHotY = (int16)(_cursorState.hotY * actualFactor / 10000); + } + + // Always scale the cursor for the game + _cursorState.vW = (int16)(_cursorState.w * screenScaleFactor / 10000); + _cursorState.vH = (int16)(_cursorState.h * screenScaleFactor / 10000); + _cursorState.vHotX = (int16)(_cursorState.hotX * screenScaleFactor / 10000); + _cursorState.vHotY = (int16)(_cursorState.hotY * screenScaleFactor / 10000); +} + +void OpenGLGraphicsManager::refreshAspectRatio() { + if (_videoMode.aspectRatioCorrection == kAspectRatioOriginal) { + _aspectWidth = _videoMode.overlayWidth; + _aspectHeight = _videoMode.overlayHeight; + } else { + _aspectWidth = _videoMode.hardwareWidth; + _aspectHeight = _videoMode.hardwareHeight; + + uint aspectRatio = _videoMode.hardwareWidth * 10000 / _videoMode.hardwareHeight; + uint desiredAspectRatio = getAspectRatio(); + + // Adjust one screen dimension for mantaining the aspect ratio + if (aspectRatio < desiredAspectRatio) + _aspectHeight = _aspectWidth * 10000 / desiredAspectRatio; + else if (aspectRatio > desiredAspectRatio) + _aspectWidth = _aspectHeight * desiredAspectRatio / 10000; + } + + // Adjust x and y for centering the screen + _aspectX = (_videoMode.hardwareWidth - _aspectWidth) / 2; + _aspectY = (_videoMode.hardwareHeight - _aspectHeight) / 2; +} + +void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &gltype) { + if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 + bpp = 4; + intFormat = GL_RGBA; + glFormat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + } else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0)) { // RGB888 + bpp = 3; + intFormat = GL_RGB; + glFormat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) { // RGB565 + bpp = 2; + intFormat = GL_RGB; + glFormat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)) { // RGB5551 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + } else if (pixelFormat.bytesPerPixel == 1) { // CLUT8 + // If uses a palette, create texture as RGB888. The pixel data will be converted + // later. + bpp = 3; + intFormat = GL_RGB; + glFormat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; +#ifndef USE_GLES + } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888 + bpp = 4; + intFormat = GL_RGBA; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; + } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV; + } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) { // ABGR8888 + bpp = 4; + intFormat = GL_RGBA; + glFormat = GL_RGBA; + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; + } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) { // BGRA8888 + bpp = 4; + intFormat = GL_RGBA; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_BYTE; + } else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0)) { // BGR888 + bpp = 3; + intFormat = GL_RGB; + glFormat = GL_BGR; + gltype = GL_UNSIGNED_BYTE; + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565 + bpp = 2; + intFormat = GL_RGB; + glFormat = GL_BGR; + gltype = GL_UNSIGNED_SHORT_5_6_5; + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12)) { // ABGR4444 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4_REV; + } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0)) { // BGRA4444 + bpp = 2; + intFormat = GL_RGBA; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; +#endif + } else { + error("OpenGLGraphicsManager: Pixel format not supported"); + } +} + +void OpenGLGraphicsManager::internUpdateScreen() { + // Clear the screen buffer + glClear(GL_COLOR_BUFFER_BIT); CHECK_GL_ERROR(); + + if (_screenNeedsRedraw || !_screenDirtyRect.isEmpty()) + // Refresh texture if dirty + refreshGameScreen(); + + int scaleFactor = _videoMode.hardwareHeight / _videoMode.screenHeight; + + glPushMatrix(); + + // Adjust game screen shake position + glTranslatef(0, _shakePos * scaleFactor, 0); CHECK_GL_ERROR(); + + // Draw the game screen + _gameTexture->drawTexture(_aspectX, _aspectY, _aspectWidth, _aspectHeight); + + glPopMatrix(); + + if (_overlayVisible) { + if (_overlayNeedsRedraw || !_overlayDirtyRect.isEmpty()) + // Refresh texture if dirty + refreshOverlay(); + + // Draw the overlay + _overlayTexture->drawTexture(_aspectX, _aspectY, _aspectWidth, _aspectHeight); + } + + if (_cursorVisible) { + if (_cursorNeedsRedraw) + // Refresh texture if dirty + refreshCursor(); + + glPushMatrix(); + + // Adjust mouse shake position, unless the overlay is visible + glTranslatef(0, _overlayVisible ? 0 : _shakePos * scaleFactor, 0); CHECK_GL_ERROR(); + + // Draw the cursor + if (_overlayVisible) + _cursorTexture->drawTexture(_cursorState.x - _cursorState.rHotX, + _cursorState.y - _cursorState.rHotY, _cursorState.rW, _cursorState.rH); + else + _cursorTexture->drawTexture(_cursorState.x - _cursorState.vHotX, + _cursorState.y - _cursorState.vHotY, _cursorState.vW, _cursorState.vH); + + glPopMatrix(); + } + +#ifdef USE_OSD + if (_osdAlpha > 0) { + // Update alpha value + const int diff = g_system->getMillis() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = 0; + } else { + // Do a fade out + _osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration; + } + } + // Set the osd transparency + glColor4f(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f); CHECK_GL_ERROR(); + + // Draw the osd texture + _osdTexture->drawTexture(0, 0, _videoMode.hardwareWidth, _videoMode.hardwareHeight); + + // Reset color + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); CHECK_GL_ERROR(); + } +#endif +} + +void OpenGLGraphicsManager::initGL() { + // Check available GL Extensions + GLTexture::initGLExtensions(); + + // Disable 3D properties + glDisable(GL_CULL_FACE); CHECK_GL_ERROR(); + glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR(); + glDisable(GL_LIGHTING); CHECK_GL_ERROR(); + glDisable(GL_FOG); CHECK_GL_ERROR(); + glDisable(GL_DITHER); CHECK_GL_ERROR(); + glShadeModel(GL_FLAT); CHECK_GL_ERROR(); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); CHECK_GL_ERROR(); + + // Setup alpha blend (For overlay and cursor) + glEnable(GL_BLEND); CHECK_GL_ERROR(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CHECK_GL_ERROR(); + + // Enable rendering with vertex and coord arrays + glEnableClientState(GL_VERTEX_ARRAY); CHECK_GL_ERROR(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR(); + + glEnable(GL_TEXTURE_2D); CHECK_GL_ERROR(); + + // Setup the GL viewport + glViewport(0, 0, _videoMode.hardwareWidth, _videoMode.hardwareHeight); CHECK_GL_ERROR(); + + // Setup coordinates system + glMatrixMode(GL_PROJECTION); CHECK_GL_ERROR(); + glLoadIdentity(); CHECK_GL_ERROR(); +#ifdef USE_GLES + glOrthox(0, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 0, -1, 1); CHECK_GL_ERROR(); +#else + glOrtho(0, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 0, -1, 1); CHECK_GL_ERROR(); +#endif + glMatrixMode(GL_MODELVIEW); CHECK_GL_ERROR(); + glLoadIdentity(); CHECK_GL_ERROR(); +} + +void OpenGLGraphicsManager::loadTextures() { +#ifdef USE_RGB_COLOR + if (_transactionDetails.formatChanged && _gameTexture) + delete _gameTexture; +#endif + + if (!_gameTexture) { + byte bpp; + GLenum intformat; + GLenum format; + GLenum type; +#ifdef USE_RGB_COLOR + getGLPixelFormat(_screenFormat, bpp, intformat, format, type); +#else + getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, intformat, format, type); +#endif + _gameTexture = new GLTexture(bpp, intformat, format, type); + } else + _gameTexture->refresh(); + + _overlayFormat = Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0); + + if (!_overlayTexture) { + byte bpp; + GLenum intformat; + GLenum format; + GLenum type; + getGLPixelFormat(_overlayFormat, bpp, intformat, format, type); + _overlayTexture = new GLTexture(bpp, intformat, format, type); + } else + _overlayTexture->refresh(); + + if (!_cursorTexture) + _cursorTexture = new GLTexture(4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); + else + _cursorTexture->refresh(); + + GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST; + _gameTexture->setFilter(filter); + _overlayTexture->setFilter(filter); + _cursorTexture->setFilter(filter); + + // Allocate texture memory and finish refreshing + _gameTexture->allocBuffer(_videoMode.screenWidth, _videoMode.screenHeight); + _overlayTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight); + _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h); + + if (_transactionDetails.formatChanged || + _oldVideoMode.screenWidth != _videoMode.screenWidth || + _oldVideoMode.screenHeight != _videoMode.screenHeight) + _screenData.create(_videoMode.screenWidth, _videoMode.screenHeight, + _screenFormat.bytesPerPixel); + + if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth || + _oldVideoMode.overlayHeight != _videoMode.overlayHeight) + _overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight, + _overlayFormat.bytesPerPixel); + + _screenNeedsRedraw = true; + _overlayNeedsRedraw = true; + _cursorNeedsRedraw = true; + +#ifdef USE_OSD + if (!_osdTexture) + _osdTexture = new GLTexture(2, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1); + else + _osdTexture->refresh(); + + _osdTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight); +#endif +} + +bool OpenGLGraphicsManager::loadGFXMode() { + // Initialize OpenGL settings + initGL(); + + loadTextures(); + + refreshCursorScale(); + + refreshAspectRatio(); + + internUpdateScreen(); + + return true; +} + +void OpenGLGraphicsManager::unloadGFXMode() { + +} + +void OpenGLGraphicsManager::setScale(int newScale) { + if (newScale == _videoMode.scaleFactor) + return; + + switch (newScale - 1) { + case OpenGL::GFX_NORMAL: + _videoMode.mode = OpenGL::GFX_NORMAL; + break; + case OpenGL::GFX_DOUBLESIZE: + _videoMode.mode = OpenGL::GFX_DOUBLESIZE; + break; + case OpenGL::GFX_TRIPLESIZE: + _videoMode.mode = OpenGL::GFX_TRIPLESIZE; + break; + } + + _videoMode.scaleFactor = newScale; + _transactionDetails.sizeChanged = true; +} + +void OpenGLGraphicsManager::setAspectRatioCorrection(int mode) { + if (_oldVideoMode.setup && _oldVideoMode.aspectRatioCorrection == mode) + return; + + if (_transactionMode == kTransactionActive) { + if (mode == -1) + // If -1, switch to next mode + _videoMode.aspectRatioCorrection = (_videoMode.aspectRatioCorrection + 1) % 3; + else + _videoMode.aspectRatioCorrection = mode; + _transactionDetails.needRefresh = true; + } +} + +Common::String OpenGLGraphicsManager::getAspectRatioName() { + switch (_videoMode.aspectRatioCorrection) { + case kAspectRatioNone: + return "None"; + case kAspectRatioConserve: + return "Conserve"; + case kAspectRatioOriginal: + return "Original"; + default: + return ""; + } +} + +uint OpenGLGraphicsManager::getAspectRatio() { + if (_videoMode.aspectRatioCorrection == kAspectRatioNone) + return _videoMode.hardwareWidth * 10000 / _videoMode.hardwareHeight; + else + return _videoMode.screenWidth * 10000 / _videoMode.screenHeight; +} + +void OpenGLGraphicsManager::adjustMouseEvent(const Common::Event &event) { + if (!event.synthetic) { + Common::Event newEvent(event); + newEvent.synthetic = true; + + if (!_videoMode.aspectRatioCorrection) { + if (_videoMode.hardwareWidth != _videoMode.overlayWidth) + newEvent.mouse.x = newEvent.mouse.x * _videoMode.overlayWidth / _videoMode.hardwareWidth; + if (_videoMode.hardwareHeight != _videoMode.overlayHeight) + newEvent.mouse.y = newEvent.mouse.y * _videoMode.overlayHeight / _videoMode.hardwareHeight; + + if (!_overlayVisible) { + newEvent.mouse.x /= _videoMode.scaleFactor; + newEvent.mouse.y /= _videoMode.scaleFactor; + } + + } else { + newEvent.mouse.x -= _aspectX; + newEvent.mouse.y -= _aspectY; + + if (_overlayVisible) { + if (_aspectWidth != _videoMode.overlayWidth) + newEvent.mouse.x = newEvent.mouse.x * _videoMode.overlayWidth / _aspectWidth; + if (_aspectHeight != _videoMode.overlayHeight) + newEvent.mouse.y = newEvent.mouse.y * _videoMode.overlayHeight / _aspectHeight; + } else { + if (_aspectWidth != _videoMode.screenWidth) + newEvent.mouse.x = newEvent.mouse.x * _videoMode.screenWidth / _aspectWidth; + if (_aspectHeight != _videoMode.screenHeight) + newEvent.mouse.y = newEvent.mouse.y * _videoMode.screenHeight / _aspectHeight; + } + } + + g_system->getEventManager()->pushEvent(newEvent); + } +} + +bool OpenGLGraphicsManager::notifyEvent(const Common::Event &event) { + switch (event.type) { + case Common::EVENT_MOUSEMOVE: + if (!event.synthetic) + setMousePos(event.mouse.x, event.mouse.y); + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + case Common::EVENT_MBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_MBUTTONUP: + adjustMouseEvent(event); + return !event.synthetic; + + default: + break; + } + + return false; +} + +bool OpenGLGraphicsManager::saveScreenshot(const char *filename) { + int width = _videoMode.hardwareWidth; + int height = _videoMode.hardwareHeight; + + // Allocate memory for screenshot + uint8 *pixels = new uint8[width * height * 3]; + + // Get pixel data from OpenGL buffer +#ifdef USE_GLES + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR(); +#else + if (_formatBGR) { + glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR(); + } else { + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); CHECK_GL_ERROR(); + } +#endif + + // Open file + Common::DumpFile out; + out.open(filename); + + // Write BMP header + out.writeByte('B'); + out.writeByte('M'); + out.writeUint32LE(height * width * 3 + 52); + out.writeUint32LE(0); + out.writeUint32LE(52); + out.writeUint32LE(40); + out.writeUint32LE(width); + out.writeUint32LE(height); + out.writeUint16LE(1); + out.writeUint16LE(24); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + + // Write pixel data to BMP + out.write(pixels, width * height * 3); + + // Free allocated memory + delete[] pixels; + + return true; +} + +#endif diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h new file mode 100644 index 0000000000..715f85dd6a --- /dev/null +++ b/backends/graphics/opengl/opengl-graphics.h @@ -0,0 +1,293 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_OPENGL_H +#define BACKENDS_GRAPHICS_OPENGL_H + +#include "backends/graphics/opengl/gltexture.h" +#include "backends/graphics/graphics.h" +#include "common/events.h" + +// Uncomment this to enable the 'on screen display' code. +#define USE_OSD 1 + +namespace OpenGL { +// The OpenGL GFX modes. They have to be inside the OpenGL namespace so they +// do not clash with the SDL GFX modes. +enum { + GFX_NORMAL = 0, + GFX_DOUBLESIZE = 1, + GFX_TRIPLESIZE = 2 +}; + +} + +/** + * Open GL graphics manager. This is an abstract class, it does not do the + * window and OpenGL context initialization. + * Derived classes should at least override internUpdateScreen for doing + * the buffers swap, and implement loadGFXMode for handling the window/context if + * needed. If USE_RGB_COLOR is enabled, getSupportedFormats must be implemented. + */ +class OpenGLGraphicsManager : public GraphicsManager, public Common::EventObserver { +public: + OpenGLGraphicsManager(); + virtual ~OpenGLGraphicsManager(); + + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + virtual bool getFeatureState(OSystem::Feature f); + + static const OSystem::GraphicsMode *supportedGraphicsModes(); + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + virtual int getGraphicsMode() const; + virtual void resetGraphicsScale(); +#ifdef USE_RGB_COLOR + virtual Graphics::PixelFormat getScreenFormat() const; + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0; +#endif + virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL); + virtual int getScreenChangeID() const; + + virtual void beginGFXTransaction(); + virtual OSystem::TransactionError endGFXTransaction(); + + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void grabPalette(byte *colors, uint start, uint num); + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + virtual Graphics::Surface *lockScreen(); + virtual void unlockScreen(); + virtual void fillScreen(uint32 col); + virtual void updateScreen(); + virtual void setShakePos(int shakeOffset); + virtual void setFocusRectangle(const Common::Rect& rect); + virtual void clearFocusRectangle(); + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual Graphics::PixelFormat getOverlayFormat() const; + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + + virtual bool showMouse(bool visible); + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL); + virtual void setCursorPalette(const byte *colors, uint start, uint num); + virtual void disableCursorPalette(bool disable); + + virtual void displayMessageOnOSD(const char *msg); + + // Override from Common::EventObserver + bool notifyEvent(const Common::Event &event); + +protected: + /** + * Setup OpenGL settings + */ + virtual void initGL(); + + /** + * Creates and refreshs OpenGL textures. + */ + virtual void loadTextures(); + + // + // GFX and video + // + enum { + kTransactionNone = 0, + kTransactionActive = 1, + kTransactionRollback = 2 + }; + + struct TransactionDetails { + bool sizeChanged; + bool needRefresh; + bool needUpdatescreen; + bool filterChanged; +#ifdef USE_RGB_COLOR + bool formatChanged; +#endif + }; + TransactionDetails _transactionDetails; + int _transactionMode; + + enum { + kAspectRatioNone, + kAspectRatioConserve, + kAspectRatioOriginal + }; + + struct VideoState { + bool setup; + + bool fullscreen; + int activeFullscreenMode; + int aspectRatioCorrection; + + int mode; + int scaleFactor; + bool antialiasing; + + int screenWidth, screenHeight; + int overlayWidth, overlayHeight; + int hardwareWidth, hardwareHeight; +#ifdef USE_RGB_COLOR + Graphics::PixelFormat format; +#endif + }; + VideoState _videoMode, _oldVideoMode; + + /** + * Sets the OpenGL texture format for the given pixel format. If format is not support will raise an error. + */ + virtual void getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &type); + + virtual void internUpdateScreen(); + virtual bool loadGFXMode(); + virtual void unloadGFXMode(); + + virtual void setScale(int newScale); + + // Drawing coordinates for the current aspect ratio + int _aspectX; + int _aspectY; + int _aspectWidth; + int _aspectHeight; + + /** + * Sets the aspect ratio mode. + * @mode the aspect ratio mode, if -1 it will switch to next mode. + */ + virtual void setAspectRatioCorrection(int mode); + + virtual void refreshAspectRatio(); + virtual Common::String getAspectRatioName(); + + /** + * Returns the current target aspect ratio x 10000 + */ + virtual uint getAspectRatio(); + + bool _formatBGR; + + // + // Game screen + // + GLTexture* _gameTexture; + Graphics::Surface _screenData; + int _screenChangeCount; + bool _screenNeedsRedraw; + Common::Rect _screenDirtyRect; + +#ifdef USE_RGB_COLOR + Graphics::PixelFormat _screenFormat; +#endif + byte *_gamePalette; + + virtual void refreshGameScreen(); + + // Shake mode + int _shakePos; + + // + // Overlay + // + GLTexture* _overlayTexture; + Graphics::Surface _overlayData; + Graphics::PixelFormat _overlayFormat; + bool _overlayVisible; + bool _overlayNeedsRedraw; + Common::Rect _overlayDirtyRect; + + virtual void refreshOverlay(); + + // + // Mouse + // + struct MousePos { + // The mouse position, using either virtual (game) or real + // (overlay) coordinates. + int16 x, y; + + // The size and hotspot of the original cursor image. + int16 w, h; + int16 hotX, hotY; + + // The size and hotspot of the scaled cursor, in real coordinates. + int16 rW, rH; + int16 rHotX, rHotY; + + // The size and hotspot of the scaled cursor, in game coordinates. + int16 vW, vH; + int16 vHotX, vHotY; + + MousePos() : x(0), y(0), w(0), h(0), hotX(0), hotY(0), + rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0), + vHotX(0), vHotY(0) {} + }; + + GLTexture* _cursorTexture; + Graphics::Surface _cursorData; + Graphics::PixelFormat _cursorFormat; + byte *_cursorPalette; + bool _cursorPaletteDisabled; + MousePos _cursorState; + bool _cursorVisible; + uint32 _cursorKeyColor; + int _cursorTargetScale; + bool _cursorNeedsRedraw; + + virtual void refreshCursor(); + virtual void refreshCursorScale(); + virtual void adjustMouseEvent(const Common::Event &event); + virtual void setMousePos(int x, int y); + + // + // Misc + // + virtual bool saveScreenshot(const char *filename); + +#ifdef USE_OSD + GLTexture *_osdTexture; + Graphics::Surface _osdSurface; + uint8 _osdAlpha; + uint32 _osdFadeStartTime; + enum { + kOSDFadeOutDelay = 2 * 1000, + kOSDFadeOutDuration = 500, + kOSDInitialAlpha = 80 + }; +#endif +}; + +#endif diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp new file mode 100644 index 0000000000..e4881a105e --- /dev/null +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -0,0 +1,584 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if defined(SDL_BACKEND) && defined(USE_OPENGL) + +#include "backends/graphics/openglsdl/openglsdl-graphics.h" +#include "backends/platform/sdl/sdl.h" +#include "common/config-manager.h" + +OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager() + : + _hwscreen(0), + _screenResized(false), + _lastFullscreenModeWidth(0), + _lastFullscreenModeHeight(0), + _desktopWidth(0), + _desktopHeight(0) { + + // Initialize SDL video subsystem + if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { + error("Could not initialize SDL: %s", SDL_GetError()); + } + + // Disable OS cursor + SDL_ShowCursor(SDL_DISABLE); + + // Get desktop resolution + const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo(); + if (videoInfo->current_w > 0 && videoInfo->current_h > 0) { + _desktopWidth = videoInfo->current_w; + _desktopHeight = videoInfo->current_h; + } + + if (ConfMan.hasKey("last_fullscreen_mode_width") && ConfMan.hasKey("last_fullscreen_mode_height")) { + _lastFullscreenModeWidth = ConfMan.getInt("last_fullscreen_mode_width"); + _lastFullscreenModeHeight = ConfMan.getInt("last_fullscreen_mode_height"); + } +} + +OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() { + +} + + +bool OpenGLSdlGraphicsManager::hasFeature(OSystem::Feature f) { + return + (f == OSystem::kFeatureFullscreenMode) || + (f == OSystem::kFeatureIconifyWindow) || + OpenGLGraphicsManager::hasFeature(f); +} + +void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + setFullscreenMode(enable); + break; + case OSystem::kFeatureIconifyWindow: + if (enable) + SDL_WM_IconifyWindow(); + break; + default: + OpenGLGraphicsManager::setFeatureState(f, enable); + } +} + +#ifdef USE_RGB_COLOR + +Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormats() const { + assert(!_supportedFormats.empty()); + return _supportedFormats; +} + +void OpenGLSdlGraphicsManager::detectSupportedFormats() { + + // Clear old list + _supportedFormats.clear(); + + // Some tables with standard formats that we always list + // as "supported". If frontend code tries to use one of + // these, we will perform the necessary format + // conversion in the background. Of course this incurs a + // performance hit, but on desktop ports this should not + // matter. We still push the currently active format to + // the front, so if frontend code just uses the first + // available format, it will get one that is "cheap" to + // use. + const Graphics::PixelFormat RGBList[] = { +#if defined(ENABLE_32BIT) + Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), // RGBA8888 +#ifndef USE_GLES + Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), // ARGB8888 +#endif + Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0), // RGB888 +#endif + Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), // RGB565 + Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), // RGB5551 + Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), // RGBA4444 +#ifndef USE_GLES + Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12) // ARGB4444 +#endif + }; +#ifndef USE_GLES + const Graphics::PixelFormat BGRList[] = { +#ifdef ENABLE_32BIT + Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), // ABGR8888 + Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), // BGRA8888 + Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0), // BGR888 +#endif + Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0), // BGR565 + Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0), // BGRA5551 + Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12), // ABGR4444 + Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0) // BGRA4444 + }; +#endif + + Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + if (_hwscreen) { + // Get our currently set hardware format + format = Graphics::PixelFormat(_hwscreen->format->BytesPerPixel, + 8 - _hwscreen->format->Rloss, 8 - _hwscreen->format->Gloss, + 8 - _hwscreen->format->Bloss, 8 - _hwscreen->format->Aloss, + _hwscreen->format->Rshift, _hwscreen->format->Gshift, + _hwscreen->format->Bshift, _hwscreen->format->Ashift); + + // Workaround to MacOSX SDL not providing an accurate Aloss value. + if (_hwscreen->format->Amask == 0) + format.aLoss = 8; + + // Push it first, as the prefered format if available + for (int i = 0; i < ARRAYSIZE(RGBList); i++) { + if (RGBList[i] == format) { + _supportedFormats.push_back(format); + break; + } + } +#ifndef USE_GLES + for (int i = 0; i < ARRAYSIZE(BGRList); i++) { + if (BGRList[i] == format) { + _supportedFormats.push_back(format); + break; + } + } +#endif + } + + // Push some RGB formats + for (int i = 0; i < ARRAYSIZE(RGBList); i++) { + if (_hwscreen && (RGBList[i].bytesPerPixel > format.bytesPerPixel)) + continue; + if (RGBList[i] != format) + _supportedFormats.push_back(RGBList[i]); + } +#ifndef USE_GLES + // Push some BGR formats + for (int i = 0; i < ARRAYSIZE(BGRList); i++) { + if (_hwscreen && (BGRList[i].bytesPerPixel > format.bytesPerPixel)) + continue; + if (BGRList[i] != format) + _supportedFormats.push_back(BGRList[i]); + } +#endif + _supportedFormats.push_back(Graphics::PixelFormat::createFormatCLUT8()); +} + +#endif + +void OpenGLSdlGraphicsManager::warpMouse(int x, int y) { + int scaledX = x; + int scaledY = y; + + if (!_videoMode.aspectRatioCorrection) { + if (_videoMode.hardwareWidth != _videoMode.overlayWidth) + scaledX = scaledX * _videoMode.hardwareWidth / _videoMode.overlayWidth; + if (_videoMode.hardwareHeight != _videoMode.overlayHeight) + scaledY = scaledY * _videoMode.hardwareHeight / _videoMode.overlayHeight; + + if (!_overlayVisible) { + scaledX *= _videoMode.scaleFactor; + scaledY *= _videoMode.scaleFactor; + } + } else { + if (_overlayVisible) { + if (_aspectWidth != _videoMode.overlayWidth) + scaledX = scaledX * _aspectWidth / _videoMode.overlayWidth; + if (_aspectHeight != _videoMode.overlayHeight) + scaledY = scaledY * _aspectHeight / _videoMode.overlayHeight; + } else { + if (_aspectWidth != _videoMode.screenWidth) + scaledX = scaledX * _aspectWidth / _videoMode.screenWidth; + if (_aspectHeight != _videoMode.screenHeight) + scaledY = scaledY * _aspectHeight / _videoMode.screenHeight; + } + + scaledX += _aspectX; + scaledY += _aspectY; + } + + SDL_WarpMouse(scaledX, scaledY); + + setMousePos(scaledX, scaledY); +} + + +// +// Intern +// + +bool OpenGLSdlGraphicsManager::setupFullscreenMode() { + SDL_Rect const* const*availableModes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_OPENGL); + + // If -1, autodetect the fullscreen mode + // The last used fullscreen mode will be prioritized, if there is no last fullscreen + // mode, the desktop resolution will be used, and in case the desktop resolution + // is not available as a fullscreen mode, the one with smallest metric will be selected. + if (_videoMode.activeFullscreenMode == -1) { + // Desktop resolution + int desktopModeIndex = -1; + + // Best metric mode + const SDL_Rect *bestMode = availableModes[0]; + int bestModeIndex = 0; + uint bestMetric = (uint)-1; + + // Iterate over all available fullscreen modes + for (int i = 0; const SDL_Rect *mode = availableModes[i]; i++) { + // Try to setup the last used fullscreen mode + if (mode->w == _lastFullscreenModeWidth && mode->h == _lastFullscreenModeHeight) { + _videoMode.hardwareWidth = _lastFullscreenModeWidth; + _videoMode.hardwareHeight = _lastFullscreenModeHeight; + _videoMode.activeFullscreenMode = i; + return true; + } + + if (mode->w == _desktopWidth && mode->h == _desktopHeight) + desktopModeIndex = i; + + if (mode->w < _videoMode.overlayWidth) + continue; + if (mode->h < _videoMode.overlayHeight) + continue; + + uint metric = mode->w * mode->h - _videoMode.overlayWidth * _videoMode.overlayHeight; + if (metric < bestMetric) { + bestMode = mode; + bestMetric = metric; + bestModeIndex = i; + } + } + + if (desktopModeIndex >= 0) { + _videoMode.hardwareWidth = _desktopWidth; + _videoMode.hardwareHeight = _desktopHeight; + + _videoMode.activeFullscreenMode = desktopModeIndex; + return true; + } else if (bestMode) { + _videoMode.hardwareWidth = bestMode->w; + _videoMode.hardwareHeight = bestMode->h; + + _videoMode.activeFullscreenMode = bestModeIndex; + return true; + } + } else { + if (!availableModes[_videoMode.activeFullscreenMode]) + _videoMode.activeFullscreenMode = 0; + + if (availableModes[_videoMode.activeFullscreenMode]) { + _videoMode.hardwareWidth = availableModes[_videoMode.activeFullscreenMode]->w; + _videoMode.hardwareHeight = availableModes[_videoMode.activeFullscreenMode]->h; + return true; + } + } + + // Could not find any suiting fullscreen mode, return false. + return false; +} + +bool OpenGLSdlGraphicsManager::loadGFXMode() { + _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; + + // If the screen was resized, do not change its size + if (!_screenResized) { + _videoMode.hardwareWidth = _videoMode.overlayWidth; + _videoMode.hardwareHeight = _videoMode.overlayHeight; + + int screenAspectRatio = _videoMode.screenWidth * 10000 / _videoMode.screenHeight; + int desiredAspectRatio = getAspectRatio(); + + // Do not downscale dimensions, only enlarge them if needed + if (screenAspectRatio > desiredAspectRatio) + _videoMode.hardwareHeight = (_videoMode.overlayWidth * 10000 + 5000) / desiredAspectRatio; + else if (screenAspectRatio < desiredAspectRatio) + _videoMode.hardwareWidth = (_videoMode.overlayHeight * desiredAspectRatio + 5000) / 10000; + + // Only adjust the overlay height if it is bigger than original one. If + // the width is modified it can break the overlay. + if (_videoMode.hardwareHeight > _videoMode.overlayHeight) + _videoMode.overlayHeight = _videoMode.hardwareHeight; + } + + _screenResized = false; + + // Setup OpenGL attributes for SDL + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + if (_videoMode.fullscreen) + if (!setupFullscreenMode()) + // Failed setuping a fullscreen mode + return false; + + uint32 flags = SDL_OPENGL; + + if (_videoMode.fullscreen) + flags |= SDL_FULLSCREEN; + else if (_videoMode.aspectRatioCorrection == kAspectRatioNone) + flags |= SDL_RESIZABLE; + + // Create our window + _hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 32, flags); +#ifdef USE_RGB_COLOR + detectSupportedFormats(); +#endif + + if (_hwscreen == NULL) { + // DON'T use error(), as this tries to bring up the debug + // console, which WON'T WORK now that _hwscreen is hosed. + + if (!_oldVideoMode.setup) { + warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); + g_system->quit(); + } else + // Cancel GFX load, and go back to last mode + return false; + } + + // Check if the screen is BGR format + _formatBGR = _hwscreen->format->Rshift != 0; + + if (_videoMode.fullscreen) { + _lastFullscreenModeWidth = _videoMode.hardwareWidth; + _lastFullscreenModeHeight = _videoMode.hardwareHeight; + ConfMan.setInt("last_fullscreen_mode_width", _lastFullscreenModeWidth); + ConfMan.setInt("last_fullscreen_mode_height", _lastFullscreenModeHeight); + } + + // Call and return parent implementation of this method + return OpenGLGraphicsManager::loadGFXMode(); +} + +void OpenGLSdlGraphicsManager::unloadGFXMode() { + if (_hwscreen) { + SDL_FreeSurface(_hwscreen); + _hwscreen = NULL; + } +} + +void OpenGLSdlGraphicsManager::internUpdateScreen() { + // Call to parent implementation of this method + OpenGLGraphicsManager::internUpdateScreen(); + + // Swap OpenGL buffers + SDL_GL_SwapBuffers(); +} + +bool OpenGLSdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) { + + // Ctrl-Alt-a toggles aspect ratio correction + if (key == 'a') { + beginGFXTransaction(); + setAspectRatioCorrection(-1); + endGFXTransaction(); +#ifdef USE_OSD + char buffer[128]; + sprintf(buffer, "Current aspect ratio mode: %s\n%d x %d -> %d x %d", + getAspectRatioName().c_str(), + _videoMode.screenWidth * _videoMode.scaleFactor, + _videoMode.screenHeight * _videoMode.scaleFactor, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); +#endif + internUpdateScreen(); + return true; + } + + // Ctrl-Alt-f toggles antialiasing + if (key == 'f') { + beginGFXTransaction(); + _videoMode.antialiasing = !_videoMode.antialiasing; + _transactionDetails.filterChanged = true; + endGFXTransaction(); +#ifdef USE_OSD + if (_videoMode.antialiasing) + displayMessageOnOSD("Active filter mode: Linear"); + else + displayMessageOnOSD("Active filter mode: Nearest"); +#endif + return true; + } + + SDLKey sdlKey = (SDLKey)key; + + // Increase/decrease the scale factor + if (sdlKey == SDLK_EQUALS || sdlKey == SDLK_PLUS || sdlKey == SDLK_MINUS || + sdlKey == SDLK_KP_PLUS || sdlKey == SDLK_KP_MINUS) { + int factor = _videoMode.scaleFactor; + factor += (sdlKey == SDLK_MINUS || sdlKey == SDLK_KP_MINUS) ? -1 : +1; + if (0 < factor && factor < 4) { + // Check if the desktop resolution has been detected + if (_desktopWidth > 0 && _desktopHeight > 0) + // If the new scale factor is too big, do not scale + if (_videoMode.screenWidth * factor > _desktopWidth || + _videoMode.screenHeight * factor > _desktopHeight) + return false; + + beginGFXTransaction(); + setScale(factor); + endGFXTransaction(); +#ifdef USE_OSD + const char *newScalerName = 0; + const OSystem::GraphicsMode *g = getSupportedGraphicsModes(); + while (g->name) { + if (g->id == _videoMode.mode) { + newScalerName = g->description; + break; + } + g++; + } + if (newScalerName) { + char buffer[128]; + sprintf(buffer, "Active graphics mode: %s\n%d x %d -> %d x %d", + newScalerName, + _videoMode.screenWidth, _videoMode.screenHeight, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); + } +#endif + return true; + } + } + return false; +} + +void OpenGLSdlGraphicsManager::setFullscreenMode(bool enable) { + if (_oldVideoMode.setup && _oldVideoMode.fullscreen == enable && + _oldVideoMode.activeFullscreenMode == _videoMode.activeFullscreenMode) + return; + + if (_transactionMode == kTransactionActive) { + _videoMode.fullscreen = enable; + _transactionDetails.needRefresh = true; + } +} + +bool OpenGLSdlGraphicsManager::isScalerHotkey(const Common::Event &event) { + if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { + const bool isScaleKey = (event.kbd.keycode == Common::KEYCODE_EQUALS || event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS || + event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS); + + return (isScaleKey || event.kbd.keycode == 'a' || event.kbd.keycode == 'f'); + } + return false; +} + +void OpenGLSdlGraphicsManager::toggleFullScreen(bool loop) { + beginGFXTransaction(); + if (_videoMode.fullscreen && loop) { + _videoMode.activeFullscreenMode += 1; + setFullscreenMode(true); + } else { + _videoMode.activeFullscreenMode = -1; + setFullscreenMode(!_videoMode.fullscreen); + } + endGFXTransaction(); +#ifdef USE_OSD + char buffer[128]; + if (_videoMode.fullscreen) + sprintf(buffer, "Fullscreen mode\n%d x %d", + _hwscreen->w, _hwscreen->h + ); + else + sprintf(buffer, "Windowed mode\n%d x %d", + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); +#endif +} + +bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) { + switch ((int)event.type) { + case Common::EVENT_KEYDOWN: + // Alt-Return and Alt-Enter toggle full screen mode + if (event.kbd.hasFlags(Common::KBD_ALT) && + (event.kbd.keycode == Common::KEYCODE_RETURN || + event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER)) { + toggleFullScreen(false); + return true; + } + + // Ctrl-Alt-Return and Ctrl-Alt-Enter switches between full screen modes + if (event.kbd.hasFlags(Common::KBD_CTRL|Common::KBD_ALT) && + (event.kbd.keycode == Common::KEYCODE_RETURN || + event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER)) { + toggleFullScreen(true); + return true; + } + + // Alt-S: Create a screenshot + if (event.kbd.hasFlags(Common::KBD_ALT) && event.kbd.keycode == 's') { + char filename[20]; + + for (int n = 0;; n++) { + SDL_RWops *file; + + sprintf(filename, "scummvm%05d.bmp", n); + file = SDL_RWFromFile(filename, "r"); + if (!file) + break; + SDL_RWclose(file); + } + if (saveScreenshot(filename)) + printf("Saved '%s'\n", filename); + else + printf("Could not save screenshot!\n"); + return true; + } + + // Ctrl-Alt-<key> will change the GFX mode + if (event.kbd.hasFlags(Common::KBD_CTRL|Common::KBD_ALT)) { + if (handleScalerHotkeys(event.kbd.keycode)) + return true; + } + case Common::EVENT_KEYUP: + return isScalerHotkey(event); + /*case OSystem_SDL::kSdlEventExpose: + break;*/ + // HACK: Handle special SDL event + case OSystem_SDL::kSdlEventResize: + beginGFXTransaction(); + // Set the new screen size. It is saved on the mouse event as part of HACK, + // there is no common resize event + _videoMode.hardwareWidth = event.mouse.x; + _videoMode.hardwareHeight = event.mouse.y; + _screenResized = true; + _transactionDetails.sizeChanged = true; + endGFXTransaction(); + return true; + + default: + break; + } + + return OpenGLGraphicsManager::notifyEvent(event); +} + +#endif diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h new file mode 100644 index 0000000000..1a54dccbcd --- /dev/null +++ b/backends/graphics/openglsdl/openglsdl-graphics.h @@ -0,0 +1,101 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_OPENGLSDL_H +#define BACKENDS_GRAPHICS_OPENGLSDL_H + +#include <SDL.h> +#if defined(ARRAYSIZE) && !defined(_WINDOWS_) +#undef ARRAYSIZE +#endif +#include <SDL_opengl.h> + +#include "backends/graphics/opengl/opengl-graphics.h" + +/** + * SDL OpenGL graphics manager + */ +class OpenGLSdlGraphicsManager : public OpenGLGraphicsManager { +public: + OpenGLSdlGraphicsManager(); + virtual ~OpenGLSdlGraphicsManager(); + + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + +#ifdef USE_RGB_COLOR + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; +#endif + + virtual void warpMouse(int x, int y); + + virtual bool notifyEvent(const Common::Event &event); + +protected: + virtual void internUpdateScreen(); + + virtual bool loadGFXMode(); + virtual void unloadGFXMode(); + + virtual void setFullscreenMode(bool enable); + + virtual bool handleScalerHotkeys(Common::KeyCode key); + virtual bool isScalerHotkey(const Common::Event &event); + +#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 + + /** + * Toggles fullscreen. + * @loop if true loop to next supported fullscreen mode + */ + virtual void toggleFullScreen(bool loop); + + /** + * Setup the fullscreen mode. + * @return false if failed finding a mode, true otherwise. + */ + virtual bool setupFullscreenMode(); + + int _lastFullscreenModeWidth; + int _lastFullscreenModeHeight; + int _desktopWidth; + int _desktopHeight; + + // Hardware screen + SDL_Surface *_hwscreen; + + // If screen was resized by the user + bool _screenResized; +}; + +#endif diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp new file mode 100644 index 0000000000..2952ad810b --- /dev/null +++ b/backends/graphics/sdl/sdl-graphics.cpp @@ -0,0 +1,2194 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#if defined(SDL_BACKEND) + +#include "backends/graphics/sdl/sdl-graphics.h" +#include "backends/events/sdl/sdl-events.h" +#include "backends/platform/sdl/sdl.h" +#include "common/config-manager.h" +#include "common/mutex.h" +#include "common/translation.h" +#include "common/util.h" +#ifdef USE_RGB_COLOR +#include "common/list.h" +#endif +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/scaler.h" +#include "graphics/scaler/aspect.h" +#include "graphics/surface.h" + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"1x", _s("Normal (no scaling)"), GFX_NORMAL}, +#ifdef USE_SCALERS + {"2x", "2x", GFX_DOUBLESIZE}, + {"3x", "3x", GFX_TRIPLESIZE}, + {"2xsai", "2xSAI", GFX_2XSAI}, + {"super2xsai", "Super2xSAI", GFX_SUPER2XSAI}, + {"supereagle", "SuperEagle", GFX_SUPEREAGLE}, + {"advmame2x", "AdvMAME2x", GFX_ADVMAME2X}, + {"advmame3x", "AdvMAME3x", GFX_ADVMAME3X}, +#ifdef USE_HQ_SCALERS + {"hq2x", "HQ2x", GFX_HQ2X}, + {"hq3x", "HQ3x", GFX_HQ3X}, +#endif + {"tv2x", "TV2x", GFX_TV2X}, + {"dotmatrix", "DotMatrix", GFX_DOTMATRIX}, +#endif + {0, 0, 0} +}; + +// Table of relative scalers magnitudes +// [definedScale - 1][scaleFactor - 1] +static ScalerProc *scalersMagn[3][3] = { +#ifdef USE_SCALERS + { Normal1x, AdvMame2x, AdvMame3x }, + { Normal1x, Normal1x, Normal1o5x }, + { Normal1x, Normal1x, Normal1x } +#else // remove dependencies on other scalers + { Normal1x, Normal1x, Normal1x }, + { Normal1x, Normal1x, Normal1x }, + { Normal1x, Normal1x, Normal1x } +#endif +}; + +static const int s_gfxModeSwitchTable[][4] = { + { GFX_NORMAL, GFX_DOUBLESIZE, GFX_TRIPLESIZE, -1 }, + { GFX_NORMAL, GFX_ADVMAME2X, GFX_ADVMAME3X, -1 }, + { GFX_NORMAL, GFX_HQ2X, GFX_HQ3X, -1 }, + { GFX_NORMAL, GFX_2XSAI, -1, -1 }, + { GFX_NORMAL, GFX_SUPER2XSAI, -1, -1 }, + { GFX_NORMAL, GFX_SUPEREAGLE, -1, -1 }, + { GFX_NORMAL, GFX_TV2X, -1, -1 }, + { GFX_NORMAL, GFX_DOTMATRIX, -1, -1 } + }; + +#ifdef USE_SCALERS +static int cursorStretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY); +#endif + +AspectRatio::AspectRatio(int w, int h) { + // TODO : Validation and so on... + // Currently, we just ensure the program don't instantiate non-supported aspect ratios + _kw = w; + _kh = h; +} + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) && defined(USE_SCALERS) +static AspectRatio getDesiredAspectRatio() { + const size_t AR_COUNT = 4; + const char *desiredAspectRatioAsStrings[AR_COUNT] = { "auto", "4/3", "16/9", "16/10" }; + const AspectRatio desiredAspectRatios[AR_COUNT] = { AspectRatio(0, 0), AspectRatio(4,3), AspectRatio(16,9), AspectRatio(16,10) }; + + //TODO : We could parse an arbitrary string, if we code enough proper validation + Common::String desiredAspectRatio = ConfMan.get("desired_screen_aspect_ratio"); + + for (size_t i = 0; i < AR_COUNT; i++) { + assert(desiredAspectRatioAsStrings[i] != NULL); + + if (!scumm_stricmp(desiredAspectRatio.c_str(), desiredAspectRatioAsStrings[i])) { + return desiredAspectRatios[i]; + } + } + // TODO : Report a warning + return AspectRatio(0, 0); +} +#endif + +SdlGraphicsManager::SdlGraphicsManager() + : +#ifdef USE_OSD + _osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0), +#endif + _hwscreen(0), _screen(0), _tmpscreen(0), +#ifdef USE_RGB_COLOR + _screenFormat(Graphics::PixelFormat::createFormatCLUT8()), + _cursorFormat(Graphics::PixelFormat::createFormatCLUT8()), +#endif + _overlayVisible(false), + _overlayscreen(0), _tmpscreen2(0), + _scalerProc(0), _screenChangeCount(0), + _mouseVisible(false), _mouseNeedsRedraw(false), _mouseData(0), _mouseSurface(0), + _mouseOrigSurface(0), _cursorTargetScale(1), _cursorPaletteDisabled(true), + _currentShakePos(0), _newShakePos(0), + _paletteDirtyStart(0), _paletteDirtyEnd(0), + _screenIsLocked(false), + _graphicsMutex(0), _transactionMode(kTransactionNone) { + + if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { + error("Could not initialize SDL: %s", SDL_GetError()); + } + + // allocate palette storage + _currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); + _cursorPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); + + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + + memset(&_mouseCurState, 0, sizeof(_mouseCurState)); + + _graphicsMutex = g_system->createMutex(); + +#ifdef _WIN32_WCE + if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) { + SDL_VideoInit("windib", 0); + sdlFlags ^= SDL_INIT_VIDEO; + } +#endif + + SDL_ShowCursor(SDL_DISABLE); + + memset(&_oldVideoMode, 0, sizeof(_oldVideoMode)); + memset(&_videoMode, 0, sizeof(_videoMode)); + memset(&_transactionDetails, 0, sizeof(_transactionDetails)); + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) && defined(USE_SCALERS) + _videoMode.mode = GFX_DOUBLESIZE; + _videoMode.scaleFactor = 2; + _videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio"); + _videoMode.desiredAspectRatio = getDesiredAspectRatio(); + _scalerProc = Normal2x; +#else // for small screen platforms + _videoMode.mode = GFX_NORMAL; + _videoMode.scaleFactor = 1; + _videoMode.aspectRatioCorrection = false; + _scalerProc = Normal1x; +#endif + _scalerType = 0; + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) + _videoMode.fullscreen = ConfMan.getBool("fullscreen"); +#else + _videoMode.fullscreen = true; +#endif + + // Register the graphics manager as a event observer + g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false); +} + +SdlGraphicsManager::~SdlGraphicsManager() { + // Unregister the event observer + if (g_system->getEventManager()->getEventDispatcher() != NULL) + g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); + + unloadGFXMode(); + g_system->deleteMutex(_graphicsMutex); + + free(_currentPalette); + free(_cursorPalette); + free(_mouseData); +} + +bool SdlGraphicsManager::hasFeature(OSystem::Feature f) { + return + (f == OSystem::kFeatureFullscreenMode) || + (f == OSystem::kFeatureAspectRatioCorrection) || + (f == OSystem::kFeatureCursorHasPalette) || + (f == OSystem::kFeatureIconifyWindow); +} + +void SdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + setFullscreenMode(enable); + break; + case OSystem::kFeatureAspectRatioCorrection: + setAspectRatioCorrection(enable); + break; + case OSystem::kFeatureIconifyWindow: + if (enable) + SDL_WM_IconifyWindow(); + break; + default: + break; + } +} + +bool SdlGraphicsManager::getFeatureState(OSystem::Feature f) { + assert (_transactionMode == kTransactionNone); + + switch (f) { + case OSystem::kFeatureFullscreenMode: + return _videoMode.fullscreen; + case OSystem::kFeatureAspectRatioCorrection: + return _videoMode.aspectRatioCorrection; + default: + return false; + } +} + +const OSystem::GraphicsMode *SdlGraphicsManager::supportedGraphicsModes() { + return s_supportedGraphicsModes; +} + +const OSystem::GraphicsMode *SdlGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +int SdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_DOUBLESIZE; +} + +void SdlGraphicsManager::resetGraphicsScale() { + setGraphicsMode(s_gfxModeSwitchTable[_scalerType][0]); +} + +void SdlGraphicsManager::beginGFXTransaction() { + assert(_transactionMode == kTransactionNone); + + _transactionMode = kTransactionActive; + + _transactionDetails.sizeChanged = false; + + _transactionDetails.needHotswap = false; + _transactionDetails.needUpdatescreen = false; + + _transactionDetails.normal1xScaler = false; +#ifdef USE_RGB_COLOR + _transactionDetails.formatChanged = false; +#endif + + _oldVideoMode = _videoMode; +} + +OSystem::TransactionError SdlGraphicsManager::endGFXTransaction() { + int errors = OSystem::kTransactionSuccess; + + assert(_transactionMode != kTransactionNone); + + if (_transactionMode == kTransactionRollback) { + if (_videoMode.fullscreen != _oldVideoMode.fullscreen) { + errors |= OSystem::kTransactionFullscreenFailed; + + _videoMode.fullscreen = _oldVideoMode.fullscreen; + } else if (_videoMode.aspectRatioCorrection != _oldVideoMode.aspectRatioCorrection) { + errors |= OSystem::kTransactionAspectRatioFailed; + + _videoMode.aspectRatioCorrection = _oldVideoMode.aspectRatioCorrection; + } else if (_videoMode.mode != _oldVideoMode.mode) { + errors |= OSystem::kTransactionModeSwitchFailed; + + _videoMode.mode = _oldVideoMode.mode; + _videoMode.scaleFactor = _oldVideoMode.scaleFactor; +#ifdef USE_RGB_COLOR + } else if (_videoMode.format != _oldVideoMode.format) { + errors |= OSystem::kTransactionFormatNotSupported; + + _videoMode.format = _oldVideoMode.format; + _screenFormat = _videoMode.format; +#endif + } else if (_videoMode.screenWidth != _oldVideoMode.screenWidth || _videoMode.screenHeight != _oldVideoMode.screenHeight) { + errors |= OSystem::kTransactionSizeChangeFailed; + + _videoMode.screenWidth = _oldVideoMode.screenWidth; + _videoMode.screenHeight = _oldVideoMode.screenHeight; + _videoMode.overlayWidth = _oldVideoMode.overlayWidth; + _videoMode.overlayHeight = _oldVideoMode.overlayHeight; + } + + if (_videoMode.fullscreen == _oldVideoMode.fullscreen && + _videoMode.aspectRatioCorrection == _oldVideoMode.aspectRatioCorrection && + _videoMode.mode == _oldVideoMode.mode && + _videoMode.screenWidth == _oldVideoMode.screenWidth && + _videoMode.screenHeight == _oldVideoMode.screenHeight) { + + // Our new video mode would now be exactly the same as the + // old one. Since we still can not assume SDL_SetVideoMode + // to be working fine, we need to invalidate the old video + // mode, so loadGFXMode would error out properly. + _oldVideoMode.setup = false; + } + } + +#ifdef USE_RGB_COLOR + if (_transactionDetails.sizeChanged || _transactionDetails.formatChanged) { +#else + if (_transactionDetails.sizeChanged) { +#endif + unloadGFXMode(); + if (!loadGFXMode()) { + if (_oldVideoMode.setup) { + _transactionMode = kTransactionRollback; + errors |= endGFXTransaction(); + } + } else { + setGraphicsModeIntern(); + clearOverlay(); + + _videoMode.setup = true; + // OSystem_SDL::pollEvent used to update the screen change count, + // but actually it gives problems when a video mode was changed + // but OSystem_SDL::pollEvent was not called. This for example + // caused a crash under certain circumstances when doing an RTL. + // To fix this issue we update the screen change count right here. + _screenChangeCount++; + } + } else if (_transactionDetails.needHotswap) { + setGraphicsModeIntern(); + if (!hotswapGFXMode()) { + if (_oldVideoMode.setup) { + _transactionMode = kTransactionRollback; + errors |= endGFXTransaction(); + } + } else { + _videoMode.setup = true; + // OSystem_SDL::pollEvent used to update the screen change count, + // but actually it gives problems when a video mode was changed + // but OSystem_SDL::pollEvent was not called. This for example + // caused a crash under certain circumstances when doing an RTL. + // To fix this issue we update the screen change count right here. + _screenChangeCount++; + + if (_transactionDetails.needUpdatescreen) + internUpdateScreen(); + } + } else if (_transactionDetails.needUpdatescreen) { + setGraphicsModeIntern(); + internUpdateScreen(); + } + + _transactionMode = kTransactionNone; + return (OSystem::TransactionError)errors; +} + +#ifdef USE_RGB_COLOR +Common::List<Graphics::PixelFormat> SdlGraphicsManager::getSupportedFormats() const { + assert(!_supportedFormats.empty()); + return _supportedFormats; +} + +void SdlGraphicsManager::detectSupportedFormats() { + + // Clear old list + _supportedFormats.clear(); + + // Some tables with standard formats that we always list + // as "supported". If frontend code tries to use one of + // these, we will perform the necessary format + // conversion in the background. Of course this incurs a + // performance hit, but on desktop ports this should not + // matter. We still push the currently active format to + // the front, so if frontend code just uses the first + // available format, it will get one that is "cheap" to + // use. + const Graphics::PixelFormat RGBList[] = { +#ifdef ENABLE_32BIT + // RGBA8888, ARGB8888, RGB888 + Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), + Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), + Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0), +#endif + // RGB565, XRGB1555, RGB555, RGBA4444, ARGB4444 + Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), + Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15), + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), + Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), + Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12) + }; + const Graphics::PixelFormat BGRList[] = { +#ifdef ENABLE_32BIT + // ABGR8888, BGRA8888, BGR888 + Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), + Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), + Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0), +#endif + // BGR565, XBGR1555, BGR555, ABGR4444, BGRA4444 + Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0), + Graphics::PixelFormat(2, 5, 5, 5, 1, 0, 5, 10, 15), + Graphics::PixelFormat(2, 5, 5, 5, 0, 0, 5, 10, 0), + Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12), + Graphics::PixelFormat(2, 4, 4, 4, 4, 4, 8, 12, 0) + }; + + Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + if (_hwscreen) { + // Get our currently set hardware format + format = Graphics::PixelFormat(_hwscreen->format->BytesPerPixel, + 8 - _hwscreen->format->Rloss, 8 - _hwscreen->format->Gloss, + 8 - _hwscreen->format->Bloss, 8 - _hwscreen->format->Aloss, + _hwscreen->format->Rshift, _hwscreen->format->Gshift, + _hwscreen->format->Bshift, _hwscreen->format->Ashift); + + // Workaround to MacOSX SDL not providing an accurate Aloss value. + if (_hwscreen->format->Amask == 0) + format.aLoss = 8; + + // Push it first, as the prefered format. + _supportedFormats.push_back(format); + } + + // TODO: prioritize matching alpha masks + int i; + + // Push some RGB formats + for (i = 0; i < ARRAYSIZE(RGBList); i++) { + if (_hwscreen && (RGBList[i].bytesPerPixel > format.bytesPerPixel)) + continue; + if (RGBList[i] != format) + _supportedFormats.push_back(RGBList[i]); + } + + // Push some BGR formats + for (i = 0; i < ARRAYSIZE(BGRList); i++) { + if (_hwscreen && (BGRList[i].bytesPerPixel > format.bytesPerPixel)) + continue; + if (BGRList[i] != format) + _supportedFormats.push_back(BGRList[i]); + } + + // Finally, we always supposed 8 bit palette graphics + _supportedFormats.push_back(Graphics::PixelFormat::createFormatCLUT8()); +} +#endif + +bool SdlGraphicsManager::setGraphicsMode(int mode) { + Common::StackLock lock(_graphicsMutex); + + assert(_transactionMode == kTransactionActive); + + if (_oldVideoMode.setup && _oldVideoMode.mode == mode) + return true; + + int newScaleFactor = 1; + + switch (mode) { + case GFX_NORMAL: + newScaleFactor = 1; + break; +#ifdef USE_SCALERS + case GFX_DOUBLESIZE: + newScaleFactor = 2; + break; + case GFX_TRIPLESIZE: + newScaleFactor = 3; + break; + + case GFX_2XSAI: + newScaleFactor = 2; + break; + case GFX_SUPER2XSAI: + newScaleFactor = 2; + break; + case GFX_SUPEREAGLE: + newScaleFactor = 2; + break; + case GFX_ADVMAME2X: + newScaleFactor = 2; + break; + case GFX_ADVMAME3X: + newScaleFactor = 3; + break; +#ifdef USE_HQ_SCALERS + case GFX_HQ2X: + newScaleFactor = 2; + break; + case GFX_HQ3X: + newScaleFactor = 3; + break; +#endif + case GFX_TV2X: + newScaleFactor = 2; + break; + case GFX_DOTMATRIX: + newScaleFactor = 2; + break; +#endif // USE_SCALERS + + default: + warning("unknown gfx mode %d", mode); + return false; + } + + _transactionDetails.normal1xScaler = (mode == GFX_NORMAL); + if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newScaleFactor) + _transactionDetails.needHotswap = true; + + _transactionDetails.needUpdatescreen = true; + + _videoMode.mode = mode; + _videoMode.scaleFactor = newScaleFactor; + + return true; +} + +void SdlGraphicsManager::setGraphicsModeIntern() { + Common::StackLock lock(_graphicsMutex); + ScalerProc *newScalerProc = 0; + + switch (_videoMode.mode) { + case GFX_NORMAL: + newScalerProc = Normal1x; + break; +#ifdef USE_SCALERS + case GFX_DOUBLESIZE: + newScalerProc = Normal2x; + break; + case GFX_TRIPLESIZE: + newScalerProc = Normal3x; + break; + + case GFX_2XSAI: + newScalerProc = _2xSaI; + break; + case GFX_SUPER2XSAI: + newScalerProc = Super2xSaI; + break; + case GFX_SUPEREAGLE: + newScalerProc = SuperEagle; + break; + case GFX_ADVMAME2X: + newScalerProc = AdvMame2x; + break; + case GFX_ADVMAME3X: + newScalerProc = AdvMame3x; + break; +#ifdef USE_HQ_SCALERS + case GFX_HQ2X: + newScalerProc = HQ2x; + break; + case GFX_HQ3X: + newScalerProc = HQ3x; + break; +#endif + case GFX_TV2X: + newScalerProc = TV2x; + break; + case GFX_DOTMATRIX: + newScalerProc = DotMatrix; + break; +#endif // USE_SCALERS + + default: + error("Unknown gfx mode %d", _videoMode.mode); + } + + _scalerProc = newScalerProc; + + if (_videoMode.mode != GFX_NORMAL) { + for (int i = 0; i < ARRAYSIZE(s_gfxModeSwitchTable); i++) { + if (s_gfxModeSwitchTable[i][1] == _videoMode.mode || s_gfxModeSwitchTable[i][2] == _videoMode.mode) { + _scalerType = i; + break; + } + } + } + + if (!_screen || !_hwscreen) + return; + + // Blit everything to the screen + _forceFull = true; + + // Even if the old and new scale factors are the same, we may have a + // different scaler for the cursor now. + blitCursor(); +} + +int SdlGraphicsManager::getGraphicsMode() const { + assert (_transactionMode == kTransactionNone); + return _videoMode.mode; +} + +void SdlGraphicsManager::initSize(uint w, uint h, const Graphics::PixelFormat *format) { + assert(_transactionMode == kTransactionActive); + +#ifdef USE_RGB_COLOR + //avoid redundant format changes + Graphics::PixelFormat newFormat; + if (!format) + newFormat = Graphics::PixelFormat::createFormatCLUT8(); + else + newFormat = *format; + + assert(newFormat.bytesPerPixel > 0); + + if (newFormat != _videoMode.format) { + _videoMode.format = newFormat; + _transactionDetails.formatChanged = true; + _screenFormat = newFormat; + } +#endif + + // Avoid redundant res changes + if ((int)w == _videoMode.screenWidth && (int)h == _videoMode.screenHeight) + return; + + _videoMode.screenWidth = w; + _videoMode.screenHeight = h; + + _transactionDetails.sizeChanged = true; +} + +int SdlGraphicsManager::effectiveScreenHeight() const { + return _videoMode.scaleFactor * + (_videoMode.aspectRatioCorrection + ? real2Aspect(_videoMode.screenHeight) + : _videoMode.screenHeight); +} + +static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &width, int &height) { + assert(&width != &height); + + if (desiredAspectRatio.isAuto()) + return; + + int kw = desiredAspectRatio.kw(); + int kh = desiredAspectRatio.kh(); + + const int w = width; + const int h = height; + + SDL_Rect const* const*availableModes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_SWSURFACE); //TODO : Maybe specify a pixel format + assert(availableModes); + + const SDL_Rect *bestMode = NULL; + uint bestMetric = (uint)-1; // Metric is wasted space + while (const SDL_Rect *mode = *availableModes++) { + if (mode->w < w) + continue; + if (mode->h < h) + continue; + if (mode->h * kw != mode->w * kh) + continue; + + uint metric = mode->w * mode->h - w * h; + if (metric > bestMetric) + continue; + + bestMetric = metric; + bestMode = mode; + } + + if (!bestMode) { + warning("Unable to enforce the desired aspect ratio!"); + return; + } + width = bestMode->w; + height = bestMode->h; +} + +bool SdlGraphicsManager::loadGFXMode() { + _forceFull = true; + +#if !defined(__MAEMO__) && !defined(GP2XWIZ) && !defined(LINUXMOTO) + _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; + + if (_videoMode.screenHeight != 200 && _videoMode.screenHeight != 400) + _videoMode.aspectRatioCorrection = false; + + if (_videoMode.aspectRatioCorrection) + _videoMode.overlayHeight = real2Aspect(_videoMode.overlayHeight); + + _videoMode.hardwareWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.hardwareHeight = effectiveScreenHeight(); +#else + _videoMode.hardwareWidth = _videoMode.overlayWidth; + _videoMode.hardwareHeight = _videoMode.overlayHeight; +#endif + + // + // Create the surface that contains the 8 bit game data + // +#ifdef USE_RGB_COLOR + _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, + _screenFormat.bytesPerPixel << 3, + ((1 << _screenFormat.rBits()) - 1) << _screenFormat.rShift , + ((1 << _screenFormat.gBits()) - 1) << _screenFormat.gShift , + ((1 << _screenFormat.bBits()) - 1) << _screenFormat.bShift , + ((1 << _screenFormat.aBits()) - 1) << _screenFormat.aShift ); + if (_screen == NULL) + error("allocating _screen failed"); + +#else + _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, 8, 0, 0, 0, 0); + if (_screen == NULL) + error("allocating _screen failed"); +#endif + + // + // Create the surface that contains the scaled graphics in 16 bit mode + // + + if (_videoMode.fullscreen) { + fixupResolutionForAspectRatio(_videoMode.desiredAspectRatio, _videoMode.hardwareWidth, _videoMode.hardwareHeight); + } + + _hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16, + _videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE + ); +#ifdef USE_RGB_COLOR + detectSupportedFormats(); +#endif + + if (_hwscreen == NULL) { + // DON'T use error(), as this tries to bring up the debug + // console, which WON'T WORK now that _hwscreen is hosed. + + if (!_oldVideoMode.setup) { + warning("SDL_SetVideoMode says we can't switch to that mode (%s)", SDL_GetError()); + g_system->quit(); + } else { + return false; + } + } + + // + // Create the surface used for the graphics in 16 bit before scaling, and also the overlay + // + + // Need some extra bytes around when using 2xSaI + _tmpscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth + 3, _videoMode.screenHeight + 3, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_tmpscreen == NULL) + error("allocating _tmpscreen failed"); + + _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_overlayscreen == NULL) + error("allocating _overlayscreen failed"); + + _overlayFormat.bytesPerPixel = _overlayscreen->format->BytesPerPixel; + + _overlayFormat.rLoss = _overlayscreen->format->Rloss; + _overlayFormat.gLoss = _overlayscreen->format->Gloss; + _overlayFormat.bLoss = _overlayscreen->format->Bloss; + _overlayFormat.aLoss = _overlayscreen->format->Aloss; + + _overlayFormat.rShift = _overlayscreen->format->Rshift; + _overlayFormat.gShift = _overlayscreen->format->Gshift; + _overlayFormat.bShift = _overlayscreen->format->Bshift; + _overlayFormat.aShift = _overlayscreen->format->Ashift; + + _tmpscreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth + 3, _videoMode.overlayHeight + 3, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_tmpscreen2 == NULL) + error("allocating _tmpscreen2 failed"); + +#ifdef USE_OSD + _osdSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _hwscreen->w, + _hwscreen->h, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + if (_osdSurface == NULL) + error("allocating _osdSurface failed"); + SDL_SetColorKey(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kOSDColorKey); +#endif + + ((SdlEventManager *)g_system->getEventManager())->resetKeyboadEmulation( + _videoMode.screenWidth * _videoMode.scaleFactor - 1, + effectiveScreenHeight() - 1); + + // Distinguish 555 and 565 mode + if (_hwscreen->format->Rmask == 0x7C00) + InitScalers(555); + else + InitScalers(565); + + return true; +} + +void SdlGraphicsManager::unloadGFXMode() { + if (_screen) { + SDL_FreeSurface(_screen); + _screen = NULL; + } + + if (_hwscreen) { + SDL_FreeSurface(_hwscreen); + _hwscreen = NULL; + } + + if (_tmpscreen) { + SDL_FreeSurface(_tmpscreen); + _tmpscreen = NULL; + } + + if (_tmpscreen2) { + SDL_FreeSurface(_tmpscreen2); + _tmpscreen2 = NULL; + } + + if (_overlayscreen) { + SDL_FreeSurface(_overlayscreen); + _overlayscreen = NULL; + } + +#ifdef USE_OSD + if (_osdSurface) { + SDL_FreeSurface(_osdSurface); + _osdSurface = NULL; + } +#endif + DestroyScalers(); +} + +bool SdlGraphicsManager::hotswapGFXMode() { + if (!_screen) + return false; + + // Keep around the old _screen & _overlayscreen so we can restore the screen data + // after the mode switch. + SDL_Surface *old_screen = _screen; + _screen = NULL; + SDL_Surface *old_overlayscreen = _overlayscreen; + _overlayscreen = NULL; + + // Release the HW screen surface + SDL_FreeSurface(_hwscreen); _hwscreen = NULL; + + SDL_FreeSurface(_tmpscreen); _tmpscreen = NULL; + SDL_FreeSurface(_tmpscreen2); _tmpscreen2 = NULL; + +#ifdef USE_OSD + // Release the OSD surface + SDL_FreeSurface(_osdSurface); _osdSurface = NULL; +#endif + + // Setup the new GFX mode + if (!loadGFXMode()) { + unloadGFXMode(); + + _screen = old_screen; + _overlayscreen = old_overlayscreen; + + return false; + } + + // reset palette + SDL_SetColors(_screen, _currentPalette, 0, 256); + + // Restore old screen content + SDL_BlitSurface(old_screen, NULL, _screen, NULL); + SDL_BlitSurface(old_overlayscreen, NULL, _overlayscreen, NULL); + + // Free the old surfaces + SDL_FreeSurface(old_screen); + SDL_FreeSurface(old_overlayscreen); + + // Update cursor to new scale + blitCursor(); + + // Blit everything to the screen + internUpdateScreen(); + + return true; +} + +void SdlGraphicsManager::updateScreen() { + assert (_transactionMode == kTransactionNone); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + internUpdateScreen(); +} + +void SdlGraphicsManager::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + int height, width; + ScalerProc *scalerProc; + int scale1; + + // definitions not available for non-DEBUG here. (needed this to compile in SYMBIAN32 & linux?) +#if defined (DEBUG) && !defined(WIN32) && !defined(_WIN32_WCE) + assert(_hwscreen != NULL); + assert(_hwscreen->map->sw_data != NULL); +#endif + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos || + (_mouseNeedsRedraw && _mouseBackup.y <= _currentShakePos)) { + SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor}; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + + SDL_FillRect(_hwscreen, &blackrect, 0); + + _currentShakePos = _newShakePos; + + _forceFull = true; + } + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_screen && _paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, + _paletteDirtyStart, + _paletteDirtyEnd - _paletteDirtyStart); + + _paletteDirtyEnd = 0; + + _forceFull = true; + } + +#ifdef USE_OSD + // OSD visible (i.e. non-transparent)? + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + // Updated alpha value + const int diff = SDL_GetTicks() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = SDL_ALPHA_TRANSPARENT; + } else { + // Do a linear fade out... + const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; + } + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + _forceFull = true; + } + } +#endif + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _tmpscreen; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + scalerProc = _scalerProc; + scale1 = _videoMode.scaleFactor; + } else { + origSurf = _overlayscreen; + srcSurf = _tmpscreen2; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + scalerProc = Normal1x; + + scale1 = 1; + } + + // Add the area covered by the mouse cursor to the list of dirty rects if + // we have to redraw the mouse. + if (_mouseNeedsRedraw) + undrawMouse(); + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + _dirtyRectList[0].x = 0; + _dirtyRectList[0].y = 0; + _dirtyRectList[0].w = width; + _dirtyRectList[0].h = height; + } + + // Only draw anything if necessary + if (_numDirtyRects > 0 || _mouseNeedsRedraw) { + SDL_Rect *r; + SDL_Rect dst; + uint32 srcPitch, dstPitch; + SDL_Rect *lastRect = _dirtyRectList + _numDirtyRects; + + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + dst.x++; // Shift rect by one since 2xSai needs to access the data around + dst.y++; // any pixel to scale it, and we want to avoid mem access crashes. + + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + SDL_LockSurface(srcSurf); + SDL_LockSurface(_hwscreen); + + srcPitch = srcSurf->pitch; + dstPitch = _hwscreen->pitch; + + for (r = _dirtyRectList; r != lastRect; ++r) { + register int dst_y = r->y + _currentShakePos; + register int dst_h = 0; + register int orig_dst_y = 0; + register int rx1 = r->x * scale1; + + if (dst_y < height) { + dst_h = r->h; + if (dst_h > height - dst_y) + dst_h = height - dst_y; + + orig_dst_y = dst_y; + dst_y = dst_y * scale1; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst_y = real2Aspect(dst_y); + + assert(scalerProc != NULL); + scalerProc((byte *)srcSurf->pixels + (r->x * 2 + 2) + (r->y + 1) * srcPitch, srcPitch, + (byte *)_hwscreen->pixels + rx1 * 2 + dst_y * dstPitch, dstPitch, r->w, dst_h); + } + + r->x = rx1; + r->y = dst_y; + r->w = r->w * scale1; + r->h = dst_h * scale1; + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection && orig_dst_y < height && !_overlayVisible) + r->h = stretch200To240((uint8 *) _hwscreen->pixels, dstPitch, r->w, r->h, r->x, r->y, orig_dst_y * scale1); +#endif + } + SDL_UnlockSurface(srcSurf); + SDL_UnlockSurface(_hwscreen); + + // Readjust the dirty rect list in case we are doing a full update. + // This is necessary if shaking is active. + if (_forceFull) { + _dirtyRectList[0].y = 0; + _dirtyRectList[0].h = effectiveScreenHeight(); + } + + drawMouse(); + +#ifdef USE_OSD + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0); + } +#endif + // Finally, blit all our changes to the screen + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + } + + _numDirtyRects = 0; + _forceFull = false; + _mouseNeedsRedraw = false; +} + +bool SdlGraphicsManager::saveScreenshot(const char *filename) { + assert(_hwscreen != NULL); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + return SDL_SaveBMP(_hwscreen, filename) == 0; +} + +void SdlGraphicsManager::setFullscreenMode(bool enable) { + Common::StackLock lock(_graphicsMutex); + + if (_oldVideoMode.setup && _oldVideoMode.fullscreen == enable) + return; + + if (_transactionMode == kTransactionActive) { + _videoMode.fullscreen = enable; + _transactionDetails.needHotswap = true; + } +} + +void SdlGraphicsManager::setAspectRatioCorrection(bool enable) { + Common::StackLock lock(_graphicsMutex); + + if (_oldVideoMode.setup && _oldVideoMode.aspectRatioCorrection == enable) + return; + + if (_transactionMode == kTransactionActive) { + _videoMode.aspectRatioCorrection = enable; + _transactionDetails.needHotswap = true; + } +} + +void SdlGraphicsManager::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) { + assert (_transactionMode == kTransactionNone); + assert(src); + + if (_screen == NULL) { + warning("SdlGraphicsManager::copyRectToScreen: _screen == NULL"); + return; + } + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + assert(x >= 0 && x < _videoMode.screenWidth); + assert(y >= 0 && y < _videoMode.screenHeight); + assert(h > 0 && y + h <= _videoMode.screenHeight); + assert(w > 0 && x + w <= _videoMode.screenWidth); + + addDirtyRect(x, y, w, h); + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + +#ifdef USE_RGB_COLOR + byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth * _screenFormat.bytesPerPixel + x * _screenFormat.bytesPerPixel; + if (_videoMode.screenWidth == w && pitch == w * _screenFormat.bytesPerPixel) { + memcpy(dst, src, h*w*_screenFormat.bytesPerPixel); + } else { + do { + memcpy(dst, src, w * _screenFormat.bytesPerPixel); + src += pitch; + dst += _videoMode.screenWidth * _screenFormat.bytesPerPixel; + } while (--h); + } +#else + byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x; + if (_videoMode.screenWidth == pitch && pitch == w) { + memcpy(dst, src, h*w); + } else { + do { + memcpy(dst, src, w); + src += pitch; + dst += _videoMode.screenWidth; + } while (--h); + } +#endif + + // Unlock the screen surface + SDL_UnlockSurface(_screen); +} + +Graphics::Surface *SdlGraphicsManager::lockScreen() { + assert (_transactionMode == kTransactionNone); + + // Lock the graphics mutex + g_system->lockMutex(_graphicsMutex); + + // paranoia check + assert(!_screenIsLocked); + _screenIsLocked = true; + + // Try to lock the screen surface + if (SDL_LockSurface(_screen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + _framebuffer.pixels = _screen->pixels; + _framebuffer.w = _screen->w; + _framebuffer.h = _screen->h; + _framebuffer.pitch = _screen->pitch; +#ifdef USE_RGB_COLOR + _framebuffer.bytesPerPixel = _screenFormat.bytesPerPixel; +#else + _framebuffer.bytesPerPixel = 1; +#endif + + return &_framebuffer; +} + +void SdlGraphicsManager::unlockScreen() { + assert (_transactionMode == kTransactionNone); + + // paranoia check + assert(_screenIsLocked); + _screenIsLocked = false; + + // Unlock the screen surface + SDL_UnlockSurface(_screen); + + // Trigger a full screen update + _forceFull = true; + + // Finally unlock the graphics mutex + g_system->unlockMutex(_graphicsMutex); +} + +void SdlGraphicsManager::fillScreen(uint32 col) { + Graphics::Surface *screen = lockScreen(); + if (screen && screen->pixels) + memset(screen->pixels, col, screen->h * screen->pitch); + unlockScreen(); +} + +void SdlGraphicsManager::addDirtyRect(int x, int y, int w, int h, bool realCoordinates) { + if (_forceFull) + return; + + if (_numDirtyRects == NUM_DIRTY_RECT) { + _forceFull = true; + return; + } + + int height, width; + + if (!_overlayVisible && !realCoordinates) { + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + } else { + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + } + + // Extend the dirty region by 1 pixel for scalers + // that "smear" the screen, e.g. 2xSAI + if (!realCoordinates) { + x--; + y--; + w+=2; + h+=2; + } + + // clip + if (x < 0) { + w += x; + x = 0; + } + + if (y < 0) { + h += y; + y=0; + } + + if (w > width - x) { + w = width - x; + } + + if (h > height - y) { + h = height - y; + } + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection && !_overlayVisible && !realCoordinates) { + makeRectStretchable(x, y, w, h); + } +#endif + + if (w == width && h == height) { + _forceFull = true; + return; + } + + if (w > 0 && h > 0) { + SDL_Rect *r = &_dirtyRectList[_numDirtyRects++]; + + r->x = x; + r->y = y; + r->w = w; + r->h = h; + } +} + +int16 SdlGraphicsManager::getHeight() { + return _videoMode.screenHeight; +} + +int16 SdlGraphicsManager::getWidth() { + return _videoMode.screenWidth; +} + +void SdlGraphicsManager::setPalette(const byte *colors, uint start, uint num) { + assert(colors); + +#ifdef USE_RGB_COLOR + assert(_screenFormat.bytesPerPixel == 1); +#endif + + // Setting the palette before _screen is created is allowed - for now - + // since we don't actually set the palette until the screen is updated. + // But it could indicate a programming error, so let's warn about it. + + if (!_screen) + warning("SdlGraphicsManager::setPalette: _screen == NULL"); + + const byte *b = colors; + uint i; + SDL_Color *base = _currentPalette + start; + for (i = 0; i < num; i++) { + base[i].r = b[0]; + base[i].g = b[1]; + base[i].b = b[2]; + b += 4; + } + + if (start < _paletteDirtyStart) + _paletteDirtyStart = start; + + if (start + num > _paletteDirtyEnd) + _paletteDirtyEnd = start + num; + + // Some games blink cursors with palette + if (_cursorPaletteDisabled) + blitCursor(); +} + +void SdlGraphicsManager::grabPalette(byte *colors, uint start, uint num) { + assert(colors); + +#ifdef USE_RGB_COLOR + assert(_screenFormat.bytesPerPixel == 1); +#endif + + const SDL_Color *base = _currentPalette + start; + + for (uint i = 0; i < num; ++i) { + colors[i * 4] = base[i].r; + colors[i * 4 + 1] = base[i].g; + colors[i * 4 + 2] = base[i].b; + colors[i * 4 + 3] = 0xFF; + } +} + +void SdlGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) { + assert(colors); + const byte *b = colors; + uint i; + SDL_Color *base = _cursorPalette + start; + for (i = 0; i < num; i++) { + base[i].r = b[0]; + base[i].g = b[1]; + base[i].b = b[2]; + b += 4; + } + + _cursorPaletteDisabled = false; + blitCursor(); +} + +void SdlGraphicsManager::disableCursorPalette(bool disable) { + _cursorPaletteDisabled = disable; + blitCursor(); +} + +void SdlGraphicsManager::setShakePos(int shake_pos) { + assert (_transactionMode == kTransactionNone); + + _newShakePos = shake_pos; +} + + +#pragma mark - +#pragma mark --- Overlays --- +#pragma mark - + +void SdlGraphicsManager::showOverlay() { + assert (_transactionMode == kTransactionNone); + + int x, y; + + if (_overlayVisible) + return; + + _overlayVisible = true; + + // Since resolution could change, put mouse to adjusted position + // Fixes bug #1349059 + x = _mouseCurState.x * _videoMode.scaleFactor; + if (_videoMode.aspectRatioCorrection) + y = real2Aspect(_mouseCurState.y) * _videoMode.scaleFactor; + else + y = _mouseCurState.y * _videoMode.scaleFactor; + + warpMouse(x, y); + + clearOverlay(); +} + +void SdlGraphicsManager::hideOverlay() { + assert (_transactionMode == kTransactionNone); + + if (!_overlayVisible) + return; + + int x, y; + + _overlayVisible = false; + + // Since resolution could change, put mouse to adjusted position + // Fixes bug #1349059 + x = _mouseCurState.x / _videoMode.scaleFactor; + y = _mouseCurState.y / _videoMode.scaleFactor; + if (_videoMode.aspectRatioCorrection) + y = aspect2Real(y); + + warpMouse(x, y); + + clearOverlay(); + + _forceFull = true; +} + +void SdlGraphicsManager::clearOverlay() { + //assert (_transactionMode == kTransactionNone); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + if (!_overlayVisible) + return; + + // Clear the overlay by making the game screen "look through" everywhere. + SDL_Rect src, dst; + src.x = src.y = 0; + dst.x = dst.y = 1; + src.w = dst.w = _videoMode.screenWidth; + src.h = dst.h = _videoMode.screenHeight; + if (SDL_BlitSurface(_screen, &src, _tmpscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + SDL_LockSurface(_tmpscreen); + SDL_LockSurface(_overlayscreen); + _scalerProc((byte *)(_tmpscreen->pixels) + _tmpscreen->pitch + 2, _tmpscreen->pitch, + (byte *)_overlayscreen->pixels, _overlayscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight); + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection) + stretch200To240((uint8 *)_overlayscreen->pixels, _overlayscreen->pitch, + _videoMode.overlayWidth, _videoMode.screenHeight * _videoMode.scaleFactor, 0, 0, 0); +#endif + SDL_UnlockSurface(_tmpscreen); + SDL_UnlockSurface(_overlayscreen); + + _forceFull = true; +} + +void SdlGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) { + assert (_transactionMode == kTransactionNone); + + if (_overlayscreen == NULL) + return; + + if (SDL_LockSurface(_overlayscreen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *src = (byte *)_overlayscreen->pixels; + int h = _videoMode.overlayHeight; + do { + memcpy(buf, src, _videoMode.overlayWidth * 2); + src += _overlayscreen->pitch; + buf += pitch; + } while (--h); + + SDL_UnlockSurface(_overlayscreen); +} + +void SdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { + assert (_transactionMode == kTransactionNone); + + if (_overlayscreen == NULL) + return; + + // Clip the coordinates + if (x < 0) { + w += x; + buf -= x; + x = 0; + } + + if (y < 0) { + h += y; buf -= y * pitch; + y = 0; + } + + if (w > _videoMode.overlayWidth - x) { + w = _videoMode.overlayWidth - x; + } + + if (h > _videoMode.overlayHeight - y) { + h = _videoMode.overlayHeight - y; + } + + if (w <= 0 || h <= 0) + return; + + // Mark the modified region as dirty + addDirtyRect(x, y, w, h); + + if (SDL_LockSurface(_overlayscreen) == -1) + error("SDL_LockSurface failed: %s", SDL_GetError()); + + byte *dst = (byte *)_overlayscreen->pixels + y * _overlayscreen->pitch + x * 2; + do { + memcpy(dst, buf, w * 2); + dst += _overlayscreen->pitch; + buf += pitch; + } while (--h); + + SDL_UnlockSurface(_overlayscreen); +} + + +#pragma mark - +#pragma mark --- Mouse --- +#pragma mark - + +bool SdlGraphicsManager::showMouse(bool visible) { + if (_mouseVisible == visible) + return visible; + + bool last = _mouseVisible; + _mouseVisible = visible; + _mouseNeedsRedraw = true; + + return last; +} + +void SdlGraphicsManager::setMousePos(int x, int y) { + if (x != _mouseCurState.x || y != _mouseCurState.y) { + _mouseNeedsRedraw = true; + _mouseCurState.x = x; + _mouseCurState.y = y; + } +} + +void SdlGraphicsManager::warpMouse(int x, int y) { + int y1 = y; + + // Don't change mouse position, when mouse is outside of our window (in case of windowed mode) + if (!(SDL_GetAppState( ) & SDL_APPMOUSEFOCUS)) + return; + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + y1 = real2Aspect(y); + + if (_mouseCurState.x != x || _mouseCurState.y != y) { + if (!_overlayVisible) + SDL_WarpMouse(x * _videoMode.scaleFactor, y1 * _videoMode.scaleFactor); + else + SDL_WarpMouse(x, y1); + + // SDL_WarpMouse() generates a mouse movement event, so + // setMousePos() would be called eventually. However, the + // cannon script in CoMI calls this function twice each time + // the cannon is reloaded. Unless we update the mouse position + // immediately the second call is ignored, causing the cannon + // to change its aim. + + setMousePos(x, y); + } +} + +void SdlGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) { +#ifdef USE_RGB_COLOR + if (!format) + _cursorFormat = Graphics::PixelFormat::createFormatCLUT8(); + else if (format->bytesPerPixel <= _screenFormat.bytesPerPixel) + _cursorFormat = *format; + + if (_cursorFormat.bytesPerPixel < 4) + assert(keycolor < (uint)(1 << (_cursorFormat.bytesPerPixel << 3))); +#else + assert(keycolor <= 0xFF); +#endif + + if (w == 0 || h == 0) + return; + + _mouseCurState.hotX = hotspot_x; + _mouseCurState.hotY = hotspot_y; + + _mouseKeyColor = keycolor; + + _cursorTargetScale = cursorTargetScale; + + if (_mouseCurState.w != (int)w || _mouseCurState.h != (int)h) { + _mouseCurState.w = w; + _mouseCurState.h = h; + + if (_mouseOrigSurface) + SDL_FreeSurface(_mouseOrigSurface); + + // Allocate bigger surface because AdvMame2x adds black pixel at [0,0] + _mouseOrigSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _mouseCurState.w + 2, + _mouseCurState.h + 2, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_mouseOrigSurface == NULL) + error("allocating _mouseOrigSurface failed"); + SDL_SetColorKey(_mouseOrigSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kMouseColorKey); + } + + free(_mouseData); +#ifdef USE_RGB_COLOR + _mouseData = (byte *)malloc(w * h * _cursorFormat.bytesPerPixel); + memcpy(_mouseData, buf, w * h * _cursorFormat.bytesPerPixel); +#else + _mouseData = (byte *)malloc(w * h); + memcpy(_mouseData, buf, w * h); +#endif + + blitCursor(); +} + +void SdlGraphicsManager::blitCursor() { + byte *dstPtr; + const byte *srcPtr = _mouseData; +#ifdef USE_RGB_COLOR + uint32 color; +#else + byte color; +#endif + int w, h, i, j; + + if (!_mouseOrigSurface || !_mouseData) + return; + + _mouseNeedsRedraw = true; + + w = _mouseCurState.w; + h = _mouseCurState.h; + + SDL_LockSurface(_mouseOrigSurface); + + // Make whole surface transparent + for (i = 0; i < h + 2; i++) { + dstPtr = (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * i; + for (j = 0; j < w + 2; j++) { + *(uint16 *)dstPtr = kMouseColorKey; + dstPtr += 2; + } + } + + // Draw from [1,1] since AdvMame2x adds artefact at 0,0 + dstPtr = (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch + 2; + + SDL_Color *palette; + + if (_cursorPaletteDisabled) + palette = _currentPalette; + else + palette = _cursorPalette; + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { +#ifdef USE_RGB_COLOR + if (_cursorFormat.bytesPerPixel > 1) { + if (_cursorFormat.bytesPerPixel == 2) + color = *(const uint16 *)srcPtr; + else + color = *(const uint32 *)srcPtr; + if (color != _mouseKeyColor) { // transparent, don't draw + uint8 r, g, b; + _cursorFormat.colorToRGB(color, r, g, b); + *(uint16 *)dstPtr = SDL_MapRGB(_mouseOrigSurface->format, + r, g, b); + } + dstPtr += 2; + srcPtr += _cursorFormat.bytesPerPixel; + } else { +#endif + color = *srcPtr; + if (color != _mouseKeyColor) { // transparent, don't draw + *(uint16 *)dstPtr = SDL_MapRGB(_mouseOrigSurface->format, + palette[color].r, palette[color].g, palette[color].b); + } + dstPtr += 2; + srcPtr++; +#ifdef USE_RGB_COLOR + } +#endif + } + dstPtr += _mouseOrigSurface->pitch - w * 2; + } + + int rW, rH; + + if (_cursorTargetScale >= _videoMode.scaleFactor) { + // The cursor target scale is greater or equal to the scale at + // which the rest of the screen is drawn. We do not downscale + // the cursor image, we draw it at its original size. It will + // appear too large on screen. + + rW = w; + rH = h; + _mouseCurState.rHotX = _mouseCurState.hotX; + _mouseCurState.rHotY = _mouseCurState.hotY; + + // The virtual dimensions may be larger than the original. + + _mouseCurState.vW = w * _cursorTargetScale / _videoMode.scaleFactor; + _mouseCurState.vH = h * _cursorTargetScale / _videoMode.scaleFactor; + _mouseCurState.vHotX = _mouseCurState.hotX * _cursorTargetScale / + _videoMode.scaleFactor; + _mouseCurState.vHotY = _mouseCurState.hotY * _cursorTargetScale / + _videoMode.scaleFactor; + } else { + // The cursor target scale is smaller than the scale at which + // the rest of the screen is drawn. We scale up the cursor + // image to make it appear correct. + + rW = w * _videoMode.scaleFactor / _cursorTargetScale; + rH = h * _videoMode.scaleFactor / _cursorTargetScale; + _mouseCurState.rHotX = _mouseCurState.hotX * _videoMode.scaleFactor / + _cursorTargetScale; + _mouseCurState.rHotY = _mouseCurState.hotY * _videoMode.scaleFactor / + _cursorTargetScale; + + // The virtual dimensions will be the same as the original. + + _mouseCurState.vW = w; + _mouseCurState.vH = h; + _mouseCurState.vHotX = _mouseCurState.hotX; + _mouseCurState.vHotY = _mouseCurState.hotY; + } + +#ifdef USE_SCALERS + int rH1 = rH; // store original to pass to aspect-correction function later +#endif + + if (_videoMode.aspectRatioCorrection && _cursorTargetScale == 1) { + rH = real2Aspect(rH - 1) + 1; + _mouseCurState.rHotY = real2Aspect(_mouseCurState.rHotY); + } + + if (_mouseCurState.rW != rW || _mouseCurState.rH != rH) { + _mouseCurState.rW = rW; + _mouseCurState.rH = rH; + + if (_mouseSurface) + SDL_FreeSurface(_mouseSurface); + + _mouseSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _mouseCurState.rW, + _mouseCurState.rH, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_mouseSurface == NULL) + error("allocating _mouseSurface failed"); + + SDL_SetColorKey(_mouseSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kMouseColorKey); + } + + SDL_LockSurface(_mouseSurface); + + ScalerProc *scalerProc; + + // If possible, use the same scaler for the cursor as for the rest of + // the game. This only works well with the non-blurring scalers so we + // actually only use the 1x, 1.5x, 2x and AdvMame scalers. + + if (_cursorTargetScale == 1 && (_videoMode.mode == GFX_DOUBLESIZE || _videoMode.mode == GFX_TRIPLESIZE)) + scalerProc = _scalerProc; + else + scalerProc = scalersMagn[_cursorTargetScale - 1][_videoMode.scaleFactor - 1]; + + scalerProc((byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch + 2, + _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, + _mouseCurState.w, _mouseCurState.h); + +#ifdef USE_SCALERS + if (_videoMode.aspectRatioCorrection && _cursorTargetScale == 1) + cursorStretch200To240((uint8 *)_mouseSurface->pixels, _mouseSurface->pitch, rW, rH1, 0, 0, 0); +#endif + + SDL_UnlockSurface(_mouseSurface); + SDL_UnlockSurface(_mouseOrigSurface); +} + +#ifdef USE_SCALERS +// Basically it is kVeryFastAndUglyAspectMode of stretch200To240 from +// common/scale/aspect.cpp +static int cursorStretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY) { + int maxDstY = real2Aspect(origSrcY + height - 1); + int y; + const uint8 *startSrcPtr = buf + srcX * 2 + (srcY - origSrcY) * pitch; + uint8 *dstPtr = buf + srcX * 2 + maxDstY * pitch; + + for (y = maxDstY; y >= srcY; y--) { + const uint8 *srcPtr = startSrcPtr + aspect2Real(y) * pitch; + + if (srcPtr == dstPtr) + break; + memcpy(dstPtr, srcPtr, width * 2); + dstPtr -= pitch; + } + + return 1 + maxDstY - srcY; +} +#endif + +void SdlGraphicsManager::undrawMouse() { + const int x = _mouseBackup.x; + const int y = _mouseBackup.y; + + // When we switch bigger overlay off mouse jumps. Argh! + // This is intended to prevent undrawing offscreen mouse + if (!_overlayVisible && (x >= _videoMode.screenWidth || y >= _videoMode.screenHeight)) + return; + + if (_mouseBackup.w != 0 && _mouseBackup.h != 0) + addDirtyRect(x, y - _currentShakePos, _mouseBackup.w, _mouseBackup.h); +} + +void SdlGraphicsManager::drawMouse() { + if (!_mouseVisible || !_mouseSurface) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect dst; + int scale; + int width, height; + int hotX, hotY; + + dst.x = _mouseCurState.x; + dst.y = _mouseCurState.y; + + if (!_overlayVisible) { + scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + dst.w = _mouseCurState.vW; + dst.h = _mouseCurState.vH; + hotX = _mouseCurState.vHotX; + hotY = _mouseCurState.vHotY; + } else { + scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + hotX = _mouseCurState.rHotX; + hotY = _mouseCurState.rHotY; + } + + // The mouse is undrawn using virtual coordinates, i.e. they may be + // scaled and aspect-ratio corrected. + + _mouseBackup.x = dst.x - hotX; + _mouseBackup.y = dst.y - hotY; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + // We draw the pre-scaled cursor image, so now we need to adjust for + // scaling, shake position and aspect ratio correction manually. + + if (!_overlayVisible) { + dst.y += _currentShakePos; + } + + if (_videoMode.aspectRatioCorrection && !_overlayVisible) + dst.y = real2Aspect(dst.y); + + dst.x = scale * dst.x - _mouseCurState.rHotX; + dst.y = scale * dst.y - _mouseCurState.rHotY; + dst.w = _mouseCurState.rW; + dst.h = _mouseCurState.rH; + + // Note that SDL_BlitSurface() and addDirtyRect() will both perform any + // clipping necessary + + if (SDL_BlitSurface(_mouseSurface, NULL, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + // The screen will be updated using real surface coordinates, i.e. + // they will not be scaled or aspect-ratio corrected. + + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} + +#pragma mark - +#pragma mark --- On Screen Display --- +#pragma mark - + +#ifdef USE_OSD +void SdlGraphicsManager::displayMessageOnOSD(const char *msg) { + assert (_transactionMode == kTransactionNone); + assert(msg); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + uint i; + + // Lock the OSD surface for drawing + if (SDL_LockSurface(_osdSurface)) + error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError()); + + Graphics::Surface dst; + dst.pixels = _osdSurface->pixels; + dst.w = _osdSurface->w; + dst.h = _osdSurface->h; + dst.pitch = _osdSurface->pitch; + dst.bytesPerPixel = _osdSurface->format->BytesPerPixel; + + // The font we are going to use: + const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kOSDFont); + + // Clear everything with the "transparent" color, i.e. the colorkey + SDL_FillRect(_osdSurface, 0, kOSDColorKey); + + // Split the message into separate lines. + Common::Array<Common::String> lines; + const char *ptr; + for (ptr = msg; *ptr; ++ptr) { + if (*ptr == '\n') { + lines.push_back(Common::String(msg, ptr - msg)); + msg = ptr + 1; + } + } + lines.push_back(Common::String(msg, ptr - msg)); + + // 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 * lines.size() + 2 * vOffset; + for (i = 0; i < lines.size(); i++) { + width = MAX(width, font->getStringWidth(lines[i]) + 14); + } + + // Clip the rect + if (width > dst.w) + width = dst.w; + if (height > dst.h) + height = dst.h; + + // Draw a dark gray rect + // TODO: Rounded corners ? Border? + SDL_Rect osdRect; + osdRect.x = (dst.w - width) / 2; + osdRect.y = (dst.h - height) / 2; + osdRect.w = width; + osdRect.h = height; + SDL_FillRect(_osdSurface, &osdRect, SDL_MapRGB(_osdSurface->format, 64, 64, 64)); + + // Render the message, centered, and in white + for (i = 0; i < lines.size(); i++) { + font->drawString(&dst, lines[i], + osdRect.x, osdRect.y + i * lineHeight + vOffset + lineSpacing, osdRect.w, + SDL_MapRGB(_osdSurface->format, 255, 255, 255), + Graphics::kTextAlignCenter); + } + + // Finished drawing, so unlock the OSD surface again + SDL_UnlockSurface(_osdSurface); + + // Init the OSD display parameters, and the fade out + _osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay; + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + + // Ensure a full redraw takes place next time the screen is updated + _forceFull = true; +} +#endif + +bool SdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) { + + // Ctrl-Alt-a toggles aspect ratio correction + if (key == 'a') { + beginGFXTransaction(); + setFeatureState(OSystem::kFeatureAspectRatioCorrection, !_videoMode.aspectRatioCorrection); + endGFXTransaction(); +#ifdef USE_OSD + char buffer[128]; + if (_videoMode.aspectRatioCorrection) + sprintf(buffer, "Enabled aspect ratio correction\n%d x %d -> %d x %d", + _videoMode.screenWidth, _videoMode.screenHeight, + _hwscreen->w, _hwscreen->h + ); + else + sprintf(buffer, "Disabled aspect ratio correction\n%d x %d -> %d x %d", + _videoMode.screenWidth, _videoMode.screenHeight, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); +#endif + internUpdateScreen(); + return true; + } + + int newMode = -1; + int factor = _videoMode.scaleFactor - 1; + SDLKey sdlKey = (SDLKey)key; + + // Increase/decrease the scale factor + if (sdlKey == SDLK_EQUALS || sdlKey == SDLK_PLUS || sdlKey == SDLK_MINUS || + sdlKey == SDLK_KP_PLUS || sdlKey == SDLK_KP_MINUS) { + factor += (sdlKey == SDLK_MINUS || sdlKey == SDLK_KP_MINUS) ? -1 : +1; + if (0 <= factor && factor <= 3) { + newMode = s_gfxModeSwitchTable[_scalerType][factor]; + } + } + + const bool isNormalNumber = (SDLK_1 <= sdlKey && sdlKey <= SDLK_9); + const bool isKeypadNumber = (SDLK_KP1 <= sdlKey && sdlKey <= SDLK_KP9); + if (isNormalNumber || isKeypadNumber) { + _scalerType = sdlKey - (isNormalNumber ? SDLK_1 : SDLK_KP1); + if (_scalerType >= ARRAYSIZE(s_gfxModeSwitchTable)) + return false; + + while (s_gfxModeSwitchTable[_scalerType][factor] < 0) { + assert(factor > 0); + factor--; + } + newMode = s_gfxModeSwitchTable[_scalerType][factor]; + } + + if (newMode >= 0) { + beginGFXTransaction(); + setGraphicsMode(newMode); + endGFXTransaction(); +#ifdef USE_OSD + if (_osdSurface) { + const char *newScalerName = 0; + const OSystem::GraphicsMode *g = getSupportedGraphicsModes(); + while (g->name) { + if (g->id == _videoMode.mode) { + newScalerName = g->description; + break; + } + g++; + } + if (newScalerName) { + char buffer[128]; + sprintf(buffer, "Active graphics filter: %s\n%d x %d -> %d x %d", + newScalerName, + _videoMode.screenWidth, _videoMode.screenHeight, + _hwscreen->w, _hwscreen->h + ); + displayMessageOnOSD(buffer); + } + } +#endif + internUpdateScreen(); + + return true; + } else { + return false; + } +} + +bool SdlGraphicsManager::isScalerHotkey(const Common::Event &event) { + if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { + const bool isNormalNumber = (Common::KEYCODE_1 <= event.kbd.keycode && event.kbd.keycode <= Common::KEYCODE_9); + const bool isKeypadNumber = (Common::KEYCODE_KP1 <= event.kbd.keycode && event.kbd.keycode <= Common::KEYCODE_KP9); + const bool isScaleKey = (event.kbd.keycode == Common::KEYCODE_EQUALS || event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS || + event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS); + + if (isNormalNumber || isKeypadNumber) { + int keyValue = event.kbd.keycode - (isNormalNumber ? Common::KEYCODE_1 : Common::KEYCODE_KP1); + if (keyValue >= ARRAYSIZE(s_gfxModeSwitchTable)) + return false; + } + return (isScaleKey || event.kbd.keycode == 'a'); + } + return false; +} + +void SdlGraphicsManager::adjustMouseEvent(const Common::Event &event) { + if (!event.synthetic) { + Common::Event newEvent(event); + newEvent.synthetic = true; + if (!_overlayVisible) { + newEvent.mouse.x /= _videoMode.scaleFactor; + newEvent.mouse.y /= _videoMode.scaleFactor; + if (_videoMode.aspectRatioCorrection) + newEvent.mouse.y = aspect2Real(newEvent.mouse.y); + } + g_system->getEventManager()->pushEvent(newEvent); + } +} + +void SdlGraphicsManager::toggleFullScreen() { + beginGFXTransaction(); + setFullscreenMode(!_videoMode.fullscreen); + endGFXTransaction(); +#ifdef USE_OSD + if (_videoMode.fullscreen) + displayMessageOnOSD("Fullscreen mode"); + else + displayMessageOnOSD("Windowed mode"); +#endif +} + +bool SdlGraphicsManager::notifyEvent(const Common::Event &event) { + switch ((int)event.type) { + case Common::EVENT_KEYDOWN: + // Alt-Return and Alt-Enter toggle full screen mode + if (event.kbd.hasFlags(Common::KBD_ALT) && + (event.kbd.keycode == Common::KEYCODE_RETURN || + event.kbd.keycode == (Common::KeyCode)SDLK_KP_ENTER)) { + toggleFullScreen(); + return true; + } + + // Alt-S: Create a screenshot + if (event.kbd.hasFlags(Common::KBD_ALT) && event.kbd.keycode == 's') { + char filename[20]; + + for (int n = 0;; n++) { + SDL_RWops *file; + + sprintf(filename, "scummvm%05d.bmp", n); + file = SDL_RWFromFile(filename, "r"); + if (!file) + break; + SDL_RWclose(file); + } + if (saveScreenshot(filename)) + printf("Saved '%s'\n", filename); + else + printf("Could not save screenshot!\n"); + return true; + } + + // Ctrl-Alt-<key> will change the GFX mode + if (event.kbd.hasFlags(Common::KBD_CTRL|Common::KBD_ALT)) { + if (handleScalerHotkeys(event.kbd.keycode)) + return true; + } + case Common::EVENT_KEYUP: + return isScalerHotkey(event); + case Common::EVENT_MOUSEMOVE: + if (event.synthetic) + setMousePos(event.mouse.x, event.mouse.y); + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + case Common::EVENT_MBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_MBUTTONUP: + adjustMouseEvent(event); + return !event.synthetic; + + // HACK: Handle special SDL event + case OSystem_SDL::kSdlEventExpose: + _forceFull = true; + return false; + default: + break; + } + + return false; +} + +#endif diff --git a/backends/graphics/sdl/sdl-graphics.h b/backends/graphics/sdl/sdl-graphics.h new file mode 100644 index 0000000000..01ae44222a --- /dev/null +++ b/backends/graphics/sdl/sdl-graphics.h @@ -0,0 +1,323 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_H +#define BACKENDS_GRAPHICS_SDL_H + +#include "backends/graphics/graphics.h" +#include "graphics/scaler.h" +#include "common/events.h" +#include "common/system.h" + +#if defined(__SYMBIAN32__) +#include <esdl\SDL.h> +#else +#include <SDL.h> +#endif + +#if !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) +// Uncomment this to enable the 'on screen display' code. +#define USE_OSD 1 +#endif + +enum { + GFX_NORMAL = 0, + GFX_DOUBLESIZE = 1, + GFX_TRIPLESIZE = 2, + GFX_2XSAI = 3, + GFX_SUPER2XSAI = 4, + GFX_SUPEREAGLE = 5, + GFX_ADVMAME2X = 6, + GFX_ADVMAME3X = 7, + GFX_HQ2X = 8, + GFX_HQ3X = 9, + GFX_TV2X = 10, + GFX_DOTMATRIX = 11 +}; + + +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 SdlGraphicsManager : public GraphicsManager, public Common::EventObserver { +public: + SdlGraphicsManager(); + virtual ~SdlGraphicsManager(); + + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + virtual bool getFeatureState(OSystem::Feature f); + + static const OSystem::GraphicsMode *supportedGraphicsModes(); + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + virtual int getGraphicsMode() const; + virtual void resetGraphicsScale(); +#ifdef USE_RGB_COLOR + virtual Graphics::PixelFormat getScreenFormat() const { return _screenFormat; } + virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; +#endif + virtual void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL); + virtual int getScreenChangeID() const { return _screenChangeCount; } + + virtual void beginGFXTransaction(); + virtual OSystem::TransactionError endGFXTransaction(); + + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void grabPalette(byte *colors, uint start, uint num); + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + virtual Graphics::Surface *lockScreen(); + virtual void unlockScreen(); + virtual void fillScreen(uint32 col); + virtual void updateScreen(); + virtual void setShakePos(int shakeOffset); + virtual void setFocusRectangle(const Common::Rect& rect) {} + virtual void clearFocusRectangle() {} + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual Graphics::PixelFormat getOverlayFormat() const { return _overlayFormat; } + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + virtual int16 getOverlayHeight() { return _videoMode.overlayHeight; } + virtual int16 getOverlayWidth() { return _videoMode.overlayWidth; } + + virtual bool showMouse(bool visible); + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL); + virtual void setCursorPalette(const byte *colors, uint start, uint num); + virtual void disableCursorPalette(bool disable); + +#ifdef USE_OSD + virtual void displayMessageOnOSD(const char *msg); +#endif + + // Override from Common::EventObserver + bool notifyEvent(const Common::Event &event); + +protected: +#ifdef USE_OSD + /** Surface containing the OSD message */ + SDL_Surface *_osdSurface; + /** Transparency level of the OSD */ + uint8 _osdAlpha; + /** When to start the fade out */ + uint32 _osdFadeStartTime; + /** 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) */ + kOSDColorKey = 1, /** < Transparent color key */ + kOSDInitialAlpha = 80 /** < Initial alpha level, in percent */ + }; +#endif + + /** Hardware screen */ + SDL_Surface *_hwscreen; + + /** Unseen game screen */ + SDL_Surface *_screen; +#ifdef USE_RGB_COLOR + Graphics::PixelFormat _screenFormat; + Graphics::PixelFormat _cursorFormat; + 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 _overlayVisible; + Graphics::PixelFormat _overlayFormat; + + enum { + kTransactionNone = 0, + kTransactionActive = 1, + kTransactionRollback = 2 + }; + + struct TransactionDetails { + bool sizeChanged; + bool needHotswap; + bool needUpdatescreen; + bool normal1xScaler; +#ifdef USE_RGB_COLOR + bool formatChanged; +#endif + }; + TransactionDetails _transactionDetails; + + struct VideoState { + bool setup; + + bool fullscreen; + bool aspectRatioCorrection; + AspectRatio desiredAspectRatio; + + int mode; + int scaleFactor; + + int screenWidth, screenHeight; + int overlayWidth, overlayHeight; + int hardwareWidth, hardwareHeight; +#ifdef USE_RGB_COLOR + Graphics::PixelFormat format; +#endif + }; + VideoState _videoMode, _oldVideoMode; + + /** Force full redraw on next updateScreen */ + bool _forceFull; + + ScalerProc *_scalerProc; + int _scalerType; + int _transactionMode; + + 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 mouse position, using either virtual (game) or real + // (overlay) coordinates. + int16 x, y; + + // 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() : x(0), y(0), w(0), h(0), hotX(0), hotY(0), + rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0), + vHotX(0), vHotY(0) + { } + }; + + bool _mouseVisible; + bool _mouseNeedsRedraw; + byte *_mouseData; + SDL_Rect _mouseBackup; + MousePos _mouseCurState; +#ifdef USE_RGB_COLOR + uint32 _mouseKeyColor; +#else + byte _mouseKeyColor; +#endif + int _cursorTargetScale; + bool _cursorPaletteDisabled; + SDL_Surface *_mouseOrigSurface; + SDL_Surface *_mouseSurface; + enum { + kMouseColorKey = 1 + }; + + // Shake mode + int _currentShakePos; + int _newShakePos; + + // 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. + */ + OSystem::MutexRef _graphicsMutex; + + 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 setFullscreenMode(bool enable); + virtual void setAspectRatioCorrection(bool enable); + + virtual int effectiveScreenHeight() const; + + virtual void setGraphicsModeIntern(); + + virtual bool handleScalerHotkeys(Common::KeyCode key); + virtual bool isScalerHotkey(const Common::Event &event); + virtual void adjustMouseEvent(const Common::Event &event); + virtual void setMousePos(int x, int y); + virtual void toggleFullScreen(); + virtual bool saveScreenshot(const char *filename); +}; + +#endif diff --git a/backends/graphics/symbiansdl/symbiansdl-graphics.cpp b/backends/graphics/symbiansdl/symbiansdl-graphics.cpp new file mode 100644 index 0000000000..c89c46ddd8 --- /dev/null +++ b/backends/graphics/symbiansdl/symbiansdl-graphics.cpp @@ -0,0 +1,77 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifdef __SYMBIAN32__ + +#include "backends/graphics/symbiansdl/symbiansdl-graphics.h" +#include "backends/platform/symbian/src/SymbianActions.h" + +int SymbianSdlGraphicsManager::getDefaultGraphicsMode() const { + return GFX_NORMAL; +} + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {"1x", "Fullscreen", GFX_NORMAL}, + {0, 0, 0} +}; + +const OSystem::GraphicsMode *SymbianSdlGraphicsManager::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + +// make sure we always go to normal, even if the string might be set wrong! +bool SymbianSdlGraphicsManager::setGraphicsMode(int /*name*/) { + // let parent OSystem_SDL handle it + return SdlGraphicsManager::setGraphicsMode(getDefaultGraphicsMode()); +} + +bool SymbianSdlGraphicsManager::hasFeature(OSystem::Feature f) { + switch (f) { + case OSystem::kFeatureFullscreenMode: + case OSystem::kFeatureAspectRatioCorrection: + case OSystem::kFeatureCursorHasPalette: +#ifdef USE_VIBRA_SE_PXXX + case OSystem::kFeatureVibration: +#endif + return true; + default: + return false; + } +} + +void SymbianSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureVirtualKeyboard: + break; + case OSystem::kFeatureDisableKeyFiltering: + GUI::Actions::Instance()->beginMapping(enable); + break; + default: + SdlGraphicsManager::setFeatureState(f, enable); + } +} + +#endif + diff --git a/backends/graphics/symbiansdl/symbiansdl-graphics.h b/backends/graphics/symbiansdl/symbiansdl-graphics.h new file mode 100644 index 0000000000..b0e87c8025 --- /dev/null +++ b/backends/graphics/symbiansdl/symbiansdl-graphics.h @@ -0,0 +1,42 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_GRAPHICS_SYMBIAN_SDL_H +#define BACKENDS_GRAPHICS_SYMBIAN_SDL_H + +#include "backends/graphics/sdl/sdl-graphics.h" + +class SymbianSdlGraphicsManager : public SdlGraphicsManager { +public: + virtual bool hasFeature(OSystem::Feature f); + virtual void setFeatureState(OSystem::Feature f, bool enable); + + virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); +}; + +#endif + |