Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Drop the FT2Font intermediate buffer. #30059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
77 changes: 49 additions & 28 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
self._filter_renderers = []

self._update_methods()
self.mathtext_parser = MathTextParser('agg')
self.mathtext_parser = MathTextParser('path')

self.bbox = Bbox.from_bounds(0, 0, self.width, self.height)

Expand Down Expand Up @@ -172,36 +172,58 @@

def draw_mathtext(self, gc, x, y, s, prop, angle):
"""Draw mathtext using :mod:`matplotlib.mathtext`."""
ox, oy, width, height, descent, font_image = \
self.mathtext_parser.parse(s, self.dpi, prop,
antialiased=gc.get_antialiased())

xd = descent * sin(radians(angle))
yd = descent * cos(radians(angle))
x = round(x + ox + xd)
y = round(y - oy + yd)
self._renderer.draw_text_image(font_image, x, y + 1, angle, gc)
# y is downwards.
parse = self.mathtext_parser.parse(
s, self.dpi, prop, antialiased=gc.get_antialiased())
c = cos(radians(angle))
s = sin(radians(angle))
for font, size, char, dx, dy in parse.glyphs: # dy is upwards.
font.set_size(size, self.dpi)
bitmap = font._render_glyph(
font.get_char_index(char),
# The "y" parameter is upwards (per FreeType).
x + dx * c - dy * s, self.height - y + dx * s + dy * c, angle,
get_hinting_flag())
# draw_text_image's y is downwards & the bitmap bottom side.
self._renderer.draw_text_image(
bitmap["buffer"],
bitmap["left"],
int(self.height) - bitmap["top"] + bitmap["buffer"].shape[0],
0, gc)
if not angle:
for dx, dy, w, h in parse.rects: # dy is upwards & the rect top side.
self._renderer.draw_text_image(
np.full((round(h), round(w)), np.uint8(0xff)),
round(x + dx), round(y - dy - h),
0, gc)
else:
rgba = gc.get_rgb()

Check warning on line 200 in lib/matplotlib/backends/backend_agg.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_agg.py#L200

Added line #L200 was not covered by tests
if len(rgba) == 3 or gc.get_forced_alpha():
rgba = rgba[:3] + (gc.get_alpha(),)
gc1 = self.new_gc()
gc1.set_linewidth(0)

Check warning on line 204 in lib/matplotlib/backends/backend_agg.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_agg.py#L202-L204

Added lines #L202 - L204 were not covered by tests
for dx, dy, w, h in parse.rects: # dy is upwards & the rect top side.
path = Path._create_closed(

Check warning on line 206 in lib/matplotlib/backends/backend_agg.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_agg.py#L206

Added line #L206 was not covered by tests
[(dx, dy), (dx + w, dy), (dx + w, dy + h), (dx, dy + h)])
self._renderer.draw_path(

Check warning on line 208 in lib/matplotlib/backends/backend_agg.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_agg.py#L208

Added line #L208 was not covered by tests
gc1, path,
mpl.transforms.Affine2D()
.rotate_deg(angle).translate(x, self.height - y),
rgba)
gc1.restore()

Check warning on line 213 in lib/matplotlib/backends/backend_agg.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_agg.py#L213

Added line #L213 was not covered by tests

def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
# docstring inherited
if ismath:
return self.draw_mathtext(gc, x, y, s, prop, angle)
font = self._prepare_font(prop)
# We pass '0' for angle here, since it will be rotated (in raster
# space) in the following call to draw_text_image).
font.set_text(s, 0, flags=get_hinting_flag())
font.draw_glyphs_to_bitmap(
antialiased=gc.get_antialiased())
d = font.get_descent() / 64.0
# The descent needs to be adjusted for the angle.
xo, yo = font.get_bitmap_offset()
xo /= 64.0
yo /= 64.0
xd = d * sin(radians(angle))
yd = d * cos(radians(angle))
x = round(x + xo + xd)
y = round(y + yo + yd)
self._renderer.draw_text_image(font, x, y + 1, angle, gc)
font.set_text(s, angle, flags=get_hinting_flag())
for bitmap in font._render_glyphs(x, self.height - y):
self._renderer.draw_text_image(
bitmap["buffer"],
bitmap["left"],
int(self.height) - bitmap["top"] + bitmap["buffer"].shape[0],
0, gc)

