Add variant of newCursor which takes an array, for different DPI scales.

Resolves #1708.
This commit is contained in:
Sasha Szpakowski
2024-12-25 17:45:39 -04:00
parent 3d9d162dd6
commit 7076688a65
6 changed files with 87 additions and 19 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ public:
// Implements Module. // Implements Module.
virtual ModuleType getModuleType() const { return M_MOUSE; } virtual ModuleType getModuleType() const { return M_MOUSE; }
virtual Cursor *newCursor(love::image::ImageData *data, int hotx, int hoty) = 0; virtual Cursor *newCursor(const std::vector<image::ImageData *> &data, int hotx, int hoty) = 0;
virtual Cursor *getSystemCursor(Cursor::SystemCursor cursortype) = 0; virtual Cursor *getSystemCursor(Cursor::SystemCursor cursortype) = 0;
virtual void setCursor(Cursor *cursor) = 0; virtual void setCursor(Cursor *cursor) = 0;
+37 -11
View File
@@ -29,23 +29,49 @@ namespace mouse
namespace sdl namespace sdl
{ {
Cursor::Cursor(image::ImageData *data, int hotx, int hoty) Cursor::Cursor(const std::vector<image::ImageData *> &imageData, int hotx, int hoty)
: cursor(nullptr) : cursor(nullptr)
, type(CURSORTYPE_IMAGE) , type(CURSORTYPE_IMAGE)
, systemType(CURSOR_MAX_ENUM) , systemType(CURSOR_MAX_ENUM)
{ {
int w = data->getWidth(); if (imageData.empty())
int h = data->getHeight(); throw love::Exception("At least one ImageData must be provided for a custom cursor.");
int pitch = w * 4;
SDL_Surface *surface = SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_ABGR8888, data->getData(), pitch); std::vector<SDL_Surface *> surfaces;
if (!surface)
throw love::Exception("Cannot create cursor: out of memory.");
cursor = SDL_CreateColorCursor(surface, hotx, hoty); for (image::ImageData *data : imageData)
SDL_DestroySurface(surface); {
int w = data->getWidth();
int h = data->getHeight();
int pitch = w * 4;
if (!cursor) if (getLinearPixelFormat(data->getFormat()) != PIXELFORMAT_RGBA8_UNORM)
{
for (SDL_Surface *surface : surfaces)
SDL_DestroySurface(surface);
throw love::Exception("Cannot create cursor: ImageData pixel format must be rgba8.");
}
surfaces.push_back(SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_ABGR8888, data->getData(), pitch));
if (surfaces.back() == nullptr)
{
for (SDL_Surface *surface : surfaces)
SDL_DestroySurface(surface);
throw love::Exception("Cannot create cursor: out of memory.");
}
}
// Add alternate representations for the OS to use in different DPI scales.
for (size_t i = 1; i < surfaces.size(); i++)
SDL_AddSurfaceAlternateImage(surfaces[0], surfaces[i]);
cursor = SDL_CreateColorCursor(surfaces[0], hotx, hoty);
for (SDL_Surface *surface : surfaces)
SDL_DestroySurface(surface);
if (cursor == nullptr)
throw love::Exception("Cannot create cursor: %s", SDL_GetError()); throw love::Exception("Cannot create cursor: %s", SDL_GetError());
} }
@@ -61,7 +87,7 @@ Cursor::Cursor(mouse::Cursor::SystemCursor cursortype)
else else
throw love::Exception("Cannot create system cursor: invalid type."); throw love::Exception("Cannot create system cursor: invalid type.");
if (!cursor) if (cursor == nullptr)
throw love::Exception("Cannot create system cursor: %s", SDL_GetError()); throw love::Exception("Cannot create system cursor: %s", SDL_GetError());
} }
+3 -1
View File
@@ -28,6 +28,8 @@
// SDL // SDL
#include <SDL3/SDL_mouse.h> #include <SDL3/SDL_mouse.h>
#include <vector>
namespace love namespace love
{ {
namespace mouse namespace mouse
@@ -39,7 +41,7 @@ class Cursor : public love::mouse::Cursor
{ {
public: public:
Cursor(image::ImageData *imageData, int hotx, int hoty); Cursor(const std::vector<image::ImageData *> &imageData, int hotx, int hoty);
Cursor(SystemCursor cursortype); Cursor(SystemCursor cursortype);
~Cursor(); ~Cursor();
+1 -1
View File
@@ -84,7 +84,7 @@ Mouse::~Mouse()
SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_QuitSubSystem(SDL_INIT_VIDEO);
} }
love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty) love::mouse::Cursor *Mouse::newCursor(const std::vector<image::ImageData *> &data, int hotx, int hoty)
{ {
return new Cursor(data, hotx, hoty); return new Cursor(data, hotx, hoty);
} }
+1 -1
View File
@@ -42,7 +42,7 @@ public:
Mouse(); Mouse();
virtual ~Mouse(); virtual ~Mouse();
love::mouse::Cursor *newCursor(love::image::ImageData *data, int hotx, int hoty) override; love::mouse::Cursor *newCursor(const std::vector<image::ImageData *> &data, int hotx, int hoty) override;
love::mouse::Cursor *getSystemCursor(Cursor::SystemCursor cursortype) override; love::mouse::Cursor *getSystemCursor(Cursor::SystemCursor cursortype) override;
void setCursor(love::mouse::Cursor *cursor) override; void setCursor(love::mouse::Cursor *cursor) override;
+44 -4
View File
@@ -36,15 +36,55 @@ namespace mouse
int w_newCursor(lua_State *L) int w_newCursor(lua_State *L)
{ {
Cursor *cursor = nullptr; Cursor *cursor = nullptr;
std::vector<love::image::ImageData *> data;
if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type)) if (lua_istable(L, 1))
luax_convobj(L, 1, "image", "newImageData"); {
// Do some type checking first, because memory will leak if we hit an error in the loop after this.
for (size_t i = 1; i <= luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
if (!luax_istype(L, -1, love::image::ImageData::type)
&& !(lua_isstring(L, -1) || luax_istype(L, -1, love::filesystem::File::type) || luax_istype(L, -1, love::filesystem::FileData::type)))
{
luax_checktype<love::image::ImageData>(L, -1);
}
lua_pop(L, 1);
}
for (size_t i = 1; i <= luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
if (lua_isstring(L, -1) || luax_istype(L, -1, love::filesystem::File::type) || luax_istype(L, -1, love::filesystem::FileData::type))
luax_convobj(L, -1, "image", "newImageData");
data.push_back(luax_checktype<love::image::ImageData>(L, -1));
// If a GC step happens within the loop, previous ImageData objects created within the loop may be released.
data.back()->retain();
lua_pop(L, 1);
}
}
else
{
if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type))
luax_convobj(L, 1, "image", "newImageData");
data.push_back(luax_checktype<love::image::ImageData>(L, 1));
data.back()->retain();
}
love::image::ImageData *data = luax_checktype<love::image::ImageData>(L, 1);
int hotx = (int) luaL_optinteger(L, 2, 0); int hotx = (int) luaL_optinteger(L, 2, 0);
int hoty = (int) luaL_optinteger(L, 3, 0); int hoty = (int) luaL_optinteger(L, 3, 0);
luax_catchexcept(L, [&](){ cursor = instance()->newCursor(data, hotx, hoty); }); luax_catchexcept(L,
[&](){ cursor = instance()->newCursor(data, hotx, hoty); },
[&](bool /*shoulderror*/) { for (auto d : data) d->release(); }
);
luax_pushtype(L, cursor); luax_pushtype(L, cursor);
cursor->release(); cursor->release();