Skip to content

Commit d5e66d8

Browse files
mzivic7MyreMylar
andauthored
New draw.aaline width algorithm (#3191)
Co-authored-by: Dan Lawrence <[email protected]>
1 parent 0e3acf0 commit d5e66d8

File tree

3 files changed

+345
-51
lines changed

3 files changed

+345
-51
lines changed
224 Bytes
Loading

src_c/draw.c

Lines changed: 322 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,18 @@ draw_line_width(SDL_Surface *surf, SDL_Rect surf_clip_rect, Uint32 color,
4444
static void
4545
draw_line(SDL_Surface *surf, SDL_Rect surf_clip_rect, int x1, int y1, int x2,
4646
int y2, Uint32 color, int *drawn_area);
47-
void
48-
line_width_corners(float from_x, float from_y, float to_x, float to_y,
49-
int width, float *x1, float *y1, float *x2, float *y2,
50-
float *x3, float *y3, float *x4, float *y4);
5147
static void
5248
draw_aaline(SDL_Surface *surf, SDL_Rect surf_clip_rect,
5349
PG_PixelFormat *surf_format, Uint32 color, float startx,
5450
float starty, float endx, float endy, int *drawn_area,
5551
int disable_first_endpoint, int disable_second_endpoint,
5652
int extra_pixel_for_aalines);
5753
static void
54+
draw_aaline_width(SDL_Surface *surf, SDL_Rect surf_clip_rect,
55+
PG_PixelFormat *surf_format, Uint32 color, float from_x,
56+
float from_y, float to_x, float to_y, int width,
57+
int *drawn_area);
58+
static void
5859
draw_arc(SDL_Surface *surf, SDL_Rect surf_clip_rect, int x_center,
5960
int y_center, int radius1, int radius2, int width, double angle_start,
6061
double angle_stop, Uint32 color, int *drawn_area);
@@ -188,15 +189,8 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs)
188189
}
189190

