From c845d91997c4c792ad8637b0bf78d9d1abe1b8a1 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Fri, 4 Oct 2024 21:06:09 +0200 Subject: [PATCH 1/4] Added width argument to aaline Added to code examples Added to documentation --- buildconfig/stubs/pygame/draw.pyi | 1 + .../ref/code_examples/draw_module_example.png | Bin 8645 -> 13220 bytes .../ref/code_examples/draw_module_example.py | 4 ++ docs/reST/ref/draw.rst | 10 ++- src_c/doc/draw_doc.h | 2 +- src_c/draw.c | 64 ++++++++++++++++-- 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/buildconfig/stubs/pygame/draw.pyi b/buildconfig/stubs/pygame/draw.pyi index 57bae66712..919a65920d 100644 --- a/buildconfig/stubs/pygame/draw.pyi +++ b/buildconfig/stubs/pygame/draw.pyi @@ -82,6 +82,7 @@ def aaline( color: ColorLike, start_pos: Coordinate, end_pos: Coordinate, + width: int = 1, ) -> Rect: ... def aalines( surface: Surface, diff --git a/docs/reST/ref/code_examples/draw_module_example.png b/docs/reST/ref/code_examples/draw_module_example.png index f6fef800dd03b5c75df9871361e6bf2815049f53..6e3e6af13cc0b21b83d1f9f54e94843e8dfe3634 100644 GIT binary patch literal 13220 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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 ce13298477..701f45c28f 100644 --- a/docs/reST/ref/code_examples/draw_module_example.py +++ b/docs/reST/ref/code_examples/draw_module_example.py @@ -29,6 +29,10 @@ # Draw on the screen a green line from (0, 0) to (50, 30) # 5 pixels wide. Uses (r, g, b) color - medium sea green. pygame.draw.line(screen, (60, 179, 113), [0, 0], [50, 30], 5) + + # Draw on the screen a green antialiased line from (0, 25) to (50, 55) + # 5 pixels wide. Uses (r, g, b) color - medium sea green. + pygame.draw.aaline(screen, (60, 179, 113), [0, 25], [50, 55], 5) # Draw on the screen a green line from (0, 50) to (50, 80) # Because it is an antialiased line, it is 1 pixel wide. diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index f9300ee153..c09b28ca7e 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -456,8 +456,10 @@ object around the draw calls (see :func:`pygame.Surface.lock` and | :sl:`draw a straight antialiased line` | :sg:`aaline(surface, color, start_pos, end_pos) -> Rect` + :sg:`aaline(surface, color, start_pos, end_pos, width=1) -> Rect` - Draws a straight antialiased line on the given surface. + Draws a straight antialiased line on the given surface. There are no endcaps. + For thick lines the ends are squared off. The line has a thickness of one pixel and the endpoints have a height and width of one pixel each. @@ -543,6 +545,11 @@ object around the draw calls (see :func:`pygame.Surface.lock` and :param end_pos: end position of the line, (x, y) :type end_pos: tuple(int or float, int or float) or list(int or float, int or float) or Vector2(int or float, int or float) + :param int width: (optional) used for line thickness + + | if width >= 1, used for line thickness (default is 1) + | if width < 1, nothing will be drawn + | :returns: a rect bounding the changed pixels, if nothing is drawn the bounding rect's position will be the ``start_pos`` parameter value (float @@ -555,6 +562,7 @@ object around the draw calls (see :func:`pygame.Surface.lock` and .. versionchangedold:: 2.0.0 Added support for keyword arguments. .. versionchanged:: 2.4.0 Removed deprecated 'blend' argument .. versionchanged:: 2.5.0 ``blend`` argument readded for backcompat, but will always raise a deprecation exception when used + .. versionchanged:: 2.5.2 Added line width .. ## pygame.draw.aaline ## diff --git a/src_c/doc/draw_doc.h b/src_c/doc/draw_doc.h index b47ac37250..384936e90c 100644 --- a/src_c/doc/draw_doc.h +++ b/src_c/doc/draw_doc.h @@ -8,5 +8,5 @@ #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" -#define DOC_DRAW_AALINE "aaline(surface, color, start_pos, end_pos) -> Rect\ndraw a straight antialiased line" +#define DOC_DRAW_AALINE "aaline(surface, color, start_pos, end_pos) -> Rect\naaline(surface, color, start_pos, end_pos, width=1) -> Rect\ndraw a straight antialiased line" #define DOC_DRAW_AALINES "aalines(surface, color, closed, points) -> Rect\ndraw multiple contiguous straight antialiased line segments" diff --git a/src_c/draw.c b/src_c/draw.c index 115a6bfd26..fbdad32b22 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -44,6 +44,10 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, static void draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color, int *drawn_area); +void +line_width_corners(float from_x, float from_y, float to_x, float to_y, + int width, float *x1, float *y1, float *x2, float *y2, + float *x3, float *y3, float *x4, float *y4); static void draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty, float endx, float endy, int *drawn_area, @@ -115,16 +119,17 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) PyObject *colorobj, *start, *end; SDL_Surface *surf = NULL; float startx, starty, endx, endy; + int width = 1; /* Default width. */ PyObject *blend = NULL; int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, INT_MIN}; /* Used to store bounding box values */ Uint32 color; - static char *keywords[] = {"surface", "color", "start_pos", - "end_pos", "blend", NULL}; + static char *keywords[] = {"surface", "color", "start_pos", "end_pos", + "width", "blend", NULL}; - if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OOO|O", keywords, + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OOO|iO", keywords, &pgSurface_Type, &surfobj, &colorobj, - &start, &end, &blend)) { + &start, &end, &width, &blend)) { return NULL; /* Exception already set. */ } @@ -157,11 +162,27 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) return RAISE(PyExc_TypeError, "invalid end_pos argument"); } + if (width < 1) { + return pgRect_New4(startx, starty, 0, 0); + } + if (!pgSurface_Lock(surfobj)) { return RAISE(PyExc_RuntimeError, "error locking surface"); } - draw_aaline(surf, color, startx, starty, endx, endy, drawn_area, 0, 0, 0); + if (width > 1) { + float x1, y1, x2, y2, x3, y3, x4, y4; + line_width_corners(startx, starty, endx, endy, width, &x1, &y1, &x2, + &y2, &x3, &y3, &x4, &y4); + draw_line_width(surf, color, startx, starty, endx, endy, width, + drawn_area); + draw_aaline(surf, color, x1, y1, x2, y2, drawn_area, 0, 0, 0); + draw_aaline(surf, color, x3, y3, x4, y4, drawn_area, 0, 0, 0); + } + else { + draw_aaline(surf, color, startx, starty, endx, endy, drawn_area, 0, 0, + 0); + } if (!pgSurface_Unlock(surfobj)) { return RAISE(PyExc_RuntimeError, "error unlocking surface"); @@ -1836,6 +1857,39 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, } } +// Calculates 4 points, representing corners of draw_line_width() +// first two points assemble left line and second two - right line +void +line_width_corners(float from_x, float from_y, float to_x, float to_y, + int width, float *x1, float *y1, float *x2, float *y2, + float *x3, float *y3, float *x4, float *y4) +{ + float aa_width = (float)width / 2; + float extra_width = (1.0 - (width % 2)) / 2; + int steep = fabs(to_x - from_x) <= fabs(to_y - from_y); + + if (steep) { + *x1 = from_x + extra_width + aa_width; + *y1 = from_y; + *x2 = to_x + extra_width + aa_width; + *y2 = to_y; + *x3 = from_x + extra_width - aa_width; + *y3 = from_y; + *x4 = to_x + extra_width - aa_width; + *y4 = to_y; + } + else { + *x1 = from_x; + *y1 = from_y + extra_width + aa_width; + *x2 = to_x; + *y2 = to_y + extra_width + aa_width; + *x3 = from_x; + *y3 = from_y + extra_width - aa_width; + *x4 = to_x; + *y4 = to_y + extra_width - aa_width; + } +} + /* Algorithm modified from * https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm */ From eb8c0056a79426ff7c18201e47f92e094477d8eb Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Fri, 4 Oct 2024 21:08:04 +0200 Subject: [PATCH 2/4] Cleaned docs --- docs/reST/ref/draw.rst | 64 ------------------------------------------ 1 file changed, 64 deletions(-) diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index c09b28ca7e..f2532b0f29 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -461,70 +461,6 @@ object around the draw calls (see :func:`pygame.Surface.lock` and Draws a straight antialiased line on the given surface. There are no endcaps. For thick lines the ends are squared off. - The line has a thickness of one pixel and the endpoints have a height and - width of one pixel each. - - The way a line and its endpoints are drawn: - If both endpoints are equal, only a single pixel is drawn (after - rounding floats to nearest integer). - - Otherwise if the line is not steep (i.e. if the length along the x-axis - is greater than the height along the y-axis): - - For each endpoint: - - If ``x``, the endpoint's x-coordinate, is a whole number find - which pixels would be covered by it and draw them. - - Otherwise: - - Calculate the position of the nearest point with a whole number - for its x-coordinate, when extending the line past the - endpoint. - - Find which pixels would be covered and how much by that point. - - If the endpoint is the left one, multiply the coverage by (1 - - the decimal part of ``x``). - - Otherwise multiply the coverage by the decimal part of ``x``. - - Then draw those pixels. - - *e.g.:* - | The left endpoint of the line ``((1, 1.3), (5, 3))`` would - cover 70% of the pixel ``(1, 1)`` and 30% of the pixel - ``(1, 2)`` while the right one would cover 100% of the - pixel ``(5, 3)``. - | The left endpoint of the line ``((1.2, 1.4), (4.6, 3.1))`` - would cover 56% *(i.e. 0.8 * 70%)* of the pixel ``(1, 1)`` - and 24% *(i.e. 0.8 * 30%)* of the pixel ``(1, 2)`` while - the right one would cover 42% *(i.e. 0.6 * 70%)* of the - pixel ``(5, 3)`` and 18% *(i.e. 0.6 * 30%)* of the pixel - ``(5, 4)`` while the right - - Then for each point between the endpoints, along the line, whose - x-coordinate is a whole number: - - Find which pixels would be covered and how much by that point and - draw them. - - *e.g.:* - | The points along the line ``((1, 1), (4, 2.5))`` would be - ``(2, 1.5)`` and ``(3, 2)`` and would cover 50% of the pixel - ``(2, 1)``, 50% of the pixel ``(2, 2)`` and 100% of the pixel - ``(3, 2)``. - | The points along the line ``((1.2, 1.4), (4.6, 3.1))`` would - be ``(2, 1.8)`` (covering 20% of the pixel ``(2, 1)`` and 80% - of the pixel ``(2, 2)``), ``(3, 2.3)`` (covering 70% of the - pixel ``(3, 2)`` and 30% of the pixel ``(3, 3)``) and ``(4, - 2.8)`` (covering 20% of the pixel ``(2, 1)`` and 80% of the - pixel ``(2, 2)``) - - Otherwise do the same for steep lines as for non-steep lines except - along the y-axis instead of the x-axis (using ``y`` instead of ``x``, - top instead of left and bottom instead of right). - .. note:: Regarding float values for coordinates, a point with coordinate consisting of two whole numbers is considered being right in the center From d02e8b957e2c029dd61d4fd281384c556476faa0 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Fri, 4 Oct 2024 22:21:10 +0200 Subject: [PATCH 3/4] Added tests --- .../ref/code_examples/draw_module_example.py | 2 +- test/draw_test.py | 177 ++++++++++++++---- 2 files changed, 144 insertions(+), 35 deletions(-) diff --git a/docs/reST/ref/code_examples/draw_module_example.py b/docs/reST/ref/code_examples/draw_module_example.py index 701f45c28f..1e155247b2 100644 --- a/docs/reST/ref/code_examples/draw_module_example.py +++ b/docs/reST/ref/code_examples/draw_module_example.py @@ -29,7 +29,7 @@ # Draw on the screen a green line from (0, 0) to (50, 30) # 5 pixels wide. Uses (r, g, b) color - medium sea green. pygame.draw.line(screen, (60, 179, 113), [0, 0], [50, 30], 5) - + # Draw on the screen a green antialiased line from (0, 25) to (50, 55) # 5 pixels wide. Uses (r, g, b) color - medium sea green. pygame.draw.aaline(screen, (60, 179, 113), [0, 25], [50, 55], 5) diff --git a/test/draw_test.py b/test/draw_test.py index eb61d29164..872e72ee58 100644 --- a/test/draw_test.py +++ b/test/draw_test.py @@ -2455,7 +2455,7 @@ def test_aaline__blend_warning(self): warnings.simplefilter("always") # Trigger DeprecationWarning. self.draw_aaline( - pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2), blend + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2), 1, blend ) # Check if there is only one warning and is a DeprecationWarning. self.assertEqual(len(w), count + 1) @@ -2467,12 +2467,14 @@ def test_aaline__kwargs(self): color = pygame.Color("yellow") start_pos = (1, 1) end_pos = (2, 2) + width = 2 kwargs_list = [ { "surface": surface, "color": color, "start_pos": start_pos, "end_pos": end_pos, + "width": width, }, ] @@ -2485,6 +2487,7 @@ def test_aaline__kwargs_order_independent(self): """Ensures draw aaline's kwargs are not order dependent.""" bounds_rect = self.draw_aaline( start_pos=(1, 2), + width=2, end_pos=(2, 1), color=(10, 20, 30), surface=pygame.Surface((3, 2)), @@ -2516,6 +2519,7 @@ def test_aaline__kwargs_missing(self): "color": pygame.Color("red"), "start_pos": (2, 1), "end_pos": (2, 2), + "width": 2, } for name in ("end_pos", "start_pos", "color", "surface"): @@ -2554,30 +2558,42 @@ def test_aaline__kwarg_invalid_types(self): color = pygame.Color("green") start_pos = (1, 0) end_pos = (2, 0) + width = 1 kwargs_list = [ { "surface": pygame.Surface, # Invalid surface. "color": color, "start_pos": start_pos, "end_pos": end_pos, + "width": width, }, { "surface": surface, "color": 2.3, # Invalid color. "start_pos": start_pos, "end_pos": end_pos, + "width": width, }, { "surface": surface, "color": color, "start_pos": (0, 0, 0), # Invalid start_pos. "end_pos": end_pos, + "width": width, }, { "surface": surface, "color": color, "start_pos": start_pos, "end_pos": (0,), # Invalid end_pos. + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), + "width": 1.2, # Invalid width. }, ] @@ -2591,12 +2607,14 @@ def test_aaline__kwarg_invalid_name(self): color = pygame.Color("cyan") start_pos = (1, 1) end_pos = (2, 0) + width = 1 kwargs_list = [ { "surface": surface, "color": color, "start_pos": start_pos, "end_pos": end_pos, + "width": width, "invalid": 1, }, { @@ -2618,14 +2636,16 @@ def test_aaline__args_and_kwargs(self): color = (255, 255, 0, 0) start_pos = (0, 1) end_pos = (1, 2) + width = 1 kwargs = { "surface": surface, "color": color, "start_pos": start_pos, "end_pos": end_pos, + "width": width, } - for name in ("surface", "color", "start_pos", "end_pos"): + for name in ("surface", "color", "start_pos", "end_pos", "width"): kwargs.pop(name) if "surface" == name: @@ -2645,6 +2665,30 @@ def test_aaline__args_and_kwargs(self): self.assertIsInstance(bounds_rect, pygame.Rect) + def test_aaline__valid_width_values(self): + """Ensures draw aaline accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (2, 1) + kwargs = { + "surface": surface, + "color": line_color, + "start_pos": pos, + "end_pos": (2, 2), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_aaline(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + def test_aaline__valid_start_pos_formats(self): """Ensures draw aaline accepts different start_pos formats.""" expected_color = pygame.Color("red") @@ -2655,6 +2699,7 @@ def test_aaline__valid_start_pos_formats(self): "color": expected_color, "start_pos": None, "end_pos": (2, 2), + "width": 2, } x, y = 2, 1 # start position positions = ((x, y), (x + 0.01, y), (x, y + 0.01), (x + 0.01, y + 0.01)) @@ -2683,6 +2728,7 @@ def test_aaline__valid_end_pos_formats(self): "color": expected_color, "start_pos": (2, 1), "end_pos": None, + "width": 2, } x, y = 2, 2 # end position positions = ((x, y), (x + 0.02, y), (x, y + 0.02), (x + 0.02, y + 0.02)) @@ -2708,6 +2754,7 @@ def test_aaline__invalid_start_pos_formats(self): "color": pygame.Color("red"), "start_pos": None, "end_pos": (2, 2), + "width": 2, } start_pos_fmts = ( @@ -2731,6 +2778,7 @@ def test_aaline__invalid_end_pos_formats(self): "color": pygame.Color("red"), "start_pos": (2, 2), "end_pos": None, + "width": 2, } end_pos_fmts = ( @@ -2758,6 +2806,7 @@ def test_aaline__valid_color_formats(self): "color": None, "start_pos": pos, "end_pos": (2, 1), + "width": 2, } greens = ( (0, 255, 0), @@ -2787,6 +2836,7 @@ def test_aaline__invalid_color_formats(self): "color": None, "start_pos": (1, 1), "end_pos": (2, 1), + "width": 2, } for expected_color in (2.3, self): @@ -2804,6 +2854,17 @@ def test_aaline__color(self): self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + def test_aaline__color_with_thickness(self): + """Ensures a thick aaline is drawn using the correct color.""" + from_x = 5 + to_x = 10 + y = 5 + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aaline(surface, expected_color, (from_x, y), (to_x, y), 5) + for pos in ((x, y + i) for i in (-2, 0, 2) for x in (from_x, to_x)): + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + def test_aaline__gaps(self): """Tests if the aaline drawn contains any gaps. @@ -2818,11 +2879,28 @@ def test_aaline__gaps(self): pos = (x, 0) self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + def test_line__gaps_with_thickness(self): + """Ensures a thick aaline is drawn without any gaps.""" + expected_color = (255, 255, 255) + thickness = 5 + for surface in self._create_surfaces(): + width = surface.get_width() - 1 + h = width // 5 + w = h * 5 + self.draw_aaline(surface, expected_color, (0, 5), (w, 5 + h), thickness) + + for x in range(w + 1): + for y in range(3, 8): + pos = (x, y + ((x + 2) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + def test_aaline__bounding_rect(self): """Ensures draw aaline returns the correct bounding rect. - Test lines with endpoints on and off the surface. + Test lines with endpoints on and off the surface and a range of + width/thickness values. """ + line_color = pygame.Color("red") surf_color = pygame.Color("blue") width = height = 30 @@ -2840,16 +2918,32 @@ def test_aaline__bounding_rect(self): for pos in rect_corners_mids_and_center(surf_rect): helper_rect.center = pos - for start, end in self._rect_lines(helper_rect): - surface.fill(surf_color) # Clear for each test. + # Draw using different thicknesses. + for thickness in range(-1, 5): + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. - bounding_rect = self.draw_aaline(surface, line_color, start, end) + bounding_rect = self.draw_aaline( + surface, line_color, start, end, thickness + ) - # Calculating the expected_rect after the line is - # drawn (it uses what is actually drawn). - expected_rect = create_bounding_rect(surface, surf_color, start) + if 0 < thickness: + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, start + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(start, (0, 0)) - self.assertEqual(bounding_rect, expected_rect) + self.assertEqual( + bounding_rect, + expected_rect, + "start={}, end={}, size={}, thickness={}".format( + start, end, size, thickness + ), + ) def test_aaline__surface_clip(self): """Ensures draw aaline respects a surface's clip area.""" @@ -2863,37 +2957,52 @@ def test_aaline__surface_clip(self): clip_rect.center = surface.get_rect().center pos_rect = clip_rect.copy() # Manages the aaline's pos. - # Test centering the pos_rect along the clip rect's edge to allow for - # drawing the aaline over the clip_rect's bounds. - for center in rect_corners_mids_and_center(clip_rect): - pos_rect.center = center + for thickness in (1, 3): # Test different line widths. + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aaline over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center - # Get the expected points by drawing the aaline without the - # clip area set. - surface.set_clip(None) - surface.fill(surface_color) - self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + # Get the expected points by drawing the aaline without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aaline( + surface, + aaline_color, + pos_rect.midtop, + pos_rect.midbottom, + thickness, + ) - expected_pts = get_color_points(surface, surface_color, clip_rect, False) + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) - # Clear the surface and set the clip area. Redraw the aaline - # and check that only the clip area is modified. - surface.fill(surface_color) - surface.set_clip(clip_rect) + # Clear the surface and set the clip area. Redraw the aaline + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) - self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + self.draw_aaline( + surface, + aaline_color, + pos_rect.midtop, + pos_rect.midbottom, + thickness, + ) - surface.lock() # For possible speed up. + surface.lock() # For possible speed up. - # Check all the surface points to ensure the expected_pts - # are not surface_color. - for pt in ((x, y) for x in range(surfw) for y in range(surfh)): - if pt in expected_pts: - self.assertNotEqual(surface.get_at(pt), surface_color, pt) - else: - self.assertEqual(surface.get_at(pt), surface_color, pt) + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) - surface.unlock() + surface.unlock() class DrawAALineTest(AALineMixin, DrawTestCase): From 6135dafaaa62c80124b641c4769e65e50dea5590 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Fri, 4 Oct 2024 23:03:28 +0200 Subject: [PATCH 4/4] Fixed float to int --- src_c/draw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index fbdad32b22..bf29d2ac94 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -163,7 +163,7 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) } if (width < 1) { - return pgRect_New4(startx, starty, 0, 0); + return pgRect_New4((int)startx, (int)starty, 0, 0); } if (!pgSurface_Lock(surfobj)) { @@ -174,8 +174,8 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) float x1, y1, x2, y2, x3, y3, x4, y4; line_width_corners(startx, starty, endx, endy, width, &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4); - draw_line_width(surf, color, startx, starty, endx, endy, width, - drawn_area); + draw_line_width(surf, color, (int)startx, (int)starty, (int)endx, + (int)endy, width, drawn_area); draw_aaline(surf, color, x1, y1, x2, y2, drawn_area, 0, 0, 0); draw_aaline(surf, color, x3, y3, x4, y4, drawn_area, 0, 0, 0); } @@ -1865,7 +1865,7 @@ line_width_corners(float from_x, float from_y, float to_x, float to_y, float *x3, float *y3, float *x4, float *y4) { float aa_width = (float)width / 2; - float extra_width = (1.0 - (width % 2)) / 2; + float extra_width = (1.0f - (width % 2)) / 2; int steep = fabs(to_x - from_x) <= fabs(to_y - from_y); if (steep) {