From e8b5d277d4652f0bc6b23714bf58d07a0e64e935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 18:11:54 +0200 Subject: [PATCH 01/13] 1px wide aacircle --- buildconfig/stubs/pygame/draw.pyi | 3 + .../ref/code_examples/draw_module_example.py | 11 +- docs/reST/ref/draw.rst | 40 ++- src_c/doc/draw_doc.h | 1 + src_c/draw.c | 246 ++++++++++++++++++ 5 files changed, 297 insertions(+), 4 deletions(-) diff --git a/buildconfig/stubs/pygame/draw.pyi b/buildconfig/stubs/pygame/draw.pyi index db2c345a4d..de226c4244 100644 --- a/buildconfig/stubs/pygame/draw.pyi +++ b/buildconfig/stubs/pygame/draw.pyi @@ -55,6 +55,9 @@ def aacircle( def ellipse( surface: Surface, color: ColorValue, rect: RectValue, width: int = 0 ) -> Rect: ... +def aaellipse( + surface: Surface, color: ColorValue, rect: RectValue, width: int = 0 +) -> Rect: ... def arc( surface: Surface, color: ColorValue, diff --git a/docs/reST/ref/code_examples/draw_module_example.py b/docs/reST/ref/code_examples/draw_module_example.py index 99c2c89f9d..12563d56a3 100644 --- a/docs/reST/ref/code_examples/draw_module_example.py +++ b/docs/reST/ref/code_examples/draw_module_example.py @@ -65,6 +65,11 @@ # Draw an solid ellipse, using a rectangle as the outside boundaries pygame.draw.ellipse(screen, "red", [300, 10, 50, 20]) + # Draw an antialiased ellipse, using a rectangle as the outside boundaries + pygame.draw.ellipse(screen, "green", [230, 95, 110, 85], 3) + # Draw an antialiased filled ellipse, using a rectangle as the outside boundaries + pygame.draw.ellipse(screen, "red", [250, 115, 70, 45], 3) + # This draws a triangle using the polygon command pygame.draw.polygon(screen, "black", [[100, 100], [0, 200], [200, 200]], 5) @@ -77,13 +82,13 @@ # Draw a circle pygame.draw.circle(screen, "blue", [60, 250], 40) - + # Draw an antialiased circle with 3 pixels wide line pygame.draw.aacircle(screen, "green", [340, 250], 40, 3) - + # Draw an antialiased top right circle quadrant with 4 pixels wide line pygame.draw.aacircle(screen, "red", [340, 250], 20, 4, draw_top_right=True) - + # Draw an antialiased bottom left filled circle quadrant pygame.draw.aacircle(screen, "blue", [340, 250], 20, draw_bottom_left=True) diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index a6e09d9319..f3a4264079 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -299,6 +299,45 @@ object around the draw calls (see :func:`pygame.Surface.lock` and .. ## pygame.draw.ellipse ## + .. function:: aaellipse + + | :sl:`draw an antialiased ellipse` + | :sg:`aaellipse(surface, color, rect) -> Rect` + | :sg:`aaellipse(surface, color, rect, width=0) -> Rect` + + Draws an antialiased ellipse on the given surface. + Uses Xiaolin Wu Circle Algorithm. + adapted from: https://cgg.mff.cuni.cz/~pepca/ref/WU.pdf + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or string (for :doc:`color_list`) or int or tuple(int, int, int, [int]) + :param Rect rect: rectangle to indicate the position and dimensions of the + ellipse, the ellipse will be centered inside the rectangle and bounded + by it + :param int width: (optional) used for line thickness or to indicate that + the ellipse is to be filled (not to be confused with the width value + of the ``rect`` parameter) + + | if ``width == 0``, (default) fill the ellipse + | if ``width > 0``, used for line thickness + | if ``width < 0``, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1``, the edge lines will only grow + inward from the original boundary of the ``rect`` parameter. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the given ``rect`` + parameter and its width and height will be 0 + :rtype: Rect + + .. versionadded:: 2.6.0 + + .. ## pygame.draw.aaellipse ## + .. function:: arc | :sl:`draw an elliptical arc` @@ -606,4 +645,3 @@ object around the draw calls (see :func:`pygame.Surface.lock` and Example code for draw module. .. literalinclude:: code_examples/draw_module_example.py - diff --git a/src_c/doc/draw_doc.h b/src_c/doc/draw_doc.h index b47ac37250..e0e5b5f549 100644 --- a/src_c/doc/draw_doc.h +++ b/src_c/doc/draw_doc.h @@ -5,6 +5,7 @@ #define DOC_DRAW_CIRCLE "circle(surface, color, center, radius) -> Rect\ncircle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect\ndraw a circle" #define DOC_DRAW_AACIRCLE "aacircle(surface, color, center, radius) -> Rect\naacircle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect\ndraw an antialiased circle" #define DOC_DRAW_ELLIPSE "ellipse(surface, color, rect) -> Rect\nellipse(surface, color, rect, width=0) -> Rect\ndraw an ellipse" +#define DOC_DRAW_AAELLIPSE "aaellipse(surface, color, rect) -> Rect\naaellipse(surface, color, rect, width=0) -> Rect\ndraw an antialiased ellipse" #define DOC_DRAW_ARC "arc(surface, color, rect, start_angle, stop_angle) -> Rect\narc(surface, color, rect, start_angle, stop_angle, width=1) -> Rect\ndraw an elliptical arc" #define DOC_DRAW_LINE "line(surface, color, start_pos, end_pos) -> Rect\nline(surface, color, start_pos, end_pos, width=1) -> Rect\ndraw a straight line" #define DOC_DRAW_LINES "lines(surface, color, closed, points) -> Rect\nlines(surface, color, closed, points, width=1) -> Rect\ndraw multiple contiguous straight line segments" diff --git a/src_c/draw.c b/src_c/draw.c index c7750664d6..daf7c159e1 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -66,6 +66,12 @@ draw_circle_xaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int top_right, int top_left, int bottom_left, int bottom_right, int *drawn_area); static void +draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int a, int b, + int thickness, Uint32 color, int *drawn_area); +static void +draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int a, int b, + Uint32 color, int *drawn_area); +static void draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int *drawn_area); static void @@ -638,6 +644,74 @@ ellipse(PyObject *self, PyObject *arg, PyObject *kwargs) return pgRect_New4(rect->x, rect->y, 0, 0); } +static PyObject * +aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) +{ + pgSurfaceObject *surfobj; + PyObject *colorobj, *rectobj; + SDL_Rect *rect = NULL, temp; + SDL_Surface *surf = NULL; + Uint32 color; + int width = 0; /* Default width. */ + int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, + INT_MIN}; /* Used to store bounding box values */ + static char *keywords[] = {"surface", "color", "rect", "width", NULL}; + + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OO|i", keywords, + &pgSurface_Type, &surfobj, &colorobj, + &rectobj, &width)) { + return NULL; /* Exception already set. */ + } + + rect = pgRect_FromObject(rectobj, &temp); + + if (!rect) { + return RAISE(PyExc_TypeError, "rect argument is invalid"); + } + + surf = pgSurface_AsSurface(surfobj); + SURF_INIT_CHECK(surf) + + if (PG_SURF_BytesPerPixel(surf) <= 0 || PG_SURF_BytesPerPixel(surf) > 4) { + return PyErr_Format(PyExc_ValueError, + "unsupported surface bit depth (%d) for drawing", + PG_SURF_BytesPerPixel(surf)); + } + + CHECK_LOAD_COLOR(colorobj) + + if (width < 0) { + return pgRect_New4(rect->x, rect->y, 0, 0); + } + + if (!pgSurface_Lock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error locking surface"); + } + + if (!width) { + draw_ellipse_filled(surf, rect->x+1, rect->y+1, rect->w-2, rect->h-2, color, drawn_area); + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, color, 2, drawn_area); + } + else if (width == 1) { + draw_ellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, rect->h, color, drawn_area); + } + else { + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, color, width, drawn_area); + } + + if (!pgSurface_Unlock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error unlocking surface"); + } + + if (drawn_area[0] != INT_MAX && drawn_area[1] != INT_MAX && + drawn_area[2] != INT_MIN && drawn_area[3] != INT_MIN) + return pgRect_New4(drawn_area[0], drawn_area[1], + drawn_area[2] - drawn_area[0] + 1, + drawn_area[3] - drawn_area[1] + 1); + else + return pgRect_New4(rect->x, rect->y, 0, 0); +} + static PyObject * circle(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -2472,6 +2546,22 @@ draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, } } +static void +draw_four_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, + int x, int y, float opacity, int *drawn_area) +{ + opacity = opacity / 255.0f; + Uint32 pixel_color; + pixel_color = get_antialiased_color(surf, x0 + x, y0 - y, color, opacity); + set_and_check_rect(surf, x0 + x, y0 - y, pixel_color, drawn_area); + pixel_color = get_antialiased_color(surf, x0 - x, y0 - y, color, opacity); + set_and_check_rect(surf, x0 - x, y0 - y, pixel_color, drawn_area); + pixel_color = get_antialiased_color(surf, x0 - x, y0 + y, color, opacity); + set_and_check_rect(surf, x0 - x, y0 + y, pixel_color, drawn_area); + pixel_color = get_antialiased_color(surf, x0 + x, y0 + y, color, opacity); + set_and_check_rect(surf, x0 + x, y0 + y, pixel_color, drawn_area); +} + static void draw_eight_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, int x, int y, float opacity, int top_right, @@ -2854,6 +2944,160 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, } } +/* Xiaolin Wu Ellipse Algorithm + * adapted from: https://cgg.mff.cuni.cz/~pepca/ref/WU.pdf + * with additional line width parameter + */ + static void + draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int height, int thickness, Uint32 color, int *drawn_area) +{ + int a = width / 2; + int b = height / 2; + x0 = x0 + a; + y0 = y0 + b; + int x = 0; + int y = b; + double prev_opacity = 0.0; + int layer_b = (b - thickness); + for (int layer_a = a - thickness; layer_a <= a; layer_a++) { + float pow_layer_a = pow(layer_a, 2); + float pow_layer_b = pow(layer_b, 2); + double prev_opacity = 0.0; + x = 0; + y = b; + int ffd = round(pow_layer_a / sqrt(pow_layer_a + pow_layer_b)) + 1; + + if (layer_a == a - thickness) { + while (x < ffd) { + double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double opacity = 255.0 * (ceil(height) - height); + if (opacity < prev_opacity) { + --y; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, (float)opacity, drawn_area); + ++x; + } + x = a + 1; + y = 0; + ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + while (y < ffd) { + double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double opacity = 255.0 * (ceil(width) - width); + if (opacity < prev_opacity) { + --x; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, (float)opacity, drawn_area); + ++y; + } + } + + else if (layer_a == a) { + while (x < ffd) { + double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double opacity = 255.0 * (ceil(height) - height); + if (opacity < prev_opacity) { + --y; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); + ++x; + } + x = a + 1; + y = 0; + ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + while (y < ffd) { + double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double opacity = 255.0 * (ceil(width) - width); + if (opacity < prev_opacity) { + --x; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, 255.0f, drawn_area); + ++y; + } + } + + else { + while (x < ffd) { + double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double opacity = 255.0 * (ceil(height) - height); + if (opacity < prev_opacity) { + --y; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); + ++x; + } + x = a + 1; + y = 0; + ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + while (y < ffd) { + double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double opacity = 255.0 * (ceil(width) - width); + if (opacity < prev_opacity) { + --x; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, 255.0f, drawn_area); + ++y; + } + } + ++layer_b; + } +} + + static void + draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int height, Uint32 color, int *drawn_area) + { + int a = width / 2; + int b = height / 2; + x0 = x0 + a; + y0 = y0 + b; + double pow_a = pow(a, 2); + double pow_b = pow(b, 2); + double prev_opacity = 0.0; + // horizontal drawing + int x = 0; + int y = b; + // 45 degree coordinate, at that point switch from horizontal to vertical drawing + int ffd = round(pow_a / sqrt(pow_a + pow_b)) + 1; + while (x < ffd) { + double height = b * sqrt(1 - pow(x, 2) / pow_a); + double opacity = 255.0 * (ceil(height) - height); + if (opacity < prev_opacity) { + --y; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, (float)opacity, drawn_area); + ++x; + } + // vertical drawing + x = a + 1; + y = 0; + ffd = round(pow_b / sqrt(pow_a + pow_b)) + 1; + while (y < ffd) { + double width = a * sqrt(1 - pow(y, 2) / pow_b); + double opacity = 255.0 * (ceil(width) - width); + if (opacity < prev_opacity) { + --x; + } + prev_opacity = opacity; + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, (float)opacity, drawn_area); + ++y; + } + +} + static void draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y, Py_ssize_t num_points, Uint32 color, int *drawn_area) @@ -3115,6 +3359,8 @@ static PyMethodDef _draw_methods[] = { DOC_DRAW_LINES}, {"ellipse", (PyCFunction)ellipse, METH_VARARGS | METH_KEYWORDS, DOC_DRAW_ELLIPSE}, + {"aaellipse", (PyCFunction)aaellipse, METH_VARARGS | METH_KEYWORDS, + DOC_DRAW_AAELLIPSE}, {"arc", (PyCFunction)arc, METH_VARARGS | METH_KEYWORDS, DOC_DRAW_ARC}, {"circle", (PyCFunction)circle, METH_VARARGS | METH_KEYWORDS, DOC_DRAW_CIRCLE}, From a04eec98a10c274c78e841b75148cf94d1491dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 18:44:49 +0200 Subject: [PATCH 02/13] width and fill for aaellipse --- src_c/draw.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 72a4f807a3..45d11ac494 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -66,10 +66,10 @@ draw_circle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int top_right, int top_left, int bottom_left, int bottom_right, int *drawn_area); static void -draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int a, int b, +draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int height, int thickness, Uint32 color, int *drawn_area); static void -draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int a, int b, +draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int height, Uint32 color, int *drawn_area); static void draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, @@ -690,13 +690,13 @@ aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) if (!width) { draw_ellipse_filled(surf, rect->x+1, rect->y+1, rect->w-2, rect->h-2, color, drawn_area); - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, color, 2, drawn_area); + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, 2, color, drawn_area); } else if (width == 1) { draw_ellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, rect->h, color, drawn_area); } else { - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, color, width, drawn_area); + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, width, color, drawn_area); } if (!pgSurface_Unlock(surfobj)) { @@ -2966,12 +2966,11 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, float pow_layer_b = pow(layer_b, 2); double prev_opacity = 0.0; x = 0; - y = b; + y = layer_b; int ffd = round(pow_layer_a / sqrt(pow_layer_a + pow_layer_b)) + 1; - if (layer_a == a - thickness) { while (x < ffd) { - double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { --y; @@ -2981,11 +2980,11 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, (float)opacity, drawn_area); ++x; } - x = a + 1; + x = layer_a + 1; y = 0; ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { - double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { --x; @@ -2996,10 +2995,9 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, ++y; } } - else if (layer_a == a) { while (x < ffd) { - double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { --y; @@ -3009,11 +3007,11 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); ++x; } - x = a + 1; + x = layer_a + 1; y = 0; ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { - double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { --x; @@ -3024,10 +3022,9 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, ++y; } } - else { while (x < ffd) { - double height = b * sqrt(1 - pow(x, 2) / pow_layer_a); + double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { --y; @@ -3037,11 +3034,11 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); ++x; } - x = a + 1; + x = layer_a + 1; y = 0; ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { - double width = a * sqrt(1 - pow(y, 2) / pow_layer_b); + double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { --x; From d0320956753b9419390054e145a11940ff7acf28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 20:32:22 +0200 Subject: [PATCH 03/13] Tests Added 1px width and/or height support --- src_c/draw.c | 105 +++++--- test/draw_test.py | 660 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 731 insertions(+), 34 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 45d11ac494..1df8cb8a93 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -66,11 +66,12 @@ draw_circle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int top_right, int top_left, int bottom_left, int bottom_right, int *drawn_area); static void -draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int height, - int thickness, Uint32 color, int *drawn_area); +draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, + int height, int thickness, Uint32 color, + int *drawn_area); static void -draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int height, - Uint32 color, int *drawn_area); +draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, + int height, Uint32 color, int *drawn_area); static void draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int *drawn_area); @@ -688,15 +689,32 @@ aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) return RAISE(PyExc_RuntimeError, "error locking surface"); } - if (!width) { - draw_ellipse_filled(surf, rect->x+1, rect->y+1, rect->w-2, rect->h-2, color, drawn_area); - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, 2, color, drawn_area); + if (rect->w == 1 && rect->h == 1) { + draw_line(surf, rect->x, rect->y, rect->x, rect->y, color, drawn_area); } - else if (width == 1) { - draw_ellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, rect->h, color, drawn_area); + else if (rect->w == 1) { + draw_line(surf, rect->x, rect->y, rect->x, rect->y + rect->h - 1, + color, drawn_area); + } + else if (rect->h == 1) { + draw_line(surf, rect->x, rect->y, rect->x + rect->w - 1, rect->y, + color, drawn_area); } else { - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, width, color, drawn_area); + if (!width) { + draw_ellipse_filled(surf, rect->x + 1, rect->y + 1, rect->w - 2, + rect->h - 2, color, drawn_area); + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, 2, + color, drawn_area); + } + else if (width == 1) { + draw_ellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, + rect->h, color, drawn_area); + } + else { + draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, + width, color, drawn_area); + } } if (!pgSurface_Unlock(surfobj)) { @@ -2548,7 +2566,7 @@ draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, static void draw_four_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, - int x, int y, float opacity, int *drawn_area) + int x, int y, float opacity, int *drawn_area) { opacity = opacity / 255.0f; Uint32 pixel_color; @@ -2950,8 +2968,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, * adapted from: https://cgg.mff.cuni.cz/~pepca/ref/WU.pdf * with additional line width parameter */ - static void - draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int height, int thickness, Uint32 color, int *drawn_area) +static void +draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, + int height, int thickness, Uint32 color, + int *drawn_area) { int a = width / 2; int b = height / 2; @@ -2976,8 +2996,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, + drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, + (float)opacity, drawn_area); ++x; } x = layer_a + 1; @@ -2990,8 +3012,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, + drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, + (float)opacity, drawn_area); ++y; } } @@ -3003,8 +3027,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, + 255.0f, drawn_area); ++x; } x = layer_a + 1; @@ -3017,8 +3043,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, + 255.0f, drawn_area); ++y; } } @@ -3030,8 +3058,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, + drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, + 255.0f, drawn_area); ++x; } x = layer_a + 1; @@ -3044,8 +3074,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, 255.0f, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, + drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, + 255.0f, drawn_area); ++y; } } @@ -3053,9 +3085,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, } } - static void - draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int height, Uint32 color, int *drawn_area) - { +static void +draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, + int height, Uint32 color, int *drawn_area) +{ int a = width / 2; int b = height / 2; x0 = x0 + a; @@ -3066,7 +3099,8 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, // horizontal drawing int x = 0; int y = b; - // 45 degree coordinate, at that point switch from horizontal to vertical drawing + // 45 degree coordinate, at that point switch from horizontal to vertical + // drawing int ffd = round(pow_a / sqrt(pow_a + pow_b)) + 1; while (x < ffd) { double height = b * sqrt(1 - pow(x, 2) / pow_a); @@ -3075,8 +3109,10 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, + (float)opacity, drawn_area); ++x; } // vertical drawing @@ -3090,11 +3126,12 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, drawn_area); + draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, + (float)opacity, drawn_area); ++y; } - } static void diff --git a/test/draw_test.py b/test/draw_test.py index 3f00a9319e..263de54dcb 100644 --- a/test/draw_test.py +++ b/test/draw_test.py @@ -169,6 +169,7 @@ class DrawTestCase(unittest.TestCase): draw_circle = staticmethod(draw.circle) draw_aacircle = staticmethod(draw.aacircle) draw_ellipse = staticmethod(draw.ellipse) + draw_aaellipse = staticmethod(draw.aaellipse) draw_arc = staticmethod(draw.arc) draw_line = staticmethod(draw.line) draw_lines = staticmethod(draw.lines) @@ -1045,6 +1046,661 @@ class DrawEllipseTest(DrawEllipseMixin, DrawTestCase): """ +### AAEllipse Testing ########################################################### + + +class DrawAAEllipseMixin: + """Mixin tests for drawing antialiased ellipses. + + This class contains all the general antialiased ellipse drawing tests. + Most of test are taken from DrawEllipseMixin class. + """ + + def test_aaellipse__args(self): + """Ensures draw aaellipse accepts the correct args.""" + bounds_rect = self.draw_aaellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((0, 0), (3, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__args_without_width(self): + """Ensures draw aaellipse accepts the args without a width.""" + bounds_rect = self.draw_aaellipse( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((1, 1), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__args_with_negative_width(self): + """Ensures draw aaellipse accepts the args with negative width.""" + bounds_rect = self.draw_aaellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((2, 3), (3, 2)), -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(2, 3, 0, 0)) + + def test_aaellipse__args_with_width_gt_radius(self): + """Ensures draw aaellipse accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_aaellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_aaellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__kwargs(self): + """Ensures draw aaellipse accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aaellipse(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__kwargs_order_independent(self): + """Ensures draw aaellipse's kwargs are not order dependent.""" + bounds_rect = self.draw_aaellipse( + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=0, + rect=pygame.Rect((1, 0), (1, 1)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__args_missing(self): + """Ensures draw aaellipse detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(surface, pygame.Color("red")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse() + + def test_aaellipse__kwargs_missing(self): + """Ensures draw aaellipse detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "width": 2, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(**invalid_kwargs) + + def test_aaellipse__arg_invalid_types(self): + """Ensures draw aaellipse detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_aaellipse(surface, color, rect, "1") + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_aaellipse(surface, color, (1, 2, 3, 4, 5), 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_aaellipse(surface, 2.3, rect, 0) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aaellipse(rect, color, rect, 2) + + def test_aaellipse__kwarg_invalid_types(self): + """Ensures draw aaellipse detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "width": 1, + }, + {"surface": surface, "color": color, "rect": rect, "width": 1.1}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(**kwargs) + + def test_aaellipse__kwarg_invalid_name(self): + """Ensures draw aaellipse detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(**kwargs) + + def test_aaellipse__args_and_kwargs(self): + """Ensures draw aaellipse accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 1)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aaellipse(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aaellipse(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_aaellipse(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_aaellipse(surface, color, rect, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__valid_width_values(self): + """Ensures draw aaellipse accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (3, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_aaellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__valid_rect_formats(self): + """Ensures draw aaellipse accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = (pygame.Rect(pos, (1, 3)), (pos, (2, 1)), (pos[0], pos[1], 1, 1)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_aaellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__valid_color_formats(self): + """Ensures draw aaellipse accepts different color formats.""" + pos = (1, 1) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 2)), + "width": 0, + } + reds = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aaellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaellipse__invalid_color_formats(self): + """Ensures draw aaellipse handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "width": 1, + } + + for expected_color in (2.3, surface): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaellipse(**kwargs) + + def test_aaellipse__no_holes(self): + width = 80 + height = 70 + surface = pygame.Surface((width + 1, height + 1)) + rect = pygame.Rect(0, 0, width, height) + # thickness=1 can't be checked because of antialiasing + for thickness in range(37, 5): + surface.fill("BLACK") + print(rect, thickness) + self.draw_aaellipse(surface, "RED", rect, thickness) + for y in range(height): + number_of_changes = 0 + drawn_pixel = False + for x in range(width + 1): + if ( + not drawn_pixel + and surface.get_at((x, y)) == pygame.Color("RED") + or drawn_pixel + and surface.get_at((x, y)) == pygame.Color("BLACK") + ): + drawn_pixel = not drawn_pixel + number_of_changes += 1 + if y < thickness or y > height - thickness - 1: + self.assertEqual(number_of_changes, 2) + else: + self.assertEqual(number_of_changes, 4) + + def _check_1_pixel_sized_aaellipse( + self, surface, collide_rect, surface_color, ellipse_color + ): + # Helper method to check the surface for 1 pixel wide and/or high + # ellipses. + surf_w, surf_h = surface.get_size() + + surface.lock() # For possible speed up. + + for pos in ((x, y) for y in range(surf_h) for x in range(surf_w)): + # Since the ellipse is just a line we can use a rect to help find + # where it is expected to be drawn. + if collide_rect.collidepoint(pos): + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual( + surface.get_at(pos), + expected_color, + f"collide_rect={collide_rect}, pos={pos}", + ) + + surface.unlock() + + def test_aaellipse__1_pixel_width(self): + """Ensures an ellipse with a width of 1 is drawn correctly. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 0)) + collide_rect = rect.copy() + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd heights. + for ellipse_h in range(6, 10): + collide_rect.h = ellipse_h + rect.h = ellipse_h + + # Calculate some variable positions. + off_top = -(ellipse_h + 1) + half_off_top = -(ellipse_h // 2) + half_off_bottom = surf_h - (ellipse_h // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (off_left, half_off_top), + (off_left, center_y), + (off_left, half_off_bottom), + (off_left, off_bottom), + (center_x, off_top), + (center_x, half_off_top), + (center_x, center_y), + (center_x, half_off_bottom), + (center_x, off_bottom), + (off_right, off_top), + (off_right, half_off_top), + (off_right, center_y), + (off_right, half_off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_aaellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_aaellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_aaellipse__1_pixel_width_spanning_surface(self): + """Ensures an ellipse with a width of 1 is drawn correctly + when spanning the height of the surface. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, surf_h + 2)) # Longer than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (0, -1), # (left_edge, off_top) + (surf_w // 2, -1), # (center_x, off_top) + (surf_w - 1, -1), # (right_edge, off_top) + (surf_w, -1), + ) # (off_right, off_top) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_aaellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_aaellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_aaellipse__1_pixel_height(self): + """Ensures an ellipse with a height of 1 is drawn correctly. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (0, 1)) + collide_rect = rect.copy() + + # Calculate some positions. + off_right = surf_w + off_top = -1 + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd widths. + for ellipse_w in range(6, 10): + collide_rect.w = ellipse_w + rect.w = ellipse_w + + # Calculate some variable positions. + off_left = -(ellipse_w + 1) + half_off_left = -(ellipse_w // 2) + half_off_right = surf_w - (ellipse_w // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (half_off_left, off_top), + (center_x, off_top), + (half_off_right, off_top), + (off_right, off_top), + (off_left, center_y), + (half_off_left, center_y), + (center_x, center_y), + (half_off_right, center_y), + (off_right, center_y), + (off_left, off_bottom), + (half_off_left, off_bottom), + (center_x, off_bottom), + (half_off_right, off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_aaellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_aaellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_aaellipse__1_pixel_height_spanning_surface(self): + """Ensures an ellipse with a height of 1 is drawn correctly + when spanning the width of the surface. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (surf_w + 2, 1)) # Wider than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (-1, 0), # (off_left, top_edge) + (-1, surf_h // 2), # (off_left, center_y) + (-1, surf_h - 1), # (off_left, bottom_edge) + (-1, surf_h), + ) # (off_left, off_bottom) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_aaellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_aaellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_aaellipse__1_pixel_width_and_height(self): + """Ensures an ellipse with a width and height of 1 is drawn correctly. + + An ellipse with a width and height of 1 pixel is a single pixel. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 1)) + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_top = -1 + off_bottom = surf_h + left_edge = 0 + right_edge = surf_w - 1 + top_edge = 0 + bottom_edge = surf_h - 1 + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Draw the ellipse in different positions: center surface, + # top/bottom/left/right edges, and off-surface. + positions = ( + (off_left, off_top), + (off_left, top_edge), + (off_left, center_y), + (off_left, bottom_edge), + (off_left, off_bottom), + (left_edge, off_top), + (left_edge, top_edge), + (left_edge, center_y), + (left_edge, bottom_edge), + (left_edge, off_bottom), + (center_x, off_top), + (center_x, top_edge), + (center_x, center_y), + (center_x, bottom_edge), + (center_x, off_bottom), + (right_edge, off_top), + (right_edge, top_edge), + (right_edge, center_y), + (right_edge, bottom_edge), + (right_edge, off_bottom), + (off_right, off_top), + (off_right, top_edge), + (off_right, center_y), + (off_right, bottom_edge), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_aaellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_aaellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_aaellipse__bounding_rect(self): + """Ensures draw aaellipse returns the correct bounding rect. + + Tests ellipses on and off the surface and a range of width/thickness + values. + """ + ellipse_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # ellipses off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the ellipse's rect position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + ellipse_rect = pygame.Rect((0, 0), (width, height)) + setattr(ellipse_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aaellipse( + surface, ellipse_color, ellipse_rect, thickness + ) + + # Calculating the expected_rect after the ellipse + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, ellipse_rect.topleft + ) + + self.assertEqual(bounding_rect, expected_rect) + + +class DrawAAEllipseTest(DrawAAEllipseMixin, DrawTestCase): + """Test draw module function ellipse. + + This class inherits the general tests from DrawAAEllipseMixin. It is also + the class to add any draw.aaellipse specific tests to. + """ + + ### Line/Lines/AALine/AALines Testing ######################################### @@ -7143,6 +7799,7 @@ def test_color_validation(self): draw.lines(surf, col, True, points) draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + draw.aaellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) draw.circle(surf, col, (7, 3), 2) draw.aacircle(surf, col, (7, 3), 2) draw.polygon(surf, col, points, 0) @@ -7167,6 +7824,9 @@ def test_color_validation(self): with self.assertRaises(TypeError): draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + with self.assertRaises(TypeError): + draw.aaellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + with self.assertRaises(TypeError): draw.circle(surf, col, (7, 3), 2) From 6cd117e051ec93dc014d0e0175ddf9d071f1f0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 20:42:58 +0200 Subject: [PATCH 04/13] Code examples --- .../ref/code_examples/draw_module_example.png | Bin 8645 -> 13631 bytes .../ref/code_examples/draw_module_example.py | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/reST/ref/code_examples/draw_module_example.png b/docs/reST/ref/code_examples/draw_module_example.png index f6fef800dd03b5c75df9871361e6bf2815049f53..8b7e118b2035ab2e9719a0a9025cd3d82b5cd072 100644 GIT binary patch literal 13631 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 diff --git a/docs/reST/ref/code_examples/draw_module_example.py b/docs/reST/ref/code_examples/draw_module_example.py index 12563d56a3..0273384e31 100644 --- a/docs/reST/ref/code_examples/draw_module_example.py +++ b/docs/reST/ref/code_examples/draw_module_example.py @@ -60,15 +60,15 @@ ) # Draw an ellipse outline, using a rectangle as the outside boundaries - pygame.draw.ellipse(screen, "red", [225, 10, 50, 20], 2) + pygame.draw.ellipse(screen, "red", [235, 10, 50, 20], 2) # Draw an solid ellipse, using a rectangle as the outside boundaries - pygame.draw.ellipse(screen, "red", [300, 10, 50, 20]) + pygame.draw.ellipse(screen, "red", [310, 10, 50, 20]) # Draw an antialiased ellipse, using a rectangle as the outside boundaries - pygame.draw.ellipse(screen, "green", [230, 95, 110, 85], 3) + pygame.draw.aaellipse(screen, "red", [235, 40, 50, 20], 3) # Draw an antialiased filled ellipse, using a rectangle as the outside boundaries - pygame.draw.ellipse(screen, "red", [250, 115, 70, 45], 3) + pygame.draw.aaellipse(screen, "red", [310, 40, 50, 20]) # This draws a triangle using the polygon command pygame.draw.polygon(screen, "black", [[100, 100], [0, 200], [200, 200]], 5) From 2e260034200557550e81d3be95fd88b0fdc55397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 20:50:32 +0200 Subject: [PATCH 05/13] Removed unused variable --- src_c/draw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src_c/draw.c b/src_c/draw.c index 1df8cb8a93..ace4865f13 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -2979,7 +2979,6 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, y0 = y0 + b; int x = 0; int y = b; - double prev_opacity = 0.0; int layer_b = (b - thickness); for (int layer_a = a - thickness; layer_a <= a; layer_a++) { float pow_layer_a = pow(layer_a, 2); From 3957b626a895033381027effd8f44290a64d97df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 21:37:48 +0200 Subject: [PATCH 06/13] fixed doubles and ints --- src_c/draw.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index ace4865f13..07d114225e 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -2981,12 +2981,13 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int y = b; int layer_b = (b - thickness); for (int layer_a = a - thickness; layer_a <= a; layer_a++) { - float pow_layer_a = pow(layer_a, 2); - float pow_layer_b = pow(layer_b, 2); + double pow_layer_a = pow(layer_a, 2); + double pow_layer_b = pow(layer_b, 2); double prev_opacity = 0.0; x = 0; y = layer_b; - int ffd = round(pow_layer_a / sqrt(pow_layer_a + pow_layer_b)) + 1; + int ffd = + (int)round(pow_layer_a / sqrt(pow_layer_a + pow_layer_b)) + 1; if (layer_a == a - thickness) { while (x < ffd) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); @@ -3003,7 +3004,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + ffd = + (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); @@ -3034,7 +3036,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + ffd = + (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); @@ -3065,7 +3068,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; + ffd = + (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; while (y < ffd) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); @@ -3100,7 +3104,7 @@ draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int y = b; // 45 degree coordinate, at that point switch from horizontal to vertical // drawing - int ffd = round(pow_a / sqrt(pow_a + pow_b)) + 1; + int ffd = (int)round(pow_a / sqrt(pow_a + pow_b)) + 1; while (x < ffd) { double height = b * sqrt(1 - pow(x, 2) / pow_a); double opacity = 255.0 * (ceil(height) - height); @@ -3117,7 +3121,7 @@ draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, // vertical drawing x = a + 1; y = 0; - ffd = round(pow_b / sqrt(pow_a + pow_b)) + 1; + ffd = (int)round(pow_b / sqrt(pow_a + pow_b)) + 1; while (y < ffd) { double width = a * sqrt(1 - pow(y, 2) / pow_b); double opacity = 255.0 * (ceil(width) - width); From e594ab4e9ae7a2ca98a5e0151ca6e835746ae7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Mon, 22 Jul 2024 23:14:47 +0200 Subject: [PATCH 07/13] Renamed variables --- src_c/draw.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 07d114225e..7aa6239875 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -2983,13 +2983,13 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, for (int layer_a = a - thickness; layer_a <= a; layer_a++) { double pow_layer_a = pow(layer_a, 2); double pow_layer_b = pow(layer_b, 2); + double layers_sum_root = sqrt(pow_layer_a + pow_layer_b); double prev_opacity = 0.0; x = 0; y = layer_b; - int ffd = - (int)round(pow_layer_a / sqrt(pow_layer_a + pow_layer_b)) + 1; + int deg45 = (int)round(pow_layer_a / layers_sum_root) + 1; if (layer_a == a - thickness) { - while (x < ffd) { + while (x < deg45) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { @@ -3004,9 +3004,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = - (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; - while (y < ffd) { + deg45 = (int)round(pow_layer_b / layers_sum_root) + 1; + while (y < deg45) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { @@ -3021,7 +3020,7 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } } else if (layer_a == a) { - while (x < ffd) { + while (x < deg45) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { @@ -3036,9 +3035,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = - (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; - while (y < ffd) { + deg45 = (int)round(pow_layer_b / layers_sum_root) + 1; + while (y < deg45) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { @@ -3053,7 +3051,7 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } } else { - while (x < ffd) { + while (x < deg45) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { @@ -3068,9 +3066,9 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } x = layer_a + 1; y = 0; - ffd = + deg45 = (int)round(pow_layer_b / sqrt(pow_layer_a + pow_layer_b)) + 1; - while (y < ffd) { + while (y < deg45) { double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { @@ -3104,8 +3102,8 @@ draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int y = b; // 45 degree coordinate, at that point switch from horizontal to vertical // drawing - int ffd = (int)round(pow_a / sqrt(pow_a + pow_b)) + 1; - while (x < ffd) { + int deg45 = (int)round(pow_a / sqrt(pow_a + pow_b)) + 1; + while (x < deg45) { double height = b * sqrt(1 - pow(x, 2) / pow_a); double opacity = 255.0 * (ceil(height) - height); if (opacity < prev_opacity) { @@ -3121,8 +3119,8 @@ draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, // vertical drawing x = a + 1; y = 0; - ffd = (int)round(pow_b / sqrt(pow_a + pow_b)) + 1; - while (y < ffd) { + deg45 = (int)round(pow_b / sqrt(pow_a + pow_b)) + 1; + while (y < deg45) { double width = a * sqrt(1 - pow(y, 2) / pow_b); double opacity = 255.0 * (ceil(width) - width); if (opacity < prev_opacity) { From b87a1cbe2ea993ba4d0a7c920dcf6dd8a59268dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20=C5=BDivi=C4=87?= Date: Wed, 31 Jul 2024 12:34:44 +0200 Subject: [PATCH 08/13] Renamed internal functions draw_aaellipse_xiaolinwu draw_aacircle_xiaolinwu --- src_c/draw.c | 90 +++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 7aa6239875..9315e4dc6b 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -58,20 +58,22 @@ static void draw_circle_bresenham_thin(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int *drawn_area); static void -draw_circle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, - int thickness, Uint32 color, int top_right, int top_left, - int bottom_left, int bottom_right, int *drawn_area); +draw_aacircle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, + int thickness, Uint32 color, int top_right, + int top_left, int bottom_left, int bottom_right, + int *drawn_area); static void -draw_circle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, - Uint32 color, int top_right, int top_left, - int bottom_left, int bottom_right, int *drawn_area); +draw_aacircle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, + Uint32 color, int top_right, int top_left, + int bottom_left, int bottom_right, + int *drawn_area); static void -draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, - int height, int thickness, Uint32 color, - int *drawn_area); +draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, + int height, int thickness, Uint32 color, + int *drawn_area); static void -draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, - int height, Uint32 color, int *drawn_area); +draw_aaellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, + int height, Uint32 color, int *drawn_area); static void draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, int *drawn_area); @@ -704,16 +706,16 @@ aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) if (!width) { draw_ellipse_filled(surf, rect->x + 1, rect->y + 1, rect->w - 2, rect->h - 2, color, drawn_area); - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, 2, - color, drawn_area); + draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, + 2, color, drawn_area); } else if (width == 1) { - draw_ellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, - rect->h, color, drawn_area); + draw_aaellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, + rect->h, color, drawn_area); } else { - draw_ellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, - width, color, drawn_area); + draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, + width, color, drawn_area); } } @@ -912,33 +914,33 @@ aacircle(PyObject *self, PyObject *args, PyObject *kwargs) if (!width || width == radius) { draw_circle_filled(surf, posx, posy, radius - 1, color, drawn_area); - draw_circle_xiaolinwu(surf, posx, posy, radius, 2, color, 1, 1, 1, - 1, drawn_area); + draw_aacircle_xiaolinwu(surf, posx, posy, radius, 2, color, 1, 1, + 1, 1, drawn_area); } else if (width == 1) { - draw_circle_xiaolinwu_thin(surf, posx, posy, radius, color, 1, 1, - 1, 1, drawn_area); + draw_aacircle_xiaolinwu_thin(surf, posx, posy, radius, color, 1, 1, + 1, 1, drawn_area); } else { - draw_circle_xiaolinwu(surf, posx, posy, radius, width, color, 1, 1, - 1, 1, drawn_area); + draw_aacircle_xiaolinwu(surf, posx, posy, radius, width, color, 1, + 1, 1, 1, drawn_area); } } else { if (!width || width == radius) { - draw_circle_xiaolinwu(surf, posx, posy, radius, radius, color, - top_right, top_left, bottom_left, - bottom_right, drawn_area); + draw_aacircle_xiaolinwu(surf, posx, posy, radius, radius, color, + top_right, top_left, bottom_left, + bottom_right, drawn_area); } else if (width == 1) { - draw_circle_xiaolinwu_thin(surf, posx, posy, radius, color, - top_right, top_left, bottom_left, - bottom_right, drawn_area); + draw_aacircle_xiaolinwu_thin(surf, posx, posy, radius, color, + top_right, top_left, bottom_left, + bottom_right, drawn_area); } else { - draw_circle_xiaolinwu(surf, posx, posy, radius, width, color, - top_right, top_left, bottom_left, - bottom_right, drawn_area); + draw_aacircle_xiaolinwu(surf, posx, posy, radius, width, color, + top_right, top_left, bottom_left, + bottom_right, drawn_area); } } @@ -2627,9 +2629,10 @@ draw_eight_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, * with additional line width parameter and quadrants option */ static void -draw_circle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, - int thickness, Uint32 color, int top_right, int top_left, - int bottom_left, int bottom_right, int *drawn_area) +draw_aacircle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, + int thickness, Uint32 color, int top_right, + int top_left, int bottom_left, int bottom_right, + int *drawn_area) { for (int layer_radius = radius - thickness; layer_radius <= radius; layer_radius++) { @@ -2693,9 +2696,10 @@ draw_circle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, } static void -draw_circle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, - Uint32 color, int top_right, int top_left, - int bottom_left, int bottom_right, int *drawn_area) +draw_aacircle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, + Uint32 color, int top_right, int top_left, + int bottom_left, int bottom_right, + int *drawn_area) { int x = 0; int y = radius; @@ -2969,9 +2973,9 @@ draw_ellipse_thickness(SDL_Surface *surf, int x0, int y0, int width, * with additional line width parameter */ static void -draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, - int height, int thickness, Uint32 color, - int *drawn_area) +draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, + int height, int thickness, Uint32 color, + int *drawn_area) { int a = width / 2; int b = height / 2; @@ -3087,8 +3091,8 @@ draw_ellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, } static void -draw_ellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, - int height, Uint32 color, int *drawn_area) +draw_aaellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, + int height, Uint32 color, int *drawn_area) { int a = width / 2; int b = height / 2; From d05a0cea9dd190cda76942e1ccd9b4161e0972eb Mon Sep 17 00:00:00 2001 From: Marko Zivic <92328096+mzivic7@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:45:05 +0200 Subject: [PATCH 09/13] Update buildconfig --- buildconfig/stubs/pygame/draw.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconfig/stubs/pygame/draw.pyi b/buildconfig/stubs/pygame/draw.pyi index bbefdcb2e2..2f278c1f94 100644 --- a/buildconfig/stubs/pygame/draw.pyi +++ b/buildconfig/stubs/pygame/draw.pyi @@ -56,7 +56,7 @@ def ellipse( surface: Surface, color: ColorLike, rect: RectLike, width: int = 0 ) -> Rect: ... def aaellipse( - surface: Surface, color: ColorValue, rect: RectValue, width: int = 0 + surface: Surface, color: ColorLike, rect: RectLike, width: int = 0 ) -> Rect: ... def arc( surface: Surface, From a26e212cda0bc785b6b8b64d76a0c7bcee863a0a Mon Sep 17 00:00:00 2001 From: Marko Zivic <92328096+mzivic7@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:53:24 +0200 Subject: [PATCH 10/13] Cleaned docs --- docs/reST/ref/draw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index 8353b8f7e8..ddb3e0940c 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -308,7 +308,7 @@ object around the draw calls (see :func:`pygame.Surface.lock` and Draws an antialiased ellipse on the given surface. Uses Xiaolin Wu Circle Algorithm. adapted from: https://cgg.mff.cuni.cz/~pepca/ref/WU.pdf - + :param Surface surface: surface to draw on :param color: color to draw with, the alpha value is optional if using a tuple ``(RGB[A])`` From 20757c7cabc22b06b03034cbd8da25d8f0d2bb48 Mon Sep 17 00:00:00 2001 From: Dan Lawrence Date: Sat, 5 Oct 2024 16:49:30 +0100 Subject: [PATCH 11/13] add updated image --- .../ref/code_examples/draw_module_example.png | Bin 13631 -> 27766 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/reST/ref/code_examples/draw_module_example.png b/docs/reST/ref/code_examples/draw_module_example.png index 8b7e118b2035ab2e9719a0a9025cd3d82b5cd072..4c465db6699275014bd5b35d1912166185abfc97 100644 GIT binary patch delta 24204 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 kvk)sB4%ffNFyLfOGpU}-HLQ5A&MX+ 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 F|6dBe%^Ls! From f02d8c88e869689f0cf2d2f975d559cdb2c75d92 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sat, 9 Nov 2024 18:53:28 +0100 Subject: [PATCH 12/13] Bugfixes update version in docs comments symetric -> symmetric fix thickness > width/2 or height/2 fix bug with width/height < 4 with thickness fix small vertical ellipse not same horizontal fix small width=height drawing wrong circle --- docs/reST/ref/draw.rst | 2 +- src_c/draw.c | 520 +++++++++++++++++++++++------------------ 2 files changed, 293 insertions(+), 229 deletions(-) diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index da1faa3084..77d3f27a64 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -334,7 +334,7 @@ object around the draw calls (see :func:`pygame.Surface.lock` and parameter and its width and height will be 0 :rtype: Rect - .. versionadded:: 2.6.0 + .. versionadded:: 2.5.3 .. ## pygame.draw.aaellipse ## diff --git a/src_c/draw.c b/src_c/draw.c index f6e5b4b054..75da3d87b8 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -672,158 +672,6 @@ arc(PyObject *self, PyObject *arg, PyObject *kwargs) return pgRect_New4(rect->x, rect->y, 0, 0); } -static PyObject * -ellipse(PyObject *self, PyObject *arg, PyObject *kwargs) -{ - pgSurfaceObject *surfobj; - PyObject *colorobj, *rectobj; - SDL_Rect *rect = NULL, temp; - SDL_Surface *surf = NULL; - Uint32 color; - int width = 0; /* Default width. */ - int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, - INT_MIN}; /* Used to store bounding box values */ - static char *keywords[] = {"surface", "color", "rect", "width", NULL}; - - if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OO|i", keywords, - &pgSurface_Type, &surfobj, &colorobj, - &rectobj, &width)) { - return NULL; /* Exception already set. */ - } - - rect = pgRect_FromObject(rectobj, &temp); - - if (!rect) { - return RAISE(PyExc_TypeError, "rect argument is invalid"); - } - - surf = pgSurface_AsSurface(surfobj); - SURF_INIT_CHECK(surf) - - if (PG_SURF_BytesPerPixel(surf) <= 0 || PG_SURF_BytesPerPixel(surf) > 4) { - return PyErr_Format(PyExc_ValueError, - "unsupported surface bit depth (%d) for drawing", - PG_SURF_BytesPerPixel(surf)); - } - - CHECK_LOAD_COLOR(colorobj) - - if (width < 0) { - return pgRect_New4(rect->x, rect->y, 0, 0); - } - - if (!pgSurface_Lock(surfobj)) { - return RAISE(PyExc_RuntimeError, "error locking surface"); - } - - if (!width || - width >= MIN(rect->w / 2 + rect->w % 2, rect->h / 2 + rect->h % 2)) { - draw_ellipse_filled(surf, rect->x, rect->y, rect->w, rect->h, color, - drawn_area); - } - else { - draw_ellipse_thickness(surf, rect->x, rect->y, rect->w, rect->h, - width - 1, color, drawn_area); - } - - if (!pgSurface_Unlock(surfobj)) { - return RAISE(PyExc_RuntimeError, "error unlocking surface"); - } - - if (drawn_area[0] != INT_MAX && drawn_area[1] != INT_MAX && - drawn_area[2] != INT_MIN && drawn_area[3] != INT_MIN) - return pgRect_New4(drawn_area[0], drawn_area[1], - drawn_area[2] - drawn_area[0] + 1, - drawn_area[3] - drawn_area[1] + 1); - else - return pgRect_New4(rect->x, rect->y, 0, 0); -} - -static PyObject * -aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) -{ - pgSurfaceObject *surfobj; - PyObject *colorobj, *rectobj; - SDL_Rect *rect = NULL, temp; - SDL_Surface *surf = NULL; - Uint32 color; - int width = 0; /* Default width. */ - int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, - INT_MIN}; /* Used to store bounding box values */ - static char *keywords[] = {"surface", "color", "rect", "width", NULL}; - - if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OO|i", keywords, - &pgSurface_Type, &surfobj, &colorobj, - &rectobj, &width)) { - return NULL; /* Exception already set. */ - } - - rect = pgRect_FromObject(rectobj, &temp); - - if (!rect) { - return RAISE(PyExc_TypeError, "rect argument is invalid"); - } - - surf = pgSurface_AsSurface(surfobj); - SURF_INIT_CHECK(surf) - - if (PG_SURF_BytesPerPixel(surf) <= 0 || PG_SURF_BytesPerPixel(surf) > 4) { - return PyErr_Format(PyExc_ValueError, - "unsupported surface bit depth (%d) for drawing", - PG_SURF_BytesPerPixel(surf)); - } - - CHECK_LOAD_COLOR(colorobj) - - if (width < 0) { - return pgRect_New4(rect->x, rect->y, 0, 0); - } - - if (!pgSurface_Lock(surfobj)) { - return RAISE(PyExc_RuntimeError, "error locking surface"); - } - - if (rect->w == 1 && rect->h == 1) { - draw_line(surf, rect->x, rect->y, rect->x, rect->y, color, drawn_area); - } - else if (rect->w == 1) { - draw_line(surf, rect->x, rect->y, rect->x, rect->y + rect->h - 1, - color, drawn_area); - } - else if (rect->h == 1) { - draw_line(surf, rect->x, rect->y, rect->x + rect->w - 1, rect->y, - color, drawn_area); - } - else { - if (!width) { - draw_ellipse_filled(surf, rect->x + 1, rect->y + 1, rect->w - 2, - rect->h - 2, color, drawn_area); - draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, - 2, color, drawn_area); - } - else if (width == 1) { - draw_aaellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, - rect->h, color, drawn_area); - } - else { - draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, - width, color, drawn_area); - } - } - - if (!pgSurface_Unlock(surfobj)) { - return RAISE(PyExc_RuntimeError, "error unlocking surface"); - } - - if (drawn_area[0] != INT_MAX && drawn_area[1] != INT_MAX && - drawn_area[2] != INT_MIN && drawn_area[3] != INT_MIN) - return pgRect_New4(drawn_area[0], drawn_area[1], - drawn_area[2] - drawn_area[0] + 1, - drawn_area[3] - drawn_area[1] + 1); - else - return pgRect_New4(rect->x, rect->y, 0, 0); -} - static PyObject * circle(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -1048,6 +896,187 @@ aacircle(PyObject *self, PyObject *args, PyObject *kwargs) return pgRect_New4(posx, posy, 0, 0); } +static PyObject * +ellipse(PyObject *self, PyObject *arg, PyObject *kwargs) +{ + pgSurfaceObject *surfobj; + PyObject *colorobj, *rectobj; + SDL_Rect *rect = NULL, temp; + SDL_Surface *surf = NULL; + Uint32 color; + int width = 0; /* Default width. */ + int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, + INT_MIN}; /* Used to store bounding box values */ + static char *keywords[] = {"surface", "color", "rect", "width", NULL}; + + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OO|i", keywords, + &pgSurface_Type, &surfobj, &colorobj, + &rectobj, &width)) { + return NULL; /* Exception already set. */ + } + + rect = pgRect_FromObject(rectobj, &temp); + + if (!rect) { + return RAISE(PyExc_TypeError, "rect argument is invalid"); + } + + surf = pgSurface_AsSurface(surfobj); + SURF_INIT_CHECK(surf) + + if (PG_SURF_BytesPerPixel(surf) <= 0 || PG_SURF_BytesPerPixel(surf) > 4) { + return PyErr_Format(PyExc_ValueError, + "unsupported surface bit depth (%d) for drawing", + PG_SURF_BytesPerPixel(surf)); + } + + CHECK_LOAD_COLOR(colorobj) + + if (width < 0) { + return pgRect_New4(rect->x, rect->y, 0, 0); + } + + if (!pgSurface_Lock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error locking surface"); + } + + if (!width || + width >= MIN(rect->w / 2 + rect->w % 2, rect->h / 2 + rect->h % 2)) { + draw_ellipse_filled(surf, rect->x, rect->y, rect->w, rect->h, color, + drawn_area); + } + else { + draw_ellipse_thickness(surf, rect->x, rect->y, rect->w, rect->h, + width - 1, color, drawn_area); + } + + if (!pgSurface_Unlock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error unlocking surface"); + } + + if (drawn_area[0] != INT_MAX && drawn_area[1] != INT_MAX && + drawn_area[2] != INT_MIN && drawn_area[3] != INT_MIN) + return pgRect_New4(drawn_area[0], drawn_area[1], + drawn_area[2] - drawn_area[0] + 1, + drawn_area[3] - drawn_area[1] + 1); + else + return pgRect_New4(rect->x, rect->y, 0, 0); +} + +static PyObject * +aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) +{ + pgSurfaceObject *surfobj; + PyObject *colorobj, *rectobj; + SDL_Rect *rect = NULL, temp; + SDL_Surface *surf = NULL; + Uint32 color; + int width = 0; /* Default width. */ + int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, + INT_MIN}; /* Used to store bounding box values */ + static char *keywords[] = {"surface", "color", "rect", "width", NULL}; + + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OO|i", keywords, + &pgSurface_Type, &surfobj, &colorobj, + &rectobj, &width)) { + return NULL; /* Exception already set. */ + } + + rect = pgRect_FromObject(rectobj, &temp); + + if (!rect) { + return RAISE(PyExc_TypeError, "rect argument is invalid"); + } + + surf = pgSurface_AsSurface(surfobj); + SURF_INIT_CHECK(surf) + + if (PG_SURF_BytesPerPixel(surf) <= 0 || PG_SURF_BytesPerPixel(surf) > 4) { + return PyErr_Format(PyExc_ValueError, + "unsupported surface bit depth (%d) for drawing", + PG_SURF_BytesPerPixel(surf)); + } + + CHECK_LOAD_COLOR(colorobj) + + if (width < 0) { + return pgRect_New4(rect->x, rect->y, 0, 0); + } + + if (!pgSurface_Lock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error locking surface"); + } + + // limit width to size of the rect + if (width != 1 && + (width >= (int)(rect->w / 2) || width >= (int)(rect->h / 2))) { + width = 0; + } + + if (rect->w == 1 && rect->h == 1) { + draw_line(surf, rect->x, rect->y, rect->x, rect->y, color, drawn_area); + } + else if (rect->w == 1) { + // h - 1 because line is drawn 1px longer + draw_line(surf, rect->x, rect->y, rect->x, rect->y + rect->h - 1, + color, drawn_area); + } + else if (rect->h == 1) { + // w - 1 because line is drawn 1px longer + draw_line(surf, rect->x, rect->y, rect->x + rect->w - 1, rect->y, + color, drawn_area); + } + else if (rect->h == rect->w) { + int radius = (int)(rect->w / 2); + PyObject *ret = NULL; + PyObject *center = pg_tuple_couple_from_values_int(rect->x + radius, + rect->y + radius); + PyObject *args = + Py_BuildValue("(OOOii)", surfobj, colorobj, center, radius, width); + + if (!args) { + return NULL; /* Exception already set. */ + } + + ret = aacircle(NULL, args, NULL); + Py_DECREF(args); + return ret; + } + else if (width == 0) { + // larger ellipse is needed when drawing aaellipse with even width + if (rect->w % 2) { + draw_ellipse_filled(surf, rect->x + 1, rect->y + 1, rect->w - 2, + rect->h - 2, color, drawn_area); + } + else { + draw_ellipse_filled(surf, rect->x + 1, rect->y + 1, rect->w - 1, + rect->h - 2, color, drawn_area); + } + draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, 2, + color, drawn_area); + } + else if (width == 1) { + draw_aaellipse_xiaolinwu_thin(surf, rect->x, rect->y, rect->w, rect->h, + color, drawn_area); + } + else { + draw_aaellipse_xiaolinwu(surf, rect->x, rect->y, rect->w, rect->h, + width, color, drawn_area); + } + + if (!pgSurface_Unlock(surfobj)) { + return RAISE(PyExc_RuntimeError, "error unlocking surface"); + } + + if (drawn_area[0] != INT_MAX && drawn_area[1] != INT_MAX && + drawn_area[2] != INT_MIN && drawn_area[3] != INT_MIN) + return pgRect_New4(drawn_area[0], drawn_area[1], + drawn_area[2] - drawn_area[0] + 1, + drawn_area[3] - drawn_area[1] + 1); + else + return pgRect_New4(rect->x, rect->y, 0, 0); +} + static PyObject * polygon(PyObject *self, PyObject *arg, PyObject *kwargs) { @@ -1301,6 +1330,14 @@ swap(float *a, float *b) *b = temp; } +static void +swap_int(int *a, int *b) +{ + int temp = *a; + *a = *b; + *b = temp; +} + static int compare_int(const void *a, const void *b) { @@ -2719,10 +2756,14 @@ draw_circle_filled(SDL_Surface *surf, int x0, int y0, int radius, Uint32 color, } static void -draw_four_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, - int x, int y, float opacity, int *drawn_area) +draw_four_symmetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, + int x, int y, float opacity, int swap_xy, + int *drawn_area) { opacity = opacity / 255.0f; + if (swap_xy) { + swap_int(&x, &y); + } Uint32 pixel_color; pixel_color = get_antialiased_color(surf, x0 + x, y0 - y, color, opacity); set_and_check_rect(surf, x0 + x, y0 - y, pixel_color, drawn_area); @@ -2735,10 +2776,10 @@ draw_four_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, } static void -draw_eight_symetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, - int x, int y, float opacity, int top_right, - int top_left, int bottom_left, int bottom_right, - int *drawn_area) +draw_eight_symmetric_pixels(SDL_Surface *surf, int x0, int y0, Uint32 color, + int x, int y, float opacity, int top_right, + int top_left, int bottom_left, int bottom_right, + int *drawn_area) { opacity = opacity / 255.0f; Uint32 pixel_color; @@ -2800,10 +2841,10 @@ draw_aacircle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, --y; } prev_opacity = opacity; - draw_eight_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - top_right, top_left, bottom_left, - bottom_right, drawn_area); - draw_eight_symetric_pixels( + draw_eight_symmetric_pixels(surf, x0, y0, color, x, y, 255.0f, + top_right, top_left, bottom_left, + bottom_right, drawn_area); + draw_eight_symmetric_pixels( surf, x0, y0, color, x, y - 1, (float)opacity, top_right, top_left, bottom_left, bottom_right, drawn_area); ++x; @@ -2817,11 +2858,11 @@ draw_aacircle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, --y; } prev_opacity = opacity; - draw_eight_symetric_pixels(surf, x0, y0, color, x, y, - 255.0f - (float)opacity, top_right, - top_left, bottom_left, bottom_right, - drawn_area); - draw_eight_symetric_pixels( + draw_eight_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, top_right, + top_left, bottom_left, + bottom_right, drawn_area); + draw_eight_symmetric_pixels( surf, x0, y0, color, x, y - 1, 255.0f, top_right, top_left, bottom_left, bottom_right, drawn_area); ++x; @@ -2835,10 +2876,10 @@ draw_aacircle_xiaolinwu(SDL_Surface *surf, int x0, int y0, int radius, --y; } prev_opacity = opacity; - draw_eight_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - top_right, top_left, bottom_left, - bottom_right, drawn_area); - draw_eight_symetric_pixels( + draw_eight_symmetric_pixels(surf, x0, y0, color, x, y, 255.0f, + top_right, top_left, bottom_left, + bottom_right, drawn_area); + draw_eight_symmetric_pixels( surf, x0, y0, color, x, y - 1, 255.0f, top_right, top_left, bottom_left, bottom_right, drawn_area); ++x; @@ -2864,12 +2905,12 @@ draw_aacircle_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int radius, --y; } prev_opacity = opacity; - draw_eight_symetric_pixels( + draw_eight_symmetric_pixels( surf, x0, y0, color, x, y, 255.0f - (float)opacity, top_right, top_left, bottom_left, bottom_right, drawn_area); - draw_eight_symetric_pixels(surf, x0, y0, color, x, y - 1, - (float)opacity, top_right, top_left, - bottom_left, bottom_right, drawn_area); + draw_eight_symmetric_pixels(surf, x0, y0, color, x, y - 1, + (float)opacity, top_right, top_left, + bottom_left, bottom_right, drawn_area); ++x; } } @@ -3129,14 +3170,20 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, int height, int thickness, Uint32 color, int *drawn_area) { + int vertical; int a = width / 2; int b = height / 2; + vertical = a < b; x0 = x0 + a; y0 = y0 + b; + if (vertical) { + swap_int(&a, &b); + } int x = 0; int y = b; int layer_b = (b - thickness); for (int layer_a = a - thickness; layer_a <= a; layer_a++) { + // in case when thickness = w/2 or thickness = h/2 double pow_layer_a = pow(layer_a, 2); double pow_layer_b = pow(layer_b, 2); double layers_sum_root = sqrt(pow_layer_a + pow_layer_b); @@ -3144,37 +3191,44 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, x = 0; y = layer_b; int deg45 = (int)round(pow_layer_a / layers_sum_root) + 1; + // inner antialiased layer if (layer_a == a - thickness) { - while (x < deg45) { - double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); - double opacity = 255.0 * (ceil(height) - height); - if (opacity < prev_opacity) { - --y; + if (width > 3 && height > 3) { + while (x < deg45) { + double height = + layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); + double opacity = 255.0 * (ceil(height) - height); + if (opacity < prev_opacity) { + --y; + } + prev_opacity = opacity; + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f, vertical, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y - 1, + (float)opacity, vertical, + drawn_area); + ++x; } - prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, - (float)opacity, drawn_area); - ++x; - } - x = layer_a + 1; - y = 0; - deg45 = (int)round(pow_layer_b / layers_sum_root) + 1; - while (y < deg45) { - double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); - double opacity = 255.0 * (ceil(width) - width); - if (opacity < prev_opacity) { - --x; + x = layer_a + 1; + y = 0; + deg45 = (int)round(pow_layer_b / layers_sum_root) + 1; + while (y < deg45) { + double width = layer_a * sqrt(1 - pow(y, 2) / pow_layer_b); + double opacity = 255.0 * (ceil(width) - width); + if (opacity < prev_opacity) { + --x; + } + prev_opacity = opacity; + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f, vertical, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x - 1, y, + (float)opacity, vertical, + drawn_area); + ++y; } - prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, - (float)opacity, drawn_area); - ++y; } } + // between fully opaque layers else if (layer_a == a) { while (x < deg45) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); @@ -3183,10 +3237,11 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, - 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, - 255.0f, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, vertical, + drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y - 1, + 255.0f, vertical, drawn_area); ++x; } x = layer_a + 1; @@ -3199,13 +3254,15 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, - 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, - 255.0f, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, vertical, + drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x - 1, y, + 255.0f, vertical, drawn_area); ++y; } } + // outer antialiased layer else { while (x < deg45) { double height = layer_b * sqrt(1 - pow(x, 2) / pow_layer_a); @@ -3214,10 +3271,10 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, - 255.0f, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, 255.0f, + vertical, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y - 1, + 255.0f, vertical, drawn_area); ++x; } x = layer_a + 1; @@ -3231,10 +3288,10 @@ draw_aaellipse_xiaolinwu(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, 255.0f, - drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, - 255.0f, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, 255.0f, + vertical, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x - 1, y, + 255.0f, vertical, drawn_area); ++y; } } @@ -3246,19 +3303,24 @@ static void draw_aaellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, int height, Uint32 color, int *drawn_area) { + int vertical; int a = width / 2; int b = height / 2; + vertical = a < b; x0 = x0 + a; y0 = y0 + b; + if (vertical) { + swap_int(&a, &b); + } double pow_a = pow(a, 2); double pow_b = pow(b, 2); double prev_opacity = 0.0; - // horizontal drawing - int x = 0; - int y = b; // 45 degree coordinate, at that point switch from horizontal to vertical // drawing int deg45 = (int)round(pow_a / sqrt(pow_a + pow_b)) + 1; + // horizontal drawing + int x = 0; + int y = b; while (x < deg45) { double height = b * sqrt(1 - pow(x, 2) / pow_a); double opacity = 255.0 * (ceil(height) - height); @@ -3266,10 +3328,11 @@ draw_aaellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, --y; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, - 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x, y - 1, - (float)opacity, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, vertical, + drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y - 1, + (float)opacity, vertical, drawn_area); ++x; } // vertical drawing @@ -3283,10 +3346,11 @@ draw_aaellipse_xiaolinwu_thin(SDL_Surface *surf, int x0, int y0, int width, --x; } prev_opacity = opacity; - draw_four_symetric_pixels(surf, x0, y0, color, x, y, - 255.0f - (float)opacity, drawn_area); - draw_four_symetric_pixels(surf, x0, y0, color, x - 1, y, - (float)opacity, drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x, y, + 255.0f - (float)opacity, vertical, + drawn_area); + draw_four_symmetric_pixels(surf, x0, y0, color, x - 1, y, + (float)opacity, vertical, drawn_area); ++y; } } From 9ea2fa5caa3e15e20c138d58703e11f02b8952fe Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sun, 10 Nov 2024 00:02:24 +0100 Subject: [PATCH 13/13] Fix wrong return rect aacircle is returning different return rect from aaellipse when it is off-surface --- src_c/draw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 7d785c6e51..420c5ff863 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -1037,17 +1037,20 @@ aaellipse(PyObject *self, PyObject *arg, PyObject *kwargs) } else if (rect->h == rect->w) { int radius = (int)(rect->w / 2); + SDL_Rect *ret_rect = NULL; PyObject *ret = NULL; PyObject *center = pg_tuple_couple_from_values_int(rect->x + radius, rect->y + radius); PyObject *args = Py_BuildValue("(OOOii)", surfobj, colorobj, center, radius, width); - if (!args) { return NULL; /* Exception already set. */ } - ret = aacircle(NULL, args, NULL); + ret_rect = pgRect_FromObject(ret, &temp); + if (ret_rect->w == 0 && ret_rect->h == 0) { + ret = pgRect_New4(rect->x, rect->y, 0, 0); + } Py_DECREF(args); return ret; }