diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 44f24a97bbe..d5fc52ab12a 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -78,6 +78,7 @@ Sets a custom mouse cursor image for the defined [param shape]. This means the user's operating system and mouse cursor theme will no longer influence the mouse cursor's appearance. The image must be [code]256x256[/code] or smaller for correct appearance. [param hotspot] can optionally be set to define the area where the cursor will click. By default, [param hotspot] is set to [code]Vector2(0, 0)[/code], which is the top-left corner of the image. See also [method cursor_set_shape]. + [param cursor] can be either [Texture2D] or [Image]. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index d9b2ef59e38..6c1ae1b0ef1 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -325,7 +325,7 @@ Sets a custom mouse cursor image, which is only visible inside the game window. The hotspot can also be specified. Passing [code]null[/code] to the image parameter resets to the system cursor. See [enum CursorShape] for the list of shapes. - [param image]'s size must be lower than or equal to 256×256. To avoid rendering issues, sizes lower than or equal to 128×128 are recommended. + [param image] can be either [Texture2D] or [Image] and its size must be lower than or equal to 256×256. To avoid rendering issues, sizes lower than or equal to 128×128 are recommended. [param hotspot] must be within [param image]'s size. [b]Note:[/b] [AnimatedTexture]s aren't supported as custom mouse cursors. If using an [AnimatedTexture], only the first frame will be displayed. [b]Note:[/b] The [b]Lossless[/b], [b]Lossy[/b] or [b]Uncompressed[/b] compression modes are recommended. The [b]Video RAM[/b] compression mode can be used, but it will be decompressed on the CPU, which means loading times are slowed down and no memory is saved compared to lossless modes. diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index c957dea32d4..528c688a9ce 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -990,36 +990,9 @@ void DisplayServerWayland::cursor_set_custom_image(const Ref &p_cursor wayland_thread.cursor_shape_clear_custom_image(p_shape); } - Ref texture = p_cursor; - ERR_FAIL_COND(!texture.is_valid()); - Size2i texture_size; - - Ref atlas_texture = texture; - - if (atlas_texture.is_valid()) { - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - - // NOTE: The Wayland protocol says nothing about cursor size limits, yet if - // the texture is larger than 256x256 it won't show at least on sway. - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - ERR_FAIL_COND(texture_size.height == 0 || texture_size.width == 0); - - Ref image = texture->get_image(); - ERR_FAIL_COND(!image.is_valid()); - - if (image->is_compressed()) { - image = image->duplicate(true); - Error err = image->decompress(); - ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); - } + Rect2 atlas_rect; + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + ERR_FAIL_COND(image.is_null()); CustomCursor &cursor = custom_cursors[p_shape]; diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index e42967eb7b5..5b8db1be47a 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -59,8 +59,6 @@ #include "core/config/project_settings.h" #include "core/input/input.h" -#include "scene/resources/atlas_texture.h" -#include "scene/resources/texture.h" #include "servers/display_server.h" #include diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 35bfe81827a..d58b5b93d78 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -41,7 +41,6 @@ #include "core/string/ustring.h" #include "drivers/png/png_driver_common.h" #include "main/main.h" -#include "scene/resources/atlas_texture.h" #if defined(VULKAN_ENABLED) #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" @@ -3064,39 +3063,10 @@ void DisplayServerX11::cursor_set_custom_image(const Ref &p_cursor, Cu cursors_cache.erase(p_shape); } - Ref texture = p_cursor; - ERR_FAIL_COND(!texture.is_valid()); - Ref atlas_texture = p_cursor; - Size2i texture_size; - Rect2i atlas_rect; - - if (atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - - Ref image = texture->get_image(); - - ERR_FAIL_COND(!image.is_valid()); - if (image->is_compressed()) { - image = image->duplicate(true); - Error err = image->decompress(); - ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); - } + Rect2 atlas_rect; + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + ERR_FAIL_COND(image.is_null()); + Vector2i texture_size = image->get_size(); // Create the cursor structure XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height); @@ -3115,7 +3085,7 @@ void DisplayServerX11::cursor_set_custom_image(const Ref &p_cursor, Cu int row_index = floor(index / texture_size.width) + atlas_rect.position.y; int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; - if (atlas_texture.is_valid()) { + if (atlas_rect.has_area()) { column_index = MIN(column_index, atlas_rect.size.width - 1); row_index = MIN(row_index, atlas_rect.size.height - 1); } diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 7ec2d0f65fb..d2e41799878 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -48,7 +48,6 @@ #include "core/os/keyboard.h" #include "drivers/png/png_driver_common.h" #include "main/main.h" -#include "scene/resources/atlas_texture.h" #include "scene/resources/image_texture.h" #if defined(GLES3_ENABLED) @@ -4037,39 +4036,10 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref &p_cursor, cursors_cache.erase(p_shape); } - Ref texture = p_cursor; - ERR_FAIL_COND(!texture.is_valid()); - Ref atlas_texture = p_cursor; - Size2 texture_size; Rect2 atlas_rect; - - if (atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - - Ref image = texture->get_image(); - - ERR_FAIL_COND(!image.is_valid()); - if (image->is_compressed()) { - image = image->duplicate(true); - Error err = image->decompress(); - ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); - } + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + ERR_FAIL_COND(image.is_null()); + Vector2i texture_size = image->get_size(); NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nullptr @@ -4092,7 +4062,7 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref &p_cursor, int row_index = floor(i / texture_size.width) + atlas_rect.position.y; int column_index = (i % int(texture_size.width)) + atlas_rect.position.x; - if (atlas_texture.is_valid()) { + if (atlas_rect.has_area()) { column_index = MIN(column_index, atlas_rect.size.width - 1); row_index = MIN(row_index, atlas_rect.size.height - 1); } diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index bc4c0d22f0f..2e3afc49ca7 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -36,7 +36,6 @@ #include "core/config/project_settings.h" #include "core/object/callable_method_pointer.h" -#include "scene/resources/atlas_texture.h" #include "servers/rendering/dummy/rasterizer_dummy.h" #ifdef GLES3_ENABLED @@ -511,43 +510,12 @@ DisplayServer::CursorShape DisplayServerWeb::cursor_get_shape() const { void DisplayServerWeb::cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); if (p_cursor.is_valid()) { - Ref texture = p_cursor; - ERR_FAIL_COND(!texture.is_valid()); - Ref atlas_texture = p_cursor; - Size2 texture_size; Rect2 atlas_rect; + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + ERR_FAIL_COND(image.is_null()); + Vector2i texture_size = image->get_size(); - if (atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - - Ref image = texture->get_image(); - - ERR_FAIL_COND(!image.is_valid()); - - image = image->duplicate(true); - - if (image->is_compressed()) { - Error err = image->decompress(); - ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); - } - - if (atlas_texture.is_valid()) { + if (atlas_rect.has_area()) { image->crop_from_point( atlas_rect.position.x, atlas_rect.position.y, diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 3162bb3fb9a..ced8dce65a7 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -38,7 +38,6 @@ #include "core/version.h" #include "drivers/png/png_driver_common.h" #include "main/main.h" -#include "scene/resources/atlas_texture.h" #if defined(VULKAN_ENABLED) #include "rendering_context_driver_vulkan_windows.h" @@ -2393,38 +2392,10 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref &p_cursor cursors_cache.erase(p_shape); } - Ref texture = p_cursor; - ERR_FAIL_COND(!texture.is_valid()); - Ref atlas_texture = p_cursor; - Size2 texture_size; Rect2 atlas_rect; - - if (atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - - Ref image = texture->get_image(); - - ERR_FAIL_COND(!image.is_valid()); - if (image->is_compressed()) { - image = image->duplicate(true); - Error err = image->decompress(); - ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); - } + Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect); + ERR_FAIL_COND(image.is_null()); + Vector2i texture_size = image->get_size(); UINT image_size = texture_size.width * texture_size.height; @@ -2453,7 +2424,7 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref &p_cursor int row_index = floor(index / texture_size.width) + atlas_rect.position.y; int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; - if (atlas_texture.is_valid()) { + if (atlas_rect.has_area()) { column_index = MIN(column_index, atlas_rect.size.width - 1); row_index = MIN(row_index, atlas_rect.size.height - 1); } diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 6631d44f639..199152b5e89 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -31,6 +31,7 @@ #include "display_server.h" #include "core/input/input.h" +#include "scene/resources/atlas_texture.h" #include "scene/resources/texture.h" #include "servers/display_server_headless.h" @@ -987,6 +988,43 @@ void DisplayServer::_bind_methods() { BIND_ENUM_CONSTANT(TTS_UTTERANCE_BOUNDARY); } +Ref DisplayServer::_get_cursor_image_from_resource(const Ref &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect) { + Ref image; + ERR_FAIL_COND_V_MSG(p_hotspot.x < 0 || p_hotspot.y < 0, image, "Hotspot outside cursor image."); + + Size2 texture_size; + + Ref texture = p_cursor; + if (texture.is_valid()) { + Ref atlas_texture = p_cursor; + + if (atlas_texture.is_valid()) { + texture = atlas_texture->get_atlas(); + r_atlas_rect.size = texture->get_size(); + r_atlas_rect.position = atlas_texture->get_region().position; + texture_size = atlas_texture->get_region().size; + } else { + texture_size = texture->get_size(); + } + image = texture->get_image(); + } else { + image = p_cursor; + ERR_FAIL_COND_V(image.is_null(), image); + texture_size = image->get_size(); + } + + ERR_FAIL_COND_V_MSG(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height, image, "Hotspot outside cursor image."); + ERR_FAIL_COND_V_MSG(texture_size.width > 256 || texture_size.height > 256, image, "Cursor image too big. Max supported size is 256x256."); + + ERR_FAIL_COND_V(image.is_null(), image); + if (image->is_compressed()) { + image = image->duplicate(true); + Error err = image->decompress(); + ERR_FAIL_COND_V_MSG(err != OK, Ref(), "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); + } + return image; +} + void DisplayServer::register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers) { ERR_FAIL_COND(server_create_count == MAX_SERVERS); // Headless display server is always last diff --git a/servers/display_server.h b/servers/display_server.h index 0f6c43ebe03..7608d76b304 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -37,6 +37,7 @@ #include "core/variant/callable.h" class Texture2D; +class Image; class DisplayServer : public Object { GDCLASS(DisplayServer, Object) @@ -86,6 +87,8 @@ private: protected: static void _bind_methods(); + static Ref _get_cursor_image_from_resource(const Ref &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect); + enum { MAX_SERVERS = 64 };