From 224b94c0ce02349fbdfc99300f0350ad37b9581e Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 9 Jun 2018 10:55:34 +0200 Subject: [PATCH 1/5] Decompress cursors using SDL software renderer on Mac or if OSG >= 3.5.8 --- CHANGELOG.md | 1 + components/sdlutil/sdlcursormanager.cpp | 83 ++++++++++++++++++++----- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb72c7cac2..6210f3f114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Bug #4433: Guard behaviour is incorrect with Alarm = 0 Bug #4443: Goodbye option and dialogue choices are not mutually exclusive Feature #4444: Per-group KF-animation files support + Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK 0.44.0 ------ diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 65aa2106fd..368d8e5027 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -7,11 +7,14 @@ #include #include +#include +#include #include #include #include #include +#include #include #include "imagetosurface.hpp" @@ -22,6 +25,13 @@ USE_GRAPHICSWINDOW() #endif +#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 8) || defined(__APPLE__) +#define OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION 1 +#else +#define OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION 0 +#endif + +#if !OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION namespace { @@ -132,17 +142,6 @@ namespace osg::ref_ptr geom; -#if defined(__APPLE__) - // Extra flip needed on OS X systems due to a driver bug - const char* envval = getenv("OPENMW_CURSOR_WORKAROUND"); - bool workaround = !envval || envval == std::string("1"); - std::string vendorString = (const char*)glGetString(GL_VENDOR); - if (!envval) - workaround = vendorString.find("Intel") != std::string::npos || vendorString.find("ATI") != std::string::npos || vendorString.find("AMD") != std::string::npos; - if (workaround) - geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,1,0), osg::Vec3(2,0,0), osg::Vec3(0,-2,0)); - else -#endif geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0)); geom->drawImplementation(renderInfo); @@ -157,6 +156,7 @@ namespace } } +#endif namespace SDLUtil { @@ -220,13 +220,63 @@ namespace SDLUtil #endif } +#if OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION + typedef std::unique_ptr SDLSurfacePtr; + + SDLSurfacePtr decompress(osg::Image* source, int rotDegrees) + { + int width = source->s(); + int height = source->t(); + bool useAlpha = source->isImageTranslucent(); + + osg::ref_ptr decompressedImage = new osg::Image; + decompressedImage->setFileName(source->getFileName()); + decompressedImage->allocateImage(width, height, 1, useAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE); + for (int s=0; ssetColor(source->getColor(s,t,0), s,t,0); + + Uint32 redMask = 0x000000ff; + Uint32 greenMask = 0x0000ff00; + Uint32 blueMask = 0x00ff0000; + Uint32 alphaMask = useAlpha ? 0xff000000 : 0; + + SDL_Surface *cursorSurface = SDL_CreateRGBSurfaceFrom(decompressedImage->data(), + width, + height, + decompressedImage->getPixelSizeInBits(), + decompressedImage->getRowSizeInBytes(), + redMask, + greenMask, + blueMask, + alphaMask); + + SDL_Surface *targetSurface = SDL_CreateRGBSurface(0, width, height, 32, redMask, greenMask, blueMask, alphaMask); + SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(targetSurface); + + SDL_RenderClear(renderer); + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); + + SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); + + SDL_DestroyTexture(cursorTexture); + SDL_FreeSurface(cursorSurface); + SDL_DestroyRenderer(renderer); + + return SDLSurfacePtr(targetSurface, SDL_FreeSurface); + } +#endif + void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y) { - osg::ref_ptr decompressed; - if (mCursorMap.find(name) != mCursorMap.end()) return; +#if !OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION + osg::ref_ptr decompressed; + try { decompressed = decompress(image, static_cast(rotDegrees)); } catch (std::exception& e) { @@ -239,10 +289,15 @@ namespace SDLUtil //set the cursor and store it for later SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); - mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); //clean up SDL_FreeSurface(surf); +#else + auto surf = decompress(image, rotDegrees); + //set the cursor and store it for later + SDL_Cursor* curs = SDL_CreateColorCursor(surf.get(), hotspot_x, hotspot_y); +#endif + mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); } } From 359f87ab9f4ad628b231705bcfd7b5f76c30071f Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 16 Jun 2018 12:12:32 +0200 Subject: [PATCH 2/5] Change imageToSurface to return a unique_ptr to avoid manual surface cleanup --- apps/openmw/engine.cpp | 5 ++--- components/sdlutil/imagetosurface.cpp | 4 ++-- components/sdlutil/imagetosurface.hpp | 6 ++++-- components/sdlutil/sdlcursormanager.cpp | 17 +++++------------ 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 78e368cfcc..65ea181fbd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -428,9 +428,8 @@ void OMW::Engine::setWindowIcon() else { osg::ref_ptr image = result.getImage(); - SDL_Surface* surface = SDLUtil::imageToSurface(image, true); - SDL_SetWindowIcon(mWindow, surface); - SDL_FreeSurface(surface); + auto surface = SDLUtil::imageToSurface(image, true); + SDL_SetWindowIcon(mWindow, surface.get()); } } diff --git a/components/sdlutil/imagetosurface.cpp b/components/sdlutil/imagetosurface.cpp index 6313c0a8fd..6e68c0f458 100644 --- a/components/sdlutil/imagetosurface.cpp +++ b/components/sdlutil/imagetosurface.cpp @@ -6,7 +6,7 @@ namespace SDLUtil { -SDL_Surface* imageToSurface(osg::Image *image, bool flip) +SurfaceUniquePtr imageToSurface(osg::Image *image, bool flip) { int width = image->s(); int height = image->t(); @@ -22,7 +22,7 @@ SDL_Surface* imageToSurface(osg::Image *image, bool flip) static_cast(clr.g() * 255), static_cast(clr.b() * 255), static_cast(clr.a() * 255)); } - return surface; + return SurfaceUniquePtr(surface, SDL_FreeSurface); } } diff --git a/components/sdlutil/imagetosurface.hpp b/components/sdlutil/imagetosurface.hpp index ad0457433c..9ce56b9090 100644 --- a/components/sdlutil/imagetosurface.hpp +++ b/components/sdlutil/imagetosurface.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H #define OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H +#include + struct SDL_Surface; namespace osg @@ -10,10 +12,10 @@ namespace osg namespace SDLUtil { + typedef std::unique_ptr SurfaceUniquePtr; /// Convert an osg::Image to an SDL_Surface. - /// @note The returned surface must be freed using SDL_FreeSurface. - SDL_Surface* imageToSurface(osg::Image* image, bool flip=false); + SurfaceUniquePtr imageToSurface(osg::Image* image, bool flip=false); } diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 368d8e5027..bf265dc7e8 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -221,9 +221,7 @@ namespace SDLUtil } #if OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION - typedef std::unique_ptr SDLSurfacePtr; - - SDLSurfacePtr decompress(osg::Image* source, int rotDegrees) + SurfaceUniquePtr decompress(osg::Image* source, int rotDegrees) { int width = source->s(); int height = source->t(); @@ -265,7 +263,7 @@ namespace SDLUtil SDL_FreeSurface(cursorSurface); SDL_DestroyRenderer(renderer); - return SDLSurfacePtr(targetSurface, SDL_FreeSurface); + return SurfaceUniquePtr(targetSurface, SDL_FreeSurface); } #endif @@ -285,18 +283,13 @@ namespace SDLUtil return; } - SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, true); - - //set the cursor and store it for later - SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); - - //clean up - SDL_FreeSurface(surf); + auto surf = SDLUtil::imageToSurface(decompressed, true); #else auto surf = decompress(image, rotDegrees); +#endif //set the cursor and store it for later SDL_Cursor* curs = SDL_CreateColorCursor(surf.get(), hotspot_x, hotspot_y); -#endif + mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); } From 75d79e98b9d91b3f251c5dbfbf4c5a3589292cc2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 16 Jun 2018 12:38:16 +0200 Subject: [PATCH 3/5] Force software decompression if OPENMW_DECOMPRESS_TEXTURES is set --- components/sdlutil/sdlcursormanager.cpp | 140 ++++++++++++------------ 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index bf265dc7e8..1747c9b941 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -25,15 +25,14 @@ USE_GRAPHICSWINDOW() #endif -#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 8) || defined(__APPLE__) -#define OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION 1 -#else -#define OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION 0 -#endif - -#if !OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION -namespace +namespace CursorDecompression { + // macOS builds use the OSG fork that includes DXTC commit + #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 8) || defined(__APPLE__) + static const bool DXTCSupported = true; + #else + static const bool DXTCSupported = false; + #endif class MyGraphicsContext { public: @@ -90,10 +89,8 @@ namespace osg::ref_ptr _gc; }; - osg::ref_ptr decompress (osg::ref_ptr source, float rotDegrees) + SDLUtil::SurfaceUniquePtr hardwareDecompress (osg::ref_ptr source, float rotDegrees) { - // TODO: use software decompression once S3TC patent expires - int width = source->s(); int height = source->t(); @@ -152,11 +149,55 @@ namespace source->releaseGLObjects(); texture->releaseGLObjects(); - return resultImage; + return SDLUtil::imageToSurface(resultImage, true); + } + + SDLUtil::SurfaceUniquePtr softwareDecompress (osg::ref_ptr source, float rotDegrees) + { + int width = source->s(); + int height = source->t(); + bool useAlpha = source->isImageTranslucent(); + + osg::ref_ptr decompressedImage = new osg::Image; + decompressedImage->setFileName(source->getFileName()); + decompressedImage->allocateImage(width, height, 1, useAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE); + for (int s=0; ssetColor(source->getColor(s,t,0), s,t,0); + + Uint32 redMask = 0x000000ff; + Uint32 greenMask = 0x0000ff00; + Uint32 blueMask = 0x00ff0000; + Uint32 alphaMask = useAlpha ? 0xff000000 : 0; + + SDL_Surface *cursorSurface = SDL_CreateRGBSurfaceFrom(decompressedImage->data(), + width, + height, + decompressedImage->getPixelSizeInBits(), + decompressedImage->getRowSizeInBytes(), + redMask, + greenMask, + blueMask, + alphaMask); + + SDL_Surface *targetSurface = SDL_CreateRGBSurface(0, width, height, 32, redMask, greenMask, blueMask, alphaMask); + SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(targetSurface); + + SDL_RenderClear(renderer); + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); + + SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); + + SDL_DestroyTexture(cursorTexture); + SDL_FreeSurface(cursorSurface); + SDL_DestroyRenderer(renderer); + + return SDLUtil::SurfaceUniquePtr(targetSurface, SDL_FreeSurface); } } -#endif namespace SDLUtil { @@ -220,77 +261,32 @@ namespace SDLUtil #endif } -#if OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION - SurfaceUniquePtr decompress(osg::Image* source, int rotDegrees) - { - int width = source->s(); - int height = source->t(); - bool useAlpha = source->isImageTranslucent(); - - osg::ref_ptr decompressedImage = new osg::Image; - decompressedImage->setFileName(source->getFileName()); - decompressedImage->allocateImage(width, height, 1, useAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE); - for (int s=0; ssetColor(source->getColor(s,t,0), s,t,0); - - Uint32 redMask = 0x000000ff; - Uint32 greenMask = 0x0000ff00; - Uint32 blueMask = 0x00ff0000; - Uint32 alphaMask = useAlpha ? 0xff000000 : 0; - - SDL_Surface *cursorSurface = SDL_CreateRGBSurfaceFrom(decompressedImage->data(), - width, - height, - decompressedImage->getPixelSizeInBits(), - decompressedImage->getRowSizeInBytes(), - redMask, - greenMask, - blueMask, - alphaMask); - - SDL_Surface *targetSurface = SDL_CreateRGBSurface(0, width, height, 32, redMask, greenMask, blueMask, alphaMask); - SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(targetSurface); - - SDL_RenderClear(renderer); - - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); - - SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); - - SDL_DestroyTexture(cursorTexture); - SDL_FreeSurface(cursorSurface); - SDL_DestroyRenderer(renderer); - - return SurfaceUniquePtr(targetSurface, SDL_FreeSurface); - } -#endif - void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 hotspot_x, Uint8 hotspot_y) { if (mCursorMap.find(name) != mCursorMap.end()) return; -#if !OPENMW_USE_SOFTWARE_CURSOR_DECOMPRESSION - osg::ref_ptr decompressed; + static bool forceSoftwareDecompression = (getenv("OPENMW_DECOMPRESS_TEXTURES") != 0); + + SurfaceUniquePtr (*decompressionFunction)(osg::ref_ptr, float); + if (forceSoftwareDecompression || CursorDecompression::DXTCSupported) { + decompressionFunction = CursorDecompression::softwareDecompress; + } else { + decompressionFunction = CursorDecompression::hardwareDecompress; + } try { - decompressed = decompress(image, static_cast(rotDegrees)); + auto surface = decompressionFunction(image, static_cast(rotDegrees)); + + //set the cursor and store it for later + SDL_Cursor* curs = SDL_CreateColorCursor(surface.get(), hotspot_x, hotspot_y); + + mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); } catch (std::exception& e) { std::cerr << e.what() << std::endl; std::cerr <<"Using default cursor."< Date: Wed, 20 Jun 2018 15:43:55 +0200 Subject: [PATCH 4/5] Move the changelog entry to 0.44.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6210f3f114..471df4f53c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ Bug #4433: Guard behaviour is incorrect with Alarm = 0 Bug #4443: Goodbye option and dialogue choices are not mutually exclusive Feature #4444: Per-group KF-animation files support - Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK 0.44.0 ------ @@ -109,6 +108,7 @@ Feature #4423: Rebalance soul gem values Task #4015: Use AppVeyor build artifact features to make continuous builds available Editor: New (and more complete) icon set + Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK 0.43.0 ------ From 9cf815505bcad7203a966f95bb8a0059fee5e4f3 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 20 Jun 2018 16:21:23 +0200 Subject: [PATCH 5/5] formatting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 471df4f53c..789aea001c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Bug #4412: openmw-iniimporter ignores data paths from config Bug #4413: Moving with 0 strength uses all of your fatigue Bug #4420: Camera flickering when I open up and close menus while sneaking + Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK Bug #4435: Item health is considered a signed integer Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game Feature #1786: Round up encumbrance value in the encumbrance bar @@ -108,7 +109,6 @@ Feature #4423: Rebalance soul gem values Task #4015: Use AppVeyor build artifact features to make continuous builds available Editor: New (and more complete) icon set - Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK 0.43.0 ------