Repository: python-pillow/Pillow

Issue:
Wrong color conversion from HSV string
### What did you do?

I tried to create an image with `HSV` mode and set its color to blue with following kwargs

- `color=(170, 255, 255)  # 170 == 255 * (2/3)`
- `color="hsv(240, 100%, 100%)"`

### What did you expect to happen?

To get a blue image with both approaches.

### What actually happened?

The `color="hsv(240, 100%, 100%)"` kwarg created a white image.

### What are your OS, Python and Pillow versions?

* OS: Arch Linux 
* Python: Python 3.10.11
* Pillow: 9.4.0

---

```python
Image.new("HSV", (200, 200), (170, 255, 255)).show()
```

![tmp7n00mtvm](https://github.com/python-pillow/Pillow/assets/44382264/e58e0f44-1df0-4a5d-8946-9cfb621bbff2)


```python
Image.new("HSV", (200, 200), color="hsv(240, 100%, 100%)").show()
```

![tmpewrf26xo](https://github.com/python-pillow/Pillow/assets/44382264/ab915f04-db92-42d4-a65c-f963cacc61e4)

I tried converting to `RGB` first, but that has the same result.

```python
# blue
Image.new("HSV", (200, 200), color=(170, 255, 255)).convert("RGB").save("image.png")

# white
Image.new("HSV", (200, 200), color="hsv(240, 100%, 100%)").convert("RGB").save("image.png")
```

---

Something weird is also happening with `getpixel()` method.

When a tuple is used to set a color it works. The image is still in `HSV` mode, so 170 is 240 hue, and 255 is 100% saturation and value.
The pixel bytes have expected values.

```python
Image.new("HSV", (200, 200), (170, 255, 255)).getpixel((0,0))
Out[173]: (170, 255, 255)
```

However, when a string is used, then the pixel bytes are totally off. It looks like it is returning `RGB` mode, since this is blue color in RGB code.

```python
Image.new("HSV", (200, 200), color="hsv(240, 100%, 100%)").getpixel((0,0))
Out[174]: (0, 0, 255)
```


So I tried using **`RGB` mode** for the image and that works as expected.

```python
Image.new("RGB", (200, 200), (0, 0, 255)).getpixel((0,0))
Out[177]: (0, 0, 255)

Image.new("RGB", (200, 200), color="hsv(240, 100%, 100%)").getpixel((0,0))
Out[178]: (0, 0, 255)
```


Discussion:
The documentation for [`Image.new()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.new) states
> When creating RGB images, you can also use color strings as supported by the ImageColor module.

This is your problem. When creating **RGB** images.

So `color="hsv(240, 100%, 100%)"` is being interpreted as a RGB value of `(0, 0, 255)`, which is then read by the HSV mode as hsv(0, 0%, 100%), a.k.a white.


Aha, I just read source code of [getrgb()](https://pillow.readthedocs.io/en/stable/_modules/PIL/ImageColor.html#getrgb) and I see it indeed returns RGB code only.

However, that makes it a bit bad developer experience. It would be nice if the string was interpreted according to the mode I set. So this would create a blue image:

```python
im = Image.new("HSV", (200, 200), color="hsv(240, 100%, 100%)")

# would print: (170, 255, 255)
print(im.getpixel((0, 0)))

# would show blue image, it would need to convert it to RGB color space before, of course
im.show()
```

I would say this can be a feature request for [getcolor()](https://pillow.readthedocs.io/en/stable/_modules/PIL/ImageColor.html#getcolor), that would call `gethsv()` instead of `getrgb()` when `HSV` mode is set. :slightly_smiling_face: 

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -2882,7 +2882,7 @@ def new(mode, size, color=0):
     :param color: What color to use for the image.  Default is black.
        If given, this should be a single integer or floating point value
        for single-band modes, and a tuple for multi-band modes (one value
-       per band).  When creating RGB images, you can also use color
+       per band).  When creating RGB or HSV images, you can also use color
        strings as supported by the ImageColor module.  If the color is
        None, the image is not initialised.
     :returns: An :py:class:`~PIL.Image.Image` object.
diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py
--- a/src/PIL/ImageColor.py
+++ b/src/PIL/ImageColor.py
@@ -122,9 +122,11 @@ def getrgb(color):
 
 def getcolor(color, mode):
     """
-    Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a
-    greyscale value if ``mode`` is not color or a palette image. If the string
-    cannot be parsed, this function raises a :py:exc:`ValueError` exception.
+    Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if
+    ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is
+    not color or a palette image, converts the RGB value to a greyscale value.
+    If the string cannot be parsed, this function raises a :py:exc:`ValueError`
+    exception.
 
     .. versionadded:: 1.1.4
 
@@ -137,7 +139,13 @@ def getcolor(color, mode):
     if len(color) == 4:
         color, alpha = color[:3], color[3]
 
-    if Image.getmodebase(mode) == "L":
+    if mode == "HSV":
+        from colorsys import rgb_to_hsv
+
+        r, g, b = color
+        h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255)
+        return int(h * 255), int(s * 255), int(v * 255)
+    elif Image.getmodebase(mode) == "L":
         r, g, b = color
         # ITU-R Recommendation 601-2 for nonlinear RGB
         # scaled to 24 bits to match the convert's implementation.


Test patch:
diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py
--- a/Tests/test_imagecolor.py
+++ b/Tests/test_imagecolor.py
@@ -193,6 +193,10 @@ def test_rounding_errors():
     Image.new("LA", (1, 1), "white")
 
 
+def test_color_hsv():
+    assert (170, 255, 255) == ImageColor.getcolor("hsv(240, 100%, 100%)", "HSV")
+
+
 def test_color_too_long():
     # Arrange
     color_too_long = "hsl(" + "1" * 40 + "," + "1" * 40 + "%," + "1" * 40 + "%)"