From 13902cacec66a146740a5a6f56dd899e8cd8ffb7 Mon Sep 17 00:00:00 2001 From: MewPurPur Date: Fri, 12 Jul 2024 01:52:10 +0300 Subject: [PATCH] Optimize StyleBoxFlat.draw() --- scene/resources/style_box_flat.cpp | 147 ++++++++++++++--------------- 1 file changed, 70 insertions(+), 77 deletions(-) diff --git a/scene/resources/style_box_flat.cpp b/scene/resources/style_box_flat.cpp index 52d02e92cb45..7ce9324d69d9 100644 --- a/scene/resources/style_box_flat.cpp +++ b/scene/resources/style_box_flat.cpp @@ -226,33 +226,16 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re real_t border_right = style_rect.size.width - inner_rect.size.width - border_left; real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top; - real_t rad; - - // Top left. - rad = MIN(border_top, border_left); - inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0); - - // Top right; - rad = MIN(border_top, border_right); - inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0); - - // Bottom right. - rad = MIN(border_bottom, border_right); - inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0); - - // Bottom left. - rad = MIN(border_bottom, border_left); - inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0); + inner_corner_radius[0] = MAX(corner_radius[0] - MIN(border_top, border_left), 0); // Top left. + inner_corner_radius[1] = MAX(corner_radius[1] - MIN(border_top, border_right), 0); // Top right. + inner_corner_radius[2] = MAX(corner_radius[2] - MIN(border_bottom, border_right), 0); // Bottom right. + inner_corner_radius[3] = MAX(corner_radius[3] - MIN(border_bottom, border_left), 0); // Bottom left. } inline void draw_rounded_rectangle(Vector &verts, Vector &indices, Vector &colors, const Rect2 &style_rect, const real_t corner_radius[4], const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) { int vert_offset = verts.size(); - if (!vert_offset) { - vert_offset = 0; - } - - int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; + int adapted_corner_detail = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0) ? corner_detail : 1; bool draw_border = !is_filled; @@ -280,30 +263,44 @@ inline void draw_rounded_rectangle(Vector &verts, Vector &indices, // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this // method either draw a ring or a filled rounded rectangle, but not both. - int max_inner_outer = draw_border ? 2 : 1; - - for (int corner_index = 0; corner_index < 4; corner_index++) { + real_t quarter_arc_rad = Math_PI / 2.0; + Point2 ring_rect_center = ring_rect.get_center(); + + int colors_size = colors.size(); + int verts_size = verts.size(); + int new_verts_amount = (adapted_corner_detail + 1) * (draw_border ? 8 : 4); + colors.resize(colors_size + new_verts_amount); + verts.resize(verts_size + new_verts_amount); + Color *colors_ptr = colors.ptrw(); + Vector2 *verts_ptr = verts.ptrw(); + + for (int corner_idx = 0; corner_idx < 4; corner_idx++) { for (int detail = 0; detail <= adapted_corner_detail; detail++) { - for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) { - real_t radius; - Color color; - Point2 corner_point; - if (inner_outer == 0) { - radius = inner_corner_radius[corner_index]; - color = inner_color; - corner_point = inner_points[corner_index]; - } else { - radius = ring_corner_radius[corner_index]; - color = outer_color; - corner_point = outer_points[corner_index]; - } + int idx_ofs = (adapted_corner_detail + 1) * corner_idx + detail; + if (draw_border) { + idx_ofs *= 2; + } - const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; - const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; - const float x_skew = -skew.x * (y - ring_rect.get_center().y); - const float y_skew = -skew.y * (x - ring_rect.get_center().x); - verts.push_back(Vector2(x + x_skew, y + y_skew)); - colors.push_back(color); + const real_t pt_angle = (corner_idx + detail / (double)adapted_corner_detail) * quarter_arc_rad + Math_PI; + const real_t angle_cosine = cos(pt_angle); + const real_t angle_sine = sin(pt_angle); + + { + const real_t x = inner_corner_radius[corner_idx] * angle_cosine + inner_points[corner_idx].x; + const real_t y = inner_corner_radius[corner_idx] * angle_sine + inner_points[corner_idx].y; + const float x_skew = -skew.x * (y - ring_rect_center.y); + const float y_skew = -skew.y * (x - ring_rect_center.x); + verts_ptr[verts_size + idx_ofs] = Vector2(x + x_skew, y + y_skew); + colors_ptr[colors_size + idx_ofs] = inner_color; + } + + if (draw_border) { + const real_t x = ring_corner_radius[corner_idx] * angle_cosine + outer_points[corner_idx].x; + const real_t y = ring_corner_radius[corner_idx] * angle_sine + outer_points[corner_idx].y; + const float x_skew = -skew.x * (y - ring_rect_center.y); + const float y_skew = -skew.y * (x - ring_rect_center.x); + verts_ptr[verts_size + idx_ofs + 1] = Vector2(x + x_skew, y + y_skew); + colors_ptr[colors_size + idx_ofs + 1] = outer_color; } } } @@ -313,10 +310,15 @@ inline void draw_rounded_rectangle(Vector &verts, Vector &indices, // Fill the indices and the colors for the border. if (draw_border) { + int indices_size = indices.size(); + indices.resize(indices_size + ring_vert_count * 3); + int *indices_ptr = indices.ptrw(); + for (int i = 0; i < ring_vert_count; i++) { - indices.push_back(vert_offset + ((i + 0) % ring_vert_count)); - indices.push_back(vert_offset + ((i + 2) % ring_vert_count)); - indices.push_back(vert_offset + ((i + 1) % ring_vert_count)); + int idx_ofs = indices_size + i * 3; + indices_ptr[idx_ofs] = vert_offset + i % ring_vert_count; + indices_ptr[idx_ofs + 1] = vert_offset + (i + 2) % ring_vert_count; + indices_ptr[idx_ofs + 2] = vert_offset + (i + 1) % ring_vert_count; } } @@ -327,40 +329,30 @@ inline void draw_rounded_rectangle(Vector &verts, Vector &indices, int stripes_count = ring_vert_count / 2 - 1; int last_vert_id = ring_vert_count - 1; + int indices_size = indices.size(); + indices.resize(indices_size + stripes_count * 6); + int *indices_ptr = indices.ptrw(); + for (int i = 0; i < stripes_count; i++) { + int idx_ofs = indices_size + i * 6; // Polygon 1. - indices.push_back(vert_offset + i); - indices.push_back(vert_offset + last_vert_id - i - 1); - indices.push_back(vert_offset + i + 1); + indices_ptr[idx_ofs] = vert_offset + i; + indices_ptr[idx_ofs + 1] = vert_offset + last_vert_id - i - 1; + indices_ptr[idx_ofs + 2] = vert_offset + i + 1; // Polygon 2. - indices.push_back(vert_offset + i); - indices.push_back(vert_offset + last_vert_id - 0 - i); - indices.push_back(vert_offset + last_vert_id - 1 - i); + indices_ptr[idx_ofs + 3] = vert_offset + i; + indices_ptr[idx_ofs + 4] = vert_offset + last_vert_id - i; + indices_ptr[idx_ofs + 5] = vert_offset + last_vert_id - i - 1; } } } inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) { - if (p_values[p_index_a] + p_values[p_index_b] > p_width) { - real_t factor; - real_t new_value; - - factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]); - - new_value = (p_values[p_index_a] * factor); - if (new_value < adapted_values[p_index_a]) { - adapted_values[p_index_a] = new_value; - } - new_value = (p_values[p_index_b] * factor); - if (new_value < adapted_values[p_index_b]) { - adapted_values[p_index_b] = new_value; - } - } else { - adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]); - adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]); - } - adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]); - adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]); + real_t value_a = p_values[p_index_a]; + real_t value_b = p_values[p_index_b]; + real_t factor = MIN(1.0, p_width / (value_a + value_b)); + adapted_values[p_index_a] = MIN(MIN(value_a * factor, p_max_a), adapted_values[p_index_a]); + adapted_values[p_index_b] = MIN(MIN(value_b * factor, p_max_b), adapted_values[p_index_b]); } Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const { @@ -388,7 +380,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); - // Only enable antialiasing if it is actually needed. This improve performances + // Only enable antialiasing if it is actually needed. This improves performance // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; @@ -428,7 +420,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { Vector colors; Vector uvs; - // Create shadow + // Create shadow. if (draw_shadow) { Rect2 shadow_inner_rect = style_rect; shadow_inner_rect.position += shadow_offset; @@ -538,9 +530,10 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { // Compute UV coordinates. Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0); uvs.resize(verts.size()); + Point2 *uvs_ptr = uvs.ptrw(); for (int i = 0; i < verts.size(); i++) { - uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; - uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; + uvs_ptr[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; + uvs_ptr[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; } // Draw stylebox.