Repository: python-pillow/Pillow

Issue:
Rounded rectangle corners got an offset 
### What did you do?

I use the new rounded rectangle method with the given code and values.

### What did you expect to happen?

I get a rounded rectangle

### What actually happened?

I got a rounded rectangle, but the bottom corners got an offset:

![test](https://user-images.githubusercontent.com/24532065/113515659-196a2b80-9576-11eb-97ea-4462b12a3bad.png)

### What are your OS, Python and Pillow versions?

* OS: Windows 10
* Python: 3.9
* Pillow: 8.2.0


```python
xy =  [(104, 37), (109, 87)]
radius = 3
fill = (196, 196, 196, 255)

fac = 5
width, height = (xy[1][0] - xy[0][0]) * fac, (xy[1][1] - xy[0][1]) * fac
img = Image.new('RGBA', (width, height))
image_draw = ImageDraw.Draw(img)
image_draw.rounded_rectangle([(0, 0), (width, height)], radius * fac, fill=fill)
img.save('test.png')  # <- Image was taken from here
img = img.resize((int(width / fac), int(height / fac)), Image.ANTIALIAS)
image.paste(img, (xy[0][0], xy[0][1]), img)
```



Discussion:

Resolving patch:
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -282,6 +282,7 @@ def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):
             # If the corners have no curve, that is a rectangle
             return self.rectangle(xy, fill, outline, width)
 
+        r = d // 2
         ink, fill = self._getink(outline, fill)
 
         def draw_corners(pieslice):
@@ -315,36 +316,28 @@ def draw_corners(pieslice):
             draw_corners(True)
 
             if full_x:
-                self.draw.draw_rectangle(
-                    (x0, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
-                )
+                self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill, 1)
             else:
-                self.draw.draw_rectangle(
-                    (x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y1), fill, 1
-                )
+                self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill, 1)
             if not full_x and not full_y:
-                self.draw.draw_rectangle(
-                    (x0, y0 + d / 2 + 1, x0 + d / 2, y1 - d / 2 - 1), fill, 1
-                )
-                self.draw.draw_rectangle(
-                    (x1 - d / 2, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
-                )
+                self.draw.draw_rectangle((x0, y0 + r + 1, x0 + r, y1 - r - 1), fill, 1)
+                self.draw.draw_rectangle((x1 - r, y0 + r + 1, x1, y1 - r - 1), fill, 1)
         if ink is not None and ink != fill and width != 0:
             draw_corners(False)
 
             if not full_x:
                 self.draw.draw_rectangle(
-                    (x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y0 + width - 1), ink, 1
+                    (x0 + r + 1, y0, x1 - r - 1, y0 + width - 1), ink, 1
                 )
                 self.draw.draw_rectangle(
-                    (x0 + d / 2 + 1, y1 - width + 1, x1 - d / 2 - 1, y1), ink, 1
+                    (x0 + r + 1, y1 - width + 1, x1 - r - 1, y1), ink, 1
                 )
             if not full_y:
                 self.draw.draw_rectangle(
-                    (x0, y0 + d / 2 + 1, x0 + width - 1, y1 - d / 2 - 1), ink, 1
+                    (x0, y0 + r + 1, x0 + width - 1, y1 - r - 1), ink, 1
                 )
                 self.draw.draw_rectangle(
-                    (x1 - width + 1, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), ink, 1
+                    (x1 - width + 1, y0 + r + 1, x1, y1 - r - 1), ink, 1
                 )
 
     def _multiline_check(self, text):


Test patch:
diff --git a/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_given.png b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_given.png
new file mode 100644
Binary files /dev/null and b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_given.png differ
diff --git a/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_height.png b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_height.png
new file mode 100644
Binary files /dev/null and b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_height.png differ
diff --git a/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_width.png b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_width.png
new file mode 100644
Binary files /dev/null and b/Tests/images/imagedraw_rounded_rectangle_non_integer_radius_width.png differ
diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py
--- a/Tests/test_imagedraw.py
+++ b/Tests/test_imagedraw.py
@@ -722,6 +722,29 @@ def test_rounded_rectangle(xy):
     assert_image_equal_tofile(im, "Tests/images/imagedraw_rounded_rectangle.png")
 
 
+@pytest.mark.parametrize(
+    "xy, radius, type",
+    [
+        ((10, 20, 190, 180), 30.5, "given"),
+        ((10, 10, 181, 190), 90, "width"),
+        ((10, 20, 190, 181), 85, "height"),
+    ],
+)
+def test_rounded_rectangle_non_integer_radius(xy, radius, type):
+    # Arrange
+    im = Image.new("RGB", (200, 200))
+    draw = ImageDraw.Draw(im)
+
+    # Act
+    draw.rounded_rectangle(xy, radius, fill="red", outline="green", width=5)
+
+    # Assert
+    assert_image_equal_tofile(
+        im,
+        "Tests/images/imagedraw_rounded_rectangle_non_integer_radius_" + type + ".png",
+    )
+
+
 def test_rounded_rectangle_zero_radius():
     # Arrange
     im = Image.new("RGB", (W, H))