From 78ee0bfdaf00a6d03717bc06112034bcc663c1f0 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Thu, 17 Feb 2005 23:01:00 +0000 Subject: [PATCH] Mouse part of big patch #1013937 (OSystem layer with bigger resolution) svn-id: r16800 --- backends/sdl/graphics.cpp | 449 ++++++++++++++++++++++---------------- backends/sdl/sdl-common.h | 23 +- backends/sdl/sdl.cpp | 14 +- common/scaler.cpp | 40 ++++ common/scaler.h | 1 + common/system.h | 37 +++- scumm/cursor.cpp | 3 +- scumm/resource_v7he.cpp | 14 +- 8 files changed, 364 insertions(+), 217 deletions(-) diff --git a/backends/sdl/graphics.cpp b/backends/sdl/graphics.cpp index daa9906594c..1687befdcd0 100644 --- a/backends/sdl/graphics.cpp +++ b/backends/sdl/graphics.cpp @@ -42,6 +42,16 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { {0, 0, 0} }; +// Table of relative scalers magnitudes +// [definedScale-1][_scaleFactor-1] +static ScalerProc *scalersMagn[3][3] = { + { Normal1x, AdvMame2x, AdvMame3x }, + { Normal1x, Normal1x, Normal1o5x }, + { Normal1x, Normal1x, Normal1x } +}; + +static int cursorStretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY); + const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const { return s_supportedGraphicsModes; } @@ -367,6 +377,9 @@ void OSystem_SDL::hotswapGFXMode() { SDL_FreeSurface(old_screen); SDL_FreeSurface(old_tmpscreen); + // Update cursor to new scale + blitCursor(); + // Blit everything to the screen internUpdateScreen(); @@ -399,9 +412,6 @@ void OSystem_SDL::internUpdateScreen() { _forceFull = true; } - // Make sure the mouse is drawn, if it should be drawn. - drawMouse(); - // Check whether the palette was changed in the meantime and update the // screen surface accordingly. if (_paletteDirtyEnd != 0) { @@ -434,6 +444,8 @@ void OSystem_SDL::internUpdateScreen() { } #endif + undrawMouse(); + // Force a full redraw if requested if (_forceFull) { _numDirtyRects = 1; @@ -522,14 +534,20 @@ void OSystem_SDL::internUpdateScreen() { _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); + } else { + drawMouse(); + if (_numDirtyRects) + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); } _numDirtyRects = 0; @@ -562,8 +580,6 @@ void OSystem_SDL::setFullscreenMode(bool enable) { assert(_hwscreen != 0); _fullscreen ^= true; - undrawMouse(); - #if defined(MACOSX) && !SDL_VERSION_ATLEAST(1, 2, 6) // On OS X, SDL_WM_ToggleFullScreen is currently not implemented. Worse, // before SDL 1.2.6 it always returned -1 (which would indicate a @@ -672,9 +688,6 @@ void OSystem_SDL::copyRectToScreen(const byte *src, int pitch, int x, int y, int addDirtyRect(x, y, w, h); } - /* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */ - undrawMouse(); - // Try to lock the screen surface if (SDL_LockSurface(_screen) == -1) error("SDL_LockSurface failed: %s", SDL_GetError()); @@ -696,10 +709,19 @@ void OSystem_SDL::copyRectToScreen(const byte *src, int pitch, int x, int y, int } -void OSystem_SDL::addDirtyRect(int x, int y, int w, int h) { +void OSystem_SDL::addDirtyRect(int x, int y, int w, int h, bool mouseRect) { if (_forceFull) return; + if (mouseRect) { + SDL_Rect *r = &_dirtyRectList[_numDirtyRects++]; + r->x = x; + r->y = y; + r->w = w; + r->h = h; + return; + } + if (_numDirtyRects == NUM_DIRTY_RECT) _forceFull = true; else { @@ -846,6 +868,27 @@ void OSystem_SDL::setPalette(const byte *colors, uint start, uint num) { if (start + num > _paletteDirtyEnd) _paletteDirtyEnd = start + num; + + // Some games blink cursors with palette + if (!_overlayVisible && !_cursorHasOwnPalette) + blitCursor(); +} + +void OSystem_SDL::setCursorPalette(const byte *colors, uint start, uint num) { + 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; + } + + _cursorHasOwnPalette = true; + + if (!_overlayVisible) + blitCursor(); } void OSystem_SDL::setShakePos(int shake_pos) { @@ -862,9 +905,6 @@ void OSystem_SDL::setShakePos(int shake_pos) { void OSystem_SDL::showOverlay() { assert (_transactionMode == kTransactionNone); - // hide the mouse - undrawMouse(); - _overlayVisible = true; clearOverlay(); } @@ -872,9 +912,6 @@ void OSystem_SDL::showOverlay() { void OSystem_SDL::hideOverlay() { assert (_transactionMode == kTransactionNone); - // hide the mouse - undrawMouse(); - _overlayVisible = false; _forceFull = true; } @@ -887,9 +924,6 @@ void OSystem_SDL::clearOverlay() { Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends - // hide the mouse - undrawMouse(); - // Clear the overlay by making the game screen "look through" everywhere. SDL_Rect src, dst; src.x = src.y = 0; @@ -911,9 +945,6 @@ void OSystem_SDL::grabOverlay(OverlayColor *buf, int pitch) { if (_tmpscreen == NULL) return; - // hide the mouse - undrawMouse(); - if (SDL_LockSurface(_tmpscreen) == -1) error("SDL_LockSurface failed: %s", SDL_GetError()); @@ -964,9 +995,6 @@ void OSystem_SDL::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, i _cksumValid = false; addDirtyRect(x, y, w, h); - /* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */ - undrawMouse(); - if (SDL_LockSurface(_tmpscreen) == -1) error("SDL_LockSurface failed: %s", SDL_GetError()); @@ -1000,17 +1028,13 @@ bool OSystem_SDL::showMouse(bool visible) { bool last = _mouseVisible; _mouseVisible = visible; - if (visible) - drawMouse(); - else - undrawMouse(); + updateScreen(); return last; } void OSystem_SDL::setMousePos(int x, int y) { if (x != _mouseCurState.x || y != _mouseCurState.y) { - undrawMouse(); _mouseCurState.x = x; _mouseCurState.y = y; updateScreen(); @@ -1032,24 +1056,154 @@ void OSystem_SDL::warpMouse(int x, int y) { } } -void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor) { - - undrawMouse(); - - assert(w <= MAX_MOUSE_W); - assert(h <= MAX_MOUSE_H); - _mouseCurState.w = w; - _mouseCurState.h = h; - +void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) { _mouseHotspotX = hotspot_x; _mouseHotspotY = 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); _mouseData = (byte *)malloc(w * h); memcpy(_mouseData, buf, w * h); + blitCursor(); +} + +void OSystem_SDL::blitCursor() { + byte *dstPtr; + const byte *srcPtr = _mouseData; + byte color; + int w, h; + + if (!_mouseOrigSurface) + return; + + w = _mouseCurState.w; + h = _mouseCurState.h; + + SDL_LockSurface(_mouseOrigSurface); + + // Make whole surface transparent + for (int i = 0; i < h + 2; i++) { + dstPtr = (byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * i; + for (int 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; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + color = *srcPtr; + if (color != _mouseKeyColor) { // transparent, don't draw + if (_cursorHasOwnPalette && !_overlayVisible) + *(uint16 *)dstPtr = SDL_MapRGB(_mouseOrigSurface->format, + _cursorPalette[color].r, _cursorPalette[color].g, + _cursorPalette[color].b); + else + *(uint16 *)dstPtr = SDL_MapRGB(_mouseOrigSurface->format, + _currentPalette[color].r, _currentPalette[color].g, + _currentPalette[color].b); + } + dstPtr += 2; + srcPtr++; + } + dstPtr += _mouseOrigSurface->pitch - w * 2; + } + + int hW, hH, hH1; + + if (_cursorTargetScale >= _scaleFactor) { + hW = w; + hH = hH1 = h; + } else { + hW = w * _scaleFactor / _cursorTargetScale; + hH = hH1 = h * _scaleFactor / _cursorTargetScale; + } + + if (_adjustAspectRatio) { + hH = real2Aspect(hH - 1) + 1; + } + + if (_mouseCurState.hW != hW || _mouseCurState.hH != hH) { + _mouseCurState.hW = hW; + _mouseCurState.hH = hH; + + if (_mouseSurface) + SDL_FreeSurface(_mouseSurface); + + _mouseSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _mouseCurState.hW, + _mouseCurState.hH, + 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); + (scalersMagn[_cursorTargetScale-1][_scaleFactor-1])((byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch + 2, + _mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch, + _mouseCurState.w, _mouseCurState.h); + + if (_adjustAspectRatio) + cursorStretch200To240((uint8 *)_mouseSurface->pixels, _mouseSurface->pitch, hW, hH1, 0, 0, 0); + + SDL_UnlockSurface(_mouseSurface); + SDL_UnlockSurface(_mouseOrigSurface); +} + +// 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; } void OSystem_SDL::toggleMouseGrab() { @@ -1059,159 +1213,78 @@ void OSystem_SDL::toggleMouseGrab() { SDL_WM_GrabInput(SDL_GRAB_OFF); } -void OSystem_SDL::drawMouse() { - if (_mouseDrawn || !_mouseVisible || !_mouseData) - return; - - int x = _mouseCurState.x - _mouseHotspotX; - int y = _mouseCurState.y - _mouseHotspotY; - int w = _mouseCurState.w; - int h = _mouseCurState.h; - byte color; - const byte *src = _mouseData; // Image representing the mouse - - // clip the mouse rect, and addjust the src pointer accordingly - if (x < 0) { - w += x; - src -= x; - x = 0; - } - if (y < 0) { - h += y; - src -= y * _mouseCurState.w; - y = 0; - } - - if (w > _screenWidth - x) - w = _screenWidth - x; - if (h > _screenHeight - y) - h = _screenHeight - y; - - // Quick check to see if anything has to be drawn at all - if (w <= 0 || h <= 0) - return; - - // Draw the mouse cursor; backup the covered area in "bak" - if (SDL_LockSurface(_overlayVisible ? _tmpscreen : _screen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - // Mark as dirty - addDirtyRect(x, y, w, h); - - if (!_overlayVisible) { - byte *bak = _mouseBackup; // Surface used to backup the area obscured by the mouse - byte *dst; // Surface we are drawing into - - dst = (byte *)_screen->pixels + y * _screenWidth + x; - while (h > 0) { - int width = w; - while (width > 0) { - *bak++ = *dst; - color = *src++; - if (color != _mouseKeyColor) // transparent, don't draw - *dst = color; - dst++; - width--; - } - src += _mouseCurState.w - w; - bak += MAX_MOUSE_W - w; - dst += _screenWidth - w; - h--; - } - - } else { - uint16 *bak = (uint16 *)_mouseBackup; // Surface used to backup the area obscured by the mouse - byte *dst; // Surface we are drawing into - - dst = (byte *)_tmpscreen->pixels + (y + 1) * _tmpscreen->pitch + (x + 1) * 2; - while (h > 0) { - int width = w; - while (width > 0) { - *bak++ = *(uint16 *)dst; - color = *src++; - if (color != 0xFF) // 0xFF = transparent, don't draw - *(uint16 *)dst = RGBToColor(_currentPalette[color].r, _currentPalette[color].g, _currentPalette[color].b); - dst += 2; - width--; - } - src += _mouseCurState.w - w; - bak += MAX_MOUSE_W - w; - dst += _tmpscreen->pitch - w * 2; - h--; - } - } - - SDL_UnlockSurface(_overlayVisible ? _tmpscreen : _screen); - - // Finally, set the flag to indicate the mouse has been drawn - _mouseDrawn = true; -} - void OSystem_SDL::undrawMouse() { - assert (_transactionMode == kTransactionNone || _transactionMode == kTransactionCommit); - - if (!_mouseDrawn) - return; - _mouseDrawn = false; - - int old_mouse_x = _mouseCurState.x - _mouseHotspotX; - int old_mouse_y = _mouseCurState.y - _mouseHotspotY; - int old_mouse_w = _mouseCurState.w; - int old_mouse_h = _mouseCurState.h; - - // clip the mouse rect, and addjust the src pointer accordingly - if (old_mouse_x < 0) { - old_mouse_w += old_mouse_x; - old_mouse_x = 0; + if (_mouseBackup.w) { + if (_adjustAspectRatio) + addDirtyRect(_mouseBackup.x, aspect2Real(_mouseBackup.y), _mouseBackup.w, + _mouseBackup.h); + else + addDirtyRect(_mouseBackup.x, _mouseBackup.y, _mouseBackup.w, + _mouseBackup.h); } - if (old_mouse_y < 0) { - old_mouse_h += old_mouse_y; - old_mouse_y = 0; - } - - if (old_mouse_w > _screenWidth - old_mouse_x) - old_mouse_w = _screenWidth - old_mouse_x; - if (old_mouse_h > _screenHeight - old_mouse_y) - old_mouse_h = _screenHeight - old_mouse_y; - - // Quick check to see if anything has to be drawn at all - if (old_mouse_w <= 0 || old_mouse_h <= 0) - return; - - if (SDL_LockSurface(_overlayVisible ? _tmpscreen : _screen) == -1) - error("SDL_LockSurface failed: %s", SDL_GetError()); - - int x, y; - if (!_overlayVisible) { - byte *dst, *bak = _mouseBackup; - - // No need to do clipping here, since drawMouse() did that already - dst = (byte *)_screen->pixels + old_mouse_y * _screenWidth + old_mouse_x; - for (y = 0; y < old_mouse_h; ++y, bak += MAX_MOUSE_W, dst += _screenWidth) { - for (x = 0; x < old_mouse_w; ++x) { - dst[x] = bak[x]; - } - } - - } else { - - byte *dst; - uint16 *bak = (uint16 *)_mouseBackup; - - // No need to do clipping here, since drawMouse() did that already - dst = (byte *)_tmpscreen->pixels + (old_mouse_y + 1) * _tmpscreen->pitch + (old_mouse_x + 1) * 2; - for (y = 0; y < old_mouse_h; ++y, bak += MAX_MOUSE_W, dst += _tmpscreen->pitch) { - for (x = 0; x < old_mouse_w; ++x) { - *((uint16 *)dst + x) = bak[x]; - } - } - } - - addDirtyRect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h); - - SDL_UnlockSurface(_overlayVisible ? _tmpscreen : _screen); } +void OSystem_SDL::drawMouse() { + if (!_mouseVisible) { + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; + return; + } + + SDL_Rect src, dst; + bool scale; + + scale = (_scaleFactor > _cursorTargetScale); + + dst.x = _mouseCurState.x - _mouseHotspotX / _cursorTargetScale; + dst.y = _mouseCurState.y - _mouseHotspotY / _cursorTargetScale; + + dst.w = _mouseCurState.hW; + dst.h = _mouseCurState.hH; + src.x = src.y = 0; + + // clip the mouse rect, and adjust the src pointer accordingly + int dx, dy; + + dx = dst.x; dy = dst.y; + dx = scale ? dst.x * _scaleFactor / _cursorTargetScale : dst.x; + dy = scale ? dst.y * _scaleFactor / _cursorTargetScale : dst.y; + if (_adjustAspectRatio) + dy = real2Aspect(dy); + + if (dst.x < 0) { + dst.w += dx; + src.x -= dx; + dst.x = 0; + } + if (dst.y < 0) { + dst.h += dy; + src.y -= dy; + dst.y = 0; + } + + // Quick check to see if anything has to be drawn at all + if (dst.w <= 0 || dst.h <= 0) + return; + + src.w = dst.w; + src.h = dst.h; + + if (_adjustAspectRatio) + dst.y = real2Aspect(dst.y); + + _mouseBackup.x = dst.x; + _mouseBackup.y = dst.y; + _mouseBackup.w = dst.w; + _mouseBackup.h = dst.h; + + dst.x *= _scaleFactor; + dst.y *= _scaleFactor; + + if (SDL_BlitSurface(_mouseSurface, &src, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + addDirtyRect(dst.x, dst.y, dst.w, dst.h, true); +} #pragma mark - #pragma mark --- Mouse --- diff --git a/backends/sdl/sdl-common.h b/backends/sdl/sdl-common.h index e060f3a6b47..7296131b1e5 100644 --- a/backends/sdl/sdl-common.h +++ b/backends/sdl/sdl-common.h @@ -82,7 +82,10 @@ public: virtual void warpMouse(int x, int y); // overloaded by CE backend // Set the bitmap that's used when drawing the cursor. - void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor); + void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale); + + // Set colors of cursor palette + void setCursorPalette(const byte *colors, uint start, uint num); // Shaking is used in SCUMM. Set current shake position. void setShakePos(int shake_pos); @@ -258,7 +261,8 @@ protected: }; struct MousePos { - int16 x, y, w, h; + int16 x, y, w, h, hW, hH; + MousePos() : x(0), y(0), w(0), h(0), hW(0), hH(0) {} }; // mouse @@ -266,11 +270,18 @@ protected: bool _mouseVisible; bool _mouseDrawn; byte *_mouseData; - byte *_mouseBackup; + SDL_Rect _mouseBackup; MousePos _mouseCurState; int16 _mouseHotspotX; int16 _mouseHotspotY; byte _mouseKeyColor; + int _cursorTargetScale; + bool _cursorHasOwnPalette; + SDL_Surface *_mouseOrigSurface; + SDL_Surface *_mouseSurface; + enum { + kMouseColorKey = 1 + }; // joystick SDL_Joystick *_joystick; @@ -283,6 +294,9 @@ protected: 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. @@ -293,10 +307,11 @@ protected: void addDirtyRgnAuto(const byte *buf); void makeChecksums(const byte *buf); - virtual void addDirtyRect(int x, int y, int w, int h); // overloaded by CE backend + virtual void addDirtyRect(int x, int y, int w, int h, bool mouseRect = false); // overloaded by CE backend virtual void drawMouse(); // overloaded by CE backend virtual void undrawMouse(); // overloaded by CE backend + void blitCursor(); /** Set the position of the virtual mouse cursor. */ void setMousePos(int x, int y); diff --git a/backends/sdl/sdl.cpp b/backends/sdl/sdl.cpp index ece356e13c4..0ed0568dd24 100644 --- a/backends/sdl/sdl.cpp +++ b/backends/sdl/sdl.cpp @@ -100,8 +100,9 @@ OSystem_SDL::OSystem_SDL() _tmpscreen(0), _overlayVisible(false), _samplesPerSec(0), _cdrom(0), _scalerProc(0), _modeChanged(false), _dirtyChecksums(0), - _mouseVisible(false), _mouseDrawn(false), _mouseData(0), - _mouseHotspotX(0), _mouseHotspotY(0), + _mouseVisible(false), _mouseDrawn(false), _mouseData(0), _mouseSurface(0), + _mouseOrigSurface(0), _mouseHotspotX(0), _mouseHotspotY(0), _cursorTargetScale(1), + _cursorHasOwnPalette(false), _joystick(0), _currentShakePos(0), _newShakePos(0), _paletteDirtyStart(0), _paletteDirtyEnd(0), @@ -109,9 +110,9 @@ OSystem_SDL::OSystem_SDL() // allocate palette storage _currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); + _cursorPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256); - // allocate the dirty rect storage - _mouseBackup = (byte *)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2); + _mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0; // reset mouse state memset(&_km, 0, sizeof(_km)); @@ -123,7 +124,7 @@ OSystem_SDL::OSystem_SDL() OSystem_SDL::~OSystem_SDL() { free(_dirtyChecksums); free(_currentPalette); - free(_mouseBackup); + free(_cursorPalette); free(_mouseData); } @@ -147,7 +148,8 @@ bool OSystem_SDL::hasFeature(Feature f) { return (f == kFeatureFullscreenMode) || (f == kFeatureAspectRatioCorrection) || - (f == kFeatureAutoComputeDirtyRects); + (f == kFeatureAutoComputeDirtyRects) || + (f == kFeatureCursorHasPalette); } void OSystem_SDL::setFeatureState(Feature f, bool enable) { diff --git a/common/scaler.cpp b/common/scaler.cpp index 701afbda90e..30de3a7ae62 100644 --- a/common/scaler.cpp +++ b/common/scaler.cpp @@ -169,6 +169,46 @@ void Normal3x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit } } +#define INTERPOLATE INTERPOLATE +#define Q_INTERPOLATE Q_INTERPOLATE + +/** + * Trivial nearest-neighbour 1.5x scaler. + */ +template +void Normal1o5xTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, + int width, int height) { + uint8 *r; + const uint32 dstPitch2 = dstPitch * 2; + const uint32 dstPitch3 = dstPitch * 3; + const uint32 srcPitch2 = srcPitch * 2; + + assert(((int)dstPtr & 1) == 0); + while (height) { + r = dstPtr; + for (int i = 0; i < width; i += 2, r += 6) { + uint16 color0 = *(((const uint16 *)srcPtr) + i); + uint16 color1 = *(((const uint16 *)srcPtr) + i + 1); + uint16 color2 = *(((const uint16 *)(srcPtr + srcPitch)) + i); + uint16 color3 = *(((const uint16 *)(srcPtr + srcPitch)) + i + 1); + + *(uint16 *)(r + 0) = color0; + *(uint16 *)(r + 2) = INTERPOLATE(color0, color1); + *(uint16 *)(r + 4) = color1; + *(uint16 *)(r + 0 + dstPitch) = INTERPOLATE(color0, color2); + *(uint16 *)(r + 2 + dstPitch) = Q_INTERPOLATE(color0, color1, color2, color3); + *(uint16 *)(r + 4 + dstPitch) = INTERPOLATE(color1, color3); + *(uint16 *)(r + 0 + dstPitch2) = color2; + *(uint16 *)(r + 2 + dstPitch2) = INTERPOLATE(color2, color3); + *(uint16 *)(r + 4 + dstPitch2) = color3; + } + srcPtr += srcPitch2; + dstPtr += dstPitch3; + height -= 2; + } +} +MAKE_WRAPPER(Normal1o5x) + /** * The Scale2x filter, also known as AdvMame2x. * See also http://scale2x.sourceforge.net diff --git a/common/scaler.h b/common/scaler.h index e77b8aedc54..c582e71f791 100644 --- a/common/scaler.h +++ b/common/scaler.h @@ -41,6 +41,7 @@ DECLARE_SCALER(AdvMame3x); DECLARE_SCALER(Normal1x); DECLARE_SCALER(Normal2x); DECLARE_SCALER(Normal3x); +DECLARE_SCALER(Normal1o5x); DECLARE_SCALER(TV2x); DECLARE_SCALER(DotMatrix); DECLARE_SCALER(HQ2x); diff --git a/common/system.h b/common/system.h index 7d3a0ea8f72..fad4e6c8412 100644 --- a/common/system.h +++ b/common/system.h @@ -96,7 +96,15 @@ public: * Implementing this is purely optional, and no harm should arise * when not doing so (except for decreased speed in said frontends). */ - kFeatureAutoComputeDirtyRects + kFeatureAutoComputeDirtyRects, + + /** + * This flags determines either cursor can have its own palette or not + * It is currently used only by some Macintosh versions of Humongous + * Entertainment games. If backend doesn't implement this feature then + * engine switches to b/w version of cursors. + */ + kFeatureCursorHasPalette }; /** @@ -273,6 +281,18 @@ public: */ virtual void setPalette(const byte *colors, uint start, uint num) = 0; + /** + * Replace the specified range of cursor the palette with new colors. + * The palette entries from 'start' till (start+num-1) will be replaced - so + * a full palette update is accomplished via start=0, num=256. + * + * Backends which implement it should have kFeatureCursorHasPalette flag set + * + * @see setPalette + * @see kFeatureCursorHasPalette + */ + virtual void setCursorPalette(const byte *colors, uint start, uint num) {}; + /** * Blit a bitmap to the virtual screen. * The real screen will not immediately be updated to reflect the changes. @@ -365,14 +385,15 @@ public: /** * Set the bitmap used for drawing the cursor. * - * @param buf the pixmap data to be used (8bit/pixel) - * @param w width of the mouse cursor - * @param h height of the mouse cursor - * @param hotspotX horizontal offset from the left side to the hotspot - * @param hotspotY vertical offset from the top side to the hotspot - * @param keycolor transparency color index + * @param buf the pixmap data to be used (8bit/pixel) + * @param w width of the mouse cursor + * @param h height of the mouse cursor + * @param hotspotX horizontal offset from the left side to the hotspot + * @param hotspotY vertical offset from the top side to the hotspot + * @param keycolor transparency color index + * @param cursorTargetScale scale factor which cursor is designed for */ - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255) = 0; + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int cursorTargetScale = 1) = 0; //@} diff --git a/scumm/cursor.cpp b/scumm/cursor.cpp index bfb5ddc6c22..3d032098ef4 100644 --- a/scumm/cursor.cpp +++ b/scumm/cursor.cpp @@ -107,7 +107,8 @@ void ScummEngine_v6::setCursorTransparency(int a) { void ScummEngine::updateCursor() { _system->setMouseCursor(_grabbedCursor, _cursor.width, _cursor.height, - _cursor.hotspotX, _cursor.hotspotY); + _cursor.hotspotX, _cursor.hotspotY, 255, + (_heversion == 70 ? 2 : 1)); } void ScummEngine_v6::grabCursor(int x, int y, int w, int h) { diff --git a/scumm/resource_v7he.cpp b/scumm/resource_v7he.cpp index ab2b2c078c0..29f77705299 100644 --- a/scumm/resource_v7he.cpp +++ b/scumm/resource_v7he.cpp @@ -1532,10 +1532,9 @@ void MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *hotspot_x = dis.readUint16BE(); *w = *h = 16; - // FIXME - // Color cursors use their own palette. - // So we can't use it for now and use B/W version - return; + // Use b/w cursor on backends which don't support cursor palettes + if (!_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette)) + return; dis.readUint32BE(); // reserved dis.readUint32BE(); // cursorID @@ -1600,12 +1599,7 @@ void MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int palette[c * 4 + 3] = 0; } - // TODO: Here we should set separate cursor palette. - // It requires cursor to be rendered on a different surface at - // least in SDL backend. - // HACK: now set global palett just to see if colors are correct. - // this affects subtitles colors - _vm->_system->setPalette(palette, 0, ctSize); + _vm->_system->setCursorPalette(palette, 0, ctSize); numBytes = (iconBounds[2] - iconBounds[0]) *