Pillow/Tests/test_imagedraw.py

907 lines
28 KiB
Python
Raw Normal View History

import os.path
import unittest
2019-07-28 23:40:03 +03:00
from PIL import Image, ImageColor, ImageDraw, ImageFont, features
2015-08-25 15:27:18 +03:00
from .helper import PillowTestCase, hopper
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (190, 190, 190)
2019-06-13 18:54:46 +03:00
DEFAULT_MODE = "RGB"
IMAGES_PATH = os.path.join("Tests", "images", "imagedraw")
2014-05-12 18:32:04 +04:00
# Image size
2014-06-23 23:51:31 +04:00
W, H = 100, 100
2014-05-12 18:32:04 +04:00
# Bounding box points
2014-06-23 23:51:31 +04:00
X0 = int(W / 4)
X1 = int(X0 * 3)
Y0 = int(H / 4)
Y1 = int(X0 * 3)
2014-05-12 18:32:04 +04:00
# Two kinds of bounding box
2014-06-23 23:51:31 +04:00
BBOX1 = [(X0, Y0), (X1, Y1)]
BBOX2 = [X0, Y0, X1, Y1]
2014-05-12 18:32:04 +04:00
# Two kinds of coordinate sequences
2014-06-23 23:51:31 +04:00
POINTS1 = [(10, 10), (20, 40), (30, 30)]
POINTS2 = [10, 10, 20, 40, 30, 30]
KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)]
2019-07-28 23:40:03 +03:00
HAS_FREETYPE = features.check("freetype2")
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
class TestImageDraw(PillowTestCase):
def test_sanity(self):
2014-09-05 13:36:24 +04:00
im = hopper("RGB").copy()
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
draw = ImageDraw.ImageDraw(im)
draw = ImageDraw.Draw(im)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
draw.ellipse(list(range(4)))
draw.line(list(range(10)))
draw.polygon(list(range(100)))
draw.rectangle(list(range(4)))
2014-05-12 18:32:04 +04:00
2015-12-30 01:37:50 +03:00
def test_valueerror(self):
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
with Image.open("Tests/images/chi.gif") as im:
2015-12-30 01:37:50 +03:00
Improve handling of file resources Follow Python's file object semantics. User code is responsible for closing resources (usually through a context manager) in a deterministic way. To achieve this, remove __del__ functions. These functions used to closed open file handlers in an attempt to silence Python ResourceWarnings. However, using __del__ has the following drawbacks: - __del__ isn't called until the object's reference count reaches 0. Therefore, resource handlers remain open or in use longer than necessary. - The __del__ method isn't guaranteed to execute on system exit. See the Python documentation: https://docs.python.org/3/reference/datamodel.html#object.__del__ > It is not guaranteed that __del__() methods are called for objects > that still exist when the interpreter exits. - Exceptions that occur inside __del__ are ignored instead of raised. This has the potential of hiding bugs. This is also in the Python documentation: > Warning: Due to the precarious circumstances under which __del__() > methods are invoked, exceptions that occur during their execution > are ignored, and a warning is printed to sys.stderr instead. Instead, always close resource handlers when they are no longer in use. This will close the file handler at a specified point in the user's code and not wait until the interpreter chooses to. It is always guaranteed to run. And, if an exception occurs while closing the file handler, the bug will not be ignored. Now, when code receives a ResourceWarning, it will highlight an area that is mishandling resources. It should not simply be silenced, but fixed by closing resources with a context manager. All warnings that were emitted during tests have been cleaned up. To enable warnings, I passed the `-Wa` CLI option to Python. This exposed some mishandling of resources in ImageFile.__init__() and SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
draw = ImageDraw.Draw(im)
draw.line((0, 0), fill=(0, 0, 0))
2015-12-30 01:37:50 +03:00
2015-07-03 08:03:25 +03:00
def test_mode_mismatch(self):
im = hopper("RGB").copy()
2017-09-01 14:05:40 +03:00
self.assertRaises(ValueError, ImageDraw.ImageDraw, im, mode="L")
2015-07-03 08:03:25 +03:00
def helper_arc(self, bbox, start, end):
2014-06-10 13:10:47 +04:00
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
# Act
draw.arc(bbox, start, end)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
def test_arc1(self):
self.helper_arc(BBOX1, 0, 180)
self.helper_arc(BBOX1, 0.5, 180.4)
2014-06-10 13:10:47 +04:00
def test_arc2(self):
self.helper_arc(BBOX2, 0, 180)
self.helper_arc(BBOX2, 0.5, 180.4)
def test_arc_end_le_start(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
start = 270.5
end = 0
# Act
draw.arc(BBOX1, start=start, end=end)
# Assert
self.assert_image_equal(
2019-06-13 18:54:46 +03:00
im, Image.open("Tests/images/imagedraw_arc_end_le_start.png")
)
def test_arc_no_loops(self):
# No need to go in loops
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
start = 5
end = 370
# Act
draw.arc(BBOX1, start=start, end=end)
# Assert
self.assert_image_similar(
2019-06-13 18:54:46 +03:00
im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1
)
def test_arc_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width.png"
# Act
draw.arc(BBOX1, 10, 260, width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2019-05-03 16:37:37 +03:00
def test_arc_width_pieslice_large(self):
# Tests an arc with a large enough width that it is a pieslice
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width_pieslice.png"
# Act
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_arc_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width_fill.png"
# Act
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_arc_width_non_whole_angle(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width_non_whole_angle.png"
# Act
draw.arc(BBOX1, 10, 259.5, width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2014-06-10 13:10:47 +04:00
def test_bitmap(self):
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
with Image.open("Tests/images/pil123rgba.png") as small:
small = small.resize((50, 50), Image.NEAREST)
2019-11-25 23:03:23 +03:00
# Act
draw.bitmap((10, 10), small)
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png"))
2017-05-15 18:04:33 +03:00
def helper_chord(self, mode, bbox, start, end):
2014-06-10 13:10:47 +04:00
# Arrange
2017-05-15 18:04:33 +03:00
im = Image.new(mode, (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2017-05-15 18:04:33 +03:00
expected = "Tests/images/imagedraw_chord_{}.png".format(mode)
2014-06-10 13:10:47 +04:00
# Act
draw.chord(bbox, start, end, fill="red", outline="yellow")
2014-06-10 13:10:47 +04:00
# Assert
2017-05-16 10:33:38 +03:00
self.assert_image_similar(im, Image.open(expected), 1)
2014-06-10 13:10:47 +04:00
def test_chord1(self):
2017-05-15 18:04:33 +03:00
for mode in ["RGB", "L"]:
self.helper_chord(mode, BBOX1, 0, 180)
self.helper_chord(mode, BBOX1, 0.5, 180.4)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
def test_chord2(self):
2017-05-15 18:04:33 +03:00
for mode in ["RGB", "L"]:
self.helper_chord(mode, BBOX2, 0, 180)
self.helper_chord(mode, BBOX2, 0.5, 180.4)
2014-05-12 18:32:04 +04:00
def test_chord_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_chord_width.png"
# Act
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_chord_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_chord_width_fill.png"
# Act
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2017-05-15 18:23:00 +03:00
def helper_ellipse(self, mode, bbox):
2014-06-10 13:10:47 +04:00
# Arrange
2017-05-15 18:23:00 +03:00
im = Image.new(mode, (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2017-05-15 18:23:00 +03:00
expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
# Act
draw.ellipse(bbox, fill="green", outline="blue")
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
# Assert
2017-05-16 10:33:38 +03:00
self.assert_image_similar(im, Image.open(expected), 1)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
def test_ellipse1(self):
2017-05-15 18:23:00 +03:00
for mode in ["RGB", "L"]:
self.helper_ellipse(mode, BBOX1)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
def test_ellipse2(self):
2017-05-15 18:23:00 +03:00
for mode in ["RGB", "L"]:
self.helper_ellipse(mode, BBOX2)
2014-05-12 18:32:04 +04:00
2015-06-05 17:46:50 +03:00
def test_ellipse_edge(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
2019-06-13 18:54:46 +03:00
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
2015-06-05 17:46:50 +03:00
# Assert
self.assert_image_similar(
2019-06-13 18:54:46 +03:00
im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1
)
2015-06-05 17:46:50 +03:00
def test_ellipse_symmetric(self):
2019-06-13 18:54:46 +03:00
for bbox in [(25, 25, 76, 76), (25, 25, 75, 75)]:
im = Image.new("RGB", (101, 101))
draw = ImageDraw.Draw(im)
draw.ellipse(bbox, fill="green", outline="blue")
self.assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT))
def test_ellipse_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width.png"
# Act
draw.ellipse(BBOX1, outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2019-05-03 16:37:37 +03:00
def test_ellipse_width_large(self):
# Arrange
im = Image.new("RGB", (500, 500))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width_large.png"
# Act
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_ellipse_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width_fill.png"
# Act
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2014-06-10 13:10:47 +04:00
def helper_line(self, points):
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-05-12 18:32:04 +04:00
2014-06-10 13:10:47 +04:00
# Act
draw.line(points, fill="yellow", width=2)
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png"))
2014-06-10 13:10:47 +04:00
def test_line1(self):
2014-06-23 23:51:31 +04:00
self.helper_line(POINTS1)
2014-06-10 13:10:47 +04:00
def test_line2(self):
2014-06-23 23:51:31 +04:00
self.helper_line(POINTS2)
2017-01-31 09:22:33 +03:00
def test_shape1(self):
2017-01-30 22:50:48 +03:00
# Arrange
im = Image.new("RGB", (100, 100), "white")
draw = ImageDraw.Draw(im)
x0, y0 = 5, 5
x1, y1 = 5, 50
x2, y2 = 95, 50
x3, y3 = 95, 5
# Act
s = ImageDraw.Outline()
s.move(x0, y0)
s.curve(x1, y1, x2, y2, x3, y3)
s.line(x0, y0)
draw.shape(s, fill=1)
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_shape1.png"))
2017-01-31 09:22:33 +03:00
def test_shape2(self):
# Arrange
im = Image.new("RGB", (100, 100), "white")
draw = ImageDraw.Draw(im)
x0, y0 = 95, 95
x1, y1 = 95, 50
x2, y2 = 5, 50
x3, y3 = 5, 95
# Act
s = ImageDraw.Outline()
s.move(x0, y0)
s.curve(x1, y1, x2, y2, x3, y3)
s.line(x0, y0)
draw.shape(s, outline="blue")
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_shape2.png"))
2017-01-30 22:50:48 +03:00
def helper_pieslice(self, bbox, start, end):
2014-06-10 13:10:47 +04:00
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-06-10 13:10:47 +04:00
# Act
draw.pieslice(bbox, start, end, fill="white", outline="blue")
2014-06-10 13:10:47 +04:00
# Assert
self.assert_image_similar(
2019-06-13 18:54:46 +03:00
im, Image.open("Tests/images/imagedraw_pieslice.png"), 1
)
2014-06-10 13:10:47 +04:00
def test_pieslice1(self):
self.helper_pieslice(BBOX1, -90, 45)
self.helper_pieslice(BBOX1, -90.5, 45.4)
2014-06-10 13:10:47 +04:00
def test_pieslice2(self):
self.helper_pieslice(BBOX2, -90, 45)
self.helper_pieslice(BBOX2, -90.5, 45.4)
def test_pieslice_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_pieslice_width.png"
# Act
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_pieslice_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_pieslice_width_fill.png"
# Act
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2014-06-10 13:10:47 +04:00
def helper_point(self, points):
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-06-10 13:10:47 +04:00
# Act
draw.point(points, fill="yellow")
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png"))
2014-06-10 13:10:47 +04:00
def test_point1(self):
2014-06-23 23:51:31 +04:00
self.helper_point(POINTS1)
2014-06-10 13:10:47 +04:00
def test_point2(self):
2014-06-23 23:51:31 +04:00
self.helper_point(POINTS2)
2014-06-10 13:10:47 +04:00
def helper_polygon(self, points):
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-06-10 13:10:47 +04:00
# Act
draw.polygon(points, fill="red", outline="blue")
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png"))
2014-06-10 13:10:47 +04:00
def test_polygon1(self):
2014-06-23 23:51:31 +04:00
self.helper_polygon(POINTS1)
2014-06-10 13:10:47 +04:00
def test_polygon2(self):
2014-06-23 23:51:31 +04:00
self.helper_polygon(POINTS2)
def test_polygon_kite(self):
# Test drawing lines of different gradients (dx>dy, dy>dx) and
# vertical (dx==0) and horizontal (dy==0) lines
2017-05-15 18:24:12 +03:00
for mode in ["RGB", "L"]:
# Arrange
im = Image.new(mode, (W, H))
draw = ImageDraw.Draw(im)
2019-06-13 18:54:46 +03:00
expected = "Tests/images/imagedraw_polygon_kite_{}.png".format(mode)
2017-05-15 18:24:12 +03:00
# Act
draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
# Assert
2017-05-16 10:33:38 +03:00
self.assert_image_equal(im, Image.open(expected))
2014-06-10 13:10:47 +04:00
def helper_rectangle(self, bbox):
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-06-10 13:10:47 +04:00
# Act
draw.rectangle(bbox, fill="black", outline="green")
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png"))
2014-06-10 13:10:47 +04:00
def test_rectangle1(self):
2014-06-23 23:51:31 +04:00
self.helper_rectangle(BBOX1)
2014-06-10 13:10:47 +04:00
def test_rectangle2(self):
2014-06-23 23:51:31 +04:00
self.helper_rectangle(BBOX2)
def test_big_rectangle(self):
# Test drawing a rectangle bigger than the image
# Arrange
im = Image.new("RGB", (W, H))
2019-06-13 18:54:46 +03:00
bbox = [(-1, -1), (W + 1, H + 1)]
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_big_rectangle.png"
# Act
draw.rectangle(bbox, fill="orange")
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2018-04-12 18:15:27 +03:00
def test_rectangle_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_rectangle_width.png"
# Act
draw.rectangle(BBOX1, outline="green", width=5)
# Assert
self.assert_image_equal(im, Image.open(expected))
def test_rectangle_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_rectangle_width_fill.png"
# Act
draw.rectangle(BBOX1, fill="blue", outline="green", width=5)
# Assert
self.assert_image_equal(im, Image.open(expected))
2019-06-23 00:33:55 +03:00
def test_rectangle_I16(self):
# Arrange
im = Image.new("I;16", (W, H))
draw = ImageDraw.Draw(im)
# Act
draw.rectangle(BBOX1, fill="black", outline="green")
# Assert
self.assert_image_equal(
im.convert("I"), Image.open("Tests/images/imagedraw_rectangle_I.png")
)
2014-06-10 13:10:47 +04:00
def test_floodfill(self):
2017-03-01 12:20:18 +03:00
red = ImageColor.getrgb("red")
2014-05-13 00:27:02 +04:00
2019-06-13 18:54:46 +03:00
for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]:
# Arrange
im = Image.new(mode, (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(BBOX2, outline="yellow", fill="green")
2019-06-13 18:54:46 +03:00
centre_point = (int(W / 2), int(H / 2))
2014-05-13 00:27:02 +04:00
# Act
ImageDraw.floodfill(im, centre_point, value)
# Assert
2019-06-13 18:54:46 +03:00
expected = "Tests/images/imagedraw_floodfill_" + mode + ".png"
2019-11-25 23:03:23 +03:00
with Image.open(expected) as im_floodfill:
self.assert_image_equal(im, im_floodfill)
2017-03-01 12:20:18 +03:00
# Test that using the same colour does not change the image
ImageDraw.floodfill(im, centre_point, red)
self.assert_image_equal(im, im_floodfill)
# Test that filling outside the image does not change the image
ImageDraw.floodfill(im, (W, H), red)
self.assert_image_equal(im, im_floodfill)
# Test filling at the edge of an image
im = Image.new("RGB", (1, 1))
ImageDraw.floodfill(im, (0, 0), red)
self.assert_image_equal(im, Image.new("RGB", (1, 1), red))
2014-06-10 13:10:47 +04:00
def test_floodfill_border(self):
# floodfill() is experimental
2014-05-13 00:27:02 +04:00
2014-06-10 13:10:47 +04:00
# Arrange
2014-06-23 23:51:31 +04:00
im = Image.new("RGB", (W, H))
2014-06-10 13:10:47 +04:00
draw = ImageDraw.Draw(im)
2014-06-23 23:51:31 +04:00
draw.rectangle(BBOX2, outline="yellow", fill="green")
2019-06-13 18:54:46 +03:00
centre_point = (int(W / 2), int(H / 2))
2014-06-10 13:10:47 +04:00
# Act
ImageDraw.floodfill(
2019-06-13 18:54:46 +03:00
im,
centre_point,
ImageColor.getrgb("red"),
border=ImageColor.getrgb("black"),
)
2014-06-10 13:10:47 +04:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png"))
def test_floodfill_thresh(self):
# floodfill() is experimental
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(BBOX2, outline="darkgreen", fill="green")
2019-06-13 18:54:46 +03:00
centre_point = (int(W / 2), int(H / 2))
# Act
2019-06-13 18:54:46 +03:00
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"), thresh=30)
2017-08-31 14:51:21 +03:00
# Assert
2019-06-13 18:54:46 +03:00
self.assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png"))
def test_floodfill_not_negative(self):
# floodfill() is experimental
# Test that floodfill does not extend into negative coordinates
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
draw.line((W / 2, 0, W / 2, H / 2), fill="green")
draw.line((0, H / 2, W / 2, H / 2), fill="green")
# Act
ImageDraw.floodfill(im, (int(W / 4), int(H / 4)), ImageColor.getrgb("red"))
# Assert
self.assert_image_equal(
im, Image.open("Tests/images/imagedraw_floodfill_not_negative.png")
)
2019-06-13 18:54:46 +03:00
def create_base_image_draw(
self, size, mode=DEFAULT_MODE, background1=WHITE, background2=GRAY
):
2014-06-25 03:48:38 +04:00
img = Image.new(mode, size, background1)
for x in range(0, size[0]):
for y in range(0, size[1]):
if (x + y) % 2 == 0:
img.putpixel((x, y), background2)
2018-10-02 11:55:28 +03:00
return img, ImageDraw.Draw(img)
2014-06-25 03:48:38 +04:00
def test_square(self):
2019-11-25 23:03:23 +03:00
with Image.open(os.path.join(IMAGES_PATH, "square.png")) as expected:
expected.load()
img, draw = self.create_base_image_draw((10, 10))
draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK)
self.assert_image_equal(img, expected, "square as normal polygon failed")
img, draw = self.create_base_image_draw((10, 10))
draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK)
self.assert_image_equal(img, expected, "square as inverted polygon failed")
img, draw = self.create_base_image_draw((10, 10))
draw.rectangle((2, 2, 7, 7), BLACK)
self.assert_image_equal(img, expected, "square as normal rectangle failed")
img, draw = self.create_base_image_draw((10, 10))
draw.rectangle((7, 7, 2, 2), BLACK)
self.assert_image_equal(
img, expected, "square as inverted rectangle failed"
)
2014-06-25 03:48:38 +04:00
def test_triangle_right(self):
2019-11-25 23:03:23 +03:00
with Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK)
self.assert_image_equal(img, expected, "triangle right failed")
2014-06-25 03:48:38 +04:00
def test_line_horizontal(self):
2019-11-25 23:03:23 +03:00
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_horizontal_w2px_normal.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 2)
self.assert_image_equal(
img, expected, "line straight horizontal normal 2px wide failed"
)
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_horizontal_w2px_inverted.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 2)
self.assert_image_equal(
img, expected, "line straight horizontal inverted 2px wide failed"
)
with Image.open(
os.path.join(IMAGES_PATH, "line_horizontal_w3px.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 3)
self.assert_image_equal(
img, expected, "line straight horizontal normal 3px wide failed"
)
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 3)
self.assert_image_equal(
img, expected, "line straight horizontal inverted 3px wide failed"
)
with Image.open(
os.path.join(IMAGES_PATH, "line_horizontal_w101px.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((200, 110))
draw.line((5, 55, 195, 55), BLACK, 101)
self.assert_image_equal(
img, expected, "line straight horizontal 101px wide failed"
)
2014-07-01 20:07:18 +04:00
def test_line_h_s1_w2(self):
2019-06-13 18:54:46 +03:00
self.skipTest("failing")
2019-11-25 23:03:23 +03:00
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_horizontal_slope1px_w2px.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 6), BLACK, 2)
self.assert_image_equal(
img, expected, "line horizontal 1px slope 2px wide failed"
)
2014-06-25 03:48:38 +04:00
def test_line_vertical(self):
2019-11-25 23:03:23 +03:00
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_vertical_w2px_normal.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 2)
self.assert_image_equal(
img, expected, "line straight vertical normal 2px wide failed"
)
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_vertical_w2px_inverted.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 2)
self.assert_image_equal(
img, expected, "line straight vertical inverted 2px wide failed"
)
with Image.open(
os.path.join(IMAGES_PATH, "line_vertical_w3px.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 3)
self.assert_image_equal(
img, expected, "line straight vertical normal 3px wide failed"
)
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 3)
self.assert_image_equal(
img, expected, "line straight vertical inverted 3px wide failed"
)
with Image.open(
os.path.join(IMAGES_PATH, "line_vertical_w101px.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((110, 200))
draw.line((55, 5, 55, 195), BLACK, 101)
self.assert_image_equal(
img, expected, "line straight vertical 101px wide failed"
)
with Image.open(
2019-06-13 18:54:46 +03:00
os.path.join(IMAGES_PATH, "line_vertical_slope1px_w2px.png")
2019-11-25 23:03:23 +03:00
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 6, 14), BLACK, 2)
self.assert_image_equal(
img, expected, "line vertical 1px slope 2px wide failed"
)
2014-06-25 03:48:38 +04:00
def test_line_oblique_45(self):
2019-11-25 23:03:23 +03:00
with Image.open(
os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 14), BLACK, 3)
self.assert_image_equal(
img, expected, "line oblique 45 normal 3px wide A failed"
)
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 14, 5, 5), BLACK, 3)
self.assert_image_equal(
img, expected, "line oblique 45 inverted 3px wide A failed"
)
with Image.open(
os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png")
) as expected:
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 14), BLACK, 3)
self.assert_image_equal(
img, expected, "line oblique 45 normal 3px wide B failed"
)
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 14, 5), BLACK, 3)
self.assert_image_equal(
img, expected, "line oblique 45 inverted 3px wide B failed"
)
2014-06-25 03:48:38 +04:00
def test_wide_line_dot(self):
# Test drawing a wide "line" from one point to another just draws
# a single point
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_wide_line_dot.png"
# Act
draw.line([(50, 50), (50, 50)], width=3)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
2014-06-25 03:48:38 +04:00
2018-09-16 14:29:09 +03:00
def test_line_joint(self):
im = Image.new("RGB", (500, 325))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_line_joint_curve.png"
# Act
2019-06-13 18:54:46 +03:00
xy = [
(400, 280),
(380, 280),
(450, 280),
(440, 120),
(350, 200),
(310, 280),
(300, 280),
(250, 280),
(250, 200),
(150, 200),
(150, 260),
(50, 200),
(150, 50),
(250, 100),
]
2018-09-16 14:29:09 +03:00
draw.line(xy, GRAY, 50, "curve")
# Assert
self.assert_image_similar(im, Image.open(expected), 3)
2017-10-04 09:54:53 +03:00
def test_textsize_empty_string(self):
# https://github.com/python-pillow/Pillow/issues/2783
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
# Should not cause 'SystemError: <built-in method getsize of
# ImagingFont object at 0x...> returned NULL without setting an error'
draw.textsize("")
draw.textsize("\n")
draw.textsize("test\n")
2019-07-28 23:40:03 +03:00
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
def test_textsize_stroke(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20)
# Act / Assert
self.assertEqual(draw.textsize("A", font, stroke_width=2), (16, 20))
self.assertEqual(
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2), (52, 44)
)
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
def test_stroke(self):
for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items():
# Arrange
im = Image.new("RGB", (120, 130))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
# Act
draw.text(
(10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill
)
# Assert
self.assert_image_similar(
im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1
2019-07-28 23:40:03 +03:00
)
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
def test_stroke_multiline(self):
# Arrange
im = Image.new("RGB", (100, 250))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
# Act
draw.multiline_text(
(10, 10), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0"
)
# Assert
self.assert_image_similar(
im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3
)
2018-01-05 03:26:24 +03:00
def test_same_color_outline(self):
# Prepare shape
x0, y0 = 5, 5
x1, y1 = 5, 50
x2, y2 = 95, 50
x3, y3 = 95, 5
s = ImageDraw.Outline()
s.move(x0, y0)
s.curve(x1, y1, x2, y2, x3, y3)
s.line(x0, y0)
# Begin
for mode in ["RGB", "L"]:
2019-06-13 18:54:46 +03:00
for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]:
2018-01-05 03:26:24 +03:00
for operation, args in {
2019-06-13 18:54:46 +03:00
"chord": [BBOX1, 0, 180],
"ellipse": [BBOX1],
"shape": [s],
"pieslice": [BBOX1, -90, 45],
"polygon": [[(18, 30), (85, 30), (60, 72)]],
"rectangle": [BBOX1],
2018-01-05 03:26:24 +03:00
}.items():
# Arrange
im = Image.new(mode, (W, H))
draw = ImageDraw.Draw(im)
# Act
draw_method = getattr(draw, operation)
args += [fill, outline]
draw_method(*args)
# Assert
2019-06-13 18:54:46 +03:00
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
operation, mode
)
2018-01-05 03:26:24 +03:00
self.assert_image_similar(im, Image.open(expected), 1)