diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py index 2c92911d1fb..404e3433e57 100644 --- a/Tests/test_image_putalpha.py +++ b/Tests/test_image_putalpha.py @@ -1,5 +1,7 @@ from __future__ import annotations +import pytest + from PIL import Image @@ -39,6 +41,9 @@ def test_promote() -> None: assert im.mode == "RGBA" assert im.getpixel((0, 0)) == (1, 2, 3, 4) + with pytest.raises(ValueError, match="image has wrong mode"): + im.im.setalpha() + def test_readonly() -> None: im = Image.new("RGB", (1, 1), (1, 2, 3)) diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py deleted file mode 100644 index 31548bbc91f..00000000000 --- a/Tests/test_lib_image.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -import pytest - -from PIL import Image - - -def test_setmode() -> None: - im = Image.new("L", (1, 1), 255) - im.im.setmode("1") - assert im.im.getpixel((0, 0)) == 255 - im.im.setmode("L") - assert im.im.getpixel((0, 0)) == 255 - - im = Image.new("1", (1, 1), 1) - im.im.setmode("L") - assert im.im.getpixel((0, 0)) == 255 - im.im.setmode("1") - assert im.im.getpixel((0, 0)) == 255 - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.im.setmode("RGB") - assert im.im.getpixel((0, 0)) == (1, 2, 3) - im.im.setmode("RGBA") - assert im.im.getpixel((0, 0)) == (1, 2, 3, 255) - im.im.setmode("RGBX") - assert im.im.getpixel((0, 0)) == (1, 2, 3, 255) - im.im.setmode("RGB") - assert im.im.getpixel((0, 0)) == (1, 2, 3) - - with pytest.raises(ValueError): - im.im.setmode("L") - with pytest.raises(ValueError): - im.im.setmode("RGBABCDE") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3493df1e467..3c71012f26f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2044,19 +2044,19 @@ def putalpha(self, alpha: Image | int) -> None: self._ensure_mutable() - if self.mode not in ("LA", "PA", "RGBA"): - # attempt to promote self to a matching alpha mode + if self.mode in ("RGB", "RGBX"): + # promote self to RGBA + self.im.setalpha() + self._mode = "RGBA" + elif self.mode not in ("LA", "PA", "RGBA"): try: + # do things the hard way mode = getmodebase(self.mode) + "A" - try: - self.im.setmode(mode) - except (AttributeError, ValueError) as e: - # do things the hard way - im = self.im.convert(mode) - if im.mode not in ("LA", "PA", "RGBA"): - msg = "alpha channel could not be added" - raise ValueError(msg) from e # sanity check - self.im = im + im = self.im.convert(mode) + if im.mode not in ("LA", "PA", "RGBA"): + msg = "alpha channel could not be added" + raise ValueError(msg) # sanity check + self.im = im self._mode = self.im.mode except KeyError as e: msg = "illegal image mode" diff --git a/src/_imaging.c b/src/_imaging.c index 9bdb6328782..e9fd69c520d 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -2052,45 +2052,16 @@ _reduce(ImagingObject *self, PyObject *args) { return PyImagingNew(imOut); } -static int -isRGB(const ModeID mode) { - return mode == IMAGING_MODE_RGB || mode == IMAGING_MODE_RGBA || - mode == IMAGING_MODE_RGBX; -} - static PyObject * -im_setmode(ImagingObject *self, PyObject *args) { +im_setalpha(ImagingObject *self, PyObject *args) { /* attempt to modify the mode of an image in place */ - - Imaging im; - - char *mode_name; - Py_ssize_t modelen; - if (!PyArg_ParseTuple(args, "s#:setmode", &mode_name, &modelen)) { - return NULL; - } - - const ModeID mode = findModeID(mode_name); - - im = self->image; - - /* move all logic in here to the libImaging primitive */ - - if (im->mode == mode) { - ; /* same mode; always succeeds */ - } else if (isRGB(im->mode) && isRGB(mode)) { - /* color to color */ - im->mode = mode; - im->bands = modelen; - if (mode == IMAGING_MODE_RGBA) { - (void)ImagingFillBand(im, 3, 255); - } - } else { - /* trying doing an in-place conversion */ - if (!ImagingConvertInPlace(im, mode)) { - return NULL; - } + Imaging im = self->image; + if (im->mode != IMAGING_MODE_RGB && im->mode != IMAGING_MODE_RGBX) { + return ImagingError_ModeError(); } + im->mode = IMAGING_MODE_RGBA; + im->bands = 4; + (void)ImagingFillBand(im, 3, 255); if (self->access) { ImagingAccessDelete(im, self->access); @@ -3739,7 +3710,7 @@ static struct PyMethodDef methods[] = { {"split", (PyCFunction)_split, METH_NOARGS}, {"fillband", (PyCFunction)_fillband, METH_VARARGS}, - {"setmode", (PyCFunction)im_setmode, METH_VARARGS}, + {"setalpha", (PyCFunction)im_setalpha, METH_NOARGS}, {"getpalette", (PyCFunction)_getpalette, METH_VARARGS}, {"getpalettemode", (PyCFunction)_getpalettemode, METH_NOARGS}, diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 1fd14a944ef..23ab9586e30 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1734,27 +1734,3 @@ ImagingConvertTransparent(Imaging imIn, const ModeID mode, int r, int g, int b) return imOut; } - -Imaging -ImagingConvertInPlace(Imaging imIn, const ModeID mode) { - ImagingSectionCookie cookie; - ImagingShuffler convert; - int y; - - /* limited support for inplace conversion */ - if (imIn->mode == IMAGING_MODE_L && mode == IMAGING_MODE_1) { - convert = l2bit; - } else if (imIn->mode == IMAGING_MODE_1 && mode == IMAGING_MODE_L) { - convert = bit2l; - } else { - return ImagingError_ModeError(); - } - - ImagingSectionEnter(&cookie); - for (y = 0; y < imIn->ysize; y++) { - (*convert)((UINT8 *)imIn->image[y], (UINT8 *)imIn->image[y], imIn->xsize); - } - ImagingSectionLeave(&cookie); - - return imIn; -} diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 472bda5d0fd..10d9ec6fd62 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -305,8 +305,6 @@ ImagingCopy(Imaging im); extern Imaging ImagingConvert(Imaging im, ModeID mode, ImagingPalette palette, int dither); extern Imaging -ImagingConvertInPlace(Imaging im, ModeID mode); -extern Imaging ImagingConvertMatrix(Imaging im, ModeID mode, const float m[12]); extern Imaging ImagingConvertTransparent(Imaging im, ModeID mode, int r, int g, int b);