From bcfe61495b803a79a198699f2acd9d4c0d309d3e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 13:14:08 +1000 Subject: [PATCH 1/9] Update pip to fix pyqt5 install --- .ci/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 8e819631a..180fa6582 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -7,6 +7,7 @@ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ cmake imagemagick libharfbuzz-dev libfribidi-dev +pip install --upgrade pip PYTHONOPTIMIZE=0 pip install cffi pip install coverage pip install olefile @@ -20,7 +21,7 @@ if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then # "ERROR: Could not find a version that satisfies the requirement pyqt5" if [[ $TRAVIS_CPU_ARCH == "amd64" ]]; then sudo apt-get -qq install pyqt5-dev-tools - pip install pyqt5!=5.14.1 + pip install pyqt5 fi fi From c414810d12a7204eeaee764520f645e559be94a4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 15:23:18 +1000 Subject: [PATCH 2/9] Replaced property methods for n_frames and is_animated with normal properties --- src/PIL/PngImagePlugin.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0291df4b0..ee9d52b4c 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -673,7 +673,7 @@ class PngImageFile(ImageFile.ImageFile): self._text = None self.tile = self.png.im_tile self.custom_mimetype = self.png.im_custom_mimetype - self._n_frames = self.png.im_n_frames + self.n_frames = self.png.im_n_frames or 1 self.default_image = self.info.get("default_image", False) if self.png.im_palette: @@ -685,15 +685,16 @@ class PngImageFile(ImageFile.ImageFile): else: self.__prepare_idat = length # used by load_prepare() - if self._n_frames is not None: + if self.png.im_n_frames is not None: self._close_exclusive_fp_after_loading = False self.png.save_rewind() self.__rewind_idat = self.__prepare_idat self.__rewind = self.__fp.tell() if self.default_image: # IDAT chunk contains default image and not first animation frame - self._n_frames += 1 + self.n_frames += 1 self._seek(0) + self.is_animated = self.n_frames > 1 @property def text(self): @@ -710,16 +711,6 @@ class PngImageFile(ImageFile.ImageFile): self.seek(frame) return self._text - @property - def n_frames(self): - if self._n_frames is None: - return 1 - return self._n_frames - - @property - def is_animated(self): - return self._n_frames is not None and self._n_frames > 1 - def verify(self): """Verify PNG file""" From 7475c06b1c1b5145eff202d9bd3e05668825a9cd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 15:29:13 +1000 Subject: [PATCH 3/9] Assert that seeking too far raises an EOFError --- Tests/test_file_png.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 469d186e8..476995dd7 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -637,6 +637,9 @@ class TestFilePng: with Image.open(TEST_PNG_FILE) as im: im.seek(0) + with pytest.raises(EOFError): + im.seek(1) + @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS") @skip_unless_feature("zlib") From f1f177ce800c5be7d06b0119b979c661c1bfbbb9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Apr 2020 19:41:38 +1000 Subject: [PATCH 4/9] Fixed error making HTML from docs --- docs/reference/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index 46f657fce..cc0742fde 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -229,7 +229,7 @@ Plugin reference ---------------------------- .. automodule:: PIL.PngImagePlugin - :members: ChunkStream, PngImageFile, PngStream, getchunks, is_cid, putchunk + :members: ChunkStream, PngStream, getchunks, is_cid, putchunk :show-inheritance: .. autoclass:: PIL.PngImagePlugin.ChunkStream :members: From e5ac436a51765f107343b8e6dd1bc40165683539 Mon Sep 17 00:00:00 2001 From: Stanislau Tsitsianok Date: Fri, 3 Apr 2020 19:19:26 +0300 Subject: [PATCH 5/9] implemented another ellipse drawing algorithm --- src/libImaging/Draw.c | 180 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 58adc1b63..ee3a3938e 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -969,6 +969,184 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, return 0; } +// Imagine 2D plane and ellipse with center in (0, 0) and semi-major axes a and b. +// Then quarter_* stuff approximates its top right quarter (x, y >= 0) with integer +// points from set {(2x+x0, 2y+y0) | x,y in Z} where x0, y0 are from {0, 1} and +// are such that point (a, b) is in the set. + +typedef struct { + int32_t a, b, cx, cy, ex, ey; + int64_t a2, b2, a2b2; + int8_t finished; +} quarter_state; + +void quarter_init(quarter_state* s, int32_t a, int32_t b) { + if (a < 0 || b < 0) { + s->finished = 1; + } else { + s->a = a; + s->b = b; + s->cx = a; + s->cy = b % 2; + s->ex = a % 2; + s->ey = b; + s->a2 = a * a; + s->b2 = b * b; + s->a2b2 = s->a2 * s->b2; + s->finished = 0; + } +} + +// deviation of the point from ellipse curve, basically a substitution +// of the point into the ellipse equation +int64_t quarter_delta(quarter_state* s, int64_t x, int64_t y) { + return llabs(s->a2 * y * y + s->b2 * x * x - s->a2b2); +} + +int8_t quarter_next(quarter_state* s, int32_t* ret_x, int32_t* ret_y) { + if (s->finished) { + return -1; + } + *ret_x = s->cx; + *ret_y = s->cy; + if (s->cx == s->ex && s->cy == s->ey) { + s->finished = 1; + } else { + // bresenham's algorithm, possible optimization: only consider 2 of 3 + // next points depending on current slope + int32_t nx = s->cx; + int32_t ny = s->cy + 2; + int64_t ndelta = quarter_delta(s, nx, ny); + if (nx > 1) { + int64_t newdelta = quarter_delta(s, s->cx - 2, s->cy + 2); + if (ndelta > newdelta) { + nx = s->cx - 2; + ny = s->cy + 2; + ndelta = newdelta; + } + newdelta = quarter_delta(s, s->cx - 2, s->cy); + if (ndelta > newdelta) { + nx = s->cx - 2; + ny = s->cy; + } + } + s->cx = nx; + s->cy = ny; + } + return 0; +} + +// quarter_* stuff can "draw" a quarter of an ellipse with thickness 1, great. +// Now we use ellipse_* stuff to join all four quarters of two different sized +// ellipses and recieve horizontal segments of a complete ellipse with +// specified thickness. +// +// Still using integer grid with step 2 at this point (like in quarter_*) +// to ease angle clipping in future. + +typedef struct { + quarter_state st_o, st_i; + int32_t py, pl, pr; + int32_t cy, cl, cr; + // 0 - ready to take next quarter piece, 1, 2, 3 - returned 1, 2, 3 hlines + int8_t state; + int8_t finished; +} ellipse_state; + +void ellipse_init(ellipse_state* s, int32_t a, int32_t b, int32_t w) { + s->state = 0; + quarter_init(&s->st_o, a, b); + if (w < 1 || quarter_next(&s->st_o, &s->pr, &s->py) == -1) { + s->finished = 1; + } else { + s->finished = 0; + quarter_init(&s->st_i, a - 2 * (w - 1), b - 2 * (w - 1)); + s->pl = a % 2; + } +} + +int8_t ellipse_next(ellipse_state* s, int32_t* ret_x0, int32_t* ret_y, int32_t* ret_x1) { + switch (s->state) { + case 0: { + if (s->finished) { + return -1; + } + s->cy = s->py; + s->cl = s->pl; + s->cr = s->pr; + int32_t cx = 0, cy = 0; + int8_t next_ret; + while ((next_ret = quarter_next(&s->st_o, &cx, &cy)) != -1 && cy <= s->cy) { + } + s->pr = cx; + s->py = cy; + if (next_ret == -1) { + s->finished = 1; + } + while ((next_ret = quarter_next(&s->st_i, &cx, &cy)) != -1 && cy <= s->cy) { + s->cl = cx; + } + s->pl = next_ret == -1 ? 0 : cx; + *ret_x0 = -s->cr; + *ret_y = -s->cy; + *ret_x1 = -s->cl; + s->state = 1; + return 0; + } + case 1: { + *ret_x0 = s->cl; + *ret_y = -s->cy; + *ret_x1 = s->cr; + s->state = 2; + return 0; + } + case 2: { + *ret_x0 = -s->cr; + *ret_y = s->cy; + *ret_x1 = -s->cl; + s->state = 3; + return 0; + } + case 3: { + *ret_x0 = s->cl; + *ret_y = s->cy; + *ret_x1 = s->cr; + s->state = 0; + return 0; + } + default: { + assert(0); + return -1; + } + } +} + +static int +ellipseNew(Imaging im, int x0, int y0, int x1, int y1, + const void* ink_, int fill, + int width, int op) +{ + DRAW* draw; + INT32 ink; + DRAWINIT(); + + int a = x1 - x0; + int b = y1 - y0; + if (a < 0 || b < 0) + return 0; + if (fill) { + width = a + b; + } + + ellipse_state st; + ellipse_init(&st, a, b, width); + int32_t X0, Y, X1; + while (ellipse_next(&st, &X0, &Y, &X1) != -1) { + draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink); + } + return 0; +} + int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, float start, float end, const void* ink, int width, int op) @@ -988,7 +1166,7 @@ int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int width, int op) { - return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, width, CHORD, op); + return ellipseNew(im, x0, y0, x1, y1, ink, fill, width, op); } int From a491ed6889afd16a9e34f2dbe6c222dcfcde127f Mon Sep 17 00:00:00 2001 From: Stanislau Tsitsianok Date: Fri, 3 Apr 2020 22:33:47 +0300 Subject: [PATCH 6/9] fixed failing tests --- Tests/images/imagedraw_ellipse_L.png | Bin 359 -> 318 bytes Tests/images/imagedraw_ellipse_RGB.png | Bin 466 -> 408 bytes Tests/images/imagedraw_ellipse_edge.png | Bin 602 -> 622 bytes Tests/images/imagedraw_ellipse_width.png | Bin 452 -> 439 bytes Tests/images/imagedraw_ellipse_width_fill.png | Bin 477 -> 465 bytes .../images/imagedraw_ellipse_width_large.png | Bin 4455 -> 4451 bytes Tests/images/imagedraw_ellipse_zero_width.png | Bin 339 -> 333 bytes Tests/test_imagedraw.py | 4 +- Tests/test_imagedraw2.py | 2 +- src/libImaging/Draw.c | 108 +++++++++--------- 10 files changed, 56 insertions(+), 58 deletions(-) diff --git a/Tests/images/imagedraw_ellipse_L.png b/Tests/images/imagedraw_ellipse_L.png index e47e6e441c16afcc1edb2f7c2a715fd36469c096..d5959cc0820251a9d76a3f3fe0a6ed60fd8dd2ad 100644 GIT binary patch delta 291 zcmV+;0o?xQ0=@!}B!2}-L_t(|obA}%4Z<)G17L$#BP(?OIkG|*;Q=9vM9x0-=TK`u zRY3XVqz}CSj^j9v<2cS~a?Aa^y)Wj>U5^|vEkBSUxk>v50HCXki2NWDjD!pbnNg57 zBmkgu<-XRy)WWYdgcyoK5o!nuLj;2>1d$A?#E57xMR>Z_Cx1lbBBV-rlSGlYN_CS| zk+4d2lhYu{Er~Ye3d?20$bQJ&lYcdS%e%sI9fKq{C3x|!Zj!4KO%UZxGF8G+sJ@9_ zC7$U>6Hy^^BU>&>VY4aS6G5T#FW-2o@EKcpx`-mo@qcq7F=n>~7JB_M}qYeN7002ovPDHLkV1ljyfiM67 delta 332 zcmV-S0ki(T0_OsdB!4bRL_t(|ob8y=4TC@oL*4FiE3p3@tiU4uXc2sHwj+8Vwe}a4 z3=by})dh&6D2k#eihf3qlDln}%NR%lFs(o43de=C9bgCYI3DB(QNUP`c8qvq49fEy z1JiyOdeTW40)P`nCXoyyjDl!}L6J!`Lwf|IISgRpQ8>hy9Dm0tl6XN>TPVaVmV6rq z@rxzgG>IRCd!5h9X%R@FBLE%ayOUf2sm&I>gCwc7g!%uWCxHYq5Hc4Z)%W)ym9v4k z#m?@PiOf;unlM#dMitOvcxMcx_Q0 z1##LUPo^@7rbXz}ud^(mhf`-;;CxnC7;7V8E^s^KiES0000q_jzs<5c@UHIPi;m(x`6TWVTlr_Vto!Cx8eH3Z?Y4t|-67sNH7eJZ&ny!@d1#}R z(c4WYCvALp&S&@QbAJ@$8{bD{OM0)Bx@zu!$!unQ#JM2VOWFdb<5&Ht-kH4AOSD)& zdri*DM{k~Xn+C4BQFU})+y6$*Yj+|<&lea*GP$pLxlya_VbUg+w;Rt%tiEI$<1?)x zf+>l!DtT^6;?!%|-6w?vyd88HH?kaoG4^hZ+iz~ccj4Fna5ae3+l_v^w(Syko1s(I zsC-TGaOw?bpoz;OE+2jxnV-CgWnV<`;ndr+w)kCh(SKgI@^|`-x2gAz@ZO1B8TW7Q z(=)8^-;3US*JsN+cW+Sjhdqgk5AMn`=C^Vl3m2}q|G|0r{*zpYz`8Hc8X5CtYjhY$ N$kWx&Wt~$(69C3+umu1B delta 440 zcmbQie2IC2O8rbv7srr_Id5-Y>^o$@;}SSs|NZ=Oc`j9jjcX1X`)&PhqnyO1&a2eP zfB=7PUWz@CSpB`}U(juB38ScZ@y&gK+pgX*{qXkif1wN7-yhsL`up%NrDcKcDbF@+ z>sX&G6{~Dq+tr;uqhp2ttABa<@_eQ?_d6AqsZXmHGcK8Cx6)wi3@aV)AnV$=U*|rl zq)K{AT}$)L+OD_j*`?OhDkn?#@Um(9*UBARd)`|l>^O@|rdxQ+Rcl7~qf;}iR&QUM zVro(UZcbcf#NE|bS5!$mrmG5sIps!twqbWq4Gk^7X2X4L-4?lJiA;$=2^6A!O1_u+ z?IY8#1xsH`x&3|hrd2cAI9sL$$XzqKxM^$NR&D0FQx$$4PHK84bY%fY_Pq3@Le))~ zH*Uwwy6BpyD_C23l5g8fu^;zNR?OP}o?rHM$E&x8P87^IJ7ao7D@zdAj3saAJASjC-?c5V)piWGQGWL=`_9^zC+?nlC-v9U{r;Vlvvn8l zXIUk!J5#iy&P3KQWLqTfmF1f0R));4?`CyntdGdv>}L2>XW1>4`fP>Z#$%k}X2ll1 zf~DS@mPIa}U9?p&B~o|>R|w0+6yId61;+$aeABcPTD7K{E?wzRxkV{bcs7>^+nLj| z?u9H+S#~r3u;_^sQA@%ab8bvov($(6(nS4K-3+Y>9Ove#7U*T)jAC8)X#R<)J0TB3 zm`(>MOmkgZwlDO8P9uNh6|H(DA*anO6I4G;J1S-KbVX|#=bzJy!Z^z;O}rT*nNA4Z zkdY2!bQC-*n46HwiQn|Dty1W7>o4LXb|oC*-NP@xTf|C|+C{9x~LxH7Zr2X8n3 zQrB1n5jn75XWLqq-7>R)w${7u^s~$YoA_hGQLmnk7NEl-#Gc;z8Roc|#V;jLuUzrr zp2+UsySmOBiR_rNHD{;L!Qit|7s9QI(j@hsYD-VE^;Xc&o>(;Xh)&kEFZ;Ndr|BK; ztexsE{B4b9t>?lmymdzLSu#Iw7f8j6I%dhqs(lC*U#R)5gU z*8cF?Ydxkq`_k14vIYS?!8ZNZ;VUYRv&hbPufEs#jDWtKJm Z#V)P6Zr7Hy+NBIY;OXk;vd$@?2>|5e4hR4M delta 577 zcmaFIa*JhxO1+||i(^Q|oVRlwC$%UDupIpV|EBE*!_$kDSbLc#RhBaCzMOSza>e)1 zZ?*dWH-6t2ujeuX-B%B*V#2#|ZsZxk!Qs2aJeJbN5RiJmC z7Nl0GM>0*{bd4u`-|Lu7VV_HHzdtLn?(ym+mw(%4v&OgDisgQI|LrKd(C!27v8*1` z9L}bFX{x#U_K@kxS+)DuuiIb5^Ze`8pvvs?!o4ZKne*;GNbj8CIXBH}EuXaE;p3U# zUKykv$yxo#MLpH`vncZ%QMJRGC8h=Y5_yB?&-uxGs&eVpPMveE3_#%N>gTe~DWM4f D32FtN diff --git a/Tests/images/imagedraw_ellipse_width.png b/Tests/images/imagedraw_ellipse_width.png index ec0ca6731f69b8728a41a59ae2cd8ebaa0cb71c5..54cfc291ca247de31cc3033575b4993e7f704e11 100644 GIT binary patch delta 413 zcmV;O0b>5d1GfW^B!7NML_t(|obB7ea)Lk*gyD_v|IX%+3lbJ>obFkz|GQe*8M;Ow zvWx)$00000000000C?F?cLQ7Z`A19i+1|uSKr?MUH->c~rmcp~Tf%xXrmblI;jjv0 z+6o;-GOVXboiy{#-g5Z*BoNl&fq#|M^{ssD=39DDo=!5UzzY1XqYQx%{Ai93D-qkHTZ82E_!V(acfUpFF zB_J#TVF?IJP=6bi>uylwx;JLB1cW7Md2aZr70bhF-yOG8*-zkWm>e!|Ek{`E3|Da{ znG<|wxa?I-VTGQjpRQj&;M_V0d|@SrE3{j{eKL0MH;e9PYk6*1HDc+Vf9xq0uv=Nc zK6n9dSgcp(5%2EEcq>fC`-T^-{w6B`000000000002cfLlP)$?u6pK600000NkvXX Hu0mjfwqC&g delta 426 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 QQ&e%#H8X>XtImJ?8*YWK$^ZZW literal 4455 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 diff --git a/Tests/images/imagedraw_ellipse_zero_width.png b/Tests/images/imagedraw_ellipse_zero_width.png index f14a279f2fe2b17911e7fa8ced05c4829d5d7b0f..6661b7d999e37cf6593cc469b9055ff2c048c09c 100644 GIT binary patch delta 306 zcmcc2be3s?O1+4ui(^Q|oVT|P^A0=kusEpdKfiq0%HwgU$2`_ETbibSYidoHyN;Uy z3>voA&i%)v|LWRO*X5^IP36AaUsv3)-#7hP%w+pay`S6H zZhH0b=k-$EsiD%&V$04<4}C0rEsb}2=;n{7h1E~5I`~Gpxc>O5RVyDfUlz}ootM9C zjg;rvkoqNOxp=08{+(zZT0HM8SIP9yvz7lt=O&tIFIdyCiD_+WXnMr#EbmQ`jH#?s zL<7PcV2lI4nFrFqLI>v8GtL(Ma6l8Z;he3*(%H|< z00s@0>&o^wZr@dNaL?V;tCOai+zbi3tETcXYVU)K-xYTk`9)hteqC|HUd%7d*5K#u zNheo{cI!lB)NRVDX-&=MogTXRV|VzSkGVf0CZ}Fa_%^My{`jd?DdZ9_o0v{state = 0; + s->bufcnt = 0; + s->leftmost = a % 2; quarter_init(&s->st_o, a, b); if (w < 1 || quarter_next(&s->st_o, &s->pr, &s->py) == -1) { s->finished = 1; } else { s->finished = 0; quarter_init(&s->st_i, a - 2 * (w - 1), b - 2 * (w - 1)); - s->pl = a % 2; + s->pl = s->leftmost; } } int8_t ellipse_next(ellipse_state* s, int32_t* ret_x0, int32_t* ret_y, int32_t* ret_x1) { - switch (s->state) { - case 0: { - if (s->finished) { - return -1; - } - s->cy = s->py; - s->cl = s->pl; - s->cr = s->pr; - int32_t cx = 0, cy = 0; - int8_t next_ret; - while ((next_ret = quarter_next(&s->st_o, &cx, &cy)) != -1 && cy <= s->cy) { - } - s->pr = cx; - s->py = cy; - if (next_ret == -1) { - s->finished = 1; - } - while ((next_ret = quarter_next(&s->st_i, &cx, &cy)) != -1 && cy <= s->cy) { - s->cl = cx; - } - s->pl = next_ret == -1 ? 0 : cx; - *ret_x0 = -s->cr; - *ret_y = -s->cy; - *ret_x1 = -s->cl; - s->state = 1; - return 0; - } - case 1: { - *ret_x0 = s->cl; - *ret_y = -s->cy; - *ret_x1 = s->cr; - s->state = 2; - return 0; - } - case 2: { - *ret_x0 = -s->cr; - *ret_y = s->cy; - *ret_x1 = -s->cl; - s->state = 3; - return 0; - } - case 3: { - *ret_x0 = s->cl; - *ret_y = s->cy; - *ret_x1 = s->cr; - s->state = 0; - return 0; - } - default: { - assert(0); + if (s->bufcnt == 0) { + if (s->finished) { return -1; } + int32_t y = s->py; + int32_t l = s->pl; + int32_t r = s->pr; + int32_t cx = 0, cy = 0; + int8_t next_ret; + while ((next_ret = quarter_next(&s->st_o, &cx, &cy)) != -1 && cy <= y) { + } + if (next_ret == -1) { + s->finished = 1; + } else { + s->pr = cx; + s->py = cy; + } + while ((next_ret = quarter_next(&s->st_i, &cx, &cy)) != -1 && cy <= y) { + l = cx; + } + s->pl = next_ret == -1 ? s->leftmost : cx; + + if ((l > 0 || l < r) && y > 0) { + s->cl[s->bufcnt] = l == 0 ? 2 : l; + s->cy[s->bufcnt] = y; + s->cr[s->bufcnt] = r; + ++s->bufcnt; + } + if (y > 0) { + s->cl[s->bufcnt] = -r; + s->cy[s->bufcnt] = y; + s->cr[s->bufcnt] = -l; + ++s->bufcnt; + } + if (l > 0 || l < r) { + s->cl[s->bufcnt] = l == 0 ? 2 : l; + s->cy[s->bufcnt] = -y; + s->cr[s->bufcnt] = r; + ++s->bufcnt; + } + s->cl[s->bufcnt] = -r; + s->cy[s->bufcnt] = -y; + s->cr[s->bufcnt] = -l; + ++s->bufcnt; } + --s->bufcnt; + *ret_x0 = s->cl[s->bufcnt]; + *ret_y = s->cy[s->bufcnt]; + *ret_x1 = s->cr[s->bufcnt]; + return 0; } static int From 8aa2386c77721867a3642c9701b210853c132195 Mon Sep 17 00:00:00 2001 From: Stanislau Tsitsianok Date: Fri, 3 Apr 2020 22:54:37 +0300 Subject: [PATCH 7/9] added stdint.h for MSVC builds --- src/libImaging/Draw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 34d1d4bc5..20fcbe493 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -35,6 +35,7 @@ #include "Imaging.h" #include +#include #define CEIL(v) (int) ceil(v) #define FLOOR(v) ((v) >= 0.0 ? (int) (v) : (int) floor(v)) From 68f87e14ca1c984ebb2d653644b7695cf4305d34 Mon Sep 17 00:00:00 2001 From: Stanislau T <44941959+xtsm@users.noreply.github.com> Date: Sat, 4 Apr 2020 10:17:03 +0300 Subject: [PATCH 8/9] Fix grammar Co-Authored-By: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/libImaging/Draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 20fcbe493..96884ff78 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -1039,7 +1039,7 @@ int8_t quarter_next(quarter_state* s, int32_t* ret_x, int32_t* ret_y) { // quarter_* stuff can "draw" a quarter of an ellipse with thickness 1, great. // Now we use ellipse_* stuff to join all four quarters of two different sized -// ellipses and recieve horizontal segments of a complete ellipse with +// ellipses and receive horizontal segments of a complete ellipse with // specified thickness. // // Still using integer grid with step 2 at this point (like in quarter_*) From 5923f95f3147dda0763f9f3bb4e820bc38b5bd6c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 10 Apr 2020 12:15:01 +1000 Subject: [PATCH 9/9] Adjusted symmetry test --- Tests/test_imagedraw.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index cc29c097e..7a900283e 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -293,8 +293,11 @@ def test_ellipse_edge(): def test_ellipse_symmetric(): - for bbox in [(24, 25, 76, 75), (25, 25, 75, 75), (25, 24, 75, 76)]: - im = Image.new("RGB", (101, 101)) + for width, bbox in ( + (100, (24, 24, 75, 75)), + (101, (25, 25, 75, 75)), + ): + im = Image.new("RGB", (width, 100)) draw = ImageDraw.Draw(im) draw.ellipse(bbox, fill="green", outline="blue") assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT))