Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/api/next_api_changes/deprecations/28967-ES.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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
25 changes: 16 additions & 9 deletions src/_backend_agg.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face,
agg::trans_affine hatch_trans;
hatch_trans *= agg::trans_affine_scaling(1.0, -1.0);
hatch_trans *= agg::trans_affine_translation(0.0, 1.0);
hatch_trans *= agg::trans_affine_scaling(hatch_size, hatch_size);
hatch_trans *= agg::trans_affine_scaling(static_cast<double>(hatch_size),
static_cast<double>(hatch_size));
hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans);
hatch_path_curve_t hatch_path_curve(hatch_path_trans);
hatch_path_stroke_t hatch_path_stroke(hatch_path_curve);
Expand Down Expand Up @@ -739,16 +740,19 @@ inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, in

set_clipbox(gc.cliprect, theRasterizer);

auto image_height = static_cast<double>(image.shape(0)),
image_width = static_cast<double>(image.shape(1));

agg::trans_affine mtx;
mtx *= agg::trans_affine_translation(0, -image.shape(0));
mtx *= agg::trans_affine_translation(0, -image_height);
mtx *= agg::trans_affine_rotation(-angle * (agg::pi / 180.0));
mtx *= agg::trans_affine_translation(x, y);

agg::path_storage rect;
rect.move_to(0, 0);
rect.line_to(image.shape(1), 0);
rect.line_to(image.shape(1), image.shape(0));
rect.line_to(0, image.shape(0));
rect.line_to(image_width, 0);
rect.line_to(image_width, image_height);
rect.line_to(0, image_height);
rect.line_to(0, 0);
agg::conv_transform<agg::path_storage> rect2(rect, mtx);

Expand Down Expand Up @@ -842,12 +846,15 @@ inline void RendererAgg::draw_image(GCAgg &gc,
agg::trans_affine mtx;
agg::path_storage rect;

mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.shape(0))));
auto image_height = static_cast<double>(image.shape(0)),
image_width = static_cast<double>(image.shape(1));

mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image_height)));

rect.move_to(0, 0);
rect.line_to(image.shape(1), 0);
rect.line_to(image.shape(1), image.shape(0));
rect.line_to(0, image.shape(0));
rect.line_to(image_width, 0);
rect.line_to(image_width, image_height);
rect.line_to(0, image_height);
rect.line_to(0, 0);

agg::conv_transform<agg::path_storage> rect2(rect, mtx);
Expand Down
30 changes: 28 additions & 2 deletions src/_backend_agg_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,37 @@ PyRendererAgg_draw_path(RendererAgg *self,
static void
PyRendererAgg_draw_text_image(RendererAgg *self,
py::array_t<agg::int8u, py::array::c_style | py::array::forcecast> image_obj,
double x,
double y,
std::variant<double, int> vx,
std::variant<double, int> vy,
double angle,
GCAgg &gc)
{
int x, y;

if (auto value = std::get_if<double>(&vx)) {
auto api = py::module_::import("matplotlib._api");
auto warn = api.attr("warn_deprecated");
warn("since"_a="3.10", "name"_a="x", "obj_type"_a="parameter as float",
"alternative"_a="int(x)");
x = static_cast<int>(*value);
} else if (auto value = std::get_if<int>(&vx)) {
x = *value;
} else {
throw std::runtime_error("Should not happen");
}

if (auto value = std::get_if<double>(&vy)) {
auto api = py::module_::import("matplotlib._api");
auto warn = api.attr("warn_deprecated");
warn("since"_a="3.10", "name"_a="y", "obj_type"_a="parameter as float",
"alternative"_a="int(y)");
y = static_cast<int>(*value);
} else if (auto value = std::get_if<int>(&vy)) {
y = *value;
} else {
throw std::runtime_error("Should not happen");
}

// TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const.
auto image = image_obj.mutable_unchecked<2>();

Expand Down
3 changes: 2 additions & 1 deletion src/_image_resample.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,8 @@ class span_conv_alpha
{
if (m_alpha != 1.0) {
do {
span->a *= m_alpha;
span->a = static_cast<typename color_type::value_type>(
static_cast<typename color_type::calc_type>(span->a) * m_alpha);
++span;
} while (--len);
}
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");
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
9 changes: 4 additions & 5 deletions src/tri/_tri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ py::tuple TriContourGenerator::create_contour(const double& level)
Contour contour;

find_boundary_lines(contour, level);
find_interior_lines(contour, level, false, false);
find_interior_lines(contour, level, false);

return contour_line_to_segs_and_kinds(contour);
}
Expand All @@ -766,8 +766,8 @@ py::tuple TriContourGenerator::create_filled_contour(const double& lower_level,
Contour contour;

find_boundary_lines_filled(contour, lower_level, upper_level);
find_interior_lines(contour, lower_level, false, true);
find_interior_lines(contour, upper_level, true, true);
find_interior_lines(contour, lower_level, false);
find_interior_lines(contour, upper_level, true);

return contour_to_segs_and_kinds(contour);
}
Expand Down Expand Up @@ -880,8 +880,7 @@ void TriContourGenerator::find_boundary_lines_filled(Contour& contour,

void TriContourGenerator::find_interior_lines(Contour& contour,
const double& level,
bool on_upper,
bool filled)
bool on_upper)
{
const Triangulation& triang = _triangulation;
int ntri = triang.get_ntri();
Expand Down
6 changes: 2 additions & 4 deletions src/tri/_tri.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,10 @@ class TriContourGenerator final
* intersect any boundary.
* contour: Contour to add new lines to.
* level: Contour level.
* on_upper: Whether on upper or lower contour level.
* filled: Whether contours are filled or not. */
* on_upper: Whether on upper or lower contour level. */
void find_interior_lines(Contour& contour,
const double& level,
bool on_upper,
bool filled);
bool on_upper);

/* Follow contour line around boundary of the Triangulation from the
* specified TriEdge to its end which can be on either the lower or upper
Expand Down