GRAPHICS: Add support for pixels with masks and inverted pixels
This commit is contained in:
parent
8adcc29df9
commit
3db67a33ac
36 changed files with 449 additions and 91 deletions
|
@ -99,7 +99,7 @@ public:
|
|||
|
||||
virtual bool showMouse(bool visible) = 0;
|
||||
virtual void warpMouse(int x, int y) = 0;
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) = 0;
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = nullptr, const byte *mask = nullptr) = 0;
|
||||
virtual void setCursorPalette(const byte *colors, uint start, uint num) = 0;
|
||||
|
||||
virtual void displayMessageOnOSD(const Common::U32String &msg) {}
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
|
||||
bool showMouse(bool visible) override { return !visible; }
|
||||
void warpMouse(int x, int y) override {}
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override {}
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override {}
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override {}
|
||||
|
||||
private:
|
||||
|
|
|
@ -119,6 +119,14 @@ void Framebuffer::applyBlendState() {
|
|||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
|
||||
break;
|
||||
case kBlendModeAdditive:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE, GL_ONE));
|
||||
break;
|
||||
case kBlendModeMaskAlphaAndInvertByColor:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,18 @@ public:
|
|||
* Requires the image data being drawn to have its color values pre-multipled
|
||||
* with the alpha value.
|
||||
*/
|
||||
kBlendModePremultipliedTransparency
|
||||
kBlendModePremultipliedTransparency,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels add to the destination value.
|
||||
*/
|
||||
kBlendModeAdditive,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels mask out existing pixels based on the alpha value and
|
||||
* add inversions of the pixels based on the color.
|
||||
*/
|
||||
kBlendModeMaskAlphaAndInvertByColor,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,10 +73,10 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
|
|||
_pipeline(nullptr), _stretchMode(STRETCH_FIT),
|
||||
_defaultFormat(), _defaultFormatAlpha(),
|
||||
_gameScreen(nullptr), _overlay(nullptr),
|
||||
_cursor(nullptr),
|
||||
_cursor(nullptr), _cursorMask(nullptr),
|
||||
_cursorHotspotX(0), _cursorHotspotY(0),
|
||||
_cursorHotspotXScaled(0), _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0),
|
||||
_cursorKeyColor(0), _cursorDontScale(false), _cursorPaletteEnabled(false), _shakeOffsetScaled()
|
||||
_cursorKeyColor(0), _cursorUseKey(true), _cursorDontScale(false), _cursorPaletteEnabled(false), _shakeOffsetScaled()
|
||||
#if !USE_FORCED_GLES
|
||||
, _libretroPipeline(nullptr)
|
||||
#endif
|
||||
|
@ -96,6 +96,7 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() {
|
|||
delete _gameScreen;
|
||||
delete _overlay;
|
||||
delete _cursor;
|
||||
delete _cursorMask;
|
||||
#ifdef USE_OSD
|
||||
delete _osdMessageSurface;
|
||||
delete _osdIconSurface;
|
||||
|
@ -112,6 +113,8 @@ bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) const {
|
|||
case OSystem::kFeatureCursorPalette:
|
||||
case OSystem::kFeatureFilteringMode:
|
||||
case OSystem::kFeatureStretchMode:
|
||||
case OSystem::kFeatureCursorMask:
|
||||
case OSystem::kFeatureCursorMaskInvert:
|
||||
#ifdef USE_SCALERS
|
||||
case OSystem::kFeatureScalers:
|
||||
#endif
|
||||
|
@ -588,6 +591,41 @@ void OpenGLGraphicsManager::fillScreen(uint32 col) {
|
|||
_gameScreen->fill(col);
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::renderCursor() {
|
||||
/*
|
||||
Windows and Mac cursor XOR works by drawing the cursor to the screen with the formula (Destination AND Mask XOR Color)
|
||||
|
||||
OpenGL does not have an XOR blend mode though. Full inversions can be accomplished by using blend modes with
|
||||
ONE_MINUS_DST_COLOR but the problem is how to do that in a way that handles linear filtering properly.
|
||||
|
||||
To avoid color fringing, we need to produce an output of 3 separately-modulated inputs: The framebuffer modulated by
|
||||
(1 - inversion)*(1 - alpha), the inverted framebuffer modulated by inversion*(1 - alpha), and the cursor colors modulated by alpha.
|
||||
The last part is additive and not framebuffer dependent so it can just be a separate draw call. The first two are the problem
|
||||
because we can't use the unmodified framebuffer value twice if we do it in two separate draw calls, and if we do it in a single
|
||||
draw call, we can only supply one RGB input even though the inversion mask should be RGB.
|
||||
|
||||
If we only allow grayscale inversions though, then we can put inversion*(1 - alpha) in the RGB channel and
|
||||
(1 - inversion)*(1 - alpha) in the alpha channel and use and use ((1-dstColor)*src+(1-srcAlpha)*dest) blend formula to do
|
||||
the inversion and opacity mask at once. We use 1-srcAlpha instead of srcAlpha so zero-fill is transparent.
|
||||
*/
|
||||
if (_cursorMask) {
|
||||
_backBuffer.enableBlend(Framebuffer::kBlendModeMaskAlphaAndInvertByColor);
|
||||
|
||||
_pipeline->drawTexture(_cursorMask->getGLTexture(),
|
||||
_cursorX - _cursorHotspotXScaled + _shakeOffsetScaled.x,
|
||||
_cursorY - _cursorHotspotYScaled + _shakeOffsetScaled.y,
|
||||
_cursorWidthScaled, _cursorHeightScaled);
|
||||
|
||||
_backBuffer.enableBlend(Framebuffer::kBlendModeAdditive);
|
||||
} else
|
||||
_backBuffer.enableBlend(Framebuffer::kBlendModePremultipliedTransparency);
|
||||
|
||||
_pipeline->drawTexture(_cursor->getGLTexture(),
|
||||
_cursorX - _cursorHotspotXScaled + _shakeOffsetScaled.x,
|
||||
_cursorY - _cursorHotspotYScaled + _shakeOffsetScaled.y,
|
||||
_cursorWidthScaled, _cursorHeightScaled);
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::updateScreen() {
|
||||
if (!_gameScreen) {
|
||||
return;
|
||||
|
@ -616,7 +654,7 @@ void OpenGLGraphicsManager::updateScreen() {
|
|||
&& !(_libretroPipeline && _libretroPipeline->isAnimated())
|
||||
#endif
|
||||
&& !(_overlayVisible && _overlay->isDirty())
|
||||
&& !(_cursorVisible && _cursor && _cursor->isDirty())
|
||||
&& !(_cursorVisible && ((_cursor && _cursor->isDirty()) || (_cursorMask && _cursorMask->isDirty())))
|
||||
#ifdef USE_OSD
|
||||
&& !_osdMessageSurface && !_osdIconSurface
|
||||
#endif
|
||||
|
@ -629,6 +667,9 @@ void OpenGLGraphicsManager::updateScreen() {
|
|||
if (_cursorVisible && _cursor) {
|
||||
_cursor->updateGLTexture();
|
||||
}
|
||||
if (_cursorVisible && _cursorMask) {
|
||||
_cursorMask->updateGLTexture();
|
||||
}
|
||||
_overlay->updateGLTexture();
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
|
@ -665,12 +706,7 @@ void OpenGLGraphicsManager::updateScreen() {
|
|||
// This has the disadvantage of having overlay (subtitles) drawn above it
|
||||
// but the cursor will look nicer
|
||||
if (!_overlayInGUI && drawCursor) {
|
||||
_backBuffer.enableBlend(Framebuffer::kBlendModePremultipliedTransparency);
|
||||
|
||||
_pipeline->drawTexture(_cursor->getGLTexture(),
|
||||
_cursorX - _cursorHotspotXScaled + _shakeOffsetScaled.x,
|
||||
_cursorY - _cursorHotspotYScaled + _shakeOffsetScaled.y,
|
||||
_cursorWidthScaled, _cursorHeightScaled);
|
||||
renderCursor();
|
||||
drawCursor = false;
|
||||
|
||||
// Everything we need to clip has been clipped
|
||||
|
@ -691,14 +727,8 @@ void OpenGLGraphicsManager::updateScreen() {
|
|||
}
|
||||
|
||||
// Fourth step: Draw the cursor if we didn't before.
|
||||
if (drawCursor) {
|
||||
_backBuffer.enableBlend(Framebuffer::kBlendModePremultipliedTransparency);
|
||||
|
||||
_pipeline->drawTexture(_cursor->getGLTexture(),
|
||||
_cursorX - _cursorHotspotXScaled + _shakeOffsetScaled.x,
|
||||
_cursorY - _cursorHotspotYScaled + _shakeOffsetScaled.y,
|
||||
_cursorWidthScaled, _cursorHeightScaled);
|
||||
}
|
||||
if (drawCursor)
|
||||
renderCursor();
|
||||
|
||||
if (!_overlayVisible) {
|
||||
_backBuffer.enableScissorTest(false);
|
||||
|
@ -819,12 +849,12 @@ namespace {
|
|||
template<typename SrcColor, typename DstColor>
|
||||
void multiplyColorWithAlpha(const byte *src, byte *dst, const uint w, const uint h,
|
||||
const Graphics::PixelFormat &srcFmt, const Graphics::PixelFormat &dstFmt,
|
||||
const uint srcPitch, const uint dstPitch, const SrcColor keyColor) {
|
||||
const uint srcPitch, const uint dstPitch, const SrcColor keyColor, bool useKeyColor) {
|
||||
for (uint y = 0; y < h; ++y) {
|
||||
for (uint x = 0; x < w; ++x) {
|
||||
const uint32 color = *(const SrcColor *)src;
|
||||
|
||||
if (color == keyColor) {
|
||||
if (useKeyColor && color == keyColor) {
|
||||
*(DstColor *)dst = 0;
|
||||
} else {
|
||||
byte a, r, g, b;
|
||||
|
@ -849,9 +879,12 @@ void multiplyColorWithAlpha(const byte *src, byte *dst, const uint w, const uint
|
|||
}
|
||||
} // End of anonymous namespace
|
||||
|
||||
void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
|
||||
void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
_cursorUseKey = (mask == nullptr);
|
||||
if (_cursorUseKey)
|
||||
_cursorKeyColor = keycolor;
|
||||
|
||||
_cursorHotspotX = hotspotX;
|
||||
_cursorHotspotY = hotspotY;
|
||||
_cursorDontScale = dontScale;
|
||||
|
@ -859,10 +892,13 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
if (!w || !h) {
|
||||
delete _cursor;
|
||||
_cursor = nullptr;
|
||||
delete _cursorMask;
|
||||
_cursorMask = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat inputFormat;
|
||||
Graphics::PixelFormat maskFormat;
|
||||
#ifdef USE_RGB_COLOR
|
||||
if (format) {
|
||||
inputFormat = *format;
|
||||
|
@ -873,18 +909,20 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
inputFormat = Graphics::PixelFormat::createFormatCLUT8();
|
||||
#endif
|
||||
|
||||
// In case the color format has changed we will need to create the texture.
|
||||
if (!_cursor || _cursor->getFormat() != inputFormat) {
|
||||
delete _cursor;
|
||||
_cursor = nullptr;
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
bool wantScaler = (_currentState.scaleFactor > 1) && !dontScale
|
||||
&& _scalerPlugins[_currentState.scalerIndex]->get<ScalerPluginObject>().canDrawCursor();
|
||||
bool wantScaler = (_currentState.scaleFactor > 1) && !dontScale && _scalerPlugins[_currentState.scalerIndex]->get<ScalerPluginObject>().canDrawCursor();
|
||||
#else
|
||||
bool wantScaler = false;
|
||||
#endif
|
||||
|
||||
bool wantMask = (mask != nullptr);
|
||||
bool haveMask = (_cursorMask != nullptr);
|
||||
|
||||
// In case the color format has changed we will need to create the texture.
|
||||
if (!_cursor || _cursor->getFormat() != inputFormat || haveMask != wantMask) {
|
||||
delete _cursor;
|
||||
_cursor = nullptr;
|
||||
|
||||
GLenum glIntFormat, glFormat, glType;
|
||||
|
||||
Graphics::PixelFormat textureFormat;
|
||||
|
@ -899,9 +937,11 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
} else {
|
||||
textureFormat = _defaultFormatAlpha;
|
||||
}
|
||||
_cursor = createSurface(textureFormat, true, wantScaler);
|
||||
_cursor = createSurface(textureFormat, true, wantScaler, wantMask);
|
||||
assert(_cursor);
|
||||
|
||||
updateLinearFiltering();
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
if (wantScaler) {
|
||||
_cursor->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
|
||||
|
@ -909,18 +949,40 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
#endif
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
if (!_cursorMask) {
|
||||
maskFormat = _defaultFormatAlpha;
|
||||
_cursorMask = createSurface(maskFormat, true, wantScaler);
|
||||
assert(_cursorMask);
|
||||
|
||||
updateLinearFiltering();
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
if (wantScaler) {
|
||||
_cursorMask->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
delete _cursorMask;
|
||||
_cursorMask = nullptr;
|
||||
}
|
||||
|
||||
Common::Point topLeftCoord(0, 0);
|
||||
Common::Point cursorSurfaceSize(w, h);
|
||||
|
||||
// If the cursor is scalable, add a 1-texel transparent border.
|
||||
// This ensures that linear filtering falloff from the edge pixels has room to completely fade out instead of
|
||||
// being cut off at half-way. Could use border clamp too, but GLES2 doesn't support that.
|
||||
if (!_cursorDontScale) {
|
||||
topLeftCoord = Common::Point(1, 1);
|
||||
_cursor->allocate(w + 2, h + 2);
|
||||
} else {
|
||||
_cursor->allocate(w, h);
|
||||
cursorSurfaceSize += Common::Point(2, 2);
|
||||
}
|
||||
|
||||
_cursor->allocate(cursorSurfaceSize.x, cursorSurfaceSize.y);
|
||||
if (_cursorMask)
|
||||
_cursorMask->allocate(cursorSurfaceSize.x, cursorSurfaceSize.y);
|
||||
|
||||
_cursorHotspotX += topLeftCoord.x;
|
||||
_cursorHotspotY += topLeftCoord.y;
|
||||
|
||||
|
@ -930,6 +992,24 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
if (!_cursorDontScale)
|
||||
_cursor->fill(keycolor);
|
||||
_cursor->copyRectToTexture(topLeftCoord.x, topLeftCoord.y, w, h, buf, w * inputFormat.bytesPerPixel);
|
||||
|
||||
if (mask) {
|
||||
// Construct a mask of opaque pixels
|
||||
Common::Array<byte> maskBytes;
|
||||
maskBytes.resize(cursorSurfaceSize.x * cursorSurfaceSize.y, 0);
|
||||
|
||||
for (uint y = 0; y < h; y++) {
|
||||
for (uint x = 0; x < w; x++) {
|
||||
// The cursor pixels must be masked out for anything except opaque
|
||||
if (mask[y * w + x] == kCursorMaskOpaque)
|
||||
maskBytes[(y + topLeftCoord.y) * cursorSurfaceSize.x + topLeftCoord.x + x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
_cursor->setMask(&maskBytes[0]);
|
||||
} else {
|
||||
_cursor->setMask(nullptr);
|
||||
}
|
||||
} else {
|
||||
// Otherwise it is a bit more ugly because we have to handle a key
|
||||
// color properly.
|
||||
|
@ -952,18 +1032,32 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
if (dst->format.bytesPerPixel == 2) {
|
||||
if (inputFormat.bytesPerPixel == 2) {
|
||||
multiplyColorWithAlpha<uint16, uint16>((const byte *)buf, topLeftPixelPtr, w, h,
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor, _cursorUseKey);
|
||||
} else if (inputFormat.bytesPerPixel == 4) {
|
||||
multiplyColorWithAlpha<uint32, uint16>((const byte *)buf, topLeftPixelPtr, w, h,
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor, _cursorUseKey);
|
||||
}
|
||||
} else {
|
||||
} else if (dst->format.bytesPerPixel == 4) {
|
||||
if (inputFormat.bytesPerPixel == 2) {
|
||||
multiplyColorWithAlpha<uint16, uint32>((const byte *)buf, topLeftPixelPtr, w, h,
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor, _cursorUseKey);
|
||||
} else if (inputFormat.bytesPerPixel == 4) {
|
||||
multiplyColorWithAlpha<uint32, uint32>((const byte *)buf, topLeftPixelPtr, w, h,
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor);
|
||||
inputFormat, dst->format, srcPitch, dst->pitch, keycolor, _cursorUseKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace all non-opaque pixels with black pixels
|
||||
if (mask) {
|
||||
Graphics::Surface *cursorSurface = _cursor->getSurface();
|
||||
|
||||
for (uint x = 0; x < w; x++) {
|
||||
for (uint y = 0; y < h; y++) {
|
||||
uint8 maskByte = mask[y * w + x];
|
||||
|
||||
if (maskByte != kCursorMaskOpaque)
|
||||
cursorSurface->setPixel(x + topLeftCoord.x, y + topLeftCoord.y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -971,6 +1065,54 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
|
|||
_cursor->flagDirty();
|
||||
}
|
||||
|
||||
if (_cursorMask && mask) {
|
||||
// Generate the multiply+invert texture.
|
||||
// We're generating this for a blend mode where source factor is ONE_MINUS_DST_COLOR and dest factor is ONE_MINUS_SRC_ALPHA
|
||||
// In other words, positive RGB channel values will add inverted destination pixels, positive alpha values will modulate
|
||||
// RGB+Alpha = Inverted Alpha Only = Black 0 = No change
|
||||
|
||||
Graphics::Surface *cursorSurface = _cursor->getSurface();
|
||||
Graphics::Surface *maskSurface = _cursorMask->getSurface();
|
||||
maskFormat = _cursorMask->getFormat();
|
||||
|
||||
const Graphics::PixelFormat cursorFormat = cursorSurface->format;
|
||||
|
||||
bool haveXorPixels = false;
|
||||
|
||||
_cursorMask->fill(0);
|
||||
for (uint x = 0; x < w; x++) {
|
||||
for (uint y = 0; y < h; y++) {
|
||||
// See the description of renderCursor for an explanation of why this works the way it does.
|
||||
|
||||
uint8 maskOpacity = 0xff;
|
||||
|
||||
if (inputFormat.bytesPerPixel != 1) {
|
||||
uint32 cursorPixel = cursorSurface->getPixel(x + topLeftCoord.x, y + topLeftCoord.y);
|
||||
|
||||
uint8 r, g, b;
|
||||
cursorFormat.colorToARGB(cursorPixel, maskOpacity, r, g, b);
|
||||
}
|
||||
|
||||
uint8 maskInversionAdd = 0;
|
||||
|
||||
uint8 maskByte = mask[y * w + x];
|
||||
if (maskByte == kCursorMaskTransparent)
|
||||
maskOpacity = 0;
|
||||
|
||||
if (maskByte == kCursorMaskInvert) {
|
||||
maskOpacity = 0xff;
|
||||
maskInversionAdd = 0xff;
|
||||
haveXorPixels = true;
|
||||
}
|
||||
|
||||
uint32 encodedMaskPixel = maskFormat.ARGBToColor(maskOpacity, maskInversionAdd, maskInversionAdd, maskInversionAdd);
|
||||
maskSurface->setPixel(x + topLeftCoord.x, y + topLeftCoord.y, encodedMaskPixel);
|
||||
}
|
||||
}
|
||||
|
||||
_cursorMask->flagDirty();
|
||||
}
|
||||
|
||||
// In case we actually use a palette set that up properly.
|
||||
if (inputFormat.bytesPerPixel == 1) {
|
||||
updateCursorPalette();
|
||||
|
@ -1307,7 +1449,7 @@ void OpenGLGraphicsManager::notifyContextDestroy() {
|
|||
OpenGLContext.reset();
|
||||
}
|
||||
|
||||
Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha, bool wantScaler) {
|
||||
Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha, bool wantScaler, bool wantMask) {
|
||||
GLenum glIntFormat, glFormat, glType;
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
|
@ -1327,7 +1469,7 @@ Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &forma
|
|||
|
||||
if (format.bytesPerPixel == 1) {
|
||||
#if !USE_FORCED_GLES
|
||||
if (TextureCLUT8GPU::isSupportedByContext()) {
|
||||
if (TextureCLUT8GPU::isSupportedByContext() && !wantMask) {
|
||||
return new TextureCLUT8GPU();
|
||||
}
|
||||
#endif
|
||||
|
@ -1511,6 +1653,7 @@ void OpenGLGraphicsManager::updateCursorPalette() {
|
|||
_cursor->setPalette(0, 256, _gamePalette);
|
||||
}
|
||||
|
||||
if (_cursorUseKey)
|
||||
_cursor->setColorKey(_cursorKeyColor);
|
||||
}
|
||||
|
||||
|
@ -1557,6 +1700,10 @@ void OpenGLGraphicsManager::updateLinearFiltering() {
|
|||
_cursor->enableLinearFiltering(_currentState.filtering);
|
||||
}
|
||||
|
||||
if (_cursorMask) {
|
||||
_cursorMask->enableLinearFiltering(_currentState.filtering);
|
||||
}
|
||||
|
||||
// The overlay UI should also obey the filtering choice (managed via the Filter Graphics checkbox in Graphics Tab).
|
||||
// Thus, when overlay filtering is disabled, scaling in OPENGL is done with GL_NEAREST (nearest neighbor scaling).
|
||||
// It may look crude, but it should be crispier and it's left to user choice to enable filtering.
|
||||
|
|
|
@ -119,7 +119,7 @@ public:
|
|||
void clearOverlay() override;
|
||||
void grabOverlay(Graphics::Surface &surface) const override;
|
||||
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) override;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) override;
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override;
|
||||
|
||||
void displayMessageOnOSD(const Common::U32String &msg) override;
|
||||
|
@ -130,6 +130,8 @@ public:
|
|||
void grabPalette(byte *colors, uint start, uint num) const override;
|
||||
|
||||
protected:
|
||||
void renderCursor();
|
||||
|
||||
/**
|
||||
* Whether an GLES or GLES2 context is active.
|
||||
*/
|
||||
|
@ -168,7 +170,7 @@ protected:
|
|||
* @param wantScaler Whether or not a software scaler should be used.
|
||||
* @return A pointer to the surface or nullptr on failure.
|
||||
*/
|
||||
Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false, bool wantScaler = false);
|
||||
Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false, bool wantScaler = false, bool wantMask = false);
|
||||
|
||||
//
|
||||
// Transaction support
|
||||
|
@ -374,6 +376,11 @@ protected:
|
|||
*/
|
||||
Surface *_cursor;
|
||||
|
||||
/**
|
||||
* The rendering surface for the opacity and inversion mask (if any)
|
||||
*/
|
||||
Surface *_cursorMask;
|
||||
|
||||
/**
|
||||
* The X offset for the cursor hotspot in unscaled game coordinates.
|
||||
*/
|
||||
|
@ -417,6 +424,11 @@ protected:
|
|||
*/
|
||||
uint32 _cursorKeyColor;
|
||||
|
||||
/**
|
||||
* If true, use key color.
|
||||
*/
|
||||
bool _cursorUseKey;
|
||||
|
||||
/**
|
||||
* Whether no cursor scaling should be applied.
|
||||
*/
|
||||
|
|
|
@ -377,7 +377,8 @@ FakeTexture::FakeTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, con
|
|||
: Texture(glIntFormat, glFormat, glType, format),
|
||||
_fakeFormat(fakeFormat),
|
||||
_rgbData(),
|
||||
_palette(nullptr) {
|
||||
_palette(nullptr),
|
||||
_mask(nullptr) {
|
||||
if (_fakeFormat.isCLUT8()) {
|
||||
_palette = new uint32[256];
|
||||
memset(_palette, 0, sizeof(uint32));
|
||||
|
@ -386,6 +387,7 @@ FakeTexture::FakeTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, con
|
|||
|
||||
FakeTexture::~FakeTexture() {
|
||||
delete[] _palette;
|
||||
delete[] _mask;
|
||||
_palette = nullptr;
|
||||
_rgbData.free();
|
||||
}
|
||||
|
@ -402,6 +404,22 @@ void FakeTexture::allocate(uint width, uint height) {
|
|||
_rgbData.create(width, height, getFormat());
|
||||
}
|
||||
|
||||
void FakeTexture::setMask(const byte *mask) {
|
||||
if (mask) {
|
||||
const uint numPixels = _rgbData.w * _rgbData.h;
|
||||
|
||||
if (!_mask)
|
||||
_mask = new byte[numPixels];
|
||||
|
||||
memcpy(_mask, mask, numPixels);
|
||||
} else {
|
||||
delete[] _mask;
|
||||
_mask = nullptr;
|
||||
}
|
||||
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void FakeTexture::setColorKey(uint colorKey) {
|
||||
if (!_palette)
|
||||
return;
|
||||
|
@ -446,6 +464,32 @@ void FakeTexture::updateGLTexture() {
|
|||
Graphics::crossBlit(dst, src, outSurf->pitch, _rgbData.pitch, dirtyArea.width(), dirtyArea.height(), outSurf->format, _rgbData.format);
|
||||
}
|
||||
|
||||
if (_mask) {
|
||||
uint maskPitch = _rgbData.w;
|
||||
uint dirtyWidth = dirtyArea.width();
|
||||
byte destBPP = outSurf->format.bytesPerPixel;
|
||||
|
||||
const byte *maskRowStart = (_mask + dirtyArea.top * maskPitch + dirtyArea.left);
|
||||
byte *dstRowStart = dst;
|
||||
|
||||
for (uint y = dirtyArea.top; y < static_cast<uint>(dirtyArea.bottom); y++) {
|
||||
if (destBPP == 2) {
|
||||
for (uint x = 0; x < dirtyWidth; x++) {
|
||||
if (!maskRowStart[x])
|
||||
reinterpret_cast<uint16 *>(dstRowStart)[x] = 0;
|
||||
}
|
||||
} else if (destBPP == 4) {
|
||||
for (uint x = 0; x < dirtyWidth; x++) {
|
||||
if (!maskRowStart[x])
|
||||
reinterpret_cast<uint32 *>(dstRowStart)[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dstRowStart += outSurf->pitch;
|
||||
maskRowStart += maskPitch;
|
||||
}
|
||||
}
|
||||
|
||||
// Do generic handling of updating the texture.
|
||||
Texture::updateGLTexture();
|
||||
}
|
||||
|
|
|
@ -193,6 +193,13 @@ public:
|
|||
*/
|
||||
virtual void allocate(uint width, uint height) = 0;
|
||||
|
||||
/**
|
||||
* Assign a mask to the surface, where a byte value of 0 is black with 0 alpha and 1 is the normal color.
|
||||
*
|
||||
* @param mask The mask data.
|
||||
*/
|
||||
virtual void setMask(const byte *mask) {}
|
||||
|
||||
/**
|
||||
* Copy image data to the surface.
|
||||
*
|
||||
|
@ -320,6 +327,7 @@ public:
|
|||
~FakeTexture() override;
|
||||
|
||||
void allocate(uint width, uint height) override;
|
||||
void setMask(const byte *mask) override;
|
||||
|
||||
Graphics::PixelFormat getFormat() const override { return _fakeFormat; }
|
||||
|
||||
|
@ -336,6 +344,7 @@ protected:
|
|||
Graphics::Surface _rgbData;
|
||||
Graphics::PixelFormat _fakeFormat;
|
||||
uint32 *_palette;
|
||||
uint8 *_mask;
|
||||
};
|
||||
|
||||
class TextureRGB555 : public FakeTexture {
|
||||
|
|
|
@ -1948,9 +1948,12 @@ void SurfaceSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, in
|
|||
#pragma mark --- Mouse ---
|
||||
#pragma mark -
|
||||
|
||||
void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keyColor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keyColor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
bool formatChanged = false;
|
||||
|
||||
if (mask)
|
||||
warning("SurfaceSdlGraphicsManager::setMouseCursor: Masks are not supported");
|
||||
|
||||
if (format) {
|
||||
#ifndef USE_RGB_COLOR
|
||||
assert(format->bytesPerPixel == 1);
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
int16 getOverlayHeight() const override { return _videoMode.overlayHeight; }
|
||||
int16 getOverlayWidth() const override { return _videoMode.overlayWidth; }
|
||||
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override;
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override;
|
||||
|
||||
#ifdef USE_OSD
|
||||
|
|
|
@ -759,10 +759,13 @@ void AndroidGraphics3dManager::updateCursorScaling() {
|
|||
void AndroidGraphics3dManager::setMouseCursor(const void *buf, uint w, uint h,
|
||||
int hotspotX, int hotspotY,
|
||||
uint32 keycolor, bool dontScale,
|
||||
const Graphics::PixelFormat *format) {
|
||||
const Graphics::PixelFormat *format, const byte *mask) {
|
||||
ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY,
|
||||
keycolor, dontScale, format);
|
||||
|
||||
if (mask)
|
||||
warning("AndroidGraphics3dManager::setMouseCursor: Masks are not supported");
|
||||
|
||||
GLTHREADCHECK;
|
||||
|
||||
#ifdef USE_RGB_COLOR
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
|
||||
int hotspotY, uint32 keycolor,
|
||||
bool dontScale,
|
||||
const Graphics::PixelFormat *format) override;
|
||||
const Graphics::PixelFormat *format, const byte *mask) override;
|
||||
virtual void setCursorPalette(const byte *colors, uint start, uint num) override;
|
||||
|
||||
float getHiDPIScreenFactor() const override;
|
||||
|
|
|
@ -103,7 +103,7 @@ public:
|
|||
|
||||
// GraphicsManager API - Mouse
|
||||
bool showMouse(bool visible) override;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override {}
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override {}
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override {}
|
||||
|
||||
// SdlGraphicsManager API
|
||||
|
|
|
@ -260,8 +260,8 @@ void ModularGraphicsBackend::warpMouse(int x, int y) {
|
|||
_graphicsManager->warpMouse(x, y);
|
||||
}
|
||||
|
||||
void ModularGraphicsBackend::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
_graphicsManager->setMouseCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
|
||||
void ModularGraphicsBackend::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
_graphicsManager->setMouseCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format, mask);
|
||||
}
|
||||
|
||||
void ModularGraphicsBackend::setCursorPalette(const byte *colors, uint start, uint num) {
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
|
||||
bool showMouse(bool visible) override final;
|
||||
void warpMouse(int x, int y) override final;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override final;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override final;
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override final;
|
||||
bool lockMouse(bool lock) override final;
|
||||
|
||||
|
|
|
@ -767,13 +767,16 @@ void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) {
|
|||
void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h,
|
||||
int hotspotX, int hotspotY,
|
||||
uint32 keycolor, bool dontScale,
|
||||
const Graphics::PixelFormat *format) {
|
||||
const Graphics::PixelFormat *format, const byte *mask) {
|
||||
_cursorScalable = !dontScale;
|
||||
_cursorHotspotX = hotspotX;
|
||||
_cursorHotspotY = hotspotY;
|
||||
_cursorKeyColor = keycolor;
|
||||
_pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format;
|
||||
|
||||
if (mask)
|
||||
warning("OSystem_3DS::setMouseCursor: Masks are not supported");
|
||||
|
||||
if (w != (uint)_cursor.w || h != (uint)_cursor.h || _cursor.format != _pfCursor) {
|
||||
_cursor.create(w, h, _pfCursor);
|
||||
_cursorTexture.create(w, h, &DEFAULT_MODE);
|
||||
|
|
|
@ -170,7 +170,7 @@ public:
|
|||
void warpMouse(int x, int y);
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
|
||||
int hotspotY, uint32 keycolor, bool dontScale = false,
|
||||
const Graphics::PixelFormat *format = NULL);
|
||||
const Graphics::PixelFormat *format = NULL, const byte *mask = NULL);
|
||||
void setCursorPalette(const byte *colors, uint start, uint num);
|
||||
|
||||
// Transform point from touchscreen coords into gamescreen coords
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
void warpMouse(int x, int y);
|
||||
|
||||
// Set the bitmap that's used when drawing the cursor.
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
|
||||
|
||||
// Replace the specified range of cursor the palette with new colors.
|
||||
void setCursorPalette(const byte *colors, uint start, uint num);
|
||||
|
|
|
@ -295,8 +295,10 @@ void OSystem_Dreamcast::warpMouse(int x, int y)
|
|||
|
||||
void OSystem_Dreamcast::setMouseCursor(const void *buf, uint w, uint h,
|
||||
int hotspot_x, int hotspot_y,
|
||||
uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format)
|
||||
{
|
||||
uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
if (mask)
|
||||
warning("OSystem_Dreamcast::setMouseCursor: Masks are not supported");
|
||||
|
||||
_ms_cur_w = w;
|
||||
_ms_cur_h = h;
|
||||
|
||||
|
|
|
@ -549,10 +549,13 @@ void OSystem_DS::warpMouse(int x, int y) {
|
|||
_cursorPos = _screen->scaledToReal(x, y);
|
||||
}
|
||||
|
||||
void OSystem_DS::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void OSystem_DS::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
if (!buf || w == 0 || h == 0)
|
||||
return;
|
||||
|
||||
if (mask)
|
||||
warning("OSystem_DS::setMouseCursor: Masks are not supported");
|
||||
|
||||
Graphics::PixelFormat actualFormat = format ? *format : _pfCLUT8;
|
||||
if (_cursor.w != (int16)w || _cursor.h != (int16)h || _cursor.format != actualFormat)
|
||||
_cursor.create(w, h, actualFormat);
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
virtual bool showMouse(bool visible);
|
||||
|
||||
virtual void warpMouse(int x, int y);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
|
||||
|
||||
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
bool showMouse(bool visible) override;
|
||||
|
||||
void warpMouse(int x, int y) override;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL) override;
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override;
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override;
|
||||
|
||||
bool pollEvent(Common::Event &event) override;
|
||||
|
|
|
@ -502,9 +502,12 @@ void OSystem_iOS7::dirtyFullOverlayScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
void OSystem_iOS7::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void OSystem_iOS7::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
//printf("setMouseCursor(%p, %u, %u, %i, %i, %u, %d, %p)\n", (const void *)buf, w, h, hotspotX, hotspotY, keycolor, dontScale, (const void *)format);
|
||||
|
||||
//if (mask)
|
||||
// printf("OSystem_iOS7::setMouseCursor: Masks are not supported");
|
||||
|
||||
const Graphics::PixelFormat pixelFormat = format ? *format : Graphics::PixelFormat::createFormatCLUT8();
|
||||
#if 0
|
||||
printf("bytesPerPixel: %u RGBAlosses: %u,%u,%u,%u RGBAshifts: %u,%u,%u,%u\n", pixelFormat.bytesPerPixel,
|
||||
|
|
|
@ -158,7 +158,7 @@ public:
|
|||
virtual bool showMouse(bool visible);
|
||||
|
||||
virtual void warpMouse(int x, int y);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL);
|
||||
virtual void setCursorPalette(const byte *colors, uint start, uint num);
|
||||
|
||||
virtual bool pollEvent(Common::Event &event);
|
||||
|
|
|
@ -400,7 +400,7 @@ void OSystem_IPHONE::dirtyFullOverlayScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
void OSystem_IPHONE::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void OSystem_IPHONE::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
//printf("setMouseCursor(%p, %u, %u, %i, %i, %u, %d, %p)\n", (const void *)buf, w, h, hotspotX, hotspotY, keycolor, dontScale, (const void *)format);
|
||||
|
||||
const Graphics::PixelFormat pixelFormat = format ? *format : Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
|
|
@ -180,7 +180,7 @@ public:
|
|||
virtual bool showMouse(bool visible);
|
||||
|
||||
virtual void warpMouse(int x, int y);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
|
||||
virtual void setCursorPalette(const byte *colors, uint start, uint num);
|
||||
|
||||
virtual bool pollEvent(Common::Event &event);
|
||||
|
|
|
@ -766,9 +766,12 @@ void OSystem_N64::warpMouse(int x, int y) {
|
|||
_dirtyOffscreen = true;
|
||||
}
|
||||
|
||||
void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
if (!w || !h) return;
|
||||
|
||||
if (mask)
|
||||
warning("OSystem_DS::setMouseCursor: Masks are not supported");
|
||||
|
||||
_mouseHotspotX = hotspotX;
|
||||
_mouseHotspotY = hotspotY;
|
||||
|
||||
|
|
|
@ -305,8 +305,12 @@ void OSystem_PSP::warpMouse(int x, int y) {
|
|||
_cursor.setXY(x, y);
|
||||
}
|
||||
|
||||
void OSystem_PSP::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void OSystem_PSP::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
DEBUG_ENTER_FUNC();
|
||||
|
||||
if (mask)
|
||||
PSP_DEBUG_PRINT("OSystem_PSP::setMouseCursor: Masks are not supported");
|
||||
|
||||
_displayManager.waitUntilRenderFinished();
|
||||
_pendingUpdate = false;
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
// Mouse related
|
||||
bool showMouse(bool visible);
|
||||
void warpMouse(int x, int y);
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
|
||||
|
||||
// Events and input
|
||||
bool pollEvent(Common::Event &event);
|
||||
|
|
|
@ -191,7 +191,7 @@ public:
|
|||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
|
||||
int hotspotY, uint32 keycolor,
|
||||
bool dontScale,
|
||||
const Graphics::PixelFormat *format) override;
|
||||
const Graphics::PixelFormat *format, const byte *mask) override;
|
||||
|
||||
bool pollEvent(Common::Event &event) override;
|
||||
uint32 getMillis(bool skipRecord = false) override;
|
||||
|
|
|
@ -657,7 +657,11 @@ void OSystem_Wii::warpMouse(int x, int y) {
|
|||
void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
|
||||
int hotspotY, uint32 keycolor,
|
||||
bool dontScale,
|
||||
const Graphics::PixelFormat *format) {
|
||||
const Graphics::PixelFormat *format, const byte *mask) {
|
||||
|
||||
if (mask)
|
||||
printf("OSystem_Wii::setMouseCursor: Masks are not supported\n");
|
||||
|
||||
gfx_tex_format_t tex_format = GFX_TF_PALETTE_RGB5A3;
|
||||
uint tw, th;
|
||||
uint32 oldKeycolor = _mouseKeyColor;
|
||||
|
|
|
@ -120,6 +120,25 @@ enum Type {
|
|||
|
||||
} // End of namespace LogMessageType
|
||||
|
||||
/**
|
||||
* Pixel mask modes for cursor graphics.
|
||||
*/
|
||||
enum CursorMaskValue {
|
||||
// Overlapped pixel is unchanged
|
||||
kCursorMaskTransparent = 0,
|
||||
|
||||
// Overlapped pixel is replaced with the cursor pixel.
|
||||
kCursorMaskOpaque = 1,
|
||||
|
||||
/** Fully inverts the overlapped pixel regardless of the cursor color data.
|
||||
* Backend must support kFeatureCursorMaskInvert for this mode. */
|
||||
kCursorMaskInvert = 2,
|
||||
|
||||
/** Inverts the overlapped pixel based on the cursor's color value.
|
||||
* Backend must support kFeatureCursorMaskInvertUsingColor for this mode. */
|
||||
kCursorMaskInvertUsingColor = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for ScummVM backends.
|
||||
*
|
||||
|
@ -386,6 +405,24 @@ public:
|
|||
*/
|
||||
kFeatureCursorPalette,
|
||||
|
||||
/**
|
||||
* Backends supporting this feature allow specifying a mask for a
|
||||
* cursor instead of a key color.
|
||||
*/
|
||||
kFeatureCursorMask,
|
||||
|
||||
/**
|
||||
* Backends supporting this feature allow cursor masks to use mode kCursorMaskInvert in mask values,
|
||||
* which inverts the destination pixel.
|
||||
*/
|
||||
kFeatureCursorMaskInvert,
|
||||
|
||||
/**
|
||||
* Backends supporting this feature allow cursor masks to use mode kCursorMaskInvertUsingColor in the mask values,
|
||||
* which inverts the destination pixel based on the color value of the cursor.
|
||||
*/
|
||||
kFeatureCursorMaskInvertUsingColor,
|
||||
|
||||
/**
|
||||
* A backend has this feature if its overlay pixel format has an alpha
|
||||
* channel which offers at least 3-4 bits of accuracy (as opposed to
|
||||
|
@ -1312,11 +1349,13 @@ public:
|
|||
* @param keycolor Transparency color value. This should not exceed the maximum color value of the specified format.
|
||||
* In case it does, the behavior is undefined. The backend might just error out or simply ignore the
|
||||
* value. (The SDL backend will just assert to prevent abuse of this).
|
||||
* This parameter does nothing if a mask is provided.
|
||||
* @param dontScale Whether the cursor should never be scaled. An exception is high ppi displays, where the cursor
|
||||
* might be too small to notice otherwise, these are allowed to scale the cursor anyway.
|
||||
* @param format Pointer to the pixel format that the cursor graphic uses (0 means CLUT8).
|
||||
* @param mask A mask containing values from the CursorMaskValue enum for each cursor pixel.
|
||||
*/
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = nullptr) = 0;
|
||||
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = nullptr, const byte *mask = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Replace the specified range of cursor palette with new colors.
|
||||
|
|
|
@ -58,6 +58,9 @@ public:
|
|||
/** Return the cursor's surface. */
|
||||
virtual const byte *getSurface() const = 0;
|
||||
|
||||
/** Return the cursor's mask, if it has one. */
|
||||
virtual const byte *getMask() const { return nullptr; }
|
||||
|
||||
/** Return the cursor's palette in RGB format. */
|
||||
virtual const byte *getPalette() const = 0;
|
||||
/** Return the starting index of the palette. */
|
||||
|
|
|
@ -58,13 +58,16 @@ bool CursorManager::showMouse(bool visible) {
|
|||
return g_system->showMouse(visible);
|
||||
}
|
||||
|
||||
void CursorManager::pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
|
||||
void CursorManager::pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
if (!g_system->hasFeature(OSystem::kFeatureCursorMask))
|
||||
mask = nullptr;
|
||||
|
||||
Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format, mask);
|
||||
|
||||
cur->_visible = isVisible();
|
||||
_cursorStack.push(cur);
|
||||
|
||||
g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
|
||||
g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format, mask);
|
||||
}
|
||||
|
||||
void CursorManager::popCursor() {
|
||||
|
@ -76,7 +79,7 @@ void CursorManager::popCursor() {
|
|||
|
||||
if (!_cursorStack.empty()) {
|
||||
cur = _cursorStack.top();
|
||||
g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_dontScale, &cur->_format);
|
||||
g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_dontScale, &cur->_format, cur->_mask);
|
||||
} else {
|
||||
g_system->setMouseCursor(nullptr, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
@ -102,10 +105,12 @@ void CursorManager::popAllCursors() {
|
|||
g_system->showMouse(isVisible());
|
||||
}
|
||||
|
||||
void CursorManager::replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
void CursorManager::replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
if (!g_system->hasFeature(OSystem::kFeatureCursorMask))
|
||||
mask = nullptr;
|
||||
|
||||
if (_cursorStack.empty()) {
|
||||
pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
|
||||
pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format, mask);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -130,6 +135,14 @@ void CursorManager::replaceCursor(const void *buf, uint w, uint h, int hotspotX,
|
|||
if (buf && cur->_data)
|
||||
memcpy(cur->_data, buf, size);
|
||||
|
||||
delete[] cur->_mask;
|
||||
cur->_mask = nullptr;
|
||||
|
||||
if (mask) {
|
||||
cur->_mask = new byte[w * h];
|
||||
memcpy(cur->_mask, mask, w * h);
|
||||
}
|
||||
|
||||
cur->_width = w;
|
||||
cur->_height = h;
|
||||
cur->_hotspotX = hotspotX;
|
||||
|
@ -143,12 +156,12 @@ void CursorManager::replaceCursor(const void *buf, uint w, uint h, int hotspotX,
|
|||
cur->_format = Graphics::PixelFormat::createFormatCLUT8();
|
||||
#endif
|
||||
|
||||
g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
|
||||
g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format, mask);
|
||||
}
|
||||
|
||||
void CursorManager::replaceCursor(const Graphics::Cursor *cursor) {
|
||||
replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(),
|
||||
cursor->getHotspotY(), cursor->getKeyColor());
|
||||
cursor->getHotspotY(), cursor->getKeyColor(), false, nullptr, cursor->getMask());
|
||||
|
||||
if (cursor->getPalette())
|
||||
replaceCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount());
|
||||
|
@ -241,7 +254,7 @@ void CursorManager::lock(bool locked) {
|
|||
_locked = locked;
|
||||
}
|
||||
|
||||
CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
|
||||
CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
||||
#ifdef USE_RGB_COLOR
|
||||
if (!format)
|
||||
_format = Graphics::PixelFormat::createFormatCLUT8();
|
||||
|
@ -258,6 +271,12 @@ CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, in
|
|||
_data = new byte[_size];
|
||||
if (data && _data)
|
||||
memcpy(_data, data, _size);
|
||||
if (mask) {
|
||||
_mask = new byte[w * h];
|
||||
if (_mask)
|
||||
memcpy(_mask, mask, w * h);
|
||||
} else
|
||||
_mask = nullptr;
|
||||
_width = w;
|
||||
_height = h;
|
||||
_hotspotX = hotspotX;
|
||||
|
@ -268,6 +287,7 @@ CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, in
|
|||
|
||||
CursorManager::Cursor::~Cursor() {
|
||||
delete[] _data;
|
||||
delete[] _mask;
|
||||
}
|
||||
|
||||
CursorManager::Palette::Palette(const byte *colors, uint start, uint num) {
|
||||
|
|
|
@ -68,22 +68,25 @@ public:
|
|||
* can be safely freed afterwards.
|
||||
*
|
||||
* @param buf New cursor data.
|
||||
* @param mask New cursor data.
|
||||
* @param w Width.
|
||||
* @param h Height.
|
||||
* @param hotspotX Hotspot X coordinate.
|
||||
* @param hotspotY Hotspot Y coordinate.
|
||||
* @param keycolor Color value for the transparent color. This cannot exceed
|
||||
* the maximum color value as defined by format.
|
||||
* Does nothing if mask is set.
|
||||
* @param dontScale Whether the cursor should never be scaled. An exception are high PPI displays, where the cursor
|
||||
* would be too small to notice otherwise. These are allowed to scale the cursor anyway.
|
||||
* @param format Pointer to the pixel format that the cursor graphic uses.
|
||||
* CLUT8 will be used if this is null or not specified.
|
||||
* @param mask Optional pointer to cursor mask containing values from the CursorMaskValue enum.
|
||||
*
|
||||
* @note It is acceptable for the buffer to be a null pointer. It is sometimes
|
||||
* useful to push a "dummy" cursor and modify it later. The
|
||||
* cursor will be added to the stack, but not to the backend.
|
||||
*/
|
||||
void pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
|
||||
void pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL);
|
||||
|
||||
/**
|
||||
* Pop a cursor from the stack, and restore the previous one to the
|
||||
|
@ -100,18 +103,21 @@ public:
|
|||
* more optimized way of popping the old cursor before pushing the new one.
|
||||
*
|
||||
* @param buf New cursor data.
|
||||
* @param mask New cursor mask data.
|
||||
* @param w Width.
|
||||
* @param h Height.
|
||||
* @param hotspotX Hotspot X coordinate.
|
||||
* @param hotspotY Hotspot Y coordinate.
|
||||
* @param keycolor Color value for the transparent color. This cannot exceed
|
||||
* the maximum color value as defined by format.
|
||||
* Does nothing if mask is set.
|
||||
* @param dontScale Whether the cursor should never be scaled. An exception are high PPI displays, where the cursor
|
||||
* would be too small to notice otherwise. These are allowed to scale the cursor anyway.
|
||||
* @param format Pointer to the pixel format that the cursor graphic uses,
|
||||
* CLUT8 will be used if this is null or not specified.
|
||||
* @param mask Optional pointer to cursor mask containing values from the CursorMaskValue enum.
|
||||
*/
|
||||
void replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
|
||||
void replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = nullptr, const byte *mask = nullptr);
|
||||
|
||||
/**
|
||||
* Replace the current cursor on the stack.
|
||||
|
@ -211,6 +217,7 @@ private:
|
|||
|
||||
struct Cursor {
|
||||
byte *_data;
|
||||
byte *_mask;
|
||||
bool _visible;
|
||||
uint _width;
|
||||
uint _height;
|
||||
|
@ -223,9 +230,9 @@ private:
|
|||
uint _size;
|
||||
|
||||
// _format set to default by Graphics::PixelFormat default constructor
|
||||
Cursor() : _data(0), _visible(false), _width(0), _height(0), _hotspotX(0), _hotspotY(0), _keycolor(0), _dontScale(false), _size(0) {}
|
||||
Cursor() : _data(0), _mask(0), _visible(false), _width(0), _height(0), _hotspotX(0), _hotspotY(0), _keycolor(0), _dontScale(false), _size(0) {}
|
||||
|
||||
Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
|
||||
Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
|
||||
~Cursor();
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "graphics/wincursor.h"
|
||||
|
@ -45,6 +46,7 @@ public:
|
|||
byte getKeyColor() const override;
|
||||
|
||||
const byte *getSurface() const override { return _surface; }
|
||||
const byte *getMask() const override { return _mask; }
|
||||
|
||||
const byte *getPalette() const override { return _palette; }
|
||||
byte getPaletteStartIndex() const override { return 0; }
|
||||
|
@ -55,6 +57,7 @@ public:
|
|||
|
||||
private:
|
||||
byte *_surface;
|
||||
byte *_mask;
|
||||
byte _palette[256 * 3];
|
||||
|
||||
uint16 _width; ///< The cursor's width.
|
||||
|
@ -72,7 +75,8 @@ WinCursor::WinCursor() {
|
|||
_height = 0;
|
||||
_hotspotX = 0;
|
||||
_hotspotY = 0;
|
||||
_surface = 0;
|
||||
_surface = nullptr;
|
||||
_mask = nullptr;
|
||||
_keyColor = 0;
|
||||
memset(_palette, 0, 256 * 3);
|
||||
}
|
||||
|
@ -104,6 +108,9 @@ byte WinCursor::getKeyColor() const {
|
|||
bool WinCursor::readFromStream(Common::SeekableReadStream &stream) {
|
||||
clear();
|
||||
|
||||
const bool supportOpacity = g_system->hasFeature(OSystem::kFeatureCursorMask);
|
||||
const bool supportInvert = g_system->hasFeature(OSystem::kFeatureCursorMaskInvert);
|
||||
|
||||
_hotspotX = stream.readUint16LE();
|
||||
_hotspotY = stream.readUint16LE();
|
||||
|
||||
|
@ -161,6 +168,8 @@ bool WinCursor::readFromStream(Common::SeekableReadStream &stream) {
|
|||
// Parse the XOR map
|
||||
const byte *src = initialSource;
|
||||
_surface = new byte[_width * _height];
|
||||
if (supportOpacity)
|
||||
_mask = new byte[_width * _height];
|
||||
byte *dest = _surface + _width * (_height - 1);
|
||||
uint32 imagePitch = _width * bitsPerPixel / 8;
|
||||
|
||||
|
@ -223,9 +232,29 @@ bool WinCursor::readFromStream(Common::SeekableReadStream &stream) {
|
|||
src += andWidth * (_height - 1);
|
||||
|
||||
for (uint32 y = 0; y < _height; y++) {
|
||||
for (uint32 x = 0; x < _width; x++)
|
||||
if (src[x / 8] & (1 << (7 - x % 8)))
|
||||
_surface[y * _width + x] = _keyColor;
|
||||
for (uint32 x = 0; x < _width; x++) {
|
||||
byte &surfaceByte = _surface[y * _width + x];
|
||||
if (src[x / 8] & (1 << (7 - x % 8))) {
|
||||
if (_mask) {
|
||||
byte &maskByte = _mask[y * _width + x];
|
||||
if (surfaceByte == 0) {
|
||||
// Transparent
|
||||
maskByte = 0;
|
||||
} else {
|
||||
// Inverted, if the backend supports invert then emit an inverted pixel, otherwise opaque
|
||||
maskByte = supportInvert ? 2 : 1;
|
||||
}
|
||||
} else {
|
||||
// Don't support mask or invert, leave this as opaque if it's XOR so it's visible
|
||||
if (surfaceByte == 0)
|
||||
surfaceByte = _keyColor;
|
||||
}
|
||||
} else {
|
||||
// Opaque pixel
|
||||
if (_mask)
|
||||
_mask[y * _width + x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
src -= andWidth;
|
||||
}
|
||||
|
@ -235,7 +264,8 @@ bool WinCursor::readFromStream(Common::SeekableReadStream &stream) {
|
|||
}
|
||||
|
||||
void WinCursor::clear() {
|
||||
delete[] _surface; _surface = 0;
|
||||
delete[] _surface; _surface = nullptr;
|
||||
delete[] _mask; _mask = nullptr;
|
||||
}
|
||||
|
||||
WinCursorGroup::WinCursorGroup() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue