Add width parameter to arc, chord, ellipse, pieslice

This commit is contained in:
Hugo 2018-04-13 08:37:54 +03:00
parent 9dedbff713
commit d6e3ef85c2
14 changed files with 227 additions and 102 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

View File

@ -102,6 +102,30 @@ class TestImageDraw(PillowTestCase):
self.assert_image_similar(
im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1)
def test_arc_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width.png"
# Act
draw.arc(BBOX1, 10, 260, width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_arc_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width_fill.png"
# Act
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_bitmap(self):
# Arrange
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
@ -137,6 +161,30 @@ class TestImageDraw(PillowTestCase):
self.helper_chord(mode, BBOX2, 0, 180)
self.helper_chord(mode, BBOX2, 0.5, 180.4)
def test_chord_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_chord_width.png"
# Act
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_chord_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_chord_width_fill.png"
# Act
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def helper_ellipse(self, mode, bbox):
# Arrange
im = Image.new(mode, (W, H))
@ -179,6 +227,30 @@ class TestImageDraw(PillowTestCase):
draw.ellipse(bbox, fill="green", outline="blue")
self.assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT))
def test_ellipse_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width.png"
# Act
draw.ellipse(BBOX1, outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_ellipse_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width_fill.png"
# Act
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def helper_line(self, points):
# Arrange
im = Image.new("RGB", (W, H))
@ -259,6 +331,30 @@ class TestImageDraw(PillowTestCase):
self.helper_pieslice(BBOX2, -90, 45)
self.helper_pieslice(BBOX2, -90.5, 45.4)
def test_pieslice_width(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_pieslice_width.png"
# Act
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_pieslice_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_pieslice_width_fill.png"
# Act
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def helper_point(self, points):
# Arrange
im = Image.new("RGB", (W, H))

View File

@ -126,7 +126,7 @@ Methods
:returns: An image font.
.. py:method:: PIL.ImageDraw.ImageDraw.arc(xy, start, end, fill=None)
.. py:method:: PIL.ImageDraw.ImageDraw.arc(xy, start, end, fill=None, width=0)
Draws an arc (a portion of a circle outline) between the start and end
angles, inside the given bounding box.
@ -138,6 +138,9 @@ Methods
3 o'clock, increasing clockwise.
:param end: Ending angle, in degrees.
:param fill: Color to use for the arc.
:param width: The line width, in pixels.
.. versionadded:: 5.2.0
.. py:method:: PIL.ImageDraw.ImageDraw.bitmap(xy, bitmap, fill=None)
@ -150,7 +153,7 @@ Methods
To paste pixel data into an image, use the
:py:meth:`~PIL.Image.Image.paste` method on the image itself.
.. py:method:: PIL.ImageDraw.ImageDraw.chord(xy, start, end, fill=None, outline=None)
.. py:method:: PIL.ImageDraw.ImageDraw.chord(xy, start, end, fill=None, outline=None, width=0)
Same as :py:meth:`~PIL.ImageDraw.ImageDraw.arc`, but connects the end points
with a straight line.
@ -160,8 +163,11 @@ Methods
where ``x1 >= x0`` and ``y1 >= y0``.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
:param width: The line width, in pixels.
.. py:method:: PIL.ImageDraw.ImageDraw.ellipse(xy, fill=None, outline=None)
.. versionadded:: 5.2.0
.. py:method:: PIL.ImageDraw.ImageDraw.ellipse(xy, fill=None, outline=None, width=0)
Draws an ellipse inside the given bounding box.
@ -170,6 +176,9 @@ Methods
where ``x1 >= x0`` and ``y1 >= y0``.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
:param width: The line width, in pixels.
.. versionadded:: 5.2.0
.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0)
@ -185,7 +194,7 @@ Methods
.. note:: This option was broken until version 1.1.6.
.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None)
.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=0)
Same as arc, but also draws straight lines between the end points and the
center of the bounding box.
@ -198,6 +207,9 @@ Methods
:param end: Ending angle, in degrees.
:param fill: Color to use for the fill.
:param outline: Color to use for the outline.
:param width: The outer line width, in pixels.
.. versionadded:: 5.2.0
.. py:method:: PIL.ImageDraw.ImageDraw.point(xy, fill=None)

View File

@ -118,11 +118,11 @@ class ImageDraw(object):
fill = self.draw.draw_ink(fill, self.mode)
return ink, fill
def arc(self, xy, start, end, fill=None):
def arc(self, xy, start, end, fill=None, width=0):
"""Draw an arc."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_arc(xy, start, end, ink)
self.draw.draw_arc(xy, start, end, ink, width)
def bitmap(self, xy, bitmap, fill=None):
"""Draw a bitmap."""
@ -133,21 +133,21 @@ class ImageDraw(object):
if ink is not None:
self.draw.draw_bitmap(xy, bitmap.im, ink)
def chord(self, xy, start, end, fill=None, outline=None):
def chord(self, xy, start, end, fill=None, outline=None, width=0):
"""Draw a chord."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_chord(xy, start, end, fill, 1)
if ink is not None:
self.draw.draw_chord(xy, start, end, ink, 0)
self.draw.draw_chord(xy, start, end, ink, 0, width)
def ellipse(self, xy, fill=None, outline=None):
def ellipse(self, xy, fill=None, outline=None, width=0):
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
if ink is not None:
self.draw.draw_ellipse(xy, ink, 0)
self.draw.draw_ellipse(xy, ink, 0, width)
def line(self, xy, fill=None, width=0):
"""Draw a line, or a connected sequence of line segments."""
@ -164,13 +164,13 @@ class ImageDraw(object):
if ink is not None:
self.draw.draw_outline(shape, ink, 0)
def pieslice(self, xy, start, end, fill=None, outline=None):
def pieslice(self, xy, start, end, fill=None, outline=None, width=0):
"""Draw a pieslice."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_pieslice(xy, start, end, fill, 1)
if ink is not None:
self.draw.draw_pieslice(xy, start, end, ink, 0)
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
def point(self, xy, fill=None):
"""Draw one or more individual pixels."""

View File

@ -2561,9 +2561,10 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
PyObject* data;
int ink;
int width = 0;
float start, end;
int op = 0;
if (!PyArg_ParseTuple(args, "Offi|i", &data, &start, &end, &ink))
if (!PyArg_ParseTuple(args, "Offi|ii", &data, &start, &end, &ink, &width))
return NULL;
n = PyPath_Flatten(data, &xy);
@ -2577,7 +2578,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
n = ImagingDrawArc(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, op
start, end, &ink, width, op
);
free(xy);
@ -2633,9 +2634,10 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
PyObject* data;
int ink, fill;
int width = 0;
float start, end;
if (!PyArg_ParseTuple(args, "Offii",
&data, &start, &end, &ink, &fill))
if (!PyArg_ParseTuple(args, "Offii|i",
&data, &start, &end, &ink, &fill, &width))
return NULL;
n = PyPath_Flatten(data, &xy);
@ -2649,7 +2651,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
n = ImagingDrawChord(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, fill, self->blend
start, end, &ink, fill, width, self->blend
);
free(xy);
@ -2670,7 +2672,8 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
PyObject* data;
int ink;
int fill = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
int width = 0;
if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width))
return NULL;
n = PyPath_Flatten(data, &xy);
@ -2684,7 +2687,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
n = ImagingDrawEllipse(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
&ink, fill, self->blend
&ink, fill, width, self->blend
);
free(xy);
@ -2850,8 +2853,9 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
PyObject* data;
int ink, fill;
int width = 0;
float start, end;
if (!PyArg_ParseTuple(args, "Offii", &data, &start, &end, &ink, &fill))
if (!PyArg_ParseTuple(args, "Offii|i", &data, &start, &end, &ink, &fill, &width))
return NULL;
n = PyPath_Flatten(data, &xy);
@ -2865,7 +2869,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
n = ImagingDrawPieslice(self->image->image,
(int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3],
start, end, &ink, fill, self->blend
start, end, &ink, fill, width, self->blend
);
free(xy);

View File

@ -762,9 +762,10 @@ ellipsePoint(int cx, int cy, int w, int h,
static int
ellipse(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink_, int fill,
int mode, int op)
int width, int mode, int op)
{
float i;
int j;
int n;
int cx, cy;
int w, h;
@ -774,120 +775,131 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1,
DRAW* draw;
INT32 ink;
w = x1 - x0;
h = y1 - y0;
if (w < 0 || h < 0)
return 0;
DRAWINIT();
cx = (x0 + x1) / 2;
cy = (y0 + y1) / 2;
while (end < start)
end += 360;
if (end - start > 360) {
/* no need to go in loops */
end = start + 361;
if (width == 0) {
width = 1;
}
if (mode != ARC && fill) {
for (j = 0; j < width; j++) {
/* Build edge list */
/* malloc check UNDONE, FLOAT? */
Edge* e = calloc((end - start + 3), sizeof(Edge));
if (!e) {
ImagingError_MemoryError();
return -1;
w = x1 - x0;
h = y1 - y0;
if (w < 0 || h < 0)
return 0;
cx = (x0 + x1) / 2;
cy = (y0 + y1) / 2;
while (end < start)
end += 360;
if (end - start > 360) {
/* no need to go in loops */
end = start + 361;
}
n = 0;
if (mode != ARC && fill) {
for (i = start; i < end+1; i++) {
if (i > end) {
i = end;
/* Build edge list */
/* malloc check UNDONE, FLOAT? */
Edge* e = calloc((end - start + 3), sizeof(Edge));
if (!e) {
ImagingError_MemoryError();
return -1;
}
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;
}
n = 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);
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);
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;
}
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 (x != cx || y != cy) {
draw->line(im, x, y, cx, cy, ink);
draw->line(im, cx, cy, sx, sy, ink);
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);
}
} 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 (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 if (mode == CHORD) {
if (x != sx || y != sy)
draw->line(im, x, y, sx, sy, ink);
}
}
x0++;
y0++;
x1--;
y1--;
}
return 0;
}
int
ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int op)
float start, float end, const void* ink, int width, int op)
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, ARC, op);
return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, width, ARC, op);
}
int
ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill, int op)
float start, float end, const void* ink, int fill,
int width, int op)
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, CHORD, op);
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, CHORD, op);
}
int
ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1,
const void* ink, int fill, int op)
const void* ink, int fill, int width, int op)
{
return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, CHORD, op);
return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, width, CHORD, op);
}
int
ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill, int op)
float start, float end, const void* ink, int fill,
int width, int op)
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, PIESLICE, op);
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, PIESLICE, op);
}
/* -------------------------------------------------------------------- */

View File

@ -349,21 +349,22 @@ extern void ImagingCrack(Imaging im, int x0, int y0);
/* Graphics */
extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int op);
float start, float end, const void* ink, int width,
int op);
extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap,
const void* ink, int op);
extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill,
int op);
int width, int op);
extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1,
const void* ink, int fill, int op);
const void* ink, int fill, int width, int op);
extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1,
const void* ink, int op);
extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
const void* ink, int width, int op);
extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill,
int op);
int width, int op);
extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op);
extern int ImagingDrawPolygon(Imaging im, int points, int *xy,
const void* ink, int fill, int op);