From 8e8f63d4a5408daf8b907163822cad76210da725 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Jan 2020 17:43:51 +1100 Subject: [PATCH] Only draw each polygon pixel once --- .../images/imagedraw_ellipse_translucent.png | Bin 0 -> 390 bytes Tests/test_imagedraw.py | 12 ++++ src/libImaging/Draw.c | 57 ++++++++++++++++-- 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 Tests/images/imagedraw_ellipse_translucent.png diff --git a/Tests/images/imagedraw_ellipse_translucent.png b/Tests/images/imagedraw_ellipse_translucent.png new file mode 100644 index 0000000000000000000000000000000000000000..964dce678891635288ce01cdbcb7c168376e8ced GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^DImVJ6q_#(TV`nDgp%Od!VcWs}3 zD=6asi5K@Ovff{2&iC~+{Wo{{Su^dD%3x9HoUnBqKq~XvjY`gNo%?G}T6+)k=XYUD{&ufbfxA^zv!@k6WWfAsq4}HU+ ZfusNFtZZDoTc#z5@9FC2vd$@?2>_1esBi!P literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 4535a4838e2..ba5684dcd36 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -234,6 +234,18 @@ def test_ellipse2(self): for mode in ["RGB", "L"]: self.helper_ellipse(mode, BBOX2) + def test_ellipse_translucent(self): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im, "RGBA") + + # Act + draw.ellipse(BBOX1, fill=(0, 255, 0, 127)) + + # Assert + expected = Image.open("Tests/images/imagedraw_ellipse_translucent.png") + self.assert_image_similar(im, expected, 1) + def test_ellipse_edge(self): # Arrange im = Image.new("RGB", (W, H)) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index dee7c524dba..828134b8e61 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -415,6 +415,35 @@ x_cmp(const void *x0, const void *x1) } +static void +draw_horizontal_lines(Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline) +{ + int i; + for (i = 0; i < n; i++) { + if (e[i].ymin == y && e[i].ymin == e[i].ymax) { + int xmax; + int xmin = e[i].xmin; + if (*x_pos < xmin) { + // Line would be after the current position + continue; + } + + xmax = e[i].xmax; + if (*x_pos > xmin) { + // Line would be partway through x_pos, so increase the starting point + xmin = *x_pos; + if (xmax < xmin) { + // Line would now end before it started + continue; + } + } + + (*hline)(im, xmin, e[i].ymin, xmax, ink); + *x_pos = xmax+1; + } + } +} + /* * Filled polygon draw function using scan line algorithm. */ @@ -442,10 +471,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, } for (i = 0; i < n; i++) { - /* This causes the pixels of horizontal edges to be drawn twice :( - * but without it there are inconsistencies in ellipses */ if (e[i].ymin == e[i].ymax) { - (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink); continue; } if (ymin > e[i].ymin) { @@ -472,6 +498,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, } for (; ymin <= ymax; ymin++) { int j = 0; + int x_pos = 0; for (i = 0; i < edge_count; i++) { Edge* current = edge_table[i]; if (ymin >= current->ymin && ymin <= current->ymax) { @@ -485,8 +512,30 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, } qsort(xx, j, sizeof(float), x_cmp); for (i = 1; i < j; i += 2) { - (*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink); + int x_end = ROUND_DOWN(xx[i]); + if (x_end < x_pos) { + // Line would be before the current position + continue; + } + draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline); + if (x_end < x_pos) { + // Line would be before the current position + continue; + } + + int x_start = ROUND_UP(xx[i-1]); + if (x_pos > x_start) { + // Line would be partway through x_pos, so increase the starting point + x_start = x_pos; + if (x_end < x_start) { + // Line would now end before it started + continue; + } + } + (*hline)(im, x_start, ymin, x_end, ink); + x_pos = x_end+1; } + draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline); } free(xx);