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

Fix aalines overlap #2912

Merged
merged 6 commits into from
Oct 3, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 181 additions & 53 deletions src_c/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color,
int *drawn_area);
static void
draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty,
float endx, float endy, int *drawn_area);
float endx, float endy, int *drawn_area,
int disable_first_endpoint, int disable_second_endpoint,
int extra_pixel_for_aalines);
static void
draw_arc(SDL_Surface *surf, int x_center, int y_center, int radius1,
int radius2, int width, double angle_start, double angle_stop,
Expand Down Expand Up @@ -159,7 +161,7 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs)
return RAISE(PyExc_RuntimeError, "error locking surface");
}

draw_aaline(surf, color, startx, starty, endx, endy, drawn_area);
draw_aaline(surf, color, startx, starty, endx, endy, drawn_area, 0, 0, 0);

if (!pgSurface_Unlock(surfobj)) {
return RAISE(PyExc_RuntimeError, "error unlocking surface");
Expand Down Expand Up @@ -255,9 +257,13 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs)
SDL_Surface *surf = NULL;
Uint32 color;
float pts[4];
float pts_prev[4];
float *xlist, *ylist;
float x, y;
int l, t;
int extra_px;
int steep_prev;
int steep_curr;
PyObject *blend = NULL;
int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN,
INT_MIN}; /* Used to store bounding box values */
Expand Down Expand Up @@ -344,19 +350,80 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs)
return RAISE(PyExc_RuntimeError, "error locking surface");
}

for (loop = 1; loop < length; ++loop) {
/* first line - if open, add endpoint pixels.*/
pts[0] = xlist[0];
pts[1] = ylist[0];
pts[2] = xlist[1];
pts[3] = ylist[1];

/* Previous points.
* Used to compare previous and current line.*/
pts_prev[0] = pts[0];
pts_prev[1] = pts[1];
pts_prev[2] = pts[2];
pts_prev[3] = pts[3];
if (closed) {
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 1,
1, 0);
}
else {
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 0,
1, 0);
}

for (loop = 2; loop < length - 1; ++loop) {
pts[0] = xlist[loop - 1];
pts[1] = ylist[loop - 1];
pts[2] = xlist[loop];
pts[3] = ylist[loop];
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area);

/* Comparing previous and current line.
* If one is steep and other is not, extra pixel must be drawn.*/
steep_prev =
fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]);
steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]);
extra_px = steep_prev != steep_curr;
pts_prev[0] = pts[0];
pts_prev[1] = pts[1];
pts_prev[2] = pts[2];
pts_prev[3] = pts[3];
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 1,
1, extra_px);
}

/* Last line - if open, add endpoint pixels. */
pts[0] = xlist[length - 2];
pts[1] = ylist[length - 2];
pts[2] = xlist[length - 1];
pts[3] = ylist[length - 1];
steep_prev =
fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]);
steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]);
extra_px = steep_prev != steep_curr;
pts_prev[0] = pts[0];
pts_prev[1] = pts[1];
pts_prev[2] = pts[2];
pts_prev[3] = pts[3];
if (closed) {
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 1,
1, extra_px);
}
else {
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 1,
0, extra_px);
}

if (closed && length > 2) {
pts[0] = xlist[length - 1];
pts[1] = ylist[length - 1];
pts[2] = xlist[0];
pts[3] = ylist[0];
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area);
steep_prev =
fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]);
steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]);
extra_px = steep_prev != steep_curr;
draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 1,
1, extra_px);
}

