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 c4286dcc0c6..e37d43771a8 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -283,7 +283,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)) {
final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor();
parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor();
}
@@ -2311,6 +2311,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,