diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 186ee1b9c4a..6e9d51d6e41 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -604,6 +604,9 @@ If [code]true[/code], the object draws behind its parent. + + The snapping to pixel mode to use on this [CanvasItem]. + The texture filtering mode to use on this [CanvasItem]. @@ -736,5 +739,17 @@ Represents the size of the [enum ClipChildrenMode] enum. + + The [CanvasItem] will inherit the snapping mode from its parent. + + + The [CanvasItem] will not snap. + + + The [CanvasItem] will snap. + + + Represents the size of the [enum SnapToPixel] enum. + diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index c0386b056f7..ce372cf13b7 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -328,6 +328,7 @@ void CanvasItem::_notification(int p_what) { _update_texture_filter_changed(false); _update_texture_repeat_changed(false); + _update_snap_to_pixel_changed(false); if (!block_transform_notify && !xform_change.in_list()) { get_tree()->xform_change_list.add(&xform_change); @@ -1270,6 +1271,9 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode); ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode); + ClassDB::bind_method(D_METHOD("set_snap_to_pixel", "snap"), &CanvasItem::set_snap_to_pixel); + ClassDB::bind_method(D_METHOD("get_snap_to_pixel"), &CanvasItem::get_snap_to_pixel); + GDVIRTUAL_BIND(_draw); ADD_GROUP("Visibility", ""); @@ -1294,6 +1298,10 @@ void CanvasItem::_bind_methods() { ADD_GROUP("Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); + + ADD_GROUP("Snap", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_to_pixel", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled"), "set_snap_to_pixel", "get_snap_to_pixel"); + // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); ADD_SIGNAL(MethodInfo("draw")); @@ -1328,6 +1336,11 @@ void CanvasItem::_bind_methods() { BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY); BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW); BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX); + + BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_PARENT_NODE); + BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_DISABLED); + BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_ENABLED); + BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_MAX); } Transform2D CanvasItem::get_canvas_transform() const { @@ -1565,6 +1578,62 @@ CanvasItem::TextureRepeat CanvasItem::get_texture_repeat_in_tree() const { return (TextureRepeat)texture_repeat_cache; } +void CanvasItem::_refresh_snap_to_pixel_cache() const { + if (!is_inside_tree()) { + return; + } + + if (snap_to_pixel == SNAP_TO_PIXEL_PARENT_NODE) { + CanvasItem *parent_item = get_parent_item(); + if (parent_item) { + snap_to_pixel_cache = parent_item->snap_to_pixel_cache; + } else { + snap_to_pixel_cache = RS::CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT; + } + } else { + snap_to_pixel_cache = RS::CanvasItemSnapToPixel(snap_to_pixel); + } +} + +void CanvasItem::_update_self_snap_to_pixel(RS::CanvasItemSnapToPixel p_snap) { + RS::get_singleton()->canvas_item_set_snap_to_pixel(get_canvas_item(), p_snap); + queue_redraw(); +} + +void CanvasItem::_update_snap_to_pixel_changed(bool p_propagate) { + if (!is_inside_tree()) { + return; + } + _refresh_snap_to_pixel_cache(); + _update_self_snap_to_pixel(snap_to_pixel_cache); + + if (p_propagate) { + for (CanvasItem *E : children_items) { + if (!E->top_level && E->snap_to_pixel == SNAP_TO_PIXEL_PARENT_NODE) { + E->_update_snap_to_pixel_changed(true); + } + } + } +} + +void CanvasItem::set_snap_to_pixel(SnapToPixel p_snap_to_pixel) { + ERR_THREAD_GUARD; + ERR_FAIL_COND(p_snap_to_pixel >= SNAP_TO_PIXEL_MAX); + + if (snap_to_pixel == p_snap_to_pixel) { + return; + } + + snap_to_pixel = p_snap_to_pixel; + _update_snap_to_pixel_changed(true); + notify_property_list_changed(); +} + +CanvasItem::SnapToPixel CanvasItem::get_snap_to_pixel() const { + ERR_READ_THREAD_GUARD_V(SNAP_TO_PIXEL_DISABLED); + return snap_to_pixel; +} + CanvasItem::CanvasItem() : xform_change(this) { canvas_item = RenderingServer::get_singleton()->canvas_item_create(); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 028c2cb2cfa..4bf169d127a 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -73,6 +73,13 @@ public: CLIP_CHILDREN_MAX, }; + enum SnapToPixel { + SNAP_TO_PIXEL_PARENT_NODE, + SNAP_TO_PIXEL_DISABLED, + SNAP_TO_PIXEL_ENABLED, + SNAP_TO_PIXEL_MAX, + }; + private: mutable SelfList xform_change; @@ -112,8 +119,10 @@ private: mutable RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; mutable RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + mutable RS::CanvasItemSnapToPixel snap_to_pixel_cache = RS::CANVAS_ITEM_SNAP_TO_PIXEL_DISABLED; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE; + SnapToPixel snap_to_pixel = SNAP_TO_PIXEL_PARENT_NODE; Ref material; @@ -148,12 +157,15 @@ private: void _update_texture_repeat_changed(bool p_propagate); void _refresh_texture_filter_cache() const; void _update_texture_filter_changed(bool p_propagate); + void _refresh_snap_to_pixel_cache() const; + void _update_snap_to_pixel_changed(bool p_propagate); void _notify_transform_deferred(); protected: virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat); virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter); + virtual void _update_self_snap_to_pixel(RS::CanvasItemSnapToPixel p_snap); _FORCE_INLINE_ void _notify_transform() { _notify_transform(this); @@ -380,6 +392,9 @@ public: int get_canvas_layer() const; CanvasLayer *get_canvas_layer_node() const; + virtual void set_snap_to_pixel(SnapToPixel p_snap_to_pixel); + SnapToPixel get_snap_to_pixel() const; + CanvasItem(); ~CanvasItem(); }; @@ -387,6 +402,7 @@ public: VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode) +VARIANT_ENUM_CAST(CanvasItem::SnapToPixel) class CanvasTexture : public Texture2D { GDCLASS(CanvasTexture, Texture2D); diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 2a134c6e878..cef9d3ad299 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -285,7 +285,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 rect.position -= repeat_size / scale * (repeat_times / 2); } - if (snapping_2d_transforms_to_pixel) { + if ((p_canvas_item->snap_to_pixel == RS::CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT && snapping_2d_transforms_to_pixel) || (p_canvas_item->snap_to_pixel == RS::CANVAS_ITEM_SNAP_TO_PIXEL_ENABLED)) { Size2 scale = final_xform.get_scale().snappedf(0.0001); Point2 abs_scaled_position = (rect.position * scale).abs(); Point2 odd_sized_correction = Point2( @@ -2327,6 +2327,12 @@ void RendererCanvasCull::canvas_item_set_default_texture_repeat(RID p_item, RS:: ci->texture_repeat = p_repeat; } +void RendererCanvasCull::canvas_item_set_snap_to_pixel(RID p_item, RS::CanvasItemSnapToPixel p_snap) { + Item *ci = canvas_item_owner.get_or_null(p_item); + ERR_FAIL_NULL(ci); + ci->snap_to_pixel = p_snap; +} + void RendererCanvasCull::update_visibility_notifiers() { SelfList *E = visibility_notifier_list.first(); while (E) { diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 9f8cbea2e95..edcba0c2cbb 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -339,6 +339,7 @@ public: void canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter); void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat); + void canvas_item_set_snap_to_pixel(RID p_item, RS::CanvasItemSnapToPixel p_snap); void update_visibility_notifiers(); diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index cb8180f9895..2c0d9bd6328 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -458,6 +458,7 @@ public: RS::CanvasItemTextureFilter texture_filter; RS::CanvasItemTextureRepeat texture_repeat; + RS::CanvasItemSnapToPixel snap_to_pixel; Item() { commands = nullptr; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 164ec3cc09f..3e28839bd05 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -883,6 +883,8 @@ public: FUNC2(canvas_item_set_draw_behind_parent, RID, bool) + FUNC2(canvas_item_set_snap_to_pixel, RID, CanvasItemSnapToPixel) + FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) FUNC5(canvas_item_add_polyline, RID, const Vector &, const Vector &, float, bool) FUNC5(canvas_item_add_multiline, RID, const Vector &, const Vector &, float, bool) diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 693c8224885..d9ee94d942c 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -860,6 +860,13 @@ public: CANVAS_ITEM_TEXTURE_REPEAT_MAX, }; + enum CanvasItemSnapToPixel { + CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT, + CANVAS_ITEM_SNAP_TO_PIXEL_DISABLED, + CANVAS_ITEM_SNAP_TO_PIXEL_ENABLED, + CANVAS_ITEM_SNAP_TO_PIXEL_MAX, + }; + virtual RID viewport_create() = 0; enum ViewportScaling3DMode { @@ -1451,6 +1458,8 @@ public: virtual void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable) = 0; + virtual void canvas_item_set_snap_to_pixel(RID p_item, CanvasItemSnapToPixel p_snap) = 0; + enum NinePatchAxisMode { NINE_PATCH_STRETCH, NINE_PATCH_TILE,