PyMem_Free(xlist);
Expand Down Expand Up @@ -1256,13 +1323,16 @@ set_and_check_rect(SDL_Surface *surf, int x, int y, Uint32 color,

static void
draw_aaline(SDL_Surface *surf, Uint32 color, float from_x, float from_y,
float to_x, float to_y, int *drawn_area)
float to_x, float to_y, int *drawn_area,
int disable_first_endpoint, int disable_second_endpoint,
int extra_pixel_for_aalines)
{
float gradient, dx, dy, intersect_y, brightness;
int x, x_pixel_start, x_pixel_end;
Uint32 pixel_color;
float x_gap, y_endpoint, clip_left, clip_right, clip_top, clip_bottom;
int steep, y;
int line_inverted;

dx = to_x - from_x;
dy = to_y - from_y;
Expand Down Expand Up @@ -1295,6 +1365,7 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float from_x, float from_y,
swap(&clip_right, &clip_bottom);
}
if (dx < 0) {
line_inverted = 1;
swap(&from_x, &to_x);
swap(&from_y, &to_y);
dx = -dx;
Expand Down Expand Up @@ -1361,66 +1432,123 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float from_x, float from_y,
* The line is not a mathematical line of thickness zero. The same
* goes for the endpoints. The have a height and width of one pixel. */
/* First endpoint */
x_pixel_start = (int)from_x;
y_endpoint = intersect_y = from_y + gradient * (x_pixel_start - from_x);
if (to_x > clip_left + 1.0f) {
x_gap = 1 + x_pixel_start - from_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint;
y = x_pixel_start;
}
else {
x = x_pixel_start;
y = (int)y_endpoint;
}
if ((int)y_endpoint < y_endpoint) {
if (!disable_first_endpoint) {
x_pixel_start = (int)from_x;
y_endpoint = intersect_y =
from_y + gradient * (x_pixel_start - from_x);
if (to_x > clip_left + 1.0f) {
x_gap = 1 + x_pixel_start - from_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint;
y = x_pixel_start;
}
else {
x = x_pixel_start;
y = (int)y_endpoint;
}
if ((int)y_endpoint < y_endpoint) {
pixel_color = get_antialiased_color(surf, x, y, color,
brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}
if (steep) {
x--;
}
else {
y--;
}
brightness = 1 - brightness;
pixel_color =
get_antialiased_color(surf, x, y, color, brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
intersect_y += gradient;
x_pixel_start++;
}
if (steep) {
x--;
}
else {
y--;
}
else {
/* Handle extra pixel for aalines.
* It is drawn only when one line is steep and other is not.*/
if (extra_pixel_for_aalines && !line_inverted) {
x_pixel_start = (int)from_x;
y_endpoint = intersect_y =
from_y + gradient * (x_pixel_start - from_x);
if (to_x > clip_left + 1.0f) {
x_gap = 1 + x_pixel_start - from_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint;
y = x_pixel_start;
}
else {
x = x_pixel_start;
y = (int)y_endpoint;
}
if ((int)y_endpoint < y_endpoint) {
pixel_color = get_antialiased_color(surf, x, y, color,
brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}
}
}
brightness = 1 - brightness;
pixel_color =
get_antialiased_color(surf, x, y, color, brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
intersect_y += gradient;
x_pixel_start++;
/* To be sure main loop skips first endpoint.*/
x_pixel_start = (int)ceil(from_x);
intersect_y = from_y + gradient * (x_pixel_start - from_x);
}
/* Second endpoint */
x_pixel_end = (int)ceil(to_x);
if (from_x < clip_right - 1.0f) {
y_endpoint = to_y + gradient * (x_pixel_end - to_x);
x_gap = 1 - x_pixel_end + to_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint;
y = x_pixel_end;
}
else {
x = x_pixel_end;
y = (int)y_endpoint;
}
if ((int)y_endpoint < y_endpoint) {
if (!disable_second_endpoint) {
if (from_x < clip_right - 1.0f) {
y_endpoint = to_y + gradient * (x_pixel_end - to_x);
x_gap = 1 - x_pixel_end + to_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint;
y = x_pixel_end;
}
else {
x = x_pixel_end;
y = (int)y_endpoint;
}
if ((int)y_endpoint < y_endpoint) {
pixel_color = get_antialiased_color(surf, x, y, color,
brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}
if (steep) {
x--;
}
else {
y--;
}
brightness = 1 - brightness;
pixel_color =
get_antialiased_color(surf, x, y, color, brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}
if (steep) {
x--;
}
else {
y--;
}
else {
/* Handle extra pixel for aalines.
* It is drawn only when one line is steep and other is not.*/
if (extra_pixel_for_aalines && line_inverted) {
if (from_x < clip_right - 1.0f) {
y_endpoint = to_y + gradient * (x_pixel_end - to_x);
x_gap = 1 - x_pixel_end + to_x;
brightness = y_endpoint - (int)y_endpoint;
if (steep) {
x = (int)y_endpoint - 1;
mzivic7 marked this conversation as resolved.
Show resolved Hide resolved
y = x_pixel_end;
}
else {
x = x_pixel_end;
y = (int)y_endpoint - 1;
}
brightness = 1 - brightness;
pixel_color = get_antialiased_color(surf, x, y, color,
brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}
}
brightness = 1 - brightness;
pixel_color =
get_antialiased_color(surf, x, y, color, brightness * x_gap);
set_and_check_rect(surf, x, y, pixel_color, drawn_area);
}

/* main line drawing loop */
Expand Down
Loading