Skip to content

Commit

Permalink
Merge pull request #6954 from radarhere/corners
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Feb 26, 2023
2 parents 6df3ad6 + 60208a3 commit 7e8b11b
Show file tree
Hide file tree
Showing 19 changed files with 105 additions and 35 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,36 @@ def test_rounded_rectangle(xy):
assert_image_equal_tofile(im, "Tests/images/imagedraw_rounded_rectangle.png")


@pytest.mark.parametrize("top_left", (True, False))
@pytest.mark.parametrize("top_right", (True, False))
@pytest.mark.parametrize("bottom_right", (True, False))
@pytest.mark.parametrize("bottom_left", (True, False))
def test_rounded_rectangle_corners(top_left, top_right, bottom_right, bottom_left):
corners = (top_left, top_right, bottom_right, bottom_left)

# Arrange
im = Image.new("RGB", (200, 200))
draw = ImageDraw.Draw(im)

# Act
draw.rounded_rectangle(
(10, 20, 190, 180), 30, fill="red", outline="green", width=5, corners=corners
)

# Assert
suffix = "".join(
(
("y" if top_left else "n"),
("y" if top_right else "n"),
("y" if bottom_right else "n"),
("y" if bottom_left else "n"),
)
)
assert_image_equal_tofile(
im, "Tests/images/imagedraw_rounded_rectangle_corners_" + suffix + ".png"
)


@pytest.mark.parametrize(
"xy, radius, type",
[
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/ImageDraw.rst
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ Methods
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
:param width: The line width, in pixels.
:param corners: A tuple of whether to round each corner,
`(top_left, top_right, bottom_right, bottom_left)`.

.. versionadded:: 8.2.0

Expand Down
108 changes: 73 additions & 35 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,29 +295,37 @@ def rectangle(self, xy, fill=None, outline=None, width=1):
if ink is not None and ink != fill and width != 0:
self.draw.draw_rectangle(xy, ink, 0, width)

def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):
def rounded_rectangle(
self, xy, radius=0, fill=None, outline=None, width=1, *, corners=None
):
"""Draw a rounded rectangle."""
if isinstance(xy[0], (list, tuple)):
(x0, y0), (x1, y1) = xy
else:
x0, y0, x1, y1 = xy
if corners is None:
corners = (True, True, True, True)

d = radius * 2

full_x = d >= x1 - x0
if full_x:
# The two left and two right corners are joined
d = x1 - x0
full_y = d >= y1 - y0
if full_y:
# The two top and two bottom corners are joined
d = y1 - y0
if full_x and full_y:
# If all corners are joined, that is a circle
return self.ellipse(xy, fill, outline, width)

if d == 0:
# If the corners have no curve, that is a rectangle
full_x, full_y = False, False
if all(corners):
full_x = d >= x1 - x0
if full_x:
# The two left and two right corners are joined
d = x1 - x0
full_y = d >= y1 - y0
if full_y:
# The two top and two bottom corners are joined
d = y1 - y0
if full_x and full_y:
# If all corners are joined, that is a circle
return self.ellipse(xy, fill, outline, width)

if d == 0 or not any(corners):
# If the corners have no curve,
# or there are no corners,
# that is a rectangle
return self.rectangle(xy, fill, outline, width)

r = d // 2
Expand All @@ -338,12 +346,17 @@ def draw_corners(pieslice):
)
else:
# Draw four separate corners
parts = (
((x1 - d, y0, x1, y0 + d), 270, 360),
((x1 - d, y1 - d, x1, y1), 0, 90),
((x0, y1 - d, x0 + d, y1), 90, 180),
((x0, y0, x0 + d, y0 + d), 180, 270),
)
parts = []
for i, part in enumerate(
(
((x0, y0, x0 + d, y0 + d), 180, 270),
((x1 - d, y0, x1, y0 + d), 270, 360),
((x1 - d, y1 - d, x1, y1), 0, 90),
((x0, y1 - d, x0 + d, y1), 90, 180),
)
):
if corners[i]:
parts.append(part)
for part in parts:
if pieslice:
self.draw.draw_pieslice(*(part + (fill, 1)))
Expand All @@ -358,25 +371,50 @@ def draw_corners(pieslice):
else:
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 + r + 1, x0 + r, y1 - r - 1), fill, 1)
self.draw.draw_rectangle((x1 - r, y0 + r + 1, x1, y1 - r - 1), fill, 1)
left = [x0, y0, x0 + r, y1]
if corners[0]:
left[1] += r + 1
if corners[3]:
left[3] -= r + 1
self.draw.draw_rectangle(left, fill, 1)

right = [x1 - r, y0, x1, y1]
if corners[1]:
right[1] += r + 1
if corners[2]:
right[3] -= r + 1
self.draw.draw_rectangle(right, 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 + r + 1, y0, x1 - r - 1, y0 + width - 1), ink, 1
)
self.draw.draw_rectangle(
(x0 + r + 1, y1 - width + 1, x1 - r - 1, y1), ink, 1
)
top = [x0, y0, x1, y0 + width - 1]
if corners[0]:
top[0] += r + 1
if corners[1]:
top[2] -= r + 1
self.draw.draw_rectangle(top, ink, 1)

bottom = [x0, y1 - width + 1, x1, y1]
if corners[3]:
bottom[0] += r + 1
if corners[2]:
bottom[2] -= r + 1
self.draw.draw_rectangle(bottom, ink, 1)
if not full_y:
self.draw.draw_rectangle(
(x0, y0 + r + 1, x0 + width - 1, y1 - r - 1), ink, 1
)
self.draw.draw_rectangle(
(x1 - width + 1, y0 + r + 1, x1, y1 - r - 1), ink, 1
)
left = [x0, y0, x0 + width - 1, y1]
if corners[0]:
left[1] += r + 1
if corners[3]:
left[3] -= r + 1
self.draw.draw_rectangle(left, ink, 1)

right = [x1 - width + 1, y0, x1, y1]
if corners[1]:
right[1] += r + 1
if corners[2]:
right[3] -= r + 1
self.draw.draw_rectangle(right, ink, 1)

def _multiline_check(self, text):
"""Draw text."""
Expand Down

0 comments on commit 7e8b11b

Please sign in to comment.