Skip to content

Commit

Permalink
Deprecate passing floats to FT2Image
Browse files Browse the repository at this point in the history
These were silently truncated to int anyway, so we should make the types
explicit.
  • Loading branch information
QuLogic committed Oct 24, 2024
1 parent 422b6cf commit 4d40c1b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 10 deletions.
7 changes: 7 additions & 0 deletions doc/api/next_api_changes/deprecations/28967-ES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ Passing floating-point values to ``RendererAgg.draw_text_image``

Any floating-point values passed to the *x* and *y* parameters were truncated to integers
silently. This behaviour is now deprecated, and only `int` values should be used.

Passing floating-point values to ``FT2Image``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Any floating-point values passed to the `.FT2Image` constructor, or the *x0*, *y0*, *x1*,
and *y1* parameters of `.FT2Image.draw_rect_filled` were truncated to integers silently.
This behaviour is now deprecated, and only `int` values should be used.
6 changes: 3 additions & 3 deletions lib/matplotlib/_mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def to_raster(self, *, antialiased: bool) -> RasterParse:
w = xmax - xmin
h = ymax - ymin - self.box.depth
d = ymax - ymin - self.box.height
image = FT2Image(np.ceil(w), np.ceil(h + max(d, 0)))
image = FT2Image(int(np.ceil(w)), int(np.ceil(h + max(d, 0))))

# Ideally, we could just use self.glyphs and self.rects here, shifting
# their coordinates by (-xmin, -ymin), but this yields slightly
Expand All @@ -163,7 +163,7 @@ def to_raster(self, *, antialiased: bool) -> RasterParse:

for ox, oy, info in shifted.glyphs:
info.font.draw_glyph_to_bitmap(
image, ox, oy - info.metrics.iceberg, info.glyph,
image, int(ox), int(oy - info.metrics.iceberg), info.glyph,
antialiased=antialiased)
for x1, y1, x2, y2 in shifted.rects:
height = max(int(y2 - y1) - 1, 0)
Expand All @@ -172,7 +172,7 @@ def to_raster(self, *, antialiased: bool) -> RasterParse:
y = int(center - (height + 1) / 2)
else:
y = int(y1)
image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height)
image.draw_rect_filled(int(x1), y, int(np.ceil(x2)), y + height)
return RasterParse(0, 0, w, h + d, d, image)


Expand Down
6 changes: 3 additions & 3 deletions lib/matplotlib/ft2font.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class FT2Font(Buffer):
def _get_fontmap(self, string: str) -> dict[str, FT2Font]: ...
def clear(self) -> None: ...
def draw_glyph_to_bitmap(
self, image: FT2Image, x: float, y: float, glyph: Glyph, antialiased: bool = ...
self, image: FT2Image, x: int, y: int, glyph: Glyph, antialiased: bool = ...
) -> None: ...
def draw_glyphs_to_bitmap(self, antialiased: bool = ...) -> None: ...
def get_bitmap_offset(self) -> tuple[int, int]: ...
Expand Down Expand Up @@ -281,8 +281,8 @@ class FT2Font(Buffer):

@final
class FT2Image(Buffer):
def __init__(self, width: float, height: float) -> None: ...
def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
def __init__(self, width: int, height: int) -> None: ...
def draw_rect_filled(self, x0: int, y0: int, x1: int, y1: int) -> None: ...
if sys.version_info[:2] >= (3, 12):
def __buffer__(self, flags: int) -> memoryview: ...

Expand Down
50 changes: 46 additions & 4 deletions src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@
namespace py = pybind11;
using namespace pybind11::literals;

template <typename T>
using double_or_ = std::variant<double, T>;

template <typename T>
static T
_double_to_(const char *name, double_or_<T> &var)
{
if (auto value = std::get_if<double>(&var)) {
auto api = py::module_::import("matplotlib._api");

Check warning on line 24 in src/ft2font_wrapper.cpp

View check run for this annotation

Codecov / codecov/patch

src/ft2font_wrapper.cpp#L24

Added line #L24 was not covered by tests
auto warn = api.attr("warn_deprecated");
warn("since"_a="3.10", "name"_a=name, "obj_type"_a="parameter as float",
"alternative"_a="int({})"_s.format(name));
return static_cast<T>(*value);
} else if (auto value = std::get_if<T>(&var)) {
return *value;
} else {
// pybind11 will have only allowed types that match the variant, so this `else`
// can't happen. We only have this case because older macOS doesn't support
// `std::get` and using the conditional `std::get_if` means an `else` to silence
// compiler warnings about "unhandled" cases.
throw std::runtime_error("Should not happen");
}
}

/**********************************************************************
* Enumerations
* */
Expand Down Expand Up @@ -227,8 +251,15 @@ const char *PyFT2Image_draw_rect_filled__doc__ = R"""(
)""";

static void
PyFT2Image_draw_rect_filled(FT2Image *self, double x0, double y0, double x1, double y1)
PyFT2Image_draw_rect_filled(FT2Image *self,
double_or_<long> vx0, double_or_<long> vy0,
double_or_<long> vx1, double_or_<long> vy1)
{
auto x0 = _double_to_<long>("x0", vx0);
auto y0 = _double_to_<long>("y0", vy0);
auto x1 = _double_to_<long>("x1", vx1);
auto y1 = _double_to_<long>("y1", vy1);

self->draw_rect_filled(x0, y0, x1, y1);
}

Expand Down Expand Up @@ -920,7 +951,7 @@ const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""(
----------
image : FT2Image
The image buffer on which to draw the glyph.
x, y : float
x, y : int
The pixel location at which to draw the glyph.
glyph : Glyph
The glyph to draw.
Expand All @@ -933,9 +964,13 @@ const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""(
)""";

static void
PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, FT2Image &image, double xd, double yd,
PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, FT2Image &image,
double_or_<int> vxd, double_or_<int> vyd,
PyGlyph *glyph, bool antialiased = true)
{
auto xd = _double_to_<int>("x", vxd);
auto yd = _double_to_<int>("y", vyd);

self->x->draw_glyph_to_bitmap(image, xd, yd, glyph->glyphInd, antialiased);
}

Expand Down Expand Up @@ -1625,7 +1660,14 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())

py::class_<FT2Image>(m, "FT2Image", py::is_final(), py::buffer_protocol(),
PyFT2Image__doc__)
.def(py::init<double, double>(), "width"_a, "height"_a, PyFT2Image_init__doc__)
.def(py::init(
[](double_or_<long> width, double_or_<long> height) {
return new FT2Image(
_double_to_<long>("width", width),
_double_to_<long>("height", height)
);
}),
"width"_a, "height"_a, PyFT2Image_init__doc__)
.def("draw_rect_filled", &PyFT2Image_draw_rect_filled,
"x0"_a, "y0"_a, "x1"_a, "y1"_a,
PyFT2Image_draw_rect_filled__doc__)
Expand Down

0 comments on commit 4d40c1b

Please sign in to comment.