Skip to content

Commit c1c7a9f

Browse files
committed
why
1 parent 2049797 commit c1c7a9f

File tree

2 files changed

+109
-98
lines changed

2 files changed

+109
-98
lines changed

src_c/mask.c

Lines changed: 91 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,9 +1985,8 @@ extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
19851985
*/
19861986
static void
19871987
draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
1988-
SDL_Rect *area_rect, int draw_setbits, int draw_unsetbits,
1989-
SDL_Surface *setsurf, SDL_Surface *unsetsurf, Uint32 *setcolor,
1990-
Uint32 *unsetcolor)
1988+
int draw_setbits, int draw_unsetbits, SDL_Surface *setsurf,
1989+
SDL_Surface *unsetsurf, Uint32 *setcolor, Uint32 *unsetcolor)
19911990
{
19921991
Uint8 *pixel = NULL;
19931992
Uint8 bpp;
@@ -2003,15 +2002,6 @@ draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
20032002
return;
20042003
}
20052004

2006-
if (area_rect->x < 0) {
2007-
x_dest -= area_rect->x;
2008-
area_rect->w += area_rect->x;
2009-
}
2010-
if (area_rect->y < 0) {
2011-
y_dest -= area_rect->y;
2012-
area_rect->h += area_rect->y;
2013-
}
2014-
20152005
/* There is also nothing to do when the destination position is such that
20162006
* nothing will be drawn on the surface. */
20172007
if ((x_dest >= surf->w) || (y_dest >= surf->h) || (-x_dest > bitmask->w) ||
@@ -2021,23 +2011,13 @@ draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
20212011

20222012
bpp = PG_SURF_BytesPerPixel(surf);
20232013

2024-
// clamp rect width and height to not stick out of the mask
2025-
area_rect->w = MIN(area_rect->w, bitmask->w - area_rect->x);
2026-
area_rect->h = MIN(area_rect->h, bitmask->h - area_rect->y);
2027-
20282014
xm_start = (x_dest < 0) ? -x_dest : 0;
2029-
if (area_rect->x > 0) {
2030-
xm_start += area_rect->x;
2031-
}
20322015
x_start = (x_dest > 0) ? x_dest : 0;
2033-
x_end = MIN(MIN(surf->w, bitmask->w + x_dest), x_dest + area_rect->w);
2016+
x_end = MIN(surf->w, bitmask->w + x_dest);
20342017

20352018
ym_start = (y_dest < 0) ? -y_dest : 0;
2036-
if (area_rect->y > 0) {
2037-
ym_start += area_rect->y;
2038-
}
20392019
y_start = (y_dest > 0) ? y_dest : 0;
2040-
y_end = MIN(MIN(surf->h, bitmask->h + y_dest), y_dest + area_rect->h);
2020+
y_end = MIN(surf->h, bitmask->h + y_dest);
20412021

20422022
if (NULL == setsurf && NULL == unsetsurf) {
20432023
/* Draw just using color values. No surfaces. */
@@ -2181,7 +2161,7 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
21812161
PyObject *destobj = NULL, *areaobj = NULL;
21822162
SDL_Rect *area_rect, temp_rect;
21832163
SDL_Surface *surf = NULL, *setsurf = NULL, *unsetsurf = NULL;
2184-
bitmask_t *bitmask = pgMask_AsBitmap(self);
2164+
bitmask_t *bitmask = pgMask_AsBitmap(self), *area_bitmask;
21852165
Uint32 *setcolor_ptr = NULL, *unsetcolor_ptr = NULL;
21862166
Uint32 setcolor, unsetcolor;
21872167
int draw_setbits = 0, draw_unsetbits = 0;
@@ -2200,10 +2180,64 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
22002180
return NULL; /* Exception already set. */
22012181
}
22022182

2183+
if (NULL != destobj) {
2184+
int tempx = 0, tempy = 0;
2185+
2186+
/* Destination coordinates can be extracted from:
2187+
* - lists/tuples with 2 items
2188+
* - Rect (or Rect like) objects (uses x, y values) */
2189+
if (pg_TwoIntsFromObj(destobj, &tempx, &tempy)) {
2190+
x_dest = tempx;
2191+
y_dest = tempy;
2192+
}
2193+
else {
2194+
SDL_Rect temp_rect;
2195+
SDL_Rect *dest_rect = pgRect_FromObject(destobj, &temp_rect);
2196+
2197+
if (NULL != dest_rect) {
2198+
x_dest = dest_rect->x;
2199+
y_dest = dest_rect->y;
2200+
}
2201+
else {
2202+
PyErr_SetString(PyExc_TypeError, "invalid dest argument");
2203+
goto to_surface_error;
2204+
}
2205+
}
2206+
}
2207+
2208+
if (areaobj && areaobj != Py_None) {
2209+
if (!(area_rect = pgRect_FromObject(areaobj, &temp_rect))) {
2210+
PyErr_SetString(PyExc_TypeError, "invalid rectstyle argument");
2211+
goto to_surface_error;
2212+
}
2213+
}
2214+
else {
2215+
temp_rect.x = temp_rect.y = 0;
2216+
temp_rect.w = bitmask->w;
2217+
temp_rect.h = bitmask->h;
2218+
area_rect = &temp_rect;
2219+
}
2220+
2221+
if (area_rect->x < 0) {
2222+
// x_dest -= area_rect->x;
2223+
area_rect->w += area_rect->x;
2224+
area_rect->x = 0;
2225+
}
2226+
if (area_rect->y < 0) {
2227+
// y_dest -= area_rect->y;
2228+
area_rect->h += area_rect->y;
2229+
area_rect->y = 0;
2230+
}
2231+
2232+
// clamp rect width and height to not stick out of the mask
2233+
area_rect->w = MAX(MIN(area_rect->w, bitmask->w - area_rect->x), 0);
2234+
area_rect->h = MAX(MIN(area_rect->h, bitmask->h - area_rect->y), 0);
2235+
// pgRect_Normalize(area_rect);
2236+
22032237
if (Py_None == surfobj) {
2204-
surfobj =
2205-
PyObject_CallFunction((PyObject *)&pgSurface_Type, "(ii)ii",
2206-
bitmask->w, bitmask->h, PGS_SRCALPHA, 32);
2238+
surfobj = PyObject_CallFunction((PyObject *)&pgSurface_Type, "(ii)ii",
2239+
area_rect->w, area_rect->h,
2240+
PGS_SRCALPHA, 32);
22072241

22082242
if (NULL == surfobj) {
22092243
if (!PyErr_Occurred()) {
@@ -2287,44 +2321,6 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
22872321
draw_unsetbits = 1;
22882322
}
22892323

2290-
if (NULL != destobj) {
2291-
int tempx = 0, tempy = 0;
2292-
2293-
/* Destination coordinates can be extracted from:
2294-
* - lists/tuples with 2 items
2295-
* - Rect (or Rect like) objects (uses x, y values) */
2296-
if (pg_TwoIntsFromObj(destobj, &tempx, &tempy)) {
2297-
x_dest = tempx;
2298-
y_dest = tempy;
2299-
}
2300-
else {
2301-
SDL_Rect temp_rect;
2302-
SDL_Rect *dest_rect = pgRect_FromObject(destobj, &temp_rect);
2303-
2304-
if (NULL != dest_rect) {
2305-
x_dest = dest_rect->x;
2306-
y_dest = dest_rect->y;
2307-
}
2308-
else {
2309-
PyErr_SetString(PyExc_TypeError, "invalid dest argument");
2310-
goto to_surface_error;
2311-
}
2312-
}
2313-
}
2314-
2315-
if (areaobj && areaobj != Py_None) {
2316-
if (!(area_rect = pgRect_FromObject(areaobj, &temp_rect))) {
2317-
PyErr_SetString(PyExc_TypeError, "invalid rectstyle argument");
2318-
goto to_surface_error;
2319-
}
2320-
}
2321-
else {
2322-
temp_rect.x = temp_rect.y = 0;
2323-
temp_rect.w = bitmask->w;
2324-
temp_rect.h = bitmask->h;
2325-
area_rect = &temp_rect;
2326-
}
2327-
23282324
if (!pgSurface_Lock((pgSurfaceObject *)surfobj)) {
23292325
PyErr_SetString(PyExc_RuntimeError, "cannot lock surface");
23302326
goto to_surface_error;
@@ -2345,14 +2341,42 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
23452341
goto to_surface_error;
23462342
}
23472343

2344+
if (areaobj) {
2345+
assert(area_rect->w >= 0 && area_rect->w >= 0);
2346+
area_bitmask = bitmask_create(area_rect->w, area_rect->h);
2347+
if (NULL == area_bitmask) {
2348+
PyErr_Format(PyExc_MemoryError, "failed to allocate memory for a mask");
2349+
return NULL;
2350+
}
2351+
2352+
bitmask_t *overlap_bitmask = bitmask_copy(area_bitmask);
2353+
if (NULL == overlap_bitmask) {
2354+
PyErr_SetString(PyExc_MemoryError, "failed to allocate memory for a mask");
2355+
return NULL;
2356+
}
2357+
2358+
bitmask_fill(overlap_bitmask);
2359+
2360+
bitmask_overlap_mask(bitmask, overlap_bitmask, area_bitmask,
2361+
area_rect->x, area_rect->y);
2362+
bitmask_free(overlap_bitmask);
2363+
}
2364+
else {
2365+
area_bitmask = bitmask;
2366+
}
2367+
23482368
Py_BEGIN_ALLOW_THREADS; /* Release the GIL. */
23492369

2350-
draw_to_surface(surf, bitmask, x_dest, y_dest, area_rect, draw_setbits,
2370+
draw_to_surface(surf, area_bitmask, x_dest, y_dest, draw_setbits,
23512371
draw_unsetbits, setsurf, unsetsurf, setcolor_ptr,
23522372
unsetcolor_ptr);
23532373

23542374
Py_END_ALLOW_THREADS; /* Obtain the GIL. */
23552375

2376+
if (areaobj) {
2377+
bitmask_free(area_bitmask);
2378+
}
2379+
23562380
if (NULL != unsetsurf &&
23572381
!pgSurface_Unlock((pgSurfaceObject *)unsetsurfobj)) {
23582382
PyErr_SetString(PyExc_RuntimeError, "cannot unlock unsetsurface");

test/mask_test.py

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
from collections import OrderedDict
21
import copy
32
import platform
43
import random
5-
import unittest
64
import sys
5+
import unittest
6+
from collections import OrderedDict
77

88
import pygame
99
from pygame.locals import *
1010
from pygame.math import Vector2
1111

12-
1312
IS_PYPY = "PyPy" == platform.python_implementation()
1413

1514

@@ -3265,7 +3264,6 @@ def test_to_surface__zero_sized_dest_rect(self):
32653264

32663265
assertSurfaceFilled(self, to_surface, expected_color)
32673266

3268-
@unittest.expectedFailure
32693267
def test_to_surface__valid_area_formats(self):
32703268
"""Ensures to_surface handles valid area formats correctly."""
32713269
size = (3, 5)
@@ -3298,7 +3296,6 @@ def test_to_surface__valid_area_formats(self):
32983296
assertSurfaceFilled(self, to_surface, expected_color, area_rect)
32993297
assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area_rect)
33003298

3301-
@unittest.expectedFailure
33023299
def test_to_surface__invalid_area_formats(self):
33033300
"""Ensures to_surface handles invalid area formats correctly."""
33043301
mask = pygame.mask.Mask((3, 5))
@@ -3314,10 +3311,9 @@ def test_to_surface__invalid_area_formats(self):
33143311
)
33153312

33163313
for area in invalid_areas:
3317-
with self.assertRaisesRegex(TypeError, "invalid area argument"):
3318-
unused_to_surface = mask.to_surface(area=area)
3314+
with self.assertRaisesRegex(TypeError, "invalid rectstyle argument"):
3315+
mask.to_surface(area=area)
33193316

3320-
@unittest.expectedFailure
33213317
def test_to_surface__negative_sized_area_rect(self):
33223318
"""Ensures to_surface correctly handles negative sized area rects."""
33233319
size = (3, 5)
@@ -3342,7 +3338,6 @@ def test_to_surface__negative_sized_area_rect(self):
33423338
assertSurfaceFilled(self, to_surface, expected_color, area)
33433339
assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area)
33443340

3345-
@unittest.expectedFailure
33463341
def test_to_surface__zero_sized_area_rect(self):
33473342
"""Ensures to_surface correctly handles zero sized area rects."""
33483343
size = (3, 5)
@@ -4579,8 +4574,6 @@ def test_to_surface__dest_locations(self):
45794574
self, to_surface, surface_color, overlap_rect
45804575
)
45814576

4582-
@unittest.expectedFailure
4583-
@unittest.skipIf(IS_PYPY, "Segfaults on pypy")
45844577
def test_to_surface__area_locations(self):
45854578
"""Ensures area rects can be different locations on/off the mask."""
45864579
SIDE = 7
@@ -4591,10 +4584,10 @@ def test_to_surface__area_locations(self):
45914584
default_unsetcolor = pygame.Color("black")
45924585

45934586
directions = (
4594-
((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right
4595-
((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom
4596-
((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag
4597-
((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag
4587+
[(s, 0) for s in range(-SIDE, SIDE + 1)], # left to right
4588+
[(0, s) for s in range(-SIDE, SIDE + 1)], # top to bottom
4589+
[(s, s) for s in range(-SIDE, SIDE + 1)], # topleft to bottomright diag
4590+
[(-s, s) for s in range(-SIDE, SIDE + 1)], # topright to bottomleft diag
45984591
)
45994592

46004593
for fill in (True, False):
@@ -4617,7 +4610,6 @@ def test_to_surface__area_locations(self):
46174610
self, to_surface, surface_color, overlap_rect
46184611
)
46194612

4620-
@unittest.expectedFailure
46214613
def test_to_surface__dest_and_area_locations(self):
46224614
"""Ensures dest/area values can be different locations on/off the
46234615
surface/mask.
@@ -4671,7 +4663,6 @@ def test_to_surface__dest_and_area_locations(self):
46714663
self, to_surface, surface_color, dest_overlap_rect
46724664
)
46734665

4674-
@unittest.expectedFailure
46754666
def test_to_surface__area_sizes(self):
46764667
"""Ensures area rects can be different sizes."""
46774668
SIDE = 7
@@ -5131,8 +5122,6 @@ def test_to_surface__dest_off_surface_with_setsurface_unsetsurface(self):
51315122
self, to_surface, surface_color, mask_rect
51325123
)
51335124

5134-
@unittest.expectedFailure
5135-
@unittest.skipIf(IS_PYPY, "Segfaults on pypy")
51365125
def test_to_surface__area_on_mask(self):
51375126
"""Ensures area values on the mask work correctly
51385127
when using the defaults for setcolor and unsetcolor.
@@ -5156,16 +5145,18 @@ def test_to_surface__area_on_mask(self):
51565145
overlap_rect = mask_rect.clip(area_rect)
51575146
overlap_rect.topleft = (0, 0)
51585147

5159-
to_surface = mask.to_surface(surface, area=area_rect)
5148+
with self.subTest(
5149+
pos=pos, area_rect=area_rect.copy(), overlap_rect=overlap_rect
5150+
):
5151+
to_surface = mask.to_surface(surface, area=area_rect)
51605152

5161-
self.assertIs(to_surface, surface)
5162-
self.assertEqual(to_surface.get_size(), size)
5163-
assertSurfaceFilled(self, to_surface, expected_color, overlap_rect)
5164-
assertSurfaceFilledIgnoreArea(
5165-
self, to_surface, surface_color, overlap_rect
5166-
)
5153+
self.assertIs(to_surface, surface)
5154+
self.assertEqual(to_surface.get_size(), size)
5155+
assertSurfaceFilled(self, to_surface, expected_color, overlap_rect)
5156+
assertSurfaceFilledIgnoreArea(
5157+
self, to_surface, surface_color, overlap_rect
5158+
)
51675159

5168-
@unittest.expectedFailure
51695160
def test_to_surface__area_on_mask_with_setsurface_unsetsurface(self):
51705161
"""Ensures area values on the mask work correctly
51715162
when using setsurface and unsetsurface.
@@ -5222,8 +5213,6 @@ def test_to_surface__area_on_mask_with_setsurface_unsetsurface(self):
52225213
self, to_surface, surface_color, overlap_rect
52235214
)
52245215

5225-
@unittest.expectedFailure
5226-
@unittest.skipIf(IS_PYPY, "Segfaults on pypy")
52275216
def test_to_surface__area_off_mask(self):
52285217
"""Ensures area values off the mask work correctly
52295218
when using the defaults for setcolor and unsetcolor.
@@ -5259,8 +5248,6 @@ def test_to_surface__area_off_mask(self):
52595248
self, to_surface, surface_color, overlap_rect
52605249
)
52615250

5262-
@unittest.expectedFailure
5263-
@unittest.skipIf(IS_PYPY, "Segfaults on pypy")
52645251
def test_to_surface__area_off_mask_with_setsurface_unsetsurface(self):
52655252
"""Ensures area values off the mask work correctly
52665253
when using setsurface and unsetsurface.

0 commit comments

Comments
 (0)