Skip to content

Commit

Permalink
Only draw each pixel once
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Jan 16, 2021
1 parent 2ad0ac4 commit 1d17c8d
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 30 deletions.
Binary file added Tests/images/imagedraw_rounded_rectangle_both.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/imagedraw_rounded_rectangle_x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/imagedraw_rounded_rectangle_y.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions Tests/test_imagedraw.py
Expand Up @@ -728,6 +728,29 @@ def test_rounded_rectangle_zero_radius():
assert_image_equal(im, Image.open(expected))


@pytest.mark.parametrize(
"xy, suffix",
[
((20, 10, 80, 90), "x"),
((10, 20, 90, 80), "y"),
((20, 20, 80, 80), "both"),
],
)
def test_rounded_rectangle_translucent(xy, suffix):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im, "RGBA")
expected = "Tests/images/imagedraw_rounded_rectangle_" + suffix + ".png"

# Act
draw.rounded_rectangle(
xy, 30, fill=(255, 0, 0, 127), outline=(0, 255, 0, 127), width=5
)

# Assert
assert_image_equal(im, Image.open(expected))


def test_floodfill():
red = ImageColor.getrgb("red")

Expand Down
100 changes: 70 additions & 30 deletions src/PIL/ImageDraw.py
Expand Up @@ -264,43 +264,83 @@ def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):
else:
x0, y0, x1, y1 = xy

# Do not allow the diameter to be greater than the width or height
d = min(radius * 2, x1 - x0, y1 - y0)
d = radius * 2

full_x = d >= x1 - x0
if full_x:
d = x1 - x0
full_y = d >= y1 - y0
if full_y:
d = y1 - y0
if full_x and full_y:
return self.ellipse(xy, fill, outline, width)

if d == 0:
return self.rectangle(xy, fill, outline, width)

ink, fill = self._getink(outline, fill)

def draw_corners(pieslice):
if full_x:
# Draw top and bottom halves
parts = (
((x0, y0, x0 + d, y0 + d), 180, 360),
((x0, y1 - d, x0 + d, y1), 0, 180),
)
elif full_y:
# Draw left and right halves
parts = (
((x0, y0, x0 + d, y0 + d), 90, 270),
((x1 - d, y0, x1, y0 + d), 270, 90),
)
else:
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),
)
for part in parts:
if pieslice:
self.draw.draw_pieslice(*(part + (fill, 1)))
else:
self.draw.draw_arc(*(part + (ink, width)))

if fill is not None:
self.draw.draw_pieslice((x1 - d, y0, x1, y0 + d), 270, 360, fill, 1)
self.draw.draw_pieslice((x1 - d, y1 - d, x1, y1), 0, 90, fill, 1)
self.draw.draw_pieslice((x0, y1 - d, x0 + d, y1), 90, 180, fill, 1)
self.draw.draw_pieslice((x0, y0, x0 + d, y0 + d), 180, 270, fill, 1)

self.draw.draw_rectangle((x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y1), fill, 1)
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
)
draw_corners(True)

if full_x:
self.draw.draw_rectangle(
(x0, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
)
else:
self.draw.draw_rectangle(
(x0 + d / 2 + 1, y0, x1 - d / 2 - 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
)
if ink is not None and ink != fill and width != 0:
self.draw.draw_arc((x1 - d, y0, x1, y0 + d), 270, 360, ink, width)
self.draw.draw_arc((x1 - d, y1 - d, x1, y1), 0, 90, ink, width)
self.draw.draw_arc((x0, y1 - d, x0 + d, y1), 90, 180, ink, width)
self.draw.draw_arc((x0, y0, x0 + d, y0 + d), 180, 270, ink, width)
draw_corners(False)

self.draw.draw_rectangle(
(x1 - width + 1, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), ink, 1
)
self.draw.draw_rectangle(
(x0 + d / 2 + 1, y1 - width + 1, x1 - d / 2 - 1, y1), ink, 1
)
self.draw.draw_rectangle(
(x0, y0 + d / 2 + 1, x0 + width - 1, y1 - d / 2 - 1), ink, 1
)
self.draw.draw_rectangle(
(x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y0 + width - 1), ink, 1
)
if not full_x:
self.draw.draw_rectangle(
(x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y0 + width - 1), ink, 1
)
self.draw.draw_rectangle(
(x0 + d / 2 + 1, y1 - width + 1, x1 - d / 2 - 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
)
self.draw.draw_rectangle(
(x1 - width + 1, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), ink, 1
)

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

0 comments on commit 1d17c8d

Please sign in to comment.