diff --git a/Tests/images/imagedraw_arc_width_pieslice.png b/Tests/images/imagedraw_arc_width_pieslice.png new file mode 100644 index 000000000..950d95dd6 Binary files /dev/null and b/Tests/images/imagedraw_arc_width_pieslice.png differ diff --git a/Tests/images/imagedraw_ellipse_width_large.png b/Tests/images/imagedraw_ellipse_width_large.png new file mode 100644 index 000000000..9d3c3326b Binary files /dev/null and b/Tests/images/imagedraw_ellipse_width_large.png differ diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index bceb0e3d4..cff08a39c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -114,6 +114,19 @@ class TestImageDraw(PillowTestCase): # 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 @@ class TestImageDraw(PillowTestCase): # 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 d0f374fe1..cbd3e291c 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; + + if (end - start > 360) { + // no need to go in loops + end = start + 361; } - for (j = 0; j < width; j++) { + w = x1 - x0; + h = y1 - y0; + if (w <= 0 || h <= 0) + return 0; - w = x1 - x0; - h = y1 - y0; - if (w < 0 || h < 0) + cx = (x0 + x1) / 2; + cy = (y0 + y1) / 2; + + 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 (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; + + // 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; + } + + // Outer circle + 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) { + sx = x, sy = y; + } else { + add_edge(&e[n++], lx, ly, x, y); + } + lx = x, ly = y; + } + if (n == 0) return 0; - cx = (x0 + x1) / 2; - cy = (y0 + y1) / 2; + if (inner) { + // Inner circle + x0 += width - 1; + y0 += width - 1; + x1 -= width - 1; + y1 -= width - 1; - while (end < start) - end += 360; - - if (end - start > 360) { - /* no need to go in loops */ - end = start + 361; - } - - if (mode != ARC && fill) { - - /* Build edge list */ - /* malloc check UNDONE, FLOAT? */ - Edge* e = calloc((end - start + 3), sizeof(Edge)); - if (!e) { - ImagingError_MemoryError(); - return -1; - } - 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; - } - - 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); + 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) - 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); - } - } - } else if (mode == CHORD) { - if (x != sx || y != sy) - draw->line(im, x, y, sx, sy, ink); + 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; } } } - x0++; - y0++; - x1--; - y1--; + + 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) { + 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); + } + } + + draw->polygon(im, n, e, ink, 0); + + free(e); } + return 0; }