diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 878f64fae5..0622b90506 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -725,6 +725,29 @@ void RendererCanvasCull::canvas_item_set_update_when_visible(RID p_item, bool p_ canvas_item->update_when_visible = p_update; } +// Compensate the width added by the antialiasing feather by reducing the base line width. +// For line widths lower than or equal to 1.0, this is done with a multiplier, +// while line widths greater than 1.0 use a constant offset clamped to a width of 1.0. +// While the offset is not equal to `FEATHER_SIZE`, this is empirically determined +// to give a good result on various foreground/background colors. +// This method assumes the check for whether the line is antialiased +// has already been done. +float RendererCanvasCull::canvas_item_get_compensated_antialiasing_width(float p_width) const { + if (p_width > 0.0f) { + if (p_width <= (FEATHER_SIZE * 2.0f + CMP_EPSILON)) { + return p_width * 0.5f; + } else if (p_width <= (FEATHER_SIZE * 4.0f + CMP_EPSILON)) { + // Progressively lerp between the two methods (multiplier and offset). + return Math::remap(p_width, FEATHER_SIZE * 2.0f, FEATHER_SIZE * 4.0f, p_width * 0.5f, p_width - FEATHER_SIZE * 0.5f); + } else { + // Use a constant offset. + return p_width - FEATHER_SIZE * 0.5f; + } + } + + return p_width; +} + void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width, bool p_antialiased) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); @@ -734,6 +757,11 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, Vector2 diff = (p_from - p_to); Vector2 dir = diff.orthogonal().normalized(); + + if (p_antialiased) { + p_width = canvas_item_get_compensated_antialiasing_width(p_width); + } + Vector2 t = dir * p_width * 0.5; Vector2 begin_left; @@ -966,6 +994,10 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(pline); + if (p_antialiased) { + p_width = canvas_item_get_compensated_antialiasing_width(p_width); + } + if (p_width < 0) { if (p_antialiased) { WARN_PRINT("Antialiasing is not supported for thin polylines drawn using line strips (`p_width < 0`)."); @@ -1220,6 +1252,9 @@ void RendererCanvasCull::canvas_item_add_multiline(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(rect); rect->modulate = p_color; - rect->rect = p_rect; + rect->rect = rect_adjusted; // Add feathers. if (p_antialiased) { float border_size = FEATHER_SIZE; - const real_t size = MIN(p_rect.size.width, p_rect.size.height); + const real_t size = MIN(rect_adjusted.size.width, rect_adjusted.size.height); if (0.0f <= size && size < 1.0f) { border_size *= size; } - const Vector2 vec_down = Vector2(0.0f, p_rect.size.height); - const Vector2 vec_right = Vector2(p_rect.size.width, 0.0f); + const Vector2 vec_down = Vector2(0.0f, rect_adjusted.size.height); + const Vector2 vec_right = Vector2(rect_adjusted.size.width, 0.0f); - const Vector2 begin_left = p_rect.position; - const Vector2 begin_right = p_rect.position + vec_down; - const Vector2 end_left = p_rect.position + vec_right; - const Vector2 end_right = p_rect.position + p_rect.size; + const Vector2 begin_left = rect_adjusted.position; + const Vector2 begin_right = rect_adjusted.position + vec_down; + const Vector2 end_left = rect_adjusted.position + vec_right; + const Vector2 end_right = rect_adjusted.position + rect_adjusted.size; const Vector2 dir = Vector2(0.0f, -1.0f); const Vector2 dir2 = Vector2(-1.0f, 0.0f); @@ -1436,6 +1474,14 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos static const int ellipse_segments = 64; + float major = p_major; + float minor = p_minor; + if (p_antialiased) { + // Adjust the diameter to account for the antialiasing width. + major = MAX(0.0f, major - FEATHER_SIZE * 0.25f); + minor = MAX(0.0f, minor - FEATHER_SIZE * 0.25f); + } + { Item::CommandPolygon *ellipse = canvas_item->alloc_command(); ERR_FAIL_NULL(ellipse); @@ -1455,8 +1501,8 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos for (int i = 0; i < ellipse_segments + 1; i++) { float angle = i * ellipse_point_step; - points_ptr[i].x = Math::cos(angle) * p_major; - points_ptr[i].y = Math::sin(angle) * p_minor; + points_ptr[i].x = Math::cos(angle) * major; + points_ptr[i].y = Math::sin(angle) * minor; points_ptr[i] += p_pos; } @@ -1477,7 +1523,7 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos if (p_antialiased) { float border_size = FEATHER_SIZE; - const float max_axis = fmax(p_major, p_minor) * 2.0f; + const float max_axis = fmax(major, minor) * 2.0f; if (0.0f <= max_axis && max_axis < 1.0f) { border_size *= max_axis * 0.5f; } @@ -1505,12 +1551,12 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos const float c = Math::cos(angle); const float s = Math::sin(angle); - points_ptr[i * 2].x = c * p_major; - points_ptr[i * 2].y = s * p_minor; + points_ptr[i * 2].x = c * major; + points_ptr[i * 2].y = s * minor; points_ptr[i * 2] += p_pos; - points_ptr[i * 2 + 1].x = c * (p_major + border_size); - points_ptr[i * 2 + 1].y = s * (p_minor + border_size); + points_ptr[i * 2 + 1].x = c * (major + border_size); + points_ptr[i * 2 + 1].y = s * (minor + border_size); points_ptr[i * 2 + 1] += p_pos; colors_ptr[i * 2] = p_color; diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index f2ff85dd59..de9b929060 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -257,6 +257,8 @@ public: void canvas_item_set_update_when_visible(RID p_item, bool p_update); + float canvas_item_get_compensated_antialiasing_width(float p_width) const; + void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = -1.0, bool p_antialiased = false); void canvas_item_add_polyline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = -1.0, bool p_antialiased = false); void canvas_item_add_multiline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = -1.0, bool p_antialiased = false);