From d6e3ef85c2e2780410ca60258ee0e95682947ccd Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 13 Apr 2018 08:37:54 +0300 Subject: [PATCH] Add width parameter to arc, chord, ellipse, pieslice --- Tests/images/imagedraw_arc_width.png | Bin 0 -> 439 bytes Tests/images/imagedraw_arc_width_fill.png | Bin 0 -> 438 bytes Tests/images/imagedraw_chord_width.png | Bin 0 -> 488 bytes Tests/images/imagedraw_chord_width_fill.png | Bin 0 -> 507 bytes Tests/images/imagedraw_ellipse_width.png | Bin 0 -> 452 bytes Tests/images/imagedraw_ellipse_width_fill.png | Bin 0 -> 477 bytes Tests/images/imagedraw_pieslice_width.png | Bin 0 -> 488 bytes .../images/imagedraw_pieslice_width_fill.png | Bin 0 -> 500 bytes Tests/test_imagedraw.py | 96 ++++++++++ docs/reference/ImageDraw.rst | 20 ++- src/PIL/ImageDraw.py | 16 +- src/_imaging.c | 22 ++- src/libImaging/Draw.c | 166 ++++++++++-------- src/libImaging/Imaging.h | 9 +- 14 files changed, 227 insertions(+), 102 deletions(-) create mode 100644 Tests/images/imagedraw_arc_width.png create mode 100644 Tests/images/imagedraw_arc_width_fill.png create mode 100644 Tests/images/imagedraw_chord_width.png create mode 100644 Tests/images/imagedraw_chord_width_fill.png create mode 100644 Tests/images/imagedraw_ellipse_width.png create mode 100644 Tests/images/imagedraw_ellipse_width_fill.png create mode 100644 Tests/images/imagedraw_pieslice_width.png create mode 100644 Tests/images/imagedraw_pieslice_width_fill.png diff --git a/Tests/images/imagedraw_arc_width.png b/Tests/images/imagedraw_arc_width.png new file mode 100644 index 0000000000000000000000000000000000000000..ff3f1f0b21c94c490169ef922a606ba3e7ce0156 GIT binary patch literal 439 zcmV;o0Z9IdP)4zkE(~PRqzagFhNke}zOeEmIku_@0000000000006)^=XrJy;l1zP z%a3UE&=YG`qX&1ap_wjU&WnvM70y`GOGU(CxW+ouaX7|MLO#$%sX>`8+Ts3HYFws^ z^#*?#S}LTmUQVmIr9u~Lzf$2`hSN)hDAx0|+MrY@V(nTgT7C`ZlE>O+T19!R4PF2j zv@>g`X%*bFQYvHZy#_-%vo>3U!7@xCRsyjSh?PLB1Y#u+E1~(UgUGMPR2HDn2eX z>*a^Xdv3f4de~#SH^e1|=Xyl?E{nDRXS++TD;;O@-TJss+f?uJ!K!9^9$nE(5&Fxo z-U(XRy)}Mif!^VlOPxesS4QOqXo+kr(-qa--&TAwIoEjYBAaV$`VHK-W^TQ_h3j3U z=G2Yf;+hrbM$TS)FhO#efOca9=MgW3Fs6+{9aAsVTyuQ#@Z*kGo#*$m=P%fum%66d zZj*RlS9MsE*|d69uk{f!uXD6Fmf3tteK9BIRd(TduND1YZ9dyhKK()8;_{iG@1JVq zCq?M*54!t&DsKDUz3dhMO+4>!M8 zeR{qsvgS(uzikt~#`={^-?(>z*`6QQ)Qb1*`u;{}%9l^?3rzA?)A6n~-f+h2WW eD=^TZ;J_{Y`lP6ULeDZAkcg+NpUXO@geCyd0?HBq literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_chord_width.png b/Tests/images/imagedraw_chord_width.png new file mode 100644 index 0000000000000000000000000000000000000000..33a59b487c4e685f1a12c95323ea861fa17997e3 GIT binary patch literal 488 zcmeAS@N?(olHy`uVBq!ia0vp^DImaSW-L^LF;ZzGed+*1LcI z*Dq`LS}nSC!lrMZy-kXjP2kyfFtY=w0SPp`ys_=Zws(#(A7AYKvwMO2=ZF{MbPkr+~hDtPJe=kJXoZ;1VXLgvm&S$5%qzD0oRRgfl+tSMcDvP46ZwAPdQQEA-$DXYLVwSy zSo-C|v}s!u+Kd$ZDIyU+|$+1Wt~$(69A!*=T-m! literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_chord_width_fill.png b/Tests/images/imagedraw_chord_width_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..809c3ea1cdffd6aea9bc288f40f8349edb859826 GIT binary patch literal 507 zcmeAS@N?(olHy`uVBq!ia0vp^DImI;vs!9fmsl!0SRo7IWBm7qO425`@5I*+*K;>CGLJ1-(5ql zO}AcmFmCnB*M}=NeT-cB?9bE5SI_^C@nx;GJhgZ7w_uLv&E8)Z+f1_#4p(3Q%3J!x z(v|Nry?<@;`w-ug5?Az3=u}p^MV#{2nw2SgvsabY?9xw`-e>kces~#`sO77}e z|8tYt?5y4iPqiOs{g7g~-(X72x`yK178we?I_o%-~%XeL*tv{j9yuj{$11=I9@%jTaf_xjx&$G_5N z#l737=5EY$7fOE|J@3y~+aI6%tL?WwNsK++IZye9*b+|e`#Vp~@&4Ww-^%;w{zHxL udlRQ0iCv)e+dnq@YS}5Icwk_7Q2&Y3-o4D~?~4$5kc6kJpUXO@geCx&%JG~4 literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_ellipse_width.png b/Tests/images/imagedraw_ellipse_width.png new file mode 100644 index 0000000000000000000000000000000000000000..ec0ca6731f69b8728a41a59ae2cd8ebaa0cb71c5 GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-il6&m%Sud>DbwD@2Gjxt7H&;1(bid$7#YdI$gZMA$hCvtzzL+_mb^ENzl=Dj-MM#v1sH77sleP5UH zd4|=Ry@5-=Xy?`(*mE#{6We3EQ%e(h?SI}8iyZ@*5m+3DqXc(+l@Q=11{YnQe^tzKiRzFF?;nNRzfgub1aCptm; zT2^+!J|69%ea<|eNhC^Hm$i#o^istG)&HDQ%8&3%nJ#FA1JFQ-otJ;S@ zd+XF0k^PC1R_fEY>3>_p;2QC1{gSd1` z!9IWXbp4fKB~ocmpZ@EgZoQQ8P8i<bP0l+XkKH;l^C literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_ellipse_width_fill.png b/Tests/images/imagedraw_ellipse_width_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7be602916cce5f30f0e305add360c4939b3955 GIT binary patch literal 477 zcmeAS@N?(olHy`uVBq!ia0vp^DImY3` zviDB&_x4bs%kle!>~d{gTNE?{XBVHFSsHpZ+jhov!Q1=Q*W|98-J>RdZ}~Fa;{w-q z?FsS8kH6!hw#GyD+M-p*4fmRE>hcMlQrXW z^<^shkBzRZl?snrw4tqW)pzmJ|CVx`o_A%_^hYPJ7MlOr{w2{e|HGqwrw)JGd+lDZ zrQF9eo9xZKs#DH;t>AsV#q`JGoRCv9pT;*os*7ynY?ZsZZZVt?^Q_@Yh6KW*@f?wtDA@Xwl@IWdp+-PkN7lE`a(-*~6mUXk~l>t-%J zce=>dCGo7@%60Q9Pk-Gmb@xos)2CiqM@|RKpKiD^!|F`fl<9vvryDQjoD=5vK&GbY zXQ!f#KUe#%m4%p-bCtdV&N!UHx3v IIVCg!0J1dLjsO4v literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_pieslice_width.png b/Tests/images/imagedraw_pieslice_width.png new file mode 100644 index 0000000000000000000000000000000000000000..a83c4ab7b3469f0d2171148d31a4c7836311b50c GIT binary patch literal 488 zcmVP)BCwwrp=djxM5P(Q7Xf-4&n^WH0;OpIQMV$1fA81;PH}Fd;i-} zc4z&SlzT3R#0&gp$)s9xSch#jOv-oV$_r~QDQn7UcqS?9G?T*mY%A*&PRa=Do=LT$ zyWy$?VNJA^=?yVFEYtH0k=>hRpQs!+W^PQl(QmW0elk!?O&}GR?0Kr^zvt-!KgDu++TyWSW{~ zNDrxJ)_$pNrXiSb@U0D7s)=LmROu*Hx1}!Dvr?t`mSIn+;x$FF)+b%OLMKJ6zgeoK zJPpSskM(F;6`hLVv5KB&2<^;zFs&-z;J9bq5F4xTtcW0-S(O*mGi8@y#nKgHm7S6m zW0jTu-tfdS!K&jp)v_4V~kuDzoKAJ{ar z)V(~{LlkT8D$CBt_G4Q&vrN65+Y-`cDDP$7z)5%-M)gYg#;ZTXjsKK(I?W_JBhS_8 z`nPhAtJ$PI9p_ZOsB%)RW5tcPQ+i3X+hjV6l;6Fv$$Y1fwMh`t%RTt?9hC>w(#D(~ qYjIhv&QJjW0000000000{9j*)= 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) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 1bbb6e6a6..23a666e76 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -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.""" diff --git a/src/_imaging.c b/src/_imaging.c index 3cced9a0f..f1116a36c 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -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); diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index fee1cac63..37969af70 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -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); } /* -------------------------------------------------------------------- */ diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 4588602b1..e705e0a60 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -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);