Skip to content
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
15 changes: 12 additions & 3 deletions Tests/test_image_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,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)
Expand Down Expand Up @@ -353,19 +357,24 @@ def test_matrix_xyz(mode: str) -> None:

def test_matrix_identity() -> None:
# Arrange
im = hopper("RGB")
im = hopper()
assert im.mode == "RGB"

# fmt: off
identity_matrix = (
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0)
# fmt: on
assert im.mode == "RGB"

# 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)
6 changes: 3 additions & 3 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,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,
Expand Down Expand Up @@ -1052,7 +1052,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`
Expand Down Expand Up @@ -1091,7 +1091,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)))
Expand Down
39 changes: 27 additions & 12 deletions src/_imaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -1049,8 +1049,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)",
Expand All @@ -1070,27 +1077,35 @@ _convert_matrix(ImagingObject *self, PyObject *args) {
)) {
return NULL;
}
} else if (!PyArg_ParseTuple(
args, "s(ffff)", &mode_name, m + 0, m + 1, m + 2, m + 3
)) {
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 *
Expand Down
Loading