Skip to content

Commit

Permalink
Merge pull request #6978 from radarhere/coordinates
Browse files Browse the repository at this point in the history
Raise an error if ImageDraw co-ordinates are incorrectly ordered
  • Loading branch information
mergify[bot] committed Mar 1, 2023
2 parents 19e974f + a4965a7 commit 1c24fa5
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 28 deletions.
Binary file modified Tests/images/imagedraw_ellipse_various_sizes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/imagedraw_ellipse_various_sizes_filled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 25 additions & 4 deletions Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,13 @@ def ellipse_various_sizes_helper(filled):
for w in ellipse_sizes:
y = 1
for h in ellipse_sizes:
border = [x, y, x + w - 1, y + h - 1]
x1 = x + w
if w:
x1 -= 1
y1 = y + h
if h:
y1 -= 1
border = [x, y, x1, y1]
if filled:
draw.ellipse(border, fill="white")
else:
Expand Down Expand Up @@ -932,9 +938,6 @@ def test_square():
img, draw = create_base_image_draw((10, 10))
draw.rectangle((2, 2, 7, 7), BLACK)
assert_image_equal_tofile(img, expected, "square as normal rectangle failed")
img, draw = create_base_image_draw((10, 10))
draw.rectangle((7, 7, 2, 2), BLACK)
assert_image_equal_tofile(img, expected, "square as inverted rectangle failed")


def test_triangle_right():
Expand Down Expand Up @@ -1499,3 +1502,21 @@ def test_polygon2():
draw.polygon([(18, 30), (19, 31), (18, 30), (85, 30), (60, 72)], "red")
expected = "Tests/images/imagedraw_outline_polygon_RGB.png"
assert_image_similar_tofile(im, expected, 1)


@pytest.mark.parametrize("xy", ((1, 1, 0, 1), (1, 1, 1, 0)))
def test_incorrectly_ordered_coordinates(xy):
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
with pytest.raises(ValueError):
draw.arc(xy, 10, 260)
with pytest.raises(ValueError):
draw.chord(xy, 10, 260)
with pytest.raises(ValueError):
draw.ellipse(xy)
with pytest.raises(ValueError):
draw.pieslice(xy, 10, 260)
with pytest.raises(ValueError):
draw.rectangle(xy)
with pytest.raises(ValueError):
draw.rounded_rectangle(xy)
8 changes: 4 additions & 4 deletions docs/reference/ImageDraw.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ Methods
Draws a rectangle.

:param xy: Two points to define the bounding box. Sequence of either
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. The bounding box
is inclusive of both endpoints.
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``, where ``x1 >= x0`` and
``y1 >= y0``. The bounding box is inclusive of both endpoints.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
:param width: The line width, in pixels.
Expand All @@ -331,8 +331,8 @@ Methods
Draws a rounded rectangle.

:param xy: Two points to define the bounding box. Sequence of either
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. The bounding box
is inclusive of both endpoints.
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``, where ``x1 >= x0`` and
``y1 >= y0``. The bounding box is inclusive of both endpoints.
:param radius: Radius of the corners.
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
Expand Down
6 changes: 6 additions & 0 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ def rounded_rectangle(
(x0, y0), (x1, y1) = xy
else:
x0, y0, x1, y1 = xy
if x1 < x0:
msg = "x1 must be greater than or equal to x0"
raise ValueError(msg)
if y1 < y0:
msg = "y1 must be greater than or equal to y0"
raise ValueError(msg)
if corners is None:
corners = (True, True, True, True)

Expand Down
54 changes: 54 additions & 0 deletions src/_imaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view) {
static const char *must_be_sequence = "argument must be a sequence";
static const char *must_be_two_coordinates =
"coordinate list must contain exactly 2 coordinates";
static const char *incorrectly_ordered_x_coordinate =
"x1 must be greater than or equal to x0";
static const char *incorrectly_ordered_y_coordinate =
"y1 must be greater than or equal to y0";
static const char *wrong_mode = "unrecognized image mode";
static const char *wrong_raw_mode = "unrecognized raw mode";
static const char *outside_image = "image index out of range";
Expand Down Expand Up @@ -2805,6 +2809,16 @@ _draw_arc(ImagingDrawObject *self, PyObject *args) {
free(xy);
return NULL;
}
if (xy[2] < xy[0]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
free(xy);
return NULL;
}
if (xy[3] < xy[1]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
free(xy);
return NULL;
}

n = ImagingDrawArc(
self->image->image,
Expand Down Expand Up @@ -2886,6 +2900,16 @@ _draw_chord(ImagingDrawObject *self, PyObject *args) {
free(xy);
return NULL;
}
if (xy[2] < xy[0]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
free(xy);
return NULL;
}
if (xy[3] < xy[1]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
free(xy);
return NULL;
}

n = ImagingDrawChord(
self->image->image,
Expand Down Expand Up @@ -2932,6 +2956,16 @@ _draw_ellipse(ImagingDrawObject *self, PyObject *args) {
free(xy);
return NULL;
}
if (xy[2] < xy[0]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
free(xy);
return NULL;
}
if (xy[3] < xy[1]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
free(xy);
return NULL;
}

n = ImagingDrawEllipse(
self->image->image,
Expand Down Expand Up @@ -3101,6 +3135,16 @@ _draw_pieslice(ImagingDrawObject *self, PyObject *args) {
free(xy);
return NULL;
}
if (xy[2] < xy[0]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
free(xy);
return NULL;
}
if (xy[3] < xy[1]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
free(xy);
return NULL;
}

n = ImagingDrawPieslice(
self->image->image,
Expand Down Expand Up @@ -3197,6 +3241,16 @@ _draw_rectangle(ImagingDrawObject *self, PyObject *args) {
free(xy);
return NULL;
}
if (xy[2] < xy[0]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
free(xy);
return NULL;
}
if (xy[3] < xy[1]) {
PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
free(xy);
return NULL;
}

n = ImagingDrawRectangle(
self->image->image,
Expand Down
29 changes: 9 additions & 20 deletions src/libImaging/Draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,22 @@ point32(Imaging im, int x, int y, int ink) {

static inline void
point32rgba(Imaging im, int x, int y, int ink) {
unsigned int tmp1;
unsigned int tmp;

if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
UINT8 *out = (UINT8 *)im->image[y] + x * 4;
UINT8 *in = (UINT8 *)&ink;
out[0] = BLEND(in[3], out[0], in[0], tmp1);
out[1] = BLEND(in[3], out[1], in[1], tmp1);
out[2] = BLEND(in[3], out[2], in[2], tmp1);
out[0] = BLEND(in[3], out[0], in[0], tmp);
out[1] = BLEND(in[3], out[1], in[1], tmp);
out[2] = BLEND(in[3], out[2], in[2], tmp);
}
}

static inline void
hline8(Imaging im, int x0, int y0, int x1, int ink) {
int tmp, pixelwidth;
int pixelwidth;

if (y0 >= 0 && y0 < im->ysize) {
if (x0 > x1) {
tmp = x0, x0 = x1, x1 = tmp;
}
if (x0 < 0) {
x0 = 0;
} else if (x0 >= im->xsize) {
Expand All @@ -126,13 +123,9 @@ hline8(Imaging im, int x0, int y0, int x1, int ink) {

static inline void
hline32(Imaging im, int x0, int y0, int x1, int ink) {
int tmp;
INT32 *p;

if (y0 >= 0 && y0 < im->ysize) {
if (x0 > x1) {
tmp = x0, x0 = x1, x1 = tmp;
}
if (x0 < 0) {
x0 = 0;
} else if (x0 >= im->xsize) {
Expand All @@ -152,13 +145,9 @@ hline32(Imaging im, int x0, int y0, int x1, int ink) {

static inline void
hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
int tmp;
unsigned int tmp1;
unsigned int tmp;

if (y0 >= 0 && y0 < im->ysize) {
if (x0 > x1) {
tmp = x0, x0 = x1, x1 = tmp;
}
if (x0 < 0) {
x0 = 0;
} else if (x0 >= im->xsize) {
Expand All @@ -173,9 +162,9 @@ hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;
UINT8 *in = (UINT8 *)&ink;
while (x0 <= x1) {
out[0] = BLEND(in[3], out[0], in[0], tmp1);
out[1] = BLEND(in[3], out[1], in[1], tmp1);
out[2] = BLEND(in[3], out[2], in[2], tmp1);
out[0] = BLEND(in[3], out[0], in[0], tmp);
out[1] = BLEND(in[3], out[1], in[1], tmp);
out[2] = BLEND(in[3], out[2], in[2], tmp);
x0++;
out += 4;
}
Expand Down

0 comments on commit 1c24fa5

Please sign in to comment.