Merge pull request #88970 from KoBeWi/ImageCursor2D
Improve `cursor_set_custom_image()` method
This commit is contained in:
commit
6d9a529c87
10 changed files with 63 additions and 171 deletions
|
@ -78,6 +78,7 @@
|
|||
<param index="2" name="hotspot" type="Vector2" default="Vector2(0, 0)" />
|
||||
<description>
|
||||
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].
|
||||
</description>
|
||||
</method>
|
||||
<method name="cursor_set_shape">
|
||||
|
|
|
@ -325,7 +325,7 @@
|
|||
<param index="2" name="hotspot" type="Vector2" default="Vector2(0, 0)" />
|
||||
<description>
|
||||
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.
|
||||
|
|
|
@ -990,36 +990,9 @@ void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor
|
|||
wayland_thread.cursor_shape_clear_custom_image(p_shape);
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
ERR_FAIL_COND(!texture.is_valid());
|
||||
Size2i texture_size;
|
||||
|
||||
Ref<AtlasTexture> 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> 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> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
|
||||
ERR_FAIL_COND(image.is_null());
|
||||
|
||||
CustomCursor &cursor = custom_cursors[p_shape];
|
||||
|
||||
|
|
|
@ -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 <limits.h>
|
||||
|
|
|
@ -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<Resource> &p_cursor, Cu
|
|||
cursors_cache.erase(p_shape);
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
ERR_FAIL_COND(!texture.is_valid());
|
||||
Ref<AtlasTexture> 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> 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> 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<Resource> &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);
|
||||
}
|
||||
|
|
|
@ -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<Resource> &p_cursor,
|
|||
cursors_cache.erase(p_shape);
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
ERR_FAIL_COND(!texture.is_valid());
|
||||
Ref<AtlasTexture> 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> 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> 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<Resource> &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);
|
||||
}
|
||||
|
|
|
@ -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<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
|
||||
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
|
||||
if (p_cursor.is_valid()) {
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
ERR_FAIL_COND(!texture.is_valid());
|
||||
Ref<AtlasTexture> atlas_texture = p_cursor;
|
||||
Size2 texture_size;
|
||||
Rect2 atlas_rect;
|
||||
Ref<Image> 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> 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,
|
||||
|
|
|
@ -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<Resource> &p_cursor
|
|||
cursors_cache.erase(p_shape);
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
ERR_FAIL_COND(!texture.is_valid());
|
||||
Ref<AtlasTexture> 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> 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> 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<Resource> &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);
|
||||
}
|
||||
|
|
|
@ -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<Image> DisplayServer::_get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect) {
|
||||
Ref<Image> image;
|
||||
ERR_FAIL_COND_V_MSG(p_hotspot.x < 0 || p_hotspot.y < 0, image, "Hotspot outside cursor image.");
|
||||
|
||||
Size2 texture_size;
|
||||
|
||||
Ref<Texture2D> texture = p_cursor;
|
||||
if (texture.is_valid()) {
|
||||
Ref<AtlasTexture> 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<Image>(), "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
|
||||
|
|
|
@ -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<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect);
|
||||
|
||||
enum {
|
||||
MAX_SERVERS = 64
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue