diff --git a/WickedEngine/wiAppleHelper.h b/WickedEngine/wiAppleHelper.h index 2c55790d2..005fd57a0 100644 --- a/WickedEngine/wiAppleHelper.h +++ b/WickedEngine/wiAppleHelper.h @@ -23,4 +23,5 @@ void SetWindowFullScreen(void* handle, bool fullscreen); void OpenUrl(const char* url); std::string GetClipboardText(); void SetClipboardText(const char* str); +void* CreateCursorFromARGB8ImageData(const void* data, uint32_t width, uint32_t height, int hotspotX, int hotspotY); } diff --git a/WickedEngine/wiAppleHelper.mm b/WickedEngine/wiAppleHelper.mm index 20ff92f4c..c7deba265 100644 --- a/WickedEngine/wiAppleHelper.mm +++ b/WickedEngine/wiAppleHelper.mm @@ -289,4 +289,37 @@ void SetClipboardText(const char* str) [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; } +void* CreateCursorFromARGB8ImageData(const void* data, uint32_t width, uint32_t height, int hotspotX, int hotspotY) +{ + const size_t bytesPerRow = width * sizeof(uint32_t); + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes: nullptr + pixelsWide: width + pixelsHigh: height + bitsPerSample: 8 + samplesPerPixel: 4 + hasAlpha: YES + isPlanar: NO + colorSpaceName: NSDeviceRGBColorSpace + bitmapFormat: NSBitmapFormatAlphaNonpremultiplied | NSBitmapFormatAlphaFirst + bytesPerRow: bytesPerRow + bitsPerPixel: 32]; + + uint8_t* dst = [bitmap bitmapData]; + size_t bytesToCopy = size_t(height) * bytesPerRow; + std::memcpy(dst, data, bytesToCopy); + + NSImage* image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; + [image addRepresentation:bitmap]; + + NSCursor* cursor = [[NSCursor alloc] + initWithImage:image + hotSpot:NSMakePoint(hotspotX, hotspotY)]; + + static wi::vector cursors; // Lifetime extender, a proper solution would require engine to know about autorelease pools lifetimes and manage cursor lifetime with that + cursors.push_back(cursor); + + return (__bridge void*)cursor; +} + } diff --git a/WickedEngine/wiInput.cpp b/WickedEngine/wiInput.cpp index 6de10ed69..d2704af2c 100644 --- a/WickedEngine/wiInput.cpp +++ b/WickedEngine/wiInput.cpp @@ -9,6 +9,8 @@ #include "wiColor.h" #include "wiTimer.h" +#include "Utility/win32ico.h" + #include #include #include @@ -17,7 +19,6 @@ #ifdef SDL2 #include -#include "Utility/win32ico.h" #endif // SDL2 #ifdef PLATFORM_PS5 @@ -1157,32 +1158,32 @@ namespace wi::input } void SetCursorFromFile(CURSOR cursor, const char* filename) - { +{ #ifdef PLATFORM_WINDOWS_DESKTOP wchar_t wfilename[1024] = {}; wi::helper::StringConvert(filename, wfilename, arraysize(wfilename)); cursor_table[cursor] = LoadCursorFromFile(wfilename); #endif // PLATFORM_WINDOWS_DESKTOP - -#ifdef SDL2 - // In SDL extract the raw color data from win32 .CUR file and use SDL to create cursor: + +#if defined(SDL2) || defined(PLATFORM_MACOS) + // On other platforms, extract the raw color data from win32 .CUR file and create cursor from that: wi::vector data; if (wi::helper::FileRead(filename, data)) { // Extract only the first image data: ico::ICONDIRENTRY* icondirentry = (ico::ICONDIRENTRY*)(data.data() + sizeof(ico::ICONDIR)); - + int hotspotX = icondirentry->wPlanes; int hotspotY = icondirentry->wBitCount; - + uint8_t* pixeldata = (data.data() + icondirentry->dwImageOffset + sizeof(ico::BITMAPINFOHEADER)); - + const uint32_t width = icondirentry->bWidth; const uint32_t height = icondirentry->bHeight; - + // Convert BGRA to ARGB and flip vertically - wi::vector colors; - colors.reserve(width * height); + wi::vector colors; + colors.reserve(width * height); for (uint32_t y = height; y > 0; --y) { uint8_t* src = pixeldata + (y - 1) * width * 4; @@ -1192,27 +1193,31 @@ namespace wi::input src += 4; } } - + +#ifdef SDL2 SDL_Surface* surface = SDL_CreateRGBSurfaceFrom( - colors.data(), - width, - height, - 4 * 8, - 4 * width, - 0x0000ff00, - 0x00ff0000, - 0xff000000, - 0x000000ff - ); - + colors.data(), + width, + height, + 4 * 8, + 4 * width, + 0x0000ff00, + 0x00ff0000, + 0xff000000, + 0x000000ff + ); + if (surface != nullptr) { cursor_table[cursor] = SDL_CreateColorCursor(surface, hotspotX, hotspotY); SDL_FreeSurface(surface); } - } +#elif defined(PLATFORM_MACOS) + cursor_table[cursor] = wi::apple::CreateCursorFromARGB8ImageData(colors.data(), width, height, hotspotX, hotspotY); #endif // SDL2 - + } +#endif // defined(SDL2) || defined(PLATFORM_MACOS) + // refresh in case we set the current one: cursor_next = cursor_current; cursor_current = CURSOR_COUNT;