diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index e1b06699c8f..f596f009025 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -227,6 +227,10 @@ def test_trns_RGB(tmp_path: Path) -> None: assert "transparency" not in im_p.info im_p.save(f) + im.info["transparency"] = list(im.info["transparency"]) + im_rgba = im.convert("RGBA") + assert "transparency" not in im_rgba.info + im = Image.new("RGB", (1, 1)) im.info["transparency"] = im.getpixel((0, 0)) im_p = im.convert("P", palette=Image.Palette.ADAPTIVE) @@ -360,8 +364,12 @@ def test_matrix_identity() -> None: # Act # Convert with an identity matrix - converted_im = im.convert(mode="RGB", matrix=identity_matrix) + converted_im = im.convert("RGB", identity_matrix) # Assert # No change assert_image_equal(converted_im, im) + + # Test list + converted_im = im.convert("RGB", list(identity_matrix)) + assert_image_equal(converted_im, im) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8288d4c9b9c..3bec58891e4 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1021,7 +1021,7 @@ def verify(self) -> None: def convert( self, mode: str | None = None, - matrix: tuple[float, ...] | None = None, + matrix: list[float] | tuple[float, ...] | None = None, dither: Dither | None = None, palette: Palette = Palette.WEB, colors: int = 256, @@ -1056,7 +1056,7 @@ def convert( :param mode: The requested mode. See: :ref:`concept-modes`. :param matrix: An optional conversion matrix. If given, this - should be 4- or 12-tuple containing floating point values. + should be 4- or 12-sequence containing floating point values. :param dither: Dithering method, used when converting from mode "RGB" to "P" or from "RGB" or "L" to "1". Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` @@ -1094,7 +1094,7 @@ def convert( transparency = new_im.info["transparency"] def convert_transparency( - m: tuple[float, ...], v: tuple[int, int, int] + m: list[float] | tuple[float, ...], v: tuple[int, int, int] ) -> int: value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 return max(0, min(255, int(value))) diff --git a/src/_imaging.c b/src/_imaging.c index b078ed4563f..7e9c0e391c8 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1059,8 +1059,15 @@ static PyObject * _convert_matrix(ImagingObject *self, PyObject *args) { char *mode_name; float m[12]; - if (!PyArg_ParseTuple(args, "s(ffff)", &mode_name, m + 0, m + 1, m + 2, m + 3)) { - PyErr_Clear(); + PyObject *matrix; + if (!PyArg_ParseTuple(args, "sO", &mode_name, &matrix)) { + return NULL; + } + Py_ssize_t size = PySequence_Size(matrix); + if (size == -1) { + return NULL; + } + if (size == 12) { if (!PyArg_ParseTuple( args, "s(ffffffffffff)", @@ -1078,30 +1085,42 @@ _convert_matrix(ImagingObject *self, PyObject *args) { m + 10, m + 11 )) { - PyErr_SetString(PyExc_TypeError, "matrix must be tuple of length 4 or 12"); return NULL; } + } else if (size == 4) { + if (!PyArg_ParseTuple( + args, "s(ffff)", &mode_name, m + 0, m + 1, m + 2, m + 3 + )) { + return NULL; + } + } else { + PyErr_SetString(PyExc_TypeError, "matrix must be tuple of length 4 or 12"); + return NULL; } const ModeID mode = findModeID(mode_name); - return PyImagingNew(ImagingConvertMatrix(self->image, mode, m)); } static PyObject * _convert_transparent(ImagingObject *self, PyObject *args) { char *mode_name; - int r, g, b; - if (PyArg_ParseTuple(args, "s(iii)", &mode_name, &r, &g, &b)) { - const ModeID mode = findModeID(mode_name); - return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b)); + int r, g = 0, b = 0; + PyObject *transparency; + if (!PyArg_ParseTuple(args, "sO", &mode_name, &transparency)) { + return NULL; } - PyErr_Clear(); - if (PyArg_ParseTuple(args, "si", &mode_name, &r)) { - const ModeID mode = findModeID(mode_name); - return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0)); + + if (PySequence_Check(transparency)) { + if (!PyArg_ParseTuple(args, "s(iii)", &mode_name, &r, &g, &b)) { + return NULL; + } + } else if (!PyArg_ParseTuple(args, "si", &mode_name, &r)) { + return NULL; } - return NULL; + + const ModeID mode = findModeID(mode_name); + return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b)); } static PyObject *