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

Add fuzzy color support for Mask.to_surface #1747

Merged
merged 1 commit into from May 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 6 additions & 3 deletions buildconfig/pygame-stubs/mask.pyi
@@ -1,4 +1,4 @@
from typing import Optional, Union, Tuple, List, TypeVar, Sequence, NewType
from typing import Optional, Union, Text, Tuple, List, TypeVar, Sequence, NewType

from pygame.math import Vector2
from pygame.surface import Surface
Expand All @@ -9,6 +9,9 @@ _Coordinate = Union[Tuple[float, float], List[float], Vector2]
_ColorValue = Union[
Color, Tuple[int, int, int], List[int], int, Tuple[int, int, int, int]
]
_ToSurfaceColorValue = Union[
Color, Tuple[int, int, int], List[int], int, Text, Tuple[int, int, int, int]
]
_RectValue = Union[
Rect,
Union[Tuple[int, int, int, int], List[int]],
Expand Down Expand Up @@ -67,7 +70,7 @@ class Mask:
surface: Optional[Surface] = None,
setsurface: Optional[Surface] = None,
unsetsurface: Optional[Surface] = None,
setcolor: Optional[_ColorValue] = (255, 255, 255, 255),
unsetcolor: Optional[_ColorValue] = (0, 0, 0, 255),
setcolor: Optional[_ToSurfaceColorValue] = (255, 255, 255, 255),
unsetcolor: Optional[_ToSurfaceColorValue] = (0, 0, 0, 255),
dest: Optional[Union[_RectValue, _Coordinate]] = (0, 0),
) -> Surface: ...
4 changes: 2 additions & 2 deletions docs/reST/ref/mask.rst
Expand Up @@ -599,13 +599,13 @@ to store which parts collide.
``(255, 255, 255, 255)``, white), use ``None`` to skip drawing the set
bits, the ``setsurface`` parameter (if set) will takes precedence over
this parameter
:type setcolor: Color or int or tuple(int, int, int, [int]) or
:type setcolor: Color or str or int or tuple(int, int, int, [int]) or
list(int, int, int, [int]) or None
:param unsetcolor: (optional) color to draw unset bits (default is
``(0, 0, 0, 255)``, black), use ``None`` to skip drawing the unset
bits, the ``unsetsurface`` parameter (if set) will takes precedence
over this parameter
:type unsetcolor: Color or int or tuple(int, int, int, [int]) or
:type unsetcolor: Color or str or int or tuple(int, int, int, [int]) or
list(int, int, int, [int]) or None
:param dest: (optional) surface destination of where to position the
topleft corner of the mask being drawn (default is ``(0, 0)``), if a
Expand Down
21 changes: 14 additions & 7 deletions src_c/mask.c
Expand Up @@ -1794,20 +1794,21 @@ mask_connected_component(PyObject *self, PyObject *args)
*
* Returns:
* int: 1, means the color data extraction was successful and the color
* parameter contains a valid color value
* parameter contains a valid color value
* 0, means the color data extraction has failed and an exception has
* been set
* been set
*/
static int
extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
Uint32 *color)
{
if ((NULL == color_obj) || (pg_RGBAFromColorObj(color_obj, rgba_color))) {
if (NULL == color_obj) {
*color = SDL_MapRGBA(surf->format, rgba_color[0], rgba_color[1],
rgba_color[2], rgba_color[3]);
return 1;
}
else if (PyInt_Check(color_obj)) {

if (PyInt_Check(color_obj)) {
long intval = PyInt_AsLong(color_obj);

if ((-1 == intval && PyErr_Occurred()) || intval > (long)0xFFFFFFFF) {
Expand All @@ -1818,7 +1819,8 @@ extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
*color = (Uint32)intval;
return 1;
}
else if (PyLong_Check(color_obj)) {

if (PyLong_Check(color_obj)) {
unsigned long longval = PyLong_AsUnsignedLong(color_obj);

if (PyErr_Occurred() || longval > 0xFFFFFFFF) {
Expand All @@ -1830,8 +1832,13 @@ extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
return 1;
}

PyErr_SetString(PyExc_TypeError, "invalid color argument");
return 0;
if (pg_RGBAFromFuzzyColorObj(color_obj, rgba_color)) {
*color = SDL_MapRGBA(surf->format, rgba_color[0], rgba_color[1],
rgba_color[2], rgba_color[3]);
return 1;
}

return 0; /* Exception already set. */
}

/* Draws a mask on a surface.
Expand Down
56 changes: 35 additions & 21 deletions test/mask_test.py
Expand Up @@ -2913,7 +2913,7 @@ def test_to_surface__kwargs_invalid_types(self):
"surface": (1, 2, 3, 4),
"setsurface": pygame.Color("green"),
"unsetsurface": ((1, 2), (2, 1)),
"setcolor": (1, 2),
"setcolor": pygame.Mask((1, 2)),
"unsetcolor": pygame.Surface((2, 2)),
"dest": (0, 0, 0),
}
Expand Down Expand Up @@ -3015,15 +3015,18 @@ def test_to_surface__valid_setcolor_formats(self):
size = (5, 3)
mask = pygame.mask.Mask(size, fill=True)
surface = pygame.Surface(size, SRCALPHA, 32)
green = pygame.Color("green")
greens = ((0, 255, 0), (0, 255, 0, 255), surface.map_rgb(green), green)

for setcolor in greens:
if isinstance(setcolor, int):
expected_color = surface.unmap_rgb(setcolor)
else:
expected_color = setcolor
expected_color = pygame.Color("green")
test_colors = (
(0, 255, 0),
(0, 255, 0, 255),
surface.map_rgb(expected_color),
expected_color,
"green",
"#00FF00FF",
"0x00FF00FF",
)

for setcolor in test_colors:
to_surface = mask.to_surface(setcolor=setcolor)

assertSurfaceFilled(self, to_surface, expected_color)
Expand All @@ -3033,15 +3036,18 @@ def test_to_surface__valid_unsetcolor_formats(self):
size = (5, 3)
mask = pygame.mask.Mask(size)
surface = pygame.Surface(size, SRCALPHA, 32)
green = pygame.Color("green")
greens = ((0, 255, 0), (0, 255, 0, 255), surface.map_rgb(green), green)

for unsetcolor in greens:
if isinstance(unsetcolor, int):
expected_color = surface.unmap_rgb(unsetcolor)
else:
expected_color = unsetcolor
expected_color = pygame.Color("green")
test_colors = (
(0, 255, 0),
(0, 255, 0, 255),
surface.map_rgb(expected_color),
expected_color,
"green",
"#00FF00FF",
"0x00FF00FF",
)

for unsetcolor in test_colors:
to_surface = mask.to_surface(unsetcolor=unsetcolor)

assertSurfaceFilled(self, to_surface, expected_color)
Expand All @@ -3050,17 +3056,25 @@ def test_to_surface__invalid_setcolor_formats(self):
"""Ensures to_surface handles invalid setcolor formats correctly."""
mask = pygame.mask.Mask((5, 3))

for setcolor in ("green", "#00FF00FF", "0x00FF00FF"):
for setcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)):
with self.assertRaises(ValueError):
mask.to_surface(setcolor=setcolor)

for setcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1):
with self.assertRaises(TypeError):
to_surface = mask.to_surface(setcolor=setcolor)
mask.to_surface(setcolor=setcolor)

def test_to_surface__invalid_unsetcolor_formats(self):
"""Ensures to_surface handles invalid unsetcolor formats correctly."""
mask = pygame.mask.Mask((5, 3))

for unsetcolor in ("green", "#00FF00FF", "0x00FF00FF"):
for unsetcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)):
with self.assertRaises(ValueError):
mask.to_surface(unsetcolor=unsetcolor)

for unsetcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1):
with self.assertRaises(TypeError):
to_surface = mask.to_surface(unsetcolor=unsetcolor)
mask.to_surface(unsetcolor=unsetcolor)

def test_to_surface__valid_dest_formats(self):
"""Ensures to_surface handles valid dest formats correctly."""
Expand Down