Repository: python-pillow/Pillow

Issue:
Method ImageOps.pad doesn't copy palette of original image
I think i found a bug with `ImageOps.pad` function.

I have an image which mode is `P` and this image has a specific palette. So when you do pad for this image it will return an image that is displayed as completely black.

So this is origin image
![spidy](https://user-images.githubusercontent.com/41536639/190923282-05d98c8f-a6b0-43c9-9101-2db756a56a17.png)

And this image is after applying `ImageOps.pad`
![pad_spidy](https://user-images.githubusercontent.com/41536639/190923554-f76f77e1-26d2-48a2-b754-af9668c0639f.png)

This images all have the same unique values of pixels - ```(array([0, 1, 2, 3, 4, 5], dtype=uint8),
 array([0, 1, 2, 3, 4, 5], dtype=uint8))```
 
 But when convert origin and pad image to rgb it will have different uniq value -  ```(array([0, 1, 2, 3, 4, 5], dtype=uint8),
array([  0,  13,  35,  80,  83,  98, 148, 150, 171, 177, 212, 227, 255],
       dtype=uint8))```

### What did you do?
Download spidy image. It is image with mode `P` and has a specific palette. Read this image via Pillow then use `ImageOps.pad` and you will see full black image.

### What did you expect to happen?
I expect to see pad colorful spidy image.

### What actually happened?
In pad palette of origin image don't copy, so you see full black image after padding. 

### What are your OS, Python and Pillow versions?

* OS: Ubuntu 20.04
* Python: 3.9
* Pillow: 9.1.1

### Reproduce code:
```python
from PIL import Image, ImageOps
import matplotlib.pyplot as plt
import numpy as np

with Image.open("spidy.png") as origin_image:
    plt.imshow(origin_image)
    plt.show()

pad_image = ImageOps.pad(origin_image, (224, 224))
pad_image.save('pad_spidy.png')
plt.imshow(pad_image)
plt.show()

pad_uniq = np.unique(np.asarray(pad_image))
origin_uniq = np.unique(np.asarray(origin_image))

print(pad_uniq, origin_uniq)

pad_rgb_image = pad_image.convert('RGB')
origin_rgb_image = origin_image.convert('RGB')

pad_uniq_val = np.unique(np.asarray(pad_rgb_image))
origin_uniq_val = np.unique(np.asarray(origin_rgb_image))

print(pad_uniq_val, origin_uniq_val)
```



Discussion:

Resolving patch:
diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py
--- a/src/PIL/ImageOps.py
+++ b/src/PIL/ImageOps.py
@@ -21,7 +21,7 @@
 import operator
 import re
 
-from . import Image
+from . import Image, ImagePalette
 
 #
 # helpers
@@ -291,6 +291,8 @@ def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5
         out = resized
     else:
         out = Image.new(image.mode, size, color)
+        if resized.palette:
+            out.putpalette(resized.getpalette())
         if resized.width != size[0]:
             x = round((size[0] - resized.width) * max(0, min(centering[0], 1)))
             out.paste(resized, (x, 0))
@@ -396,9 +398,8 @@ def expand(image, border=0, fill=0):
     width = left + image.size[0] + right
     height = top + image.size[1] + bottom
     color = _color(fill, image.mode)
-    if image.mode == "P" and image.palette:
-        image.load()
-        palette = image.palette.copy()
+    if image.palette:
+        palette = ImagePalette.ImagePalette(palette=image.getpalette())
         if isinstance(color, tuple):
             color = palette.getcolor(color)
     else:


Test patch:
diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py
--- a/Tests/test_imageops.py
+++ b/Tests/test_imageops.py
@@ -149,6 +149,21 @@ def test_pad_round():
     assert new_im.load()[0, 2] == 1
 
 
+@pytest.mark.parametrize("mode", ("P", "PA"))
+def test_palette(mode):
+    im = hopper(mode)
+
+    # Expand
+    expanded_im = ImageOps.expand(im)
+    assert_image_equal(im.convert("RGB"), expanded_im.convert("RGB"))
+
+    # Pad
+    padded_im = ImageOps.pad(im, (256, 128), centering=(0, 0))
+    assert_image_equal(
+        im.convert("RGB"), padded_im.convert("RGB").crop((0, 0, 128, 128))
+    )
+
+
 def test_pil163():
     # Division by zero in equalize if < 255 pixels in image (@PIL163)