def get_text_width_height_descent(self, s, prop, ismath):
# docstring inherited
Expand All @@ -211,9 +233,8 @@
return super().get_text_width_height_descent(s, prop, ismath)

if ismath:
ox, oy, width, height, descent, font_image = \
self.mathtext_parser.parse(s, self.dpi, prop)
return width, height, descent
parse = self.mathtext_parser.parse(s, self.dpi, prop)
return parse.width, parse.height, parse.depth

font = self._prepare_font(prop)
font.set_text(s, 0.0, flags=get_hinting_flag())
Expand Down
2 changes: 2 additions & 0 deletions src/ft2font.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ class FT2Font
FT2Image image;
FT_Face face;
FT_Vector pen; /* untransformed origin */
public:
std::vector<FT_Glyph> glyphs;
private:
std::vector<FT2Font *> fallbacks;
std::unordered_map<FT_UInt, FT2Font *> glyph_to_font;
std::unordered_map<long, FT2Font *> char_to_font;
Expand Down
54 changes: 53 additions & 1 deletion src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,59 @@
std::vector<py::size_t> shape { im.get_height(), im.get_width() };
std::vector<py::size_t> strides { im.get_width(), 1 };
return py::buffer_info(im.get_buffer(), shape, strides);
});
})

Check warning on line 1767 in src/ft2font_wrapper.cpp

View check run for this annotation

Codecov / codecov/patch

src/ft2font_wrapper.cpp#L1767

Added line #L1767 was not covered by tests

// TODO: Return a nicer structure than dicts.
// NOTE: The lifetime of the buffers is limited and could get invalidated...
// TODO: Real antialiasing flag.
// TODO: throw_ft_error.
// x, y are upwards here
.def("_render_glyph", [](PyFT2Font *self, FT_UInt idx,
double x, double y, double angle,
LoadFlags flags) {
auto face = self->x->get_face();
auto hf = self->x->get_hinting_factor();
auto c = std::cos(angle * M_PI / 180) * 0x10000L,
s = std::sin(angle * M_PI / 180) * 0x10000L;
auto matrix = FT_Matrix{
std::lround(c / hf), std::lround(-s), std::lround(s / hf), std::lround(c)};
auto delta = FT_Vector{std::lround(x * 64), std::lround(y * 64)};
FT_Set_Transform(face, &matrix, &delta);
if (auto error = FT_Load_Glyph(face, idx, static_cast<FT_Int32>(flags))) {
throw std::runtime_error("Could not load glyph");
}
if (auto error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
throw std::runtime_error("Could not convert glyph to bitmap");
}
py::dict d;
d["left"] = face->glyph->bitmap_left;
d["top"] = face->glyph->bitmap_top;
d["buffer"] = py::array_t<uint8_t>{
{face->glyph->bitmap.rows, face->glyph->bitmap.width},
{face->glyph->bitmap.pitch, 1},
face->glyph->bitmap.buffer};
return d;
})
.def("_render_glyphs", [](PyFT2Font *self, double x, double y) {
auto origin = FT_Vector{std::lround(x * 64), std::lround(y * 64)};
py::list gs;
for (auto &g: self->x->glyphs) {
if (auto error = FT_Glyph_To_Bitmap(&g, FT_RENDER_MODE_NORMAL, &origin, 1)) {
throw std::runtime_error("Could not convert glyph to bitmap");
}
auto bg = reinterpret_cast<FT_BitmapGlyph>(g);
py::dict d;
d["left"] = bg->left;
d["top"] = bg->top;
d["buffer"] = py::array_t<uint8_t>{
{bg->bitmap.rows, bg->bitmap.width},
{bg->bitmap.pitch, 1},
bg->bitmap.buffer};
gs.append(d);
}
return gs;
})
;

m.attr("__freetype_version__") = version_string;
m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE;
Expand Down
Loading