Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of drawing methods #2080

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
248 changes: 211 additions & 37 deletions src_c/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,7 @@ static void
draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2,
int y2, int width, int *drawn_area)
{
int dx, dy, err, e2, sx, sy, start_draw, end_draw;
int dx, dy, err, e2, sx, sy, start_draw, end_draw, diff, exit, end;
int end_x = surf->clip_rect.x + surf->clip_rect.w - 1;
int end_y = surf->clip_rect.y + surf->clip_rect.h - 1;
int xinc = 0;
Expand Down Expand Up @@ -1540,13 +1540,41 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2,
}
// Bresenham's line algorithm
dx = abs(x2 - x1);
dy = abs(y2 - y1);
dy = -abs(y2 - y1);
sx = x2 > x1 ? 1 : -1;
sy = y2 > y1 ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
err = dx + dy;
// If line is more vertical than horizontal
if (xinc) {
while (y1 != (y2 + sy)) {
if (surf->clip_rect.y <= y1 && y1 <= end_y) {
end = y2 + sy;
// Set exit to y value of where line will leave surface
// Set diff to difference between starting y coordinate and the y value
// of the line's entry point to the surface
if (y2 > y1) {
exit = end_y + 1;
diff = surf->clip_rect.y - y1;
}
else {
exit = surf->clip_rect.y - 1;
diff = y1 - end_y;
}
// If line starts outside of surface
if (diff > 0) {
// Set y1 to entry y point
y1 += diff * sy;
// Adjust err by dx for the change in the y axis
err += diff * dx;
// Calculate change in x value (x = y/m), uses ceil for consistency
// between positive/negative values
diff = (int)ceil(((float)diff * dx) / (float)-dy);
x1 += diff * sx;
// Adjust err value to correct for change in x axis
err += diff * dy;
}

// Continue through normal Bresenham's line algorithm iteration
while (y1 != end) {
if (y1 != exit) {
start_draw =
MAX((x1 - width) + extra_width, surf->clip_rect.x);
end_draw = MIN(end_x, x1 + width);
Expand All @@ -1556,20 +1584,49 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2,
drawn_area);
}
}
e2 = err;
if (e2 > -dx) {
err -= dy;
else
break;
e2 = err * 2;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 < dy) {
if (e2 <= dx) {
err += dx;
y1 += sy;
}
}
}
else {
while (x1 != (x2 + sx)) {
if (surf->clip_rect.x <= x1 && x1 <= end_x) {
end = x2 + sx;
// Set exit to x value of where line will leave surface
// Set diff to difference between starting x coordinate and the x value
// of the line's entry point to the surface
if (x2 > x1) {
diff = surf->clip_rect.x - x1;
exit = end_x + 1;
}
else {
diff = x1 - end_x;
exit = surf->clip_rect.x - 1;
}
// If line starts outside of surface
if (diff > 0) {
// Set x1 to entry x point
x1 += diff * sx;
// Adjust err by dy for the change in the x axis
err += diff * dy;
// Calculate change in y value (y = mx), uses ceil for consistency
// between positive/negative values
diff = (int)ceil(((float)diff * -dy) / (float)dx);
y1 += diff * sy;
// Adjust err value to correct for change in y axis
err += diff * dx;
}

// Continue through normal Bresenham's line algorithm iteration
while (x1 != end) {
if (x1 != exit) {
start_draw =
MAX((y1 - width) + extra_width, surf->clip_rect.y);
end_draw = MIN(end_y, y1 + width);
Expand All @@ -1579,12 +1636,14 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2,
drawn_area);
}
}
e2 = err;
if (e2 > -dx) {
err -= dy;
else
break;
e2 = err * 2;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 < dy) {
if (e2 <= dx) {
err += dx;
y1 += sy;
}
Expand All @@ -1599,41 +1658,155 @@ static void
draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color,
int *drawn_area)
{
int dx, dy, err, e2, sx, sy;
int dx, dy, err, e2, sx, sy, diff, exit, end;
int xinc = 0;
if (x1 == x2 && y1 == y2) { /* Single point */
set_and_check_rect(surf, x1, y1, color, drawn_area);
return;
}
// Determine if the line is more vertical or horizontal for clipping
// purposes
if (abs(x1 - x2) <= abs(y1 - y2)) {
xinc = 1;
}

if (!clip_line(surf, &x1, &y1, &x2, &y2, 0, xinc))
return;

if (y1 == y2) { /* Horizontal line */
dx = (x1 < x2) ? 1 : -1;
for (sx = 0; sx <= abs(x1 - x2); sx++) {
set_and_check_rect(surf, x1 + dx * sx, y1, color, drawn_area);
if (x1 < x2) {
drawhorzline(surf, color, MAX(x1, surf->clip_rect.x), y1,
MIN(x2, surf->clip_rect.x + surf->clip_rect.w - 1));
add_line_to_drawn_list(
MAX(x1, surf->clip_rect.x), y1,
MIN(x2, surf->clip_rect.x + surf->clip_rect.w - 1), y1,
drawn_area);
}
else {
drawhorzline(surf, color, MAX(x2, surf->clip_rect.x), y1,
MIN(x1, surf->clip_rect.x + surf->clip_rect.w - 1));
add_line_to_drawn_list(
MAX(x2, surf->clip_rect.x), y1,
MIN(x1, surf->clip_rect.x + surf->clip_rect.w - 1), y1,
drawn_area);
}

return;
}
if (x1 == x2) { /* Vertical line */
dy = (y1 < y2) ? 1 : -1;
for (sy = 0; sy <= abs(y1 - y2); sy++)
set_and_check_rect(surf, x1, y1 + dy * sy, color, drawn_area);
if (y1 < y2) {
drawvertline(surf, color, MAX(y1, surf->clip_rect.y), x1,
MIN(y2, surf->clip_rect.y + surf->clip_rect.h - 1));
add_line_to_drawn_list(
x1, MAX(y1, surf->clip_rect.y), x1,
MIN(y2, surf->clip_rect.y + surf->clip_rect.h - 1),
drawn_area);
}
else {
drawvertline(surf, color, MAX(y2, surf->clip_rect.y), x1,
MIN(y1, surf->clip_rect.y + surf->clip_rect.h - 1));
add_line_to_drawn_list(
x1, MAX(y2, surf->clip_rect.y), x1,
MIN(y1, surf->clip_rect.y + surf->clip_rect.h - 1),
drawn_area);
}
return;
}
dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
dy = abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
while (x1 != x2 || y1 != y2) {
set_and_check_rect(surf, x1, y1, color, drawn_area);
e2 = err;
if (e2 > -dx) {
err -= dy;
x1 += sx;
dx = abs(x2 - x1);
dy = -abs(y2 - y1);
sx = x2 > x1 ? 1 : -1;
sy = y2 > y1 ? 1 : -1;
err = dx + dy;
// If line is more vertical than horizontal
if (xinc) {
end = y2 + sy;
// Set exit to y value of where line will leave surface
// Set diff to difference between starting y coordinate and the y value
// of the line's entry point to the surface
if (y2 > y1) {
exit = surf->clip_rect.y + surf->clip_rect.h;
diff = surf->clip_rect.y - y1;
}
else {
exit = surf->clip_rect.y - 1;
diff = y1 - (surf->clip_rect.y + surf->clip_rect.h - 1);
}
// If line starts outside of surface
if (diff > 0) {
// Set y1 to entry y point
y1 += diff * sy;
// Adjust err by dx for the change in the y axis
err += diff * dx;
// Calculate change in x value (x = y/m), uses ceil for consistency
// between positive/negative values
diff = (int)ceil(((float)diff * dx) / (float)-dy);
x1 += diff * sx;
// Adjust err value to correct for change in x axis
err += diff * dy;
}

// Continue through normal Bresenham's line algorithm iteration
while (y1 != end) {
if (y1 != exit) {
set_and_check_rect(surf, x1, y1, color, drawn_area);
}
else
break;
e2 = err * 2;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 <= dx) {
err += dx;
y1 += sy;
}
}
if (e2 < dy) {
err += dx;
y1 += sy;
}
else {
end = x2 + sx;
// Set exit to x value of where line will leave surface
// Set diff to difference between starting x coordinate and the x value
// of the line's entry point to the surface
if (x2 > x1) {
diff = surf->clip_rect.x - x1;
exit = surf->clip_rect.x + surf->clip_rect.w;
}
else {
diff = x1 - (surf->clip_rect.x + surf->clip_rect.w - 1);
exit = surf->clip_rect.x - 1;
}
// If line starts outside of surface
if (diff > 0) {
// Set x1 to entry x point
x1 += diff * sx;
// Adjust err by dy for the change in the x axis
err += diff * dy;
// Calculate change in y value (y = mx), uses ceil for consistency
// between positive/negative values
diff = (int)ceil(((float)diff * -dy) / (float)dx);
y1 += diff * sy;
// Adjust err value to correct for change in y axis
err += diff * dx;
}

// Continue through normal Bresenham's line algorithm iteration
while (x1 != end) {
if (x1 != exit) {
set_and_check_rect(surf, x1, y1, color, drawn_area);
}
else
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
while (x1 != end) {
if (x1 != exit) {
set_and_check_rect(surf, x1, y1, color, drawn_area);
}
else
break;
while (!(x1 == end || x1 == exit)) {
set_and_check_rect(surf, x1, y1, color, drawn_area);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't help with the speed at all, but is a little bit cleaner to read.

e2 = err * 2;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 <= dx) {
err += dx;
y1 += sy;
}
}
}
set_and_check_rect(surf, x2, y2, color, drawn_area);
}

static int
Expand Down Expand Up @@ -2633,7 +2806,8 @@ draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y,
* 3. each two x-coordinates in x_intersect are then inside the polygon
* (draw line for a pair of two such points)
*/
for (y = miny; (y <= maxy); y++) {
for (y = MAX(miny, surf->clip_rect.y);
(y <= MIN(maxy, surf->clip_rect.y + surf->clip_rect.h)); y++) {
// n_intersections is the number of intersections with the polygon
int n_intersections = 0;
for (i = 0; (i < num_points); i++) {
Expand Down