From 21c7fd251c11c899e37d5d6a2ec861aa9352058a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 May 2019 23:37:37 +1000 Subject: [PATCH] Fixed arc gaps --- Tests/images/imagedraw_arc_width_pieslice.png | Bin 0 -> 402 bytes .../images/imagedraw_ellipse_width_large.png | Bin 0 -> 4455 bytes Tests/test_imagedraw.py | 25 +++ src/libImaging/Draw.c | 199 ++++++++++-------- 4 files changed, 142 insertions(+), 82 deletions(-) create mode 100644 Tests/images/imagedraw_arc_width_pieslice.png create mode 100644 Tests/images/imagedraw_ellipse_width_large.png diff --git a/Tests/images/imagedraw_arc_width_pieslice.png b/Tests/images/imagedraw_arc_width_pieslice.png new file mode 100644 index 0000000000000000000000000000000000000000..950d95dd6b7e25bb3a0e55e0e12099e8e3b8045f GIT binary patch literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^DImqCuq z^`{^6JBqNLoiO{(w9mN<<|f~&{G|lc00XBj`z-oi`u-`J9edCI!s?k3*``&yzUlAG zSlO|oMDLsbGVj-YMZeUt?>1Jw+OgT}#21D6YjV9>t*-ekO?r~MLoabnaq?4}o>dX& z&eupCH*gfnKE094bxo`{>v4mlhST2Wh#lWldrqita%y)3=hThg?3xdzES+)elT6U% zh#3OXe_3?wWrO zC#}<%zvf)rtF6D}Tz_SUpLf4hp3Yf+;LWci?DryCm9N#P^X=hXJ+I)w+lzZ2>=q6- s`|*6;!RFVdQ&MBb@0RD}(_5c6? literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_ellipse_width_large.png b/Tests/images/imagedraw_ellipse_width_large.png new file mode 100644 index 0000000000000000000000000000000000000000..9d3c3326b71081b47f545161498e095748a43114 GIT binary patch literal 4455 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 literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index bceb0e3d4b8..cff08a39c4a 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -114,6 +114,19 @@ def test_arc_width(self): # Assert self.assert_image_similar(im, Image.open(expected), 1) + 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)) @@ -239,6 +252,18 @@ def test_ellipse_width(self): # Assert self.assert_image_similar(im, Image.open(expected), 1) + 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)) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index d0f374fe14a..cbd3e291cec 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -765,115 +765,150 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, int width, int mode, int op) { float i; - int j; + int inner; int n; - int cx, cy; + int maxEdgeCount; int w, h; - int x = 0, y = 0; - int lx = 0, ly = 0; - int sx = 0, sy = 0; + int x, y; + int cx, cy; + int lx, ly; + int sx, sy; + int lx_inner, ly_inner; + int sx_inner, sy_inner; DRAW* draw; INT32 ink; + Edge* e; DRAWINIT(); - if (width == 0) { - width = 1; - } + while (end < start) + end += 360; - for (j = 0; j < width; j++) { + if (end - start > 360) { + // no need to go in loops + end = start + 361; + } - w = x1 - x0; - h = y1 - y0; - if (w < 0 || h < 0) - return 0; + w = x1 - x0; + h = y1 - y0; + if (w <= 0 || h <= 0) + return 0; - cx = (x0 + x1) / 2; - cy = (y0 + y1) / 2; + cx = (x0 + x1) / 2; + cy = (y0 + y1) / 2; - while (end < start) - end += 360; + if (!fill && width <= 1) { + for (i = start; i < end+1; i++) { + if (i > end) { + i = end; + } + ellipsePoint(cx, cy, w, h, i, &x, &y); + if (i != start) + draw->line(im, lx, ly, x, y, ink); + else + sx = x, sy = y; + lx = x, ly = y; + } - if (end - start > 360) { - /* no need to go in loops */ - end = start + 361; + if (i != start) { + if (mode == PIESLICE) { + if (x != cx || y != cy) { + draw->line(im, x, y, cx, cy, ink); + draw->line(im, cx, cy, sx, sy, ink); + } + } else if (mode == CHORD) { + if (x != sx || y != sy) + draw->line(im, x, y, sx, sy, ink); + } } + } else { + inner = (mode == ARC || !fill) ? 1 : 0; - if (mode != ARC && fill) { + // Build edge list + // malloc check UNDONE, FLOAT? + maxEdgeCount = end - start; + if (inner) { + maxEdgeCount *= 2; + } + maxEdgeCount += 3; + e = calloc(maxEdgeCount, sizeof(Edge)); + if (!e) { + ImagingError_MemoryError(); + return -1; + } - /* Build edge list */ - /* malloc check UNDONE, FLOAT? */ - Edge* e = calloc((end - start + 3), sizeof(Edge)); - if (!e) { - ImagingError_MemoryError(); - return -1; + // Outer circle + n = 0; + for (i = start; i < end+1; i++) { + if (i > end) { + i = end; } - n = 0; - - for (i = start; i < end+1; i++) { - if (i > end) { - i = end; - } - ellipsePoint(cx, cy, w, h, i, &x, &y); - if (i != start) - add_edge(&e[n++], lx, ly, x, y); - else - sx = x, sy = y; - lx = x, ly = y; + ellipsePoint(cx, cy, w, h, i, &x, &y); + if (i == start) { + sx = x, sy = y; + } else { + add_edge(&e[n++], lx, ly, x, y); } + lx = x, ly = y; + } + if (n == 0) + return 0; - if (n > 0) { - /* close and draw polygon */ - if (mode == PIESLICE) { - if (x != cx || y != cy) { - add_edge(&e[n++], x, y, cx, cy); - add_edge(&e[n++], cx, cy, sx, sy); + if (inner) { + // Inner circle + x0 += width - 1; + y0 += width - 1; + x1 -= width - 1; + y1 -= width - 1; + + w = x1 - x0; + h = y1 - y0; + if (w <= 0 || h <= 0) { + // ARC with no gap in the middle is a PIESLICE + mode = PIESLICE; + inner = 0; + } else { + for (i = start; i < end+1; i++) { + if (i > end) { + i = end; } - } else { - if (x != sx || y != sy) - add_edge(&e[n++], x, y, sx, sy); - } - draw->polygon(im, n, e, ink, 0); - } - - free(e); - - } else { - - for (i = start; i < end+1; i++) { - if (i > end) { - i = end; + ellipsePoint(cx, cy, w, h, i, &x, &y); + if (i == start) + sx_inner = x, sy_inner = y; + else + add_edge(&e[n++], lx_inner, ly_inner, x, y); + lx_inner = x, ly_inner = y; } - ellipsePoint(cx, cy, w, h, i, &x, &y); - if (i != start) - draw->line(im, lx, ly, x, y, ink); - else - sx = x, sy = y; - lx = x, ly = y; } + } - if (i != start) { - if (mode == PIESLICE) { - if (j == 0 && (x != cx || y != cy)) { - if (width == 1) { - draw->line(im, x, y, cx, cy, ink); - draw->line(im, cx, cy, sx, sy, ink); - } else { - ImagingDrawWideLine(im, x, y, cx, cy, &ink, width, op); - ImagingDrawWideLine(im, cx, cy, sx, sy, &ink, width, op); - } + if (end - start < 360) { + // Close polygon + if (mode == PIESLICE) { + if (x != cx || y != cy) { + add_edge(&e[n++], sx, sy, cx, cy); + add_edge(&e[n++], cx, cy, lx, ly); + if (inner) { + ImagingDrawWideLine(im, sx, sy, cx, cy, &ink, width, op); + ImagingDrawWideLine(im, cx, cy, lx, ly, &ink, width, op); } - } else if (mode == CHORD) { - if (x != sx || y != sy) - draw->line(im, x, y, sx, sy, ink); } + } else if (mode == CHORD) { + add_edge(&e[n++], sx, sy, lx, ly); + if (inner) { + add_edge(&e[n++], sx_inner, sy_inner, lx_inner, ly_inner); + } + } else if (mode == ARC) { + add_edge(&e[n++], sx, sy, sx_inner, sy_inner); + add_edge(&e[n++], lx, ly, lx_inner, ly_inner); } } - x0++; - y0++; - x1--; - y1--; + + draw->polygon(im, n, e, ink, 0); + + free(e); } + return 0; }