190191
if (width > 1) {
191-
float x1, y1, x2, y2, x3, y3, x4, y4;
192-
line_width_corners(startx, starty, endx, endy, width, &x1, &y1, &x2,
193-
&y2, &x3, &y3, &x4, &y4);
194-
draw_line_width(surf, surf_clip_rect, color, (int)startx, (int)starty,
195-
(int)endx, (int)endy, width, drawn_area);
196-
draw_aaline(surf, surf_clip_rect, surf_format, color, x1, y1, x2, y2,
197-
drawn_area, 0, 0, 0);
198-
draw_aaline(surf, surf_clip_rect, surf_format, color, x3, y3, x4, y4,
199-
drawn_area, 0, 0, 0);
192+
draw_aaline_width(surf, surf_clip_rect, surf_format, color, startx,
193+
starty, endx, endy, width, drawn_area);
200194
}
201195
else {
202196
draw_aaline(surf, surf_clip_rect, surf_format, color, startx, starty,
@@ -1831,6 +1825,37 @@ drawhorzlineclipbounding(SDL_Surface *surf, SDL_Rect surf_clip_rect,
18311825
drawhorzline(surf, color, x1, y1, x2);
18321826
}
18331827

1828+
static void
1829+
drawvertlineclipbounding(SDL_Surface *surf, SDL_Rect surf_clip_rect,
1830+
Uint32 color, int y1, int x1, int y2, int *pts)
1831+
{
1832+
if (x1 < surf_clip_rect.x || x1 >= surf_clip_rect.x + surf_clip_rect.w) {
1833+
return;
1834+
}
1835+
1836+
if (y2 < y1) {
1837+
int temp = y1;
1838+
y1 = y2;
1839+
y2 = temp;
1840+
}
1841+
1842+
y1 = MAX(y1, surf_clip_rect.y);
1843+
y2 = MIN(y2, surf_clip_rect.y + surf_clip_rect.h - 1);
1844+
1845+
if (y2 < surf_clip_rect.y || y1 >= surf_clip_rect.y + surf_clip_rect.h) {
1846+
return;
1847+
}
1848+
1849+
if (y1 == y2) {
1850+
set_and_check_rect(surf, surf_clip_rect, x1, y1, color, pts);
1851+
return;
1852+
}
1853+
1854+
add_line_to_drawn_list(x1, y1, x1, y2, pts);
1855+
1856+
drawvertline(surf, color, y1, x1, y2);
1857+
}
1858+
18341859
void
18351860
swap_coordinates(int *x1, int *y1, int *x2, int *y2)
18361861
{
@@ -1986,36 +2011,295 @@ draw_line_width(SDL_Surface *surf, SDL_Rect surf_clip_rect, Uint32 color,
19862011
}
19872012
}
19882013

1989-
// Calculates 4 points, representing corners of draw_line_width()
1990-
// first two points assemble left line and second two - right line
1991-
void
1992-
line_width_corners(float from_x, float from_y, float to_x, float to_y,
1993-
int width, float *x1, float *y1, float *x2, float *y2,
1994-
float *x3, float *y3, float *x4, float *y4)
2014+
static void
2015+
draw_aaline_width(SDL_Surface *surf, SDL_Rect surf_clip_rect,
2016+
PG_PixelFormat *surf_format, Uint32 color, float from_x,
2017+
float from_y, float to_x, float to_y, int width,
2018+
int *drawn_area)
19952019
{
1996-
float aa_width = (float)width / 2;
1997-
float extra_width = (1.0f - (width % 2)) / 2;
1998-
int steep = fabs(to_x - from_x) <= fabs(to_y - from_y);
2020+
float gradient, dx, dy, intersect_y, brightness;
2021+
int x, x_pixel_start, x_pixel_end, start_draw, end_draw;
2022+
Uint32 pixel_color;
2023+
float y_endpoint, clip_left, clip_right, clip_top, clip_bottom;
2024+
int steep, y;
2025+
int extra_width = 1 - (width % 2);
2026+
2027+
width = (width / 2);
2028+
2029+
dx = to_x - from_x;
2030+
dy = to_y - from_y;
2031+
steep = fabs(dx) < fabs(dy);
2032+
2033+
/* Single point.
2034+
* A line with length 0 is drawn as a single pixel at full brightness. */
2035+
if (fabs(dx) < 0.0001 && fabs(dy) < 0.0001) {
2036+
x = (int)floor(from_x + 0.5);
2037+
y = (int)floor(from_y + 0.5);
2038+
pixel_color = get_antialiased_color(surf, surf_clip_rect, surf_format,
2039+
x, y, color, 1);
2040+
set_and_check_rect(surf, surf_clip_rect, x, y, pixel_color,
2041+
drawn_area);
2042+
if (dx != 0 && dy != 0) {
2043+
if (steep) {
2044+
start_draw = (int)(x - width + extra_width);
2045+
end_draw = (int)(x + width) - 1;
2046+
drawhorzlineclipbounding(surf, surf_clip_rect, color,
2047+
start_draw, y, end_draw, drawn_area);
2048+
}
2049+
else {
2050+
start_draw = (int)(y - width + extra_width);
2051+
end_draw = (int)(y + width) - 1;
2052+
drawvertlineclipbounding(surf, surf_clip_rect, color,
2053+
start_draw, x, end_draw, drawn_area);
2054+
}
2055+
}
2056+
return;
2057+
}
2058+
2059+
/* To draw correctly the pixels at the border of the clipping area when
2060+
* the line crosses it, we need to clip it one pixel wider in all four
2061+
* directions, and add width */
2062+
clip_left = (float)surf_clip_rect.x - 1.0f;
2063+
clip_right = (float)clip_left + surf_clip_rect.w + 1.0f;
2064+
clip_top = (float)surf_clip_rect.y - 1.0f;
2065+
clip_bottom = (float)clip_top + surf_clip_rect.h + 1.0f;
19992066

20002067
if (steep) {
2001-
*x1 = from_x + extra_width + aa_width;
2002-
*y1 = from_y;
2003-
*x2 = to_x + extra_width + aa_width;
2004-
*y2 = to_y;
2005-
*x3 = from_x + extra_width - aa_width;
2006-
*y3 = from_y;
2007-
*x4 = to_x + extra_width - aa_width;
2008-
*y4 = to_y;
2068+
swap(&from_x, &from_y);
2069+
swap(&to_x, &to_y);
2070+
swap(&dx, &dy);
2071+
swap(&clip_left, &clip_top);
2072+
swap(&clip_right, &clip_bottom);
2073+
}
2074+
if (dx < 0) {
2075+
swap(&from_x, &to_x);
2076+
swap(&from_y, &to_y);
2077+
dx = -dx;
2078+
dy = -dy;
2079+
}
2080+
2081+
if (to_x <= clip_left || from_x >= clip_right) {
2082+
/* The line is completely to the side of the surface */
2083+
return;
2084+
}
2085+
2086+
/* Note. There is no need to guard against a division by zero here. If dx
2087+
* was zero then either we had a single point (and we've returned) or it
2088+
* has been swapped with a non-zero dy. */
2089+
gradient = dy / dx;
2090+
2091+
/* No need to waste CPU cycles on pixels not on the surface. */
2092+
if (from_x < clip_left + 1) {
2093+
from_y += gradient * (clip_left + 1 - from_x);
2094+
from_x = clip_left + 1;
2095+
}
2096+
if (to_x > clip_right - 1) {
2097+
to_y += gradient * (clip_right - 1 - to_x);
2098+
to_x = clip_right - 1;
2099+
}
2100+
2101+
if (gradient > 0.0f) {
2102+
if (from_x < clip_left + 1) {
2103+
/* from_ is the topmost endpoint */
2104+
if (to_y <= clip_top || from_y >= clip_bottom) {
2105+
/* The line does not enter the surface */
2106+
return;
2107+
}
2108+
if (from_y < clip_top - width) {
2109+
from_x += (clip_top - width - from_y) / gradient;
2110+
from_y = clip_top - width;
2111+
}
2112+
if (to_y > clip_bottom + width) {
2113+
to_x += (clip_bottom + width - to_y) / gradient;
2114+
to_y = clip_bottom + width;
2115+
}
2116+
}
20092117
}
20102118
else {
2011-
*x1 = from_x;
2012-
*y1 = from_y + extra_width + aa_width;
2013-
*x2 = to_x;
2014-
*y2 = to_y + extra_width + aa_width;
2015-
*x3 = from_x;
2016-
*y3 = from_y + extra_width - aa_width;
2017-
*x4 = to_x;
2018-
*y4 = to_y + extra_width - aa_width;
2119+
if (to_x > clip_right - 1) {
2120+
/* to_ is the topmost endpoint */
2121+
if (from_y <= clip_top || to_y >= clip_bottom) {
2122+
/* The line does not enter the surface */
2123+
return;
2124+
}
2125+
if (to_y < clip_top - width) {
2126+
to_x += (clip_top - width - to_y) / gradient;
2127+
to_y = clip_top - width;
2128+
}
2129+
if (from_y > clip_bottom + width) {
2130+
from_x += (clip_bottom + width - from_y) / gradient;
2131+
from_y = clip_bottom + width;
2132+
}
2133+
}
2134+
}
2135+
2136+
/* By moving the points one pixel down, we can assume y is never negative.
2137+
* That permit us to use (int)y to round down instead of having to use
2138+
* floor(y). We then draw the pixels one higher.*/
2139+
from_y += 1.0f;
2140+
to_y += 1.0f;
2141+
2142+
/* Handle endpoints separately */
2143+
/* First endpoint */
2144+
x_pixel_start = (int)from_x;
2145+
y_endpoint = intersect_y = from_y + gradient * (x_pixel_start - from_x);
2146+
if (to_x > clip_left + 1.0f) {
2147+
brightness = y_endpoint - (int)y_endpoint;
2148+
if (steep) {
2149+
x = (int)y_endpoint;
2150+
y = x_pixel_start;
2151+
}
2152+
else {
2153+
x = x_pixel_start;
2154+
y = (int)y_endpoint;
2155+
}
2156+
if ((int)y_endpoint < y_endpoint) {
2157+
if (steep) {
2158+
pixel_color =
2159+
get_antialiased_color(surf, surf_clip_rect, surf_format,
2160+
x + width, y, color, brightness);
2161+
set_and_check_rect(surf, surf_clip_rect, x + width, y,
2162+
pixel_color, drawn_area);
2163+
}
2164+
else {
2165+
pixel_color =
2166+
get_antialiased_color(surf, surf_clip_rect, surf_format, x,
2167+
y + width, color, brightness);
2168+
set_and_check_rect(surf, surf_clip_rect, x, y + width,
2169+
pixel_color, drawn_area);
2170+
}
2171+
}
2172+
brightness = 1 - brightness;
2173+
if (steep) {
2174+
pixel_color =
2175+
get_antialiased_color(surf, surf_clip_rect, surf_format,
2176+
x - width, y, color, brightness);
2177+
set_and_check_rect(surf, surf_clip_rect,
2178+
x - width + extra_width - 1, y, pixel_color,
2179+
drawn_area);
2180+
start_draw = (int)(x - width + extra_width);
2181+
end_draw = (int)(x + width) - 1;
2182+
drawhorzlineclipbounding(surf, surf_clip_rect, color, start_draw,
2183+
y, end_draw, drawn_area);
2184+
}
2185+
else {
2186+
pixel_color = get_antialiased_color(
2187+
surf, surf_clip_rect, surf_format, x,
2188+
y - width + extra_width - 1, color, brightness);
2189+
set_and_check_rect(surf, surf_clip_rect, x,
2190+
y - width + extra_width - 1, pixel_color,
2191+
drawn_area);
2192+
start_draw = (int)(y - width + extra_width);
2193+
end_draw = (int)(y + width) - 1;
2194+
drawvertlineclipbounding(surf, surf_clip_rect, color, start_draw,
2195+
x, end_draw, drawn_area);
2196+
}
2197+
intersect_y += gradient;
2198+
x_pixel_start++;
2199+
}
2200+
2201+
/* Second endpoint */
2202+
x_pixel_end = (int)ceil(to_x);
2203+
if (from_x < clip_right - 1.0f) {
2204+
y_endpoint = to_y + gradient * (x_pixel_end - to_x);
2205+
brightness = y_endpoint - (int)y_endpoint;
2206+
if (steep) {
2207+
x = (int)y_endpoint;
2208+
y = x_pixel_end;
2209+
}
2210+
else {
2211+
x = x_pixel_end;
2212+
y = (int)y_endpoint;
2213+
}
2214+
if ((int)y_endpoint < y_endpoint) {
2215+
if (steep) {
2216+
pixel_color =
2217+
get_antialiased_color(surf, surf_clip_rect, surf_format,
2218+
x + width, y, color, brightness);
2219+
set_and_check_rect(surf, surf_clip_rect, x + width, y,
2220+
pixel_color, drawn_area);
2221+
}
2222+
else {
2223+
pixel_color =
2224+
get_antialiased_color(surf, surf_clip_rect, surf_format, x,
2225+
y + width, color, brightness);
2226+
set_and_check_rect(surf, surf_clip_rect, x, y + width,
2227+
pixel_color, drawn_area);
2228+
}
2229+
}
2230+
brightness = 1 - brightness;
2231+
if (steep) {
2232+
pixel_color = get_antialiased_color(
2233+
surf, surf_clip_rect, surf_format, x - width + extra_width - 1,
2234+
y, color, brightness);
2235+
set_and_check_rect(surf, surf_clip_rect,
2236+
x - width + extra_width - 1, y, pixel_color,
2237+
drawn_area);
2238+
start_draw = (int)(x - width);
2239+
end_draw = (int)(x + width) - 1;
2240+
drawhorzlineclipbounding(surf, surf_clip_rect, color, start_draw,
2241+
y, end_draw, drawn_area);
2242+
}
2243+
else {
2244+
pixel_color = get_antialiased_color(
2245+
surf, surf_clip_rect, surf_format, x,
2246+
y - width + extra_width - 1, color, brightness);
2247+
set_and_check_rect(surf, surf_clip_rect, x,
2248+
y - width + extra_width - 1, pixel_color,
2249+
drawn_area);
2250+
start_draw = (int)(y - width + extra_width);
2251+
end_draw = (int)(y + width) - 1;
2252+
drawvertlineclipbounding(surf, surf_clip_rect, color, start_draw,
2253+
x, end_draw, drawn_area);
2254+
}
2255+
}
2256+
2257+
/* main line drawing loop */
2258+
for (x = x_pixel_start; x < x_pixel_end; x++) {
2259+
y = (int)intersect_y;
2260+
if (steep) {
2261+
brightness = 1 - intersect_y + y;
2262+
pixel_color = get_antialiased_color(
2263+
surf, surf_clip_rect, surf_format, y - width + extra_width - 1,
2264+
x, color, brightness);
2265+
set_and_check_rect(surf, surf_clip_rect,
2266+
y - width + extra_width - 1, x, pixel_color,
2267+
drawn_area);
2268+
if (y < intersect_y) {
2269+
brightness = 1 - brightness;
2270+
pixel_color =
2271+
get_antialiased_color(surf, surf_clip_rect, surf_format,
2272+
y + width, x, color, brightness);
2273+
set_and_check_rect(surf, surf_clip_rect, y + width, x,
2274+
pixel_color, drawn_area);
2275+
}
2276+
start_draw = (int)(y - width + extra_width);
2277+
end_draw = (int)(y + width) - 1;
2278+
drawhorzlineclipbounding(surf, surf_clip_rect, color, start_draw,
2279+
x, end_draw, drawn_area);
2280+
}
2281+
else {
2282+
brightness = 1 - intersect_y + y;
2283+
pixel_color = get_antialiased_color(
2284+
surf, surf_clip_rect, surf_format, x,
2285+
y - width + extra_width - 1, color, brightness);
2286+
set_and_check_rect(surf, surf_clip_rect, x,
2287+
y - width + extra_width - 1, pixel_color,
2288+
drawn_area);
2289+
if (y < intersect_y) {
2290+
brightness = 1 - brightness;
2291+
pixel_color =
2292+
get_antialiased_color(surf, surf_clip_rect, surf_format, x,
2293+
y + width, color, brightness);
2294+
set_and_check_rect(surf, surf_clip_rect, x, y + width,
2295+
pixel_color, drawn_area);
2296+
}
2297+
start_draw = (int)(y - width + extra_width);
2298+
end_draw = (int)(y + width) - 1;
2299+
drawvertlineclipbounding(surf, surf_clip_rect, color, start_draw,
2300+
x, end_draw, drawn_area);
2301+
}
2302+
intersect_y += gradient;
20192303
}
20202304
}
20212305

0 commit comments

Comments
 (0)