diff --git a/buildconfig/stubs/pygame/color.pyi b/buildconfig/stubs/pygame/color.pyi index 10980f347c..fc2fb489fb 100644 --- a/buildconfig/stubs/pygame/color.pyi +++ b/buildconfig/stubs/pygame/color.pyi @@ -42,6 +42,7 @@ class Color(Collection[int]): def __mod__(self, other: Color) -> Color: ... def __int__(self) -> int: ... def __float__(self) -> float: ... + def __bytes__(self) -> bytes: ... def __len__(self) -> int: ... def __index__(self) -> int: ... def __invert__(self) -> Color: ... diff --git a/docs/reST/ref/color.rst b/docs/reST/ref/color.rst index 05effd8888..114559ebba 100644 --- a/docs/reST/ref/color.rst +++ b/docs/reST/ref/color.rst @@ -92,6 +92,11 @@ :returns: a newly created :class:`Color` object :rtype: Color + .. versionchanged:: 2.5.6 + ``bytes(Color(...))`` (assuming `bytes `_ is + the built-in type) now returns a ``bytes`` object (of length 4) with the RGBA values of the color, + as opposed to :class:`Color` being interpreted as an integer (think ``int(Color(...))``) causing it + to return a ``bytes`` object filled with 0s the length of said integer. .. versionchangedold:: 2.0.0 Support for tuples, lists, and :class:`Color` objects when creating :class:`Color` objects. diff --git a/src_c/color.c b/src_c/color.c index 694a7853d9..83908ebc90 100644 --- a/src_c/color.c +++ b/src_c/color.c @@ -181,6 +181,9 @@ _color_int(pgColorObject *); static PyObject * _color_float(pgColorObject *); +static PyObject * +_color_bytes(pgColorObject *); + /* Sequence protocol methods */ static Py_ssize_t _color_length(pgColorObject *); @@ -256,6 +259,17 @@ static PyMethodDef _color_methods[] = { {"premul_alpha", (PyCFunction)_premul_alpha, METH_NOARGS, DOC_COLOR_PREMULALPHA}, {"update", (PyCFunction)_color_update, METH_FASTCALL, DOC_COLOR_UPDATE}, + + /** + * While object.__bytes__(self) is listed in the Data Model reference (see: + * https://docs.python.org/3/reference/datamodel.html#object.__bytes__) it + * does not appear to have a PyTypeObject struct analog (see: + * https://docs.python.org/3/c-api/typeobj.html), so we declare it for the + * type as any other custom method. + */ + {"__bytes__", (PyCFunction)_color_bytes, METH_NOARGS, + "Get a byte representation of the color"}, + {NULL, NULL, 0, NULL}}; /** @@ -1753,6 +1767,15 @@ _color_float(pgColorObject *color) return PyFloat_FromDouble((double)tmp); } +/** + * bytes(color) + */ +static PyObject * +_color_bytes(pgColorObject *color) +{ + return PyBytes_FromStringAndSize((char *)color->data, 4); +} + /* Sequence protocol methods */ /** diff --git a/test/color_test.py b/test/color_test.py index addb2d6591..5662ef0a43 100644 --- a/test/color_test.py +++ b/test/color_test.py @@ -773,6 +773,17 @@ def test_long(self): self.assertEqual(c.a, 146) self.assertEqual(int(c), int(0x33727592)) + def test_bytes(self): + c = pygame.Color(0x00012345) + self.assertEqual(c.r, 0x00) + self.assertEqual(c.g, 0x01) + self.assertEqual(c.b, 0x23) + self.assertEqual(c.a, 0x45) + + as_bytes = bytes(c) + self.assertEqual(as_bytes, bytes([0x00, 0x01, 0x23, 0x45])) + self.assertEqual(len(as_bytes), 4) + def test_from_cmy(self): cmy = pygame.Color.from_cmy(0.5, 0.5, 0.5) cmy_tuple = pygame.Color.from_cmy((0.5, 0.5, 0.5))