From ef85b1393aeaec905f6310abca29b4e0f42db901 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 20 Jun 2018 17:19:48 +0200 Subject: Merge pull request #1740 from nikolaykasyanov/software-cursor-decompression Decompress cursors using SDL software renderer on Mac or if OSG >= 3.5.8 or if OPENMW_DECOMPRESS_TEXTURES is set --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 5 +- components/sdlutil/imagetosurface.cpp | 4 +- components/sdlutil/imagetosurface.hpp | 6 +- components/sdlutil/sdlcursormanager.cpp | 100 +++++++++++++++++++++++--------- 5 files changed, 81 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a958fbc09..41a3506cf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,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 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 65aa2106fd..1747c9b941 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,8 +25,14 @@ USE_GRAPHICSWINDOW() #endif -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: @@ -80,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(); @@ -132,17 +139,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); @@ -153,7 +149,52 @@ 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); } } @@ -222,27 +263,30 @@ namespace SDLUtil 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; + 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."<