From 01ba1e2252bb6fab3bdee4ee0b2952746d919270 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 11 Sep 2024 19:53:52 +1000 Subject: [PATCH] Accept float stroke widths --- Tests/images/imagedraw_stroke_float.png | Bin 0 -> 1509 bytes Tests/test_imagedraw.py | 14 ++++++++++++++ src/_imagingft.c | 18 +++++++++--------- 3 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 Tests/images/imagedraw_stroke_float.png diff --git a/Tests/images/imagedraw_stroke_float.png b/Tests/images/imagedraw_stroke_float.png new file mode 100644 index 0000000000000000000000000000000000000000..a590f8af0b616852c8d54e591d04265dea90447b GIT binary patch literal 1509 zcma)+dpi>f0Eb7mn8{^mF&s3vgt;u2C}ZZ7!A38(GopOnZ9cH6qE+N-r71fmL zkz`~jvY;v-<^-QkdSNMhcV&r?TbN@@SobhJcmaC8Hn=PkN zzerpf&46-HeRZoeb0Q=&%}6(H{ShMi)jeAJ;W`1r96?c`b&i?GQPuT2bk8-7i&xbL zc})(n%PNfw6nfGS&YMc4t4WM1c9{>1{;7m?1pz)45y~$DhnIXbnBNd<_i9^12X8ud zS6^`63@I9FhTqem}l${p+<T znpcwuxCqOwtUi>ATq$DB$4p9Q8&iKO-@|Eg#F1KVFdG7t;4y-aQ+H zI$Rg}OIEQfKCv4f3OXWfhy$F@K5M2!9OWIJ7-t)mQp0RxWAF!CVbi)LEQ!UNKSD1< zx(bXNje3ECbh|viOa1E(SJns_nsoi%f(@$L^PWLW<;VzAhLh>4BHb661WKGTu@PBC z58Z%D+D2}$e;*MBHq6a^+hP}5@4O9yRxI~9b<4q?P1r*SVfI|KW3Gh`Bf#{#cFrvx zz4}4+@7l$AbdO~CTgt4Z@(a1k1I^+B?e$UbzHg44f>Q@jURX_tZIJCCR96r{yH(we?{wxN&{QF6E{s|{Y$s)+q+%7VHj%gK|aq?UPp_x~WsLT&lk%aE6{kzXUYDZ(SkTJG1H zm@fW8XV#|U-jshaiQ67&sXc~G@n#oJOCn?j^ms0mlYQ?M&$(G3U>gP8G= zy;$!r None: ) +@skip_unless_feature("freetype2") +def test_stroke_float() -> None: + # Arrange + im = Image.new("RGB", (120, 130)) + draw = ImageDraw.Draw(im) + font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) + + # Act + draw.text((12, 12), "A", "#f00", font, stroke_width=0.5) + + # Assert + assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_float.png", 3.1) + + @skip_unless_feature("freetype2") def test_stroke_descender() -> None: # Arrange diff --git a/src/_imagingft.c b/src/_imagingft.c index 5c446e641..8bef45baf 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -833,7 +833,7 @@ font_render(FontObject *self, PyObject *args) { Py_ssize_t id; int mask = 0; /* is FT_LOAD_TARGET_MONO enabled? */ int color = 0; /* is FT_LOAD_COLOR enabled? */ - int stroke_width = 0; + float stroke_width = 0; PY_LONG_LONG foreground_ink_long = 0; unsigned int foreground_ink; const char *mode = NULL; @@ -853,7 +853,7 @@ font_render(FontObject *self, PyObject *args) { if (!PyArg_ParseTuple( args, - "OO|zzOzizLffO:render", + "OO|zzOzfzLffO:render", &string, &fill, &mode, @@ -919,8 +919,8 @@ font_render(FontObject *self, PyObject *args) { return NULL; } - width += stroke_width * 2 + ceil(x_start); - height += stroke_width * 2 + ceil(y_start); + width += ceil(stroke_width * 2 + x_start); + height += ceil(stroke_width * 2 + y_start); image = PyObject_CallFunction(fill, "ii", width, height); if (image == Py_None) { PyMem_Del(glyph_info); @@ -934,8 +934,8 @@ font_render(FontObject *self, PyObject *args) { Py_XDECREF(imageId); im = (Imaging)id; - x_offset -= stroke_width; - y_offset -= stroke_width; + x_offset = round(x_offset - stroke_width); + y_offset = round(y_offset - stroke_width); if (count == 0 || width == 0 || height == 0) { PyMem_Del(glyph_info); return Py_BuildValue("N(ii)", image, x_offset, y_offset); @@ -950,7 +950,7 @@ font_render(FontObject *self, PyObject *args) { FT_Stroker_Set( stroker, - (FT_Fixed)stroke_width * 64, + (FT_Fixed)round(stroke_width * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 @@ -988,8 +988,8 @@ font_render(FontObject *self, PyObject *args) { } /* set pen position to text origin */ - x = (-x_min + stroke_width + x_start) * 64; - y = (-y_max + (-stroke_width) - y_start) * 64; + x = round((-x_min + stroke_width + x_start) * 64); + y = round((-y_max + (-stroke_width) - y_start) * 64); if (stroker == NULL) { load_flags |= FT_LOAD_RENDER;