Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added area kwarg to mask.to_surface #2670

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildconfig/stubs/pygame/mask.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Mask:
setcolor: Optional[ColorLike] = (255, 255, 255, 255),
unsetcolor: Optional[ColorLike] = (0, 0, 0, 255),
dest: Union[RectLike, Point] = (0, 0),
area: Optional[RectLike] = None,
) -> Surface: ...

@deprecated("Use `Mask` instead (MaskType is an old alias)")
Expand Down
8 changes: 7 additions & 1 deletion docs/reST/ref/mask.rst
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ to store which parts collide.

| :sl:`Returns a surface with the mask drawn on it`
| :sg:`to_surface() -> Surface`
| :sg:`to_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0)) -> Surface`
| :sg:`to_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0), area=None) -> Surface`

Draws this mask on the given surface. Set bits (bits set to 1) and unset
bits (bits set to 0) can be drawn onto a surface.
Expand Down Expand Up @@ -610,6 +610,12 @@ to store which parts collide.
mask (i.e. position ``(0, 0)`` on the mask always corresponds to
position ``(0, 0)`` on the ``setsurface`` and ``unsetsurface``)
:type dest: Rect or tuple(int, int) or list(int, int) or Vector2(int, int)
:param area: (optional) rectangular portion of the mask to draw. It can be a
rect-like object (a Rect, a tuple or a list with 4 numbers or an object with a
rect attribute, etc) or it can be None (the default) in which case it will use the
entire mask. Just like with Surface.blit, if the rect's topleft is negative
the final destination will be ``dest - rect.topleft``.
:type area: Rect or rect-like object

