Skip to content

Commit 9ce3eba

Browse files
committed
Only draw each pixel once
1 parent f5d49f4 commit 9ce3eba

File tree

5 files changed

+99
-30
lines changed

5 files changed

+99
-30
lines changed
530 Bytes
Loading
567 Bytes
Loading
528 Bytes
Loading

Tests/test_imagedraw.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,30 @@ def test_rounded_rectangle_zero_radius():
734734
assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_width_fill.png")
735735

736736

737+
@pytest.mark.parametrize(
738+
"xy, suffix",
739+
[
740+
((20, 10, 80, 90), "x"),
741+
((10, 20, 90, 80), "y"),
742+
((20, 20, 80, 80), "both"),
743+
],
744+
)
745+
def test_rounded_rectangle_translucent(xy, suffix):
746+
# Arrange
747+
im = Image.new("RGB", (W, H))
748+
draw = ImageDraw.Draw(im, "RGBA")
749+
750+
# Act
751+
draw.rounded_rectangle(
752+
xy, 30, fill=(255, 0, 0, 127), outline=(0, 255, 0, 127), width=5
753+
)
754+
755+
# Assert
756+
assert_image_equal_tofile(
757+
im, "Tests/images/imagedraw_rounded_rectangle_" + suffix + ".png"
758+
)
759+
760+
737761
def test_floodfill():
738762
red = ImageColor.getrgb("red")
739763

src/PIL/ImageDraw.py

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -264,43 +264,88 @@ def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):
264264
else:
265265
x0, y0, x1, y1 = xy
266266

267-
# Do not allow the diameter to be greater than the width or height
268-
d = min(radius * 2, x1 - x0, y1 - y0)
267+
d = radius * 2
268+
269+
full_x = d >= x1 - x0
270+
if full_x:
271+
# The two left and two right corners are joined
272+
d = x1 - x0
273+
full_y = d >= y1 - y0
274+
if full_y:
275+
# The two top and two bottom corners are joined
276+
d = y1 - y0
277+
if full_x and full_y:
278+
# If all corners are joined, that is a circle
279+
return self.ellipse(xy, fill, outline, width)
280+
269281
if d == 0:
282+
# If the corners have no curve, that is a rectangle
270283
return self.rectangle(xy, fill, outline, width)
271284

272285
ink, fill = self._getink(outline, fill)
286+
287+
def draw_corners(pieslice):
288+
if full_x:
289+
# Draw top and bottom halves
290+
parts = (
291+
((x0, y0, x0 + d, y0 + d), 180, 360),
292+
((x0, y1 - d, x0 + d, y1), 0, 180),
293+
)
294+
elif full_y:
295+
# Draw left and right halves
296+
parts = (
297+
((x0, y0, x0 + d, y0 + d), 90, 270),
298+
((x1 - d, y0, x1, y0 + d), 270, 90),
299+
)
300+
else:
301+
# Draw four separate corners
302+
parts = (
303+
((x1 - d, y0, x1, y0 + d), 270, 360),
304+
((x1 - d, y1 - d, x1, y1), 0, 90),
305+
((x0, y1 - d, x0 + d, y1), 90, 180),
306+
((x0, y0, x0 + d, y0 + d), 180, 270),
307+
)
308+
for part in parts:
309+
if pieslice:
310+
self.draw.draw_pieslice(*(part + (fill, 1)))
311+
else:
312+
self.draw.draw_arc(*(part + (ink, width)))
313+
273314
if fill is not None:
274-
self.draw.draw_pieslice((x1 - d, y0, x1, y0 + d), 270, 360, fill, 1)
275-
self.draw.draw_pieslice((x1 - d, y1 - d, x1, y1), 0, 90, fill, 1)
276-
self.draw.draw_pieslice((x0, y1 - d, x0 + d, y1), 90, 180, fill, 1)
277-
self.draw.draw_pieslice((x0, y0, x0 + d, y0 + d), 180, 270, fill, 1)
278-
279-
self.draw.draw_rectangle((x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y1), fill, 1)
280-
self.draw.draw_rectangle(
281-
(x0, y0 + d / 2 + 1, x0 + d / 2, y1 - d / 2 - 1), fill, 1
282-
)
283-
self.draw.draw_rectangle(
284-
(x1 - d / 2, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
285-
)
315+
draw_corners(True)
316+
317+
if full_x:
318+
self.draw.draw_rectangle(
319+
(x0, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
320+
)
321+
else:
322+
self.draw.draw_rectangle(
323+
(x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y1), fill, 1
324+
)
325+
if not full_x and not full_y:
326+
self.draw.draw_rectangle(
327+
(x0, y0 + d / 2 + 1, x0 + d / 2, y1 - d / 2 - 1), fill, 1
328+
)
329+
self.draw.draw_rectangle(
330+
(x1 - d / 2, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), fill, 1
331+
)
286332
if ink is not None and ink != fill and width != 0:
287-
self.draw.draw_arc((x1 - d, y0, x1, y0 + d), 270, 360, ink, width)
288-
self.draw.draw_arc((x1 - d, y1 - d, x1, y1), 0, 90, ink, width)
289-
self.draw.draw_arc((x0, y1 - d, x0 + d, y1), 90, 180, ink, width)
290-
self.draw.draw_arc((x0, y0, x0 + d, y0 + d), 180, 270, ink, width)
333+
draw_corners(False)
291334

292-
self.draw.draw_rectangle(
293-
(x1 - width + 1, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), ink, 1
294-
)
295-
self.draw.draw_rectangle(
296-
(x0 + d / 2 + 1, y1 - width + 1, x1 - d / 2 - 1, y1), ink, 1
297-
)
298-
self.draw.draw_rectangle(
299-
(x0, y0 + d / 2 + 1, x0 + width - 1, y1 - d / 2 - 1), ink, 1
300-
)
301-
self.draw.draw_rectangle(
302-
(x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y0 + width - 1), ink, 1
303-
)
335+
if not full_x:
336+
self.draw.draw_rectangle(
337+
(x0 + d / 2 + 1, y0, x1 - d / 2 - 1, y0 + width - 1), ink, 1
338+
)
339+
self.draw.draw_rectangle(
340+
(x0 + d / 2 + 1, y1 - width + 1, x1 - d / 2 - 1, y1), ink, 1
341+
)
342+
if not full_y:
343+
self.draw.draw_rectangle(
344+
(x0, y0 + d / 2 + 1, x0 + width - 1, y1 - d / 2 - 1), ink, 1
345+
)
346+
self.draw.draw_rectangle(
347+
(x1 - width + 1, y0 + d / 2 + 1, x1, y1 - d / 2 - 1), ink, 1
348+
)
304349

305350
def _multiline_check(self, text):
306351
"""Draw text."""

0 commit comments

Comments
 (0)