Repository: python-pillow/Pillow

Issue:
Add rawmode argument to Image.getpalette()
[putpalette](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.putpalette) has a `rawmode` argument which accepts `"RGBA"`, allowing a palette with some transparent colors. Unfortunately [getpalette](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getpalette) does not have a similar argument.

In most but not all of the images I'm working with, the first two elements of the palette are both (0, 0, 0), with the first element corresponding to the transparent color (0, 0, 0, 0) and the second corresponding to (0, 0, 0, 255). I was able to figure this out through experimentation, but would prefer to be more precise, as I have a few images where this is not the case.

Current code:

```python
def recolor_poodle(original_path, new_path):
    image = Image.open(original_path)
    image = image.convert("P")
    unused_palette = 127
    ImageDraw.floodfill(image, (31, 21), unused_palette)
    original_palette = image.getpalette()
    new_palette = [0, 0, 0, 0]
    for palette_index in range(1, len(original_palette) // 3):
        old_rgb = [
            original_palette[3 * palette_index + channel_index]
            for channel_index in range(3)
        ]
        channels = [hex(channel)[2:].rjust(2, "0") for channel in old_rgb]
        # padding
        old_color = f"{channels[0]}{channels[1]}{channels[2]}"
        new_color = OLD_TO_NEW.get(old_color)
        if new_color is None:
            new_palette += old_rgb + [255]
        else:
            new_palette += [
                int(new_color[0:2], base=16),
                int(new_color[2:4], base=16),
                int(new_color[4:6], base=16),
                255,
            ]

    arr = numpy.asarray(image)

    im2 = Image.fromarray(arr, mode="P")
    im2.putpalette(new_palette, rawmode="RGBA")
    im2.save(new_path)
```

Desired code:

```python
def recolor_poodle(original_path, new_path):
    image = Image.open(original_path)
    image = image.convert("P")
    unused_palette = 127
    ImageDraw.floodfill(image, (31, 21), unused_palette)
    original_palette = image.getpalette(rawmode="RGBA")
    new_palette = []
    for palette_index in range(len(original_palette) // 4):
        # Skip mapping transparent colors.
        if original_palette[4 * palette_index + 3] == 0:
            new_palette += [0, 0, 0, 0]
            continue
        old_rgb = [
            original_palette[4 * palette_index + channel_index]
            for channel_index in range(3)
        ]
        channels = [hex(channel)[2:].rjust(2, "0") for channel in old_rgb]
        # padding
        old_color = f"{channels[0]}{channels[1]}{channels[2]}"
        new_color = OLD_TO_NEW.get(old_color)
        if new_color is None:
            new_palette += old_rgb + [255]
        else:
            new_palette += [
                int(new_color[0:2], base=16),
                int(new_color[2:4], base=16),
                int(new_color[4:6], base=16),
                255,
            ]

    arr = numpy.asarray(image)

    im2 = Image.fromarray(arr, mode="P")
    im2.putpalette(new_palette, rawmode="RGBA")
    im2.save(new_path)
```


Discussion:
Hi. While your feature request may still stand, I suggest talking a look at `image.palette.colors`. I expect that will solve your problem.

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -1441,19 +1441,27 @@ def getim(self):
         self.load()
         return self.im.ptr
 
-    def getpalette(self):
+    def getpalette(self, rawmode="RGB"):
         """
         Returns the image palette as a list.
 
+        :param rawmode: The mode in which to return the palette. ``None`` will
+           return the palette in its current mode.
+
+           .. versionadded:: 9.1.0
+
         :returns: A list of color values [r, g, b, ...], or None if the
            image has no palette.
         """
 
         self.load()
         try:
-            return list(self.im.getpalette())
+            mode = self.im.getpalettemode()
         except ValueError:
             return None  # no palette
+        if rawmode is None:
+            rawmode = mode
+        return list(self.im.getpalette(mode, rawmode))
 
     def getpixel(self, xy):
         """


Test patch:
diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py
--- a/Tests/test_image_getpalette.py
+++ b/Tests/test_image_getpalette.py
@@ -1,3 +1,5 @@
+from PIL import Image
+
 from .helper import hopper
 
 
@@ -17,3 +19,26 @@ def palette(mode):
     assert palette("RGBA") is None
     assert palette("CMYK") is None
     assert palette("YCbCr") is None
+
+
+def test_palette_rawmode():
+    im = Image.new("P", (1, 1))
+    im.putpalette((1, 2, 3))
+
+    for rawmode in ("RGB", None):
+        rgb = im.getpalette(rawmode)
+        assert rgb == [1, 2, 3]
+
+    # Convert the RGB palette to RGBA
+    rgba = im.getpalette("RGBA")
+    assert rgba == [1, 2, 3, 255]
+
+    im.putpalette((1, 2, 3, 4), "RGBA")
+
+    # Convert the RGBA palette to RGB
+    rgb = im.getpalette("RGB")
+    assert rgb == [1, 2, 3]
+
+    for rawmode in ("RGBA", None):
+        rgba = im.getpalette(rawmode)
+        assert rgba == [1, 2, 3, 4]