2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2018-05-17 23:14:23 +03:00
|
|
|
import os.path
|
|
|
|
|
2022-05-26 00:00:13 +03:00
|
|
|
import pytest
|
|
|
|
|
2023-04-10 10:24:16 +03:00
|
|
|
from PIL import Image, ImageDraw, ImageDraw2, features
|
2024-02-17 07:00:38 +03:00
|
|
|
from PIL._typing import Coords
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2020-02-18 01:03:32 +03:00
|
|
|
from .helper import (
|
|
|
|
assert_image_equal,
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile,
|
2021-02-21 14:22:29 +03:00
|
|
|
assert_image_similar_tofile,
|
2020-02-18 01:03:32 +03:00
|
|
|
hopper,
|
|
|
|
skip_unless_feature,
|
|
|
|
)
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2018-05-17 23:14:23 +03:00
|
|
|
BLACK = (0, 0, 0)
|
|
|
|
WHITE = (255, 255, 255)
|
|
|
|
GRAY = (190, 190, 190)
|
|
|
|
DEFAULT_MODE = "RGB"
|
|
|
|
IMAGES_PATH = os.path.join("Tests", "images", "imagedraw")
|
|
|
|
|
|
|
|
# Image size
|
|
|
|
W, H = 100, 100
|
|
|
|
|
|
|
|
# Bounding box points
|
|
|
|
X0 = int(W / 4)
|
|
|
|
X1 = int(X0 * 3)
|
|
|
|
Y0 = int(H / 4)
|
|
|
|
Y1 = int(X0 * 3)
|
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
# Bounding boxes
|
|
|
|
BBOX = (((X0, Y0), (X1, Y1)), [(X0, Y0), (X1, Y1)], (X0, Y0, X1, Y1), [X0, Y0, X1, Y1])
|
|
|
|
|
|
|
|
# Coordinate sequences
|
|
|
|
POINTS = (
|
|
|
|
((10, 10), (20, 40), (30, 30)),
|
|
|
|
[(10, 10), (20, 40), (30, 30)],
|
|
|
|
(10, 10, 20, 40, 30, 30),
|
|
|
|
[10, 10, 20, 40, 30, 30],
|
|
|
|
)
|
2018-05-17 23:14:23 +03:00
|
|
|
|
|
|
|
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_sanity() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
im = hopper("RGB").copy()
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = ImageDraw2.Pen("blue", width=7)
|
|
|
|
draw.line(list(range(10)), pen)
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2024-06-22 03:09:11 +03:00
|
|
|
draw2, handler = ImageDraw.getdraw(im)
|
|
|
|
assert draw2 is not None
|
2020-02-12 19:29:19 +03:00
|
|
|
pen = ImageDraw2.Pen("blue", width=7)
|
2024-06-22 03:09:11 +03:00
|
|
|
draw2.line(list(range(10)), pen)
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2018-05-20 17:11:07 +03:00
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
@pytest.mark.parametrize("bbox", BBOX)
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_ellipse(bbox: Coords) -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = ImageDraw2.Pen("blue", width=2)
|
|
|
|
brush = ImageDraw2.Brush("green")
|
2018-05-20 17:11:07 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Act
|
|
|
|
draw.ellipse(bbox, pen, brush)
|
2018-05-20 17:11:07 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Assert
|
2022-10-03 08:57:42 +03:00
|
|
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_RGB.png", 1)
|
2018-05-20 17:11:07 +03:00
|
|
|
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_ellipse_edge() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
brush = ImageDraw2.Brush("white")
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Act
|
2020-04-03 22:33:47 +03:00
|
|
|
draw.ellipse(((0, 0), (W - 1, H - 1)), brush)
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Assert
|
2021-02-21 14:22:29 +03:00
|
|
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
|
2018-05-17 23:14:23 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
@pytest.mark.parametrize("points", POINTS)
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_line(points: Coords) -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = ImageDraw2.Pen("yellow", width=2)
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.line(points, pen)
|
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
@pytest.mark.parametrize("points", POINTS)
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_line_pen_as_brush(points: Coords) -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = None
|
|
|
|
brush = ImageDraw2.Pen("yellow", width=2)
|
|
|
|
|
|
|
|
# Act
|
|
|
|
# Pass in the pen as the brush parameter
|
2022-12-31 07:42:38 +03:00
|
|
|
draw.line(points, pen, brush)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
@pytest.mark.parametrize("points", POINTS)
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_polygon(points: Coords) -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = ImageDraw2.Pen("blue", width=2)
|
|
|
|
brush = ImageDraw2.Brush("red")
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.polygon(points, pen, brush)
|
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, "Tests/images/imagedraw_polygon.png")
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2022-12-31 07:42:38 +03:00
|
|
|
@pytest.mark.parametrize("bbox", BBOX)
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_rectangle(bbox: Coords) -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
pen = ImageDraw2.Pen("green", width=2)
|
|
|
|
brush = ImageDraw2.Brush("black")
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.rectangle(bbox, pen, brush)
|
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle.png")
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_big_rectangle() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Test drawing a rectangle bigger than the image
|
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
bbox = [(-1, -1), (W + 1, H + 1)]
|
|
|
|
brush = ImageDraw2.Brush("orange")
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
expected = "Tests/images/imagedraw_big_rectangle.png"
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.rectangle(bbox, brush)
|
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:22:29 +03:00
|
|
|
assert_image_similar_tofile(im, expected, 1)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2020-02-18 01:03:32 +03:00
|
|
|
@skip_unless_feature("freetype2")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_text() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
font = ImageDraw2.Font("white", FONT_PATH)
|
|
|
|
expected = "Tests/images/imagedraw2_text.png"
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.text((5, 5), "ImageDraw2", font)
|
|
|
|
|
|
|
|
# Assert
|
2021-02-21 14:22:29 +03:00
|
|
|
assert_image_similar_tofile(im, expected, 13)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2020-02-18 01:03:32 +03:00
|
|
|
@skip_unless_feature("freetype2")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_textbbox() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
font = ImageDraw2.Font("white", FONT_PATH)
|
|
|
|
|
|
|
|
# Act
|
2023-04-10 10:24:16 +03:00
|
|
|
bbox = draw.textbbox((0, 0), "ImageDraw2", font)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
# Assert
|
2023-04-10 10:24:16 +03:00
|
|
|
right = 72 if features.check_feature("raqm") else 70
|
|
|
|
assert bbox == (0, 2, right, 12)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2020-02-18 01:03:32 +03:00
|
|
|
@skip_unless_feature("freetype2")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_textsize_empty_string() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
font = ImageDraw2.Font("white", FONT_PATH)
|
|
|
|
|
|
|
|
# Act
|
|
|
|
# Should not cause 'SystemError: <built-in method getsize of
|
|
|
|
# ImagingFont object at 0x...> returned NULL without setting an error'
|
2022-05-26 00:00:13 +03:00
|
|
|
draw.textbbox((0, 0), "", font)
|
|
|
|
draw.textbbox((0, 0), "\n", font)
|
|
|
|
draw.textbbox((0, 0), "test\n", font)
|
|
|
|
draw.textlength("", font)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2020-02-18 01:03:32 +03:00
|
|
|
@skip_unless_feature("freetype2")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_flush() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Arrange
|
|
|
|
im = Image.new("RGB", (W, H))
|
|
|
|
draw = ImageDraw2.Draw(im)
|
|
|
|
font = ImageDraw2.Font("white", FONT_PATH)
|
|
|
|
|
|
|
|
# Act
|
|
|
|
draw.text((5, 5), "ImageDraw2", font)
|
|
|
|
im2 = draw.flush()
|
|
|
|
|
|
|
|
# Assert
|
|
|
|
assert_image_equal(im, im2)
|