Merge pull request #2533 from hugovk/imagedraw
Add more ImageDraw.py tests and remove unused Draw.c code
3
.gitignore
vendored
|
@ -34,6 +34,9 @@ htmlcov/
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
test_images
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
|
||||||
|
|
|
@ -283,6 +283,7 @@ def Draw(im, mode=None):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return ImageDraw(im, mode)
|
return ImageDraw(im, mode)
|
||||||
|
|
||||||
|
|
||||||
# experimental access to the outline API
|
# experimental access to the outline API
|
||||||
try:
|
try:
|
||||||
Outline = Image.core.outline
|
Outline = Image.core.outline
|
||||||
|
|
BIN
Tests/images/imagedraw_arc_end_le_start.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
Tests/images/imagedraw_arc_no_loops.png
Normal file
After Width: | Height: | Size: 384 B |
BIN
Tests/images/imagedraw_big_rectangle.png
Normal file
After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 326 B |
BIN
Tests/images/imagedraw_chord_L.png
Normal file
After Width: | Height: | Size: 257 B |
BIN
Tests/images/imagedraw_chord_RGB.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
Tests/images/imagedraw_ellipse_L.png
Normal file
After Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
BIN
Tests/images/imagedraw_polygon_kite_L.png
Normal file
After Width: | Height: | Size: 499 B |
BIN
Tests/images/imagedraw_polygon_kite_RGB.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
Tests/images/imagedraw_wide_line_dot.png
Normal file
After Width: | Height: | Size: 116 B |
|
@ -30,6 +30,8 @@ BBOX2 = [X0, Y0, X1, Y1]
|
||||||
POINTS1 = [(10, 10), (20, 40), (30, 30)]
|
POINTS1 = [(10, 10), (20, 40), (30, 30)]
|
||||||
POINTS2 = [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)]
|
||||||
|
|
||||||
|
|
||||||
class TestImageDraw(PillowTestCase):
|
class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
|
@ -78,6 +80,37 @@ class TestImageDraw(PillowTestCase):
|
||||||
self.helper_arc(BBOX2, 0, 180)
|
self.helper_arc(BBOX2, 0, 180)
|
||||||
self.helper_arc(BBOX2, 0.5, 180.4)
|
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)
|
||||||
|
del draw
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(
|
||||||
|
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)
|
||||||
|
del draw
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_similar(
|
||||||
|
im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1)
|
||||||
|
|
||||||
def test_bitmap(self):
|
def test_bitmap(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
|
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
|
||||||
|
@ -92,45 +125,49 @@ class TestImageDraw(PillowTestCase):
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im, Image.open("Tests/images/imagedraw_bitmap.png"))
|
im, Image.open("Tests/images/imagedraw_bitmap.png"))
|
||||||
|
|
||||||
def helper_chord(self, bbox, start, end):
|
def helper_chord(self, mode, bbox, start, end):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new(mode, (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_chord_{}.png".format(mode)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
||||||
del draw
|
del draw
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
im, Image.open("Tests/images/imagedraw_chord.png"), 1)
|
|
||||||
|
|
||||||
def test_chord1(self):
|
def test_chord1(self):
|
||||||
self.helper_chord(BBOX1, 0, 180)
|
for mode in ["RGB", "L"]:
|
||||||
self.helper_chord(BBOX1, 0.5, 180.4)
|
self.helper_chord(mode, BBOX1, 0, 180)
|
||||||
|
self.helper_chord(mode, BBOX1, 0.5, 180.4)
|
||||||
|
|
||||||
def test_chord2(self):
|
def test_chord2(self):
|
||||||
self.helper_chord(BBOX2, 0, 180)
|
for mode in ["RGB", "L"]:
|
||||||
self.helper_chord(BBOX2, 0.5, 180.4)
|
self.helper_chord(mode, BBOX2, 0, 180)
|
||||||
|
self.helper_chord(mode, BBOX2, 0.5, 180.4)
|
||||||
|
|
||||||
def helper_ellipse(self, bbox):
|
def helper_ellipse(self, mode, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new(mode, (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(bbox, fill="green", outline="blue")
|
draw.ellipse(bbox, fill="green", outline="blue")
|
||||||
del draw
|
del draw
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
im, Image.open("Tests/images/imagedraw_ellipse.png"), 1)
|
|
||||||
|
|
||||||
def test_ellipse1(self):
|
def test_ellipse1(self):
|
||||||
self.helper_ellipse(BBOX1)
|
for mode in ["RGB", "L"]:
|
||||||
|
self.helper_ellipse(mode, BBOX1)
|
||||||
|
|
||||||
def test_ellipse2(self):
|
def test_ellipse2(self):
|
||||||
self.helper_ellipse(BBOX2)
|
for mode in ["RGB", "L"]:
|
||||||
|
self.helper_ellipse(mode, BBOX2)
|
||||||
|
|
||||||
def test_ellipse_edge(self):
|
def test_ellipse_edge(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -267,6 +304,23 @@ class TestImageDraw(PillowTestCase):
|
||||||
def test_polygon2(self):
|
def test_polygon2(self):
|
||||||
self.helper_polygon(POINTS2)
|
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
|
||||||
|
for mode in ["RGB", "L"]:
|
||||||
|
# Arrange
|
||||||
|
im = Image.new(mode, (W, H))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_polygon_kite_{}.png".format(
|
||||||
|
mode)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
|
||||||
|
del draw
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(im, Image.open(expected))
|
||||||
|
|
||||||
def helper_rectangle(self, bbox):
|
def helper_rectangle(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
|
@ -286,6 +340,21 @@ class TestImageDraw(PillowTestCase):
|
||||||
def test_rectangle2(self):
|
def test_rectangle2(self):
|
||||||
self.helper_rectangle(BBOX2)
|
self.helper_rectangle(BBOX2)
|
||||||
|
|
||||||
|
def test_big_rectangle(self):
|
||||||
|
# Test drawing a rectangle bigger than the image
|
||||||
|
# Arrange
|
||||||
|
im = Image.new("RGB", (W, H))
|
||||||
|
bbox = [(-1, -1), (W+1, H+1)]
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_big_rectangle.png"
|
||||||
|
|
||||||
|
# Act
|
||||||
|
draw.rectangle(bbox, fill="orange")
|
||||||
|
del draw
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
|
||||||
def test_floodfill(self):
|
def test_floodfill(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
|
@ -478,6 +547,21 @@ class TestImageDraw(PillowTestCase):
|
||||||
self.assert_image_equal(img, expected,
|
self.assert_image_equal(img, expected,
|
||||||
'line oblique 45 inverted 3px wide B failed')
|
'line oblique 45 inverted 3px wide B failed')
|
||||||
|
|
||||||
|
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)
|
||||||
|
del draw
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -605,11 +605,6 @@ ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
|
||||||
|
|
||||||
DRAWINIT();
|
DRAWINIT();
|
||||||
|
|
||||||
if (width <= 1) {
|
|
||||||
draw->line(im, x0, y0, x1, y1, ink);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dx = x1-x0;
|
dx = x1-x0;
|
||||||
dy = y1-y0;
|
dy = y1-y0;
|
||||||
if (dx == 0 && dy == 0) {
|
if (dx == 0 && dy == 0) {
|
||||||
|
@ -1030,20 +1025,6 @@ ImagingOutlineCurve(ImagingOutline outline, float x1, float y1,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy,
|
|
||||||
float x3, float y3)
|
|
||||||
{
|
|
||||||
/* add bezier curve based on three control points (as
|
|
||||||
in the Flash file format) */
|
|
||||||
|
|
||||||
return ImagingOutlineCurve(
|
|
||||||
outline,
|
|
||||||
(outline->x + cx + cx)/3, (outline->y + cy + cy)/3,
|
|
||||||
(cx + cx + x3)/3, (cy + cy + y3)/3,
|
|
||||||
x3, y3);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ImagingOutlineClose(ImagingOutline outline)
|
ImagingOutlineClose(ImagingOutline outline)
|
||||||
{
|
{
|
||||||
|
|