From 25be2f1a1330eaebe727d1d3c93b11f71d569f73 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 22 Feb 2024 22:03:42 -0500 Subject: [PATCH 1/5] BLD: Make ft2font classes final There appears to be no reason for them to be subtyped, as they are semi-private, and we don't do that. --- doc/api/next_api_changes/behavior/27891-ES.rst | 5 +++++ lib/matplotlib/ft2font.pyi | 5 ++++- src/ft2font_wrapper.cpp | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/27891-ES.rst diff --git a/doc/api/next_api_changes/behavior/27891-ES.rst b/doc/api/next_api_changes/behavior/27891-ES.rst new file mode 100644 index 000000000000..f60b4b320a44 --- /dev/null +++ b/doc/api/next_api_changes/behavior/27891-ES.rst @@ -0,0 +1,5 @@ +ft2font classes are now final +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ft2font classes `.ft2font.FT2Font`, and `.ft2font.FT2Image` are now final +and can no longer be subclassed. diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index 6a0716e993a5..d47614cc6f48 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -1,4 +1,4 @@ -from typing import BinaryIO, Literal, TypedDict, overload +from typing import BinaryIO, Literal, TypedDict, final, overload import numpy as np from numpy.typing import NDArray @@ -158,6 +158,7 @@ class _SfntPcltDict(TypedDict): widthType: int serifStyle: int +@final class FT2Font: ascender: int bbox: tuple[int, int, int, int] @@ -233,11 +234,13 @@ class FT2Font: self, string: str, angle: float = ..., flags: int = ... ) -> NDArray[np.float64]: ... +@final class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer. def __init__(self, width: float, height: float) -> None: ... def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ... def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ... +@final class Glyph: width: int height: int diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 0fdb0165b462..9e0226455972 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -146,7 +146,7 @@ static PyTypeObject* PyFT2Image_init_type() PyFT2ImageType.tp_name = "matplotlib.ft2font.FT2Image"; PyFT2ImageType.tp_basicsize = sizeof(PyFT2Image); PyFT2ImageType.tp_dealloc = (destructor)PyFT2Image_dealloc; - PyFT2ImageType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; + PyFT2ImageType.tp_flags = Py_TPFLAGS_DEFAULT; PyFT2ImageType.tp_methods = methods; PyFT2ImageType.tp_new = PyFT2Image_new; PyFT2ImageType.tp_init = (initproc)PyFT2Image_init; @@ -236,7 +236,7 @@ static PyTypeObject *PyGlyph_init_type() PyGlyphType.tp_name = "matplotlib.ft2font.Glyph"; PyGlyphType.tp_basicsize = sizeof(PyGlyph); PyGlyphType.tp_dealloc = (destructor)PyGlyph_dealloc; - PyGlyphType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; + PyGlyphType.tp_flags = Py_TPFLAGS_DEFAULT; PyGlyphType.tp_members = members; PyGlyphType.tp_getset = getset; @@ -1495,7 +1495,7 @@ static PyTypeObject *PyFT2Font_init_type() PyFT2FontType.tp_doc = PyFT2Font_init__doc__; PyFT2FontType.tp_basicsize = sizeof(PyFT2Font); PyFT2FontType.tp_dealloc = (destructor)PyFT2Font_dealloc; - PyFT2FontType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; + PyFT2FontType.tp_flags = Py_TPFLAGS_DEFAULT; PyFT2FontType.tp_methods = methods; PyFT2FontType.tp_getset = getset; PyFT2FontType.tp_new = PyFT2Font_new; From 5f2a89ab3a85e90eab41492acd94a78432ad2bf5 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 5 Mar 2024 23:51:22 -0500 Subject: [PATCH 2/5] Move Python code from ft2font to its wrapper This improves the encapsulation and separation of concerns between the files. --- src/ft2font.cpp | 86 +++++++++++++---------------------------- src/ft2font.h | 10 +++-- src/ft2font_wrapper.cpp | 71 +++++++++++++++++++++++++++------- 3 files changed, 90 insertions(+), 77 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index b20f224715bf..41203340dd47 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -1,18 +1,16 @@ /* -*- mode: c++; c-basic-offset: 4 -*- */ -#define NO_IMPORT_ARRAY - #include +#include #include #include #include #include #include +#include #include "ft2font.h" #include "mplutils.h" -#include "numpy_cpp.h" -#include "py_exceptions.h" #ifndef M_PI #define M_PI 3.14159265358979323846264338328 @@ -185,30 +183,6 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, m_dirty = true; } -static void ft_glyph_warn(FT_ULong charcode, std::set family_names) -{ - PyObject *text_helpers = NULL, *tmp = NULL; - std::set::iterator it = family_names.begin(); - std::stringstream ss; - ss<<*it; - while(++it != family_names.end()){ - ss<<", "<<*it; - } - - if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) || - !(tmp = PyObject_CallMethod(text_helpers, - "warn_on_missing_glyph", "(k, s)", - charcode, ss.str().c_str()))) { - goto exit; - } -exit: - Py_XDECREF(text_helpers); - Py_XDECREF(tmp); - if (PyErr_Occurred()) { - throw mpl::exception(); - } -} - // ft_outline_decomposer should be passed to FT_Outline_Decompose. On the // first pass, vertices and codes are set to NULL, and index is simply // incremented for each vertex that should be inserted, so that it is set, at @@ -296,52 +270,41 @@ static FT_Outline_Funcs ft_outline_funcs = { ft_outline_conic_to, ft_outline_cubic_to}; -PyObject* -FT2Font::get_path() +void +FT2Font::get_path(std::vector &vertices, std::vector &codes) { if (!face->glyph) { - PyErr_SetString(PyExc_RuntimeError, "No glyph loaded"); - return NULL; + throw std::runtime_error("No glyph loaded"); } ft_outline_decomposer decomposer = {}; - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; + if (FT_Error error = FT_Outline_Decompose( + &face->glyph->outline, &ft_outline_funcs, &decomposer)) { + throw std::runtime_error("FT_Outline_Decompose failed with error " + + std::to_string(error)); } if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs. - npy_intp vertices_dims[2] = { 0, 2 }; - numpy::array_view vertices(vertices_dims); - npy_intp codes_dims[1] = { 0 }; - numpy::array_view codes(codes_dims); - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); - } - npy_intp vertices_dims[2] = { decomposer.index + 1, 2 }; - numpy::array_view vertices(vertices_dims); - npy_intp codes_dims[1] = { decomposer.index + 1 }; - numpy::array_view codes(codes_dims); + return; + } + vertices.resize((decomposer.index + 1) * 2); + codes.resize(decomposer.index + 1); decomposer.index = 0; decomposer.vertices = vertices.data(); decomposer.codes = codes.data(); - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; + if (FT_Error error = FT_Outline_Decompose( + &face->glyph->outline, &ft_outline_funcs, &decomposer)) { + throw std::runtime_error("FT_Outline_Decompose failed with error " + + std::to_string(error)); } *(decomposer.vertices++) = 0; *(decomposer.vertices++) = 0; *(decomposer.codes++) = CLOSEPOLY; - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); } FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_, - std::vector &fallback_list) - : image(), face(NULL) + std::vector &fallback_list, + FT2Font::WarnFunc warn) + : ft_glyph_warn(warn), image(), face(NULL) { clear(); @@ -819,7 +782,8 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y); } -void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false) +void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer, + bool fallback = false) { if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) { // cache is only for parent FT2Font @@ -830,9 +794,11 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallb if (!FT_HAS_GLYPH_NAMES(face)) { /* Note that this generated name must match the name that is generated by ttconv in ttfont_CharStrings_getname. */ - PyOS_snprintf(buffer, 128, "uni%08x", glyph_number); + buffer.replace(0, 3, "uni"); + std::to_chars(buffer.data() + 3, buffer.data() + buffer.size(), + glyph_number, 16); } else { - if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) { + if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer.data(), buffer.size())) { throw_ft_error("Could not get glyph names", error); } } diff --git a/src/ft2font.h b/src/ft2font.h index 66b218316e90..0b2db6fe1510 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -71,9 +72,11 @@ extern FT_Library _ft2Library; class FT2Font { + typedef void (*WarnFunc)(FT_ULong charcode, std::set family_names); public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list); + FT2Font(FT_Open_Args &open_args, long hinting_factor, + std::vector &fallback_list, WarnFunc warn); virtual ~FT2Font(); void clear(); void set_size(double ptsize, double dpi); @@ -106,10 +109,10 @@ class FT2Font void get_xys(bool antialiased, std::vector &xys); void draw_glyphs_to_bitmap(bool antialiased); void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback); + void get_glyph_name(unsigned int glyph_number, std::string &buffer, bool fallback); long get_name_index(char *name); FT_UInt get_char_index(FT_ULong charcode, bool fallback); - PyObject* get_path(); + void get_path(std::vector &vertices, std::vector &codes); bool get_char_fallback_index(FT_ULong charcode, int& index) const; FT_Face const &get_face() const @@ -143,6 +146,7 @@ class FT2Font } private: + WarnFunc ft_glyph_warn; FT2Image image; FT_Face face; FT_Vector pen; /* untransformed origin */ diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 9e0226455972..3551d82f48e9 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1,11 +1,13 @@ #include "mplutils.h" #include "ft2font.h" +#include "numpy_cpp.h" #include "py_converters.h" #include "py_exceptions.h" // From Python #include +#include #include static PyObject *convert_xys_to_array(std::vector &xys) @@ -308,6 +310,31 @@ static void close_file_callback(FT_Stream stream) PyErr_Restore(type, value, traceback); } +static void +ft_glyph_warn(FT_ULong charcode, std::set family_names) +{ + PyObject *text_helpers = NULL, *tmp = NULL; + std::set::iterator it = family_names.begin(); + std::stringstream ss; + ss<<*it; + while(++it != family_names.end()){ + ss<<", "<<*it; + } + + if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) || + !(tmp = PyObject_CallMethod(text_helpers, + "warn_on_missing_glyph", "(k, s)", + charcode, ss.str().c_str()))) { + goto exit; + } +exit: + Py_XDECREF(text_helpers); + Py_XDECREF(tmp); + if (PyErr_Occurred()) { + throw mpl::exception(); + } +} + static PyObject *PyFT2Font_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyFT2Font *self; @@ -455,7 +482,8 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) Py_CLEAR(data); CALL_CPP_FULL( - "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, fallback_fonts)), + "FT2Font", + (self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn)), Py_CLEAR(self->py_file), -1); CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor))); @@ -555,13 +583,13 @@ static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args) { FT_UInt left, right, mode; int result; - int fallback = 1; + bool fallback = true; if (!PyArg_ParseTuple(args, "III:get_kerning", &left, &right, &mode)) { return NULL; } - CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, (bool)fallback))); + CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, fallback))); return PyLong_FromLong(result); } @@ -704,7 +732,7 @@ const char *PyFT2Font_load_char__doc__ = static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *kwds) { long charcode; - int fallback = 1; + bool fallback = true; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; const char *names[] = { "charcode", "flags", NULL }; @@ -717,7 +745,7 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * } FT2Font *ft_object = NULL; - CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); + CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, fallback))); return PyGlyph_from_FT2Font(ft_object); } @@ -743,7 +771,7 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject { FT_UInt glyph_index; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - int fallback = 1; + bool fallback = true; const char *names[] = { "glyph_index", "flags", NULL }; /* This makes a technically incorrect assumption that FT_Int32 is @@ -755,7 +783,7 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject } FT2Font *ft_object = NULL; - CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); + CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, fallback))); return PyGlyph_from_FT2Font(ft_object); } @@ -912,14 +940,16 @@ const char *PyFT2Font_get_glyph_name__doc__ = static PyObject *PyFT2Font_get_glyph_name(PyFT2Font *self, PyObject *args) { unsigned int glyph_number; - char buffer[128]; - int fallback = 1; + std::string buffer; + bool fallback = true; if (!PyArg_ParseTuple(args, "I:get_glyph_name", &glyph_number)) { return NULL; } - CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer, (bool)fallback))); - return PyUnicode_FromString(buffer); + buffer.resize(128); + CALL_CPP("get_glyph_name", + (self->x->get_glyph_name(glyph_number, buffer, fallback))); + return PyUnicode_FromString(buffer.c_str()); } const char *PyFT2Font_get_charmap__doc__ = @@ -962,13 +992,13 @@ static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args) { FT_UInt index; FT_ULong ccode; - int fallback = 1; + bool fallback = true; if (!PyArg_ParseTuple(args, "k:get_char_index", &ccode)) { return NULL; } - CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, (bool)fallback)); + CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, fallback)); return PyLong_FromLong(index); } @@ -1270,7 +1300,20 @@ const char *PyFT2Font_get_path__doc__ = static PyObject *PyFT2Font_get_path(PyFT2Font *self, PyObject *args) { - CALL_CPP("get_path", return self->x->get_path()); + std::vector vertices; + std::vector codes; + + CALL_CPP("get_path", self->x->get_path(vertices, codes)); + + npy_intp length = codes.size(); + npy_intp vertices_dims[2] = { length, 2 }; + numpy::array_view vertices_arr(vertices_dims); + memcpy(vertices_arr.data(), vertices.data(), sizeof(double) * vertices.size()); + npy_intp codes_dims[1] = { length }; + numpy::array_view codes_arr(codes_dims); + memcpy(codes_arr.data(), codes.data(), codes.size()); + + return Py_BuildValue("NN", vertices_arr.pyobj(), codes_arr.pyobj()); } const char *PyFT2Font_get_image__doc__ = From 9765ea168c8efec9175a1cd9fa0226c5f990389b Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 6 Mar 2024 04:12:59 -0500 Subject: [PATCH 3/5] Use std::vector directly with FT_Outline_Decompose This means we only need to do one pass through. --- src/ft2font.cpp | 108 ++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 63 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 41203340dd47..f2dc2adf91f5 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -183,35 +183,26 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, m_dirty = true; } -// ft_outline_decomposer should be passed to FT_Outline_Decompose. On the -// first pass, vertices and codes are set to NULL, and index is simply -// incremented for each vertex that should be inserted, so that it is set, at -// the end, to the total number of vertices. On a second pass, vertices and -// codes should point to correctly sized arrays, and index set again to zero, -// to get fill vertices and codes with the outline decomposition. +// ft_outline_decomposer should be passed to FT_Outline_Decompose. struct ft_outline_decomposer { - int index; - double* vertices; - unsigned char* codes; + std::vector &vertices; + std::vector &codes; }; static int ft_outline_move_to(FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - if (d->index) { - // Appending CLOSEPOLY is important to make patheffects work. - *(d->vertices++) = 0; - *(d->vertices++) = 0; - *(d->codes++) = CLOSEPOLY; - } - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = MOVETO; - } - d->index += d->index ? 2 : 1; + if (!d->vertices.empty()) { + // Appending CLOSEPOLY is important to make patheffects work. + d->vertices.push_back(0); + d->vertices.push_back(0); + d->codes.push_back(CLOSEPOLY); + } + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(MOVETO); return 0; } @@ -219,12 +210,9 @@ static int ft_outline_line_to(FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = LINETO; - } - d->index++; + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(LINETO); return 0; } @@ -232,15 +220,12 @@ static int ft_outline_conic_to(FT_Vector const* control, FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = control->x * (1. / 64.); - *(d->vertices++) = control->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE3; - *(d->codes++) = CURVE3; - } - d->index += 2; + d->vertices.push_back(control->x * (1. / 64.)); + d->vertices.push_back(control->y * (1. / 64.)); + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(CURVE3); + d->codes.push_back(CURVE3); return 0; } @@ -249,18 +234,15 @@ ft_outline_cubic_to( FT_Vector const* c1, FT_Vector const* c2, FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = c1->x * (1. / 64.); - *(d->vertices++) = c1->y * (1. / 64.); - *(d->vertices++) = c2->x * (1. / 64.); - *(d->vertices++) = c2->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - } - d->index += 3; + d->vertices.push_back(c1->x * (1. / 64.)); + d->vertices.push_back(c1->y * (1. / 64.)); + d->vertices.push_back(c2->x * (1. / 64.)); + d->vertices.push_back(c2->y * (1. / 64.)); + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(CURVE4); + d->codes.push_back(CURVE4); + d->codes.push_back(CURVE4); return 0; } @@ -276,28 +258,28 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod if (!face->glyph) { throw std::runtime_error("No glyph loaded"); } - ft_outline_decomposer decomposer = {}; + ft_outline_decomposer decomposer = { + vertices, + codes, + }; + // We can make a close-enough estimate based on number of points and number of + // contours (which produce a MOVETO each), though it's slightly underestimating due + // to higher-order curves. + size_t estimated_points = static_cast(face->glyph->outline.n_contours) + + static_cast(face->glyph->outline.n_points); + vertices.reserve(2 * estimated_points); + codes.reserve(estimated_points); if (FT_Error error = FT_Outline_Decompose( &face->glyph->outline, &ft_outline_funcs, &decomposer)) { throw std::runtime_error("FT_Outline_Decompose failed with error " + std::to_string(error)); } - if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs. + if (vertices.empty()) { // Don't append CLOSEPOLY to null glyphs. return; } - vertices.resize((decomposer.index + 1) * 2); - codes.resize(decomposer.index + 1); - decomposer.index = 0; - decomposer.vertices = vertices.data(); - decomposer.codes = codes.data(); - if (FT_Error error = FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - throw std::runtime_error("FT_Outline_Decompose failed with error " + - std::to_string(error)); - } - *(decomposer.vertices++) = 0; - *(decomposer.vertices++) = 0; - *(decomposer.codes++) = CLOSEPOLY; + vertices.push_back(0); + vertices.push_back(0); + codes.push_back(CLOSEPOLY); } FT2Font::FT2Font(FT_Open_Args &open_args, From d3da65f5d10e05c8cfd7a3cccfdfc8df115fc7b2 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 14 Aug 2024 19:35:23 -0400 Subject: [PATCH 4/5] Remove deprecated ft2font API --- .../next_api_changes/removals/27891-ES.rst | 4 ++ lib/matplotlib/ft2font.pyi | 2 - src/ft2font.cpp | 44 ------------- src/ft2font.h | 4 -- src/ft2font_wrapper.cpp | 62 ------------------- 5 files changed, 4 insertions(+), 112 deletions(-) create mode 100644 doc/api/next_api_changes/removals/27891-ES.rst diff --git a/doc/api/next_api_changes/removals/27891-ES.rst b/doc/api/next_api_changes/removals/27891-ES.rst new file mode 100644 index 000000000000..cb658e9bc671 --- /dev/null +++ b/doc/api/next_api_changes/removals/27891-ES.rst @@ -0,0 +1,4 @@ +``ft2font.FT2Image.draw_rect`` and ``ft2font.FT2Font.get_xys`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... have been removed as they are unused. diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index d47614cc6f48..0a27411ff39c 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -224,7 +224,6 @@ class FT2Font: @overload def get_sfnt_table(self, name: Literal["pclt"]) -> _SfntPcltDict | None: ... def get_width_height(self) -> tuple[int, int]: ... - def get_xys(self, antialiased: bool = ...) -> NDArray[np.float64]: ... def load_char(self, charcode: int, flags: int = ...) -> Glyph: ... def load_glyph(self, glyphindex: int, flags: int = ...) -> Glyph: ... def select_charmap(self, i: int) -> None: ... @@ -237,7 +236,6 @@ class FT2Font: @final class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer. def __init__(self, width: float, height: float) -> None: ... - def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ... def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ... @final diff --git a/src/ft2font.cpp b/src/ft2font.cpp index f2dc2adf91f5..d78d696e118f 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -145,27 +145,6 @@ void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) m_dirty = true; } -void FT2Image::draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1) -{ - if (x0 > m_width || x1 > m_width || y0 > m_height || y1 > m_height) { - throw std::runtime_error("Rect coords outside image bounds"); - } - - size_t top = y0 * m_width; - size_t bottom = y1 * m_width; - for (size_t i = x0; i < x1 + 1; ++i) { - m_buffer[i + top] = 255; - m_buffer[i + bottom] = 255; - } - - for (size_t j = y0 + 1; j < y1; ++j) { - m_buffer[x0 + j * m_width] = 255; - m_buffer[x1 + j * m_width] = 255; - } - - m_dirty = true; -} - void FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1) { @@ -716,29 +695,6 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased) } } -void FT2Font::get_xys(bool antialiased, std::vector &xys) -{ - for (size_t n = 0; n < glyphs.size(); n++) { - - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n]; - - // bitmap left and top in pixel, string bbox in subpixel - FT_Int x = (FT_Int)(bitmap->left - bbox.xMin * (1. / 64.)); - FT_Int y = (FT_Int)(bbox.yMax * (1. / 64.) - bitmap->top + 1); - // make sure the index is non-neg - x = x < 0 ? 0 : x; - y = y < 0 ? 0 : y; - xys.push_back(x); - xys.push_back(y); - } -} - void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased) { FT_Vector sub_offset; diff --git a/src/ft2font.h b/src/ft2font.h index 0b2db6fe1510..4bd924497978 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -41,7 +41,6 @@ class FT2Image void resize(long width, long height); void draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y); - void draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); void draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); unsigned char *get_buffer() @@ -104,9 +103,6 @@ class FT2Font void get_width_height(long *width, long *height); void get_bitmap_offset(long *x, long *y); long get_descent(); - // TODO: Since we know the size of the array upfront, we probably don't - // need to dynamically allocate like this - void get_xys(bool antialiased, std::vector &xys); void draw_glyphs_to_bitmap(bool antialiased); void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased); void get_glyph_name(unsigned int glyph_number, std::string &buffer, bool fallback); diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 3551d82f48e9..6d6e8722b63b 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -63,35 +63,6 @@ static void PyFT2Image_dealloc(PyFT2Image *self) Py_TYPE(self)->tp_free((PyObject *)self); } -const char *PyFT2Image_draw_rect__doc__ = - "draw_rect(self, x0, y0, x1, y1)\n" - "--\n\n" - "Draw an empty rectangle to the image.\n" - "\n" - ".. deprecated:: 3.8\n"; -; - -static PyObject *PyFT2Image_draw_rect(PyFT2Image *self, PyObject *args) -{ - char const* msg = - "FT2Image.draw_rect is deprecated since Matplotlib 3.8 and will be removed " - "in Matplotlib 3.10 as it is not used in the library. If you rely on it, " - "please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - - double x0, y0, x1, y1; - - if (!PyArg_ParseTuple(args, "dddd:draw_rect", &x0, &y0, &x1, &y1)) { - return NULL; - } - - CALL_CPP("draw_rect", (self->x->draw_rect(x0, y0, x1, y1))); - - Py_RETURN_NONE; -} - const char *PyFT2Image_draw_rect_filled__doc__ = "draw_rect_filled(self, x0, y0, x1, y1)\n" "--\n\n" @@ -137,7 +108,6 @@ static int PyFT2Image_get_buffer(PyFT2Image *self, Py_buffer *buf, int flags) static PyTypeObject* PyFT2Image_init_type() { static PyMethodDef methods[] = { - {"draw_rect", (PyCFunction)PyFT2Image_draw_rect, METH_VARARGS, PyFT2Image_draw_rect__doc__}, {"draw_rect_filled", (PyCFunction)PyFT2Image_draw_rect_filled, METH_VARARGS, PyFT2Image_draw_rect_filled__doc__}, {NULL} }; @@ -856,37 +826,6 @@ static PyObject *PyFT2Font_draw_glyphs_to_bitmap(PyFT2Font *self, PyObject *args Py_RETURN_NONE; } -const char *PyFT2Font_get_xys__doc__ = - "get_xys(self, antialiased=True)\n" - "--\n\n" - "Get the xy locations of the current glyphs.\n" - "\n" - ".. deprecated:: 3.8\n"; - -static PyObject *PyFT2Font_get_xys(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - char const* msg = - "FT2Font.get_xys is deprecated since Matplotlib 3.8 and will be removed in " - "Matplotlib 3.10 as it is not used in the library. If you rely on it, " - "please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - - bool antialiased = true; - std::vector xys; - const char *names[] = { "antialiased", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:get_xys", - (char **)names, &convert_bool, &antialiased)) { - return NULL; - } - - CALL_CPP("get_xys", (self->x->get_xys(antialiased, xys))); - - return convert_xys_to_array(xys); -} - const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = "draw_glyph_to_bitmap(self, image, x, y, glyph, antialiased=True)\n" "--\n\n" @@ -1517,7 +1456,6 @@ static PyTypeObject *PyFT2Font_init_type() {"get_bitmap_offset", (PyCFunction)PyFT2Font_get_bitmap_offset, METH_NOARGS, PyFT2Font_get_bitmap_offset__doc__}, {"get_descent", (PyCFunction)PyFT2Font_get_descent, METH_NOARGS, PyFT2Font_get_descent__doc__}, {"draw_glyphs_to_bitmap", (PyCFunction)PyFT2Font_draw_glyphs_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyphs_to_bitmap__doc__}, - {"get_xys", (PyCFunction)PyFT2Font_get_xys, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_xys__doc__}, {"draw_glyph_to_bitmap", (PyCFunction)PyFT2Font_draw_glyph_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyph_to_bitmap__doc__}, {"get_glyph_name", (PyCFunction)PyFT2Font_get_glyph_name, METH_VARARGS, PyFT2Font_get_glyph_name__doc__}, {"get_charmap", (PyCFunction)PyFT2Font_get_charmap, METH_NOARGS, PyFT2Font_get_charmap__doc__}, From 276fade9e375c3598a35f373e07f778c95baa066 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 22 Aug 2024 21:29:39 -0400 Subject: [PATCH 5/5] Remove unused FT2Image.m_dirty It is set in a few places, but never read. --- src/ft2font.cpp | 10 ++-------- src/ft2font.h | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index d78d696e118f..cb9952f3b374 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -63,12 +63,12 @@ void throw_ft_error(std::string message, FT_Error error) { throw std::runtime_error(os.str()); } -FT2Image::FT2Image() : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) +FT2Image::FT2Image() : m_buffer(NULL), m_width(0), m_height(0) { } FT2Image::FT2Image(unsigned long width, unsigned long height) - : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) + : m_buffer(NULL), m_width(0), m_height(0) { resize(width, height); } @@ -102,8 +102,6 @@ void FT2Image::resize(long width, long height) if (numBytes && m_buffer) { memset(m_buffer, 0, numBytes); } - - m_dirty = true; } void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) @@ -141,8 +139,6 @@ void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) } else { throw std::runtime_error("Unknown pixel mode"); } - - m_dirty = true; } void @@ -158,8 +154,6 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, m_buffer[i + j * m_width] = 255; } } - - m_dirty = true; } // ft_outline_decomposer should be passed to FT_Outline_Decompose. diff --git a/src/ft2font.h b/src/ft2font.h index 4bd924497978..2f24bfb01f79 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -57,7 +57,6 @@ class FT2Image } private: - bool m_dirty; unsigned char *m_buffer; unsigned long m_width; unsigned long m_height;