:returns: the ``surface`` parameter (or a newly created surface if no
``surface`` parameter was provided) with this mask drawn on it
Expand Down
2 changes: 1 addition & 1 deletion src_c/doc/mask_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
#define DOC_MASK_MASK_CONNECTEDCOMPONENT "connected_component() -> Mask\nconnected_component(pos) -> Mask\nReturns a mask containing a connected component"
#define DOC_MASK_MASK_CONNECTEDCOMPONENTS "connected_components() -> [Mask, ...]\nconnected_components(minimum=0) -> [Mask, ...]\nReturns a list of masks of connected components"
#define DOC_MASK_MASK_GETBOUNDINGRECTS "get_bounding_rects() -> [Rect, ...]\nReturns a list of bounding rects of connected components"
#define DOC_MASK_MASK_TOSURFACE "to_surface() -> Surface\nto_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0)) -> Surface\nReturns a surface with the mask drawn on it"
#define DOC_MASK_MASK_TOSURFACE "to_surface() -> Surface\nto_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0), area=None) -> Surface\nReturns a surface with the mask drawn on it"
150 changes: 105 additions & 45 deletions src_c/mask.c
Original file line number Diff line number Diff line change
Expand Up @@ -1985,7 +1985,8 @@ extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
*/
static void
draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
int draw_setbits, int draw_unsetbits, SDL_Surface *setsurf,
int x_area_offset, int y_area_offset, int draw_setbits,
int draw_unsetbits, SDL_Surface *setsurf,
SDL_Surface *unsetsurf, Uint32 *setcolor, Uint32 *unsetcolor)
{
Uint8 *pixel = NULL;
Expand Down Expand Up @@ -2050,10 +2051,12 @@ draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,

for (y = y_start, ym = ym_start; y < y_end; ++y, ++ym) {
pixel = (Uint8 *)surf->pixels + y * surf->pitch + x_start * bpp;
setpixel = (Uint8 *)setsurf->pixels + ym * setsurf->pitch +
xm_start * bpp;
unsetpixel = (Uint8 *)unsetsurf->pixels + ym * unsetsurf->pitch +
xm_start * bpp;
setpixel = (Uint8 *)setsurf->pixels +
(y_area_offset + ym) * setsurf->pitch +
(x_area_offset + xm_start) * bpp;
unsetpixel = (Uint8 *)unsetsurf->pixels +
(y_area_offset + ym) * unsetsurf->pitch +
(x_area_offset + xm_start) * bpp;

for (x = x_start, xm = xm_start; x < x_end;
++x, ++xm, pixel += bpp, setpixel += bpp, unsetpixel += bpp) {
Expand Down Expand Up @@ -2084,13 +2087,15 @@ draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
draw_unsetbits && NULL != unsetsurf && unsetsurf->h > ym;

if (use_setsurf) {
setpixel = (Uint8 *)setsurf->pixels + ym * setsurf->pitch +
xm_start * bpp;
setpixel = (Uint8 *)setsurf->pixels +
(y_area_offset + ym) * setsurf->pitch +
(x_area_offset + xm_start) * bpp;
}

if (use_unsetsurf) {
unsetpixel = (Uint8 *)unsetsurf->pixels +
ym * unsetsurf->pitch + xm_start * bpp;
(y_area_offset + ym) * unsetsurf->pitch +
(x_area_offset + xm_start) * bpp;
}

for (x = x_start, xm = xm_start; x < x_end;
Expand Down Expand Up @@ -2158,31 +2163,93 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *surfobj = Py_None, *setcolorobj = NULL, *unsetcolorobj = NULL;
PyObject *setsurfobj = Py_None, *unsetsurfobj = Py_None;
PyObject *destobj = NULL;
PyObject *destobj = NULL, *areaobj = NULL;
SDL_Rect *area_rect, temp_rect;
SDL_Surface *surf = NULL, *setsurf = NULL, *unsetsurf = NULL;
bitmask_t *bitmask = pgMask_AsBitmap(self);
bitmask_t *bitmask = pgMask_AsBitmap(self), *area_bitmask;
Uint32 *setcolor_ptr = NULL, *unsetcolor_ptr = NULL;
Uint32 setcolor, unsetcolor;
int draw_setbits = 0, draw_unsetbits = 0;
int created_surfobj = 0; /* Set to 1 if this func creates the surfobj. */
int x_dest = 0, y_dest = 0; /* Default destination coordinates. */
Uint8 dflt_setcolor[] = {255, 255, 255, 255}; /* Default set color. */
Uint8 dflt_unsetcolor[] = {0, 0, 0, 255}; /* Default unset color. */
int create_area_bitmask = 0;

static char *keywords[] = {"surface", "setsurface", "unsetsurface",
"setcolor", "unsetcolor", "dest",
NULL};
"area", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOO", keywords,
&surfobj, &setsurfobj, &unsetsurfobj,
&setcolorobj, &unsetcolorobj, &destobj)) {
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "|OOOOOOO", keywords, &surfobj, &setsurfobj,
&unsetsurfobj, &setcolorobj, &unsetcolorobj, &destobj, &areaobj)) {
return NULL; /* Exception already set. */
}

if (NULL != destobj) {
int tempx = 0, tempy = 0;

/* Destination coordinates can be extracted from:
* - lists/tuples with 2 items
* - Rect (or Rect like) objects (uses x, y values) */
if (pg_TwoIntsFromObj(destobj, &tempx, &tempy)) {
x_dest = tempx;
y_dest = tempy;
}
else {
SDL_Rect temp_rect;
SDL_Rect *dest_rect = pgRect_FromObject(destobj, &temp_rect);

if (NULL != dest_rect) {
x_dest = dest_rect->x;
y_dest = dest_rect->y;
}
else {
PyErr_SetString(PyExc_TypeError, "invalid dest argument");
goto to_surface_error;
}
}
}

if (areaobj && areaobj != Py_None) {
if (!(area_rect = pgRect_FromObject(areaobj, &temp_rect))) {
PyErr_SetString(PyExc_TypeError, "invalid rectstyle argument");
goto to_surface_error;
}

memcpy(&temp_rect, area_rect, sizeof(temp_rect));
area_rect = &temp_rect;

pgRect_Normalize(area_rect);

if (area_rect->x < 0) {
// x_dest -= area_rect->x;
area_rect->w += area_rect->x;
area_rect->x = 0;
}
if (area_rect->y < 0) {
// y_dest -= area_rect->y;
area_rect->h += area_rect->y;
area_rect->y = 0;
}

// clamp rect width and height to not stick out of the mask
area_rect->w = MAX(MIN(area_rect->w, bitmask->w - area_rect->x), 0);
area_rect->h = MAX(MIN(area_rect->h, bitmask->h - area_rect->y), 0);

create_area_bitmask = 1;
}
else {
temp_rect.x = temp_rect.y = 0;
temp_rect.w = bitmask->w;
temp_rect.h = bitmask->h;
area_rect = &temp_rect;
}

if (Py_None == surfobj) {
surfobj =
PyObject_CallFunction((PyObject *)&pgSurface_Type, "(ii)ii",
bitmask->w, bitmask->h, PGS_SRCALPHA, 32);
surfobj = PyObject_CallFunction((PyObject *)&pgSurface_Type, "(ii)ii",
area_rect->w, area_rect->h,
PGS_SRCALPHA, 32);

if (NULL == surfobj) {
if (!PyErr_Occurred()) {
Expand Down Expand Up @@ -2266,31 +2333,6 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
draw_unsetbits = 1;
}

if (NULL != destobj) {
int tempx = 0, tempy = 0;

/* Destination coordinates can be extracted from:
* - lists/tuples with 2 items
* - Rect (or Rect like) objects (uses x, y values) */
if (pg_TwoIntsFromObj(destobj, &tempx, &tempy)) {
x_dest = tempx;
y_dest = tempy;
}
else {
SDL_Rect temp_rect;
SDL_Rect *dest_rect = pgRect_FromObject(destobj, &temp_rect);

if (NULL != dest_rect) {
x_dest = dest_rect->x;
y_dest = dest_rect->y;
}
else {
PyErr_SetString(PyExc_TypeError, "invalid dest argument");
goto to_surface_error;
}
}
}

if (!pgSurface_Lock((pgSurfaceObject *)surfobj)) {
PyErr_SetString(PyExc_RuntimeError, "cannot lock surface");
goto to_surface_error;
Expand All @@ -2311,14 +2353,32 @@ mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
goto to_surface_error;
}

if (create_area_bitmask) {
area_bitmask = bitmask_create(area_rect->w, area_rect->h);
if (NULL == area_bitmask) {
PyErr_Format(PyExc_MemoryError,
"failed to allocate memory for a mask");
return NULL;
}

bitmask_draw(area_bitmask, bitmask, -area_rect->x, -area_rect->y);
}
else {
area_bitmask = bitmask;
}

Py_BEGIN_ALLOW_THREADS; /* Release the GIL. */

draw_to_surface(surf, bitmask, x_dest, y_dest, draw_setbits,
draw_unsetbits, setsurf, unsetsurf, setcolor_ptr,
unsetcolor_ptr);
draw_to_surface(surf, area_bitmask, x_dest, y_dest, area_rect->x,
area_rect->y, draw_setbits, draw_unsetbits, setsurf,
unsetsurf, setcolor_ptr, unsetcolor_ptr);

Py_END_ALLOW_THREADS; /* Obtain the GIL. */

if (create_area_bitmask) {
bitmask_free(area_bitmask);
}

if (NULL != unsetsurf &&
!pgSurface_Unlock((pgSurfaceObject *)unsetsurfobj)) {
PyErr_SetString(PyExc_RuntimeError, "cannot unlock unsetsurface");
Expand Down
Loading
Loading