diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index ae9692b39..481ec5935 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -175,6 +175,12 @@ class TestImageGetPixel(AccessTest): self.check(mode, 2**15+1) self.check(mode, 2**16-1) + def test_p_putpixel_rgb_rgba(self): + for color in [(255, 0, 0), (255, 0, 0, 255)]: + im = Image.new("P", (1, 1), 0) + im.putpixel((0, 0), color) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) + @unittest.skipIf(cffi is None, "No cffi") class TestCffiPutPixel(TestImagePutPixel): @@ -294,6 +300,13 @@ class TestCffi(AccessTest): # pixels can contain garbage if image is released self.assertEqual(px[i, 0], 0) + def test_p_putpixel_rgb_rgba(self): + for color in [(255, 0, 0), (255, 0, 0, 255)]: + im = Image.new("P", (1, 1), 0) + access = PyAccess.new(im, False) + access.putpixel((0, 0), color) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) + class TestEmbeddable(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith('win32') or diff --git a/docs/reference/PixelAccess.rst b/docs/reference/PixelAccess.rst index 522e70937..8a8569922 100644 --- a/docs/reference/PixelAccess.rst +++ b/docs/reference/PixelAccess.rst @@ -65,7 +65,8 @@ Access using negative indexes is also possible. Modifies the pixel at x,y. The color is given as a single numerical value for single band images, and a tuple for - multi-band images + multi-band images. In addition to this, RGB and RGBA tuples + are accepted for P images. :param xy: The pixel coordinate, given as (x, y). :param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6e3a9719d..092cc8040 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1651,7 +1651,8 @@ class Image(object): """ Modifies the pixel at the given position. The color is given as a single numerical value for single-band images, and a tuple for - multi-band images. + multi-band images. In addition to this, RGB and RGBA tuples are + accepted for P images. Note that this method is relatively slow. For more extensive changes, use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` @@ -1674,6 +1675,11 @@ class Image(object): if self.pyaccess: return self.pyaccess.putpixel(xy, value) + + if self.mode == "P" and \ + isinstance(value, (list, tuple)) and len(value) in [3, 4]: + # RGB or RGBA value for a P image + value = self.palette.getcolor(value) return self.im.putpixel(xy, value) def remap_palette(self, dest_map, source_palette=None): diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 35abfec81..5df1d400c 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -53,6 +53,8 @@ class PyAccess(object): # Keep pointer to im object to prevent dereferencing. self._im = img.im + if self._im.mode == "P": + self._palette = img.palette # Debugging is polluting test traces, only useful here # when hacking on PyAccess @@ -80,6 +82,12 @@ class PyAccess(object): if y < 0: y = self.ysize + y (x, y) = self.check_xy((x, y)) + + if self._im.mode == "P" and \ + isinstance(color, (list, tuple)) and len(color) in [3, 4]: + # RGB or RGBA value for a P image + color = self._palette.getcolor(color) + return self.set_pixel(x, y, color) def __getitem__(self, xy):