Skip to content

Commit 617cb43

Browse files
committed
Handle colour fonts
1 parent 219d974 commit 617cb43

File tree

2 files changed

+52
-26
lines changed

2 files changed

+52
-26
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
189189
round(0x40 * (self.height - y + dx * sin + dy * cos))]
190190
)
191191
bitmap = font._render_glyph(
192-
glyph_index, get_hinting_flag(),
192+
glyph_index, get_hinting_flag() | LoadFlags.COLOR,
193193
RenderMode.NORMAL if gc.get_antialiased() else RenderMode.MONO)
194194
buffer = np.asarray(bitmap.buffer)
195195
if not gc.get_antialiased():
@@ -221,20 +221,38 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
221221
if ismath:
222222
return self.draw_mathtext(gc, x, y, s, prop, angle)
223223
font = self._prepare_font(prop)
224-
font.set_text(s, angle, flags=get_hinting_flag(),
225-
features=mtext.get_fontfeatures() if mtext is not None else None,
226-
language=mtext.get_language() if mtext is not None else None)
227-
for bitmap in font._render_glyphs(
228-
x, self.height - y,
229-
RenderMode.NORMAL if gc.get_antialiased() else RenderMode.MONO,
230-
):
224+
cos = math.cos(math.radians(angle))
225+
sin = math.sin(math.radians(angle))
226+
load_flags = get_hinting_flag() | LoadFlags.COLOR | LoadFlags.NO_SVG
227+
items = font._layout(
228+
s, flags=load_flags,
229+
features=mtext.get_fontfeatures() if mtext is not None else None,
230+
language=mtext.get_language() if mtext is not None else None)
231+
for item in items:
232+
hf = item.ft_object._hinting_factor
233+
item.ft_object._set_transform(
234+
[[round(0x10000 * cos / hf), round(0x10000 * -sin)],
235+
[round(0x10000 * sin / hf), round(0x10000 * cos)]],
236+
[round(0x40 * (x + item.x * cos - item.y * sin)),
237+
# FreeType's y is upwards.
238+
round(0x40 * (self.height - y + item.x * sin + item.y * cos))]
239+
)
240+
bitmap = item.ft_object._render_glyph(
241+
item.glyph_index, load_flags,
242+
RenderMode.NORMAL if gc.get_antialiased() else RenderMode.MONO)
231243
buffer = bitmap.buffer
232244
if not gc.get_antialiased():
233245
buffer *= 0xff
234-
self._renderer.draw_text_image(
235-
buffer,
236-
bitmap.left, int(self.height) - bitmap.top + buffer.shape[0],
237-
0, gc)
246+
if buffer.ndim == 3:
247+
self._renderer.draw_image(
248+
gc,
249+
bitmap.left, bitmap.top - buffer.shape[0],
250+
buffer[::-1, :, [2, 1, 0, 3]])
251+
else:
252+
self._renderer.draw_text_image(
253+
buffer,
254+
bitmap.left, int(self.height) - bitmap.top + buffer.shape[0],
255+
0, gc)
238256

239257
def get_text_width_height_descent(self, s, prop, ismath):
240258
# docstring inherited

src/ft2font_wrapper.cpp

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,22 @@ struct PyPositionedBitmap {
297297
left{slot->bitmap_left}, top{slot->bitmap_top}, owning{true}
298298
{
299299
FT_Bitmap_Init(&bitmap);
300-
FT_CHECK(FT_Bitmap_Convert, _ft2Library, &slot->bitmap, &bitmap, 1);
300+
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
301+
FT_CHECK(FT_Bitmap_Convert, _ft2Library, &slot->bitmap, &bitmap, 1);
302+
} else {
303+
FT_CHECK(FT_Bitmap_Copy, _ft2Library, &slot->bitmap, &bitmap);
304+
}
301305
}
302306

303307
PyPositionedBitmap(FT_BitmapGlyph bg) :
304308
left{bg->left}, top{bg->top}, owning{true}
305309
{
306310
FT_Bitmap_Init(&bitmap);
307-
FT_CHECK(FT_Bitmap_Convert, _ft2Library, &bg->bitmap, &bitmap, 1);
311+
if (bg->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
312+
FT_CHECK(FT_Bitmap_Convert, _ft2Library, &bg->bitmap, &bitmap, 1);
313+
} else {
314+
FT_CHECK(FT_Bitmap_Copy, _ft2Library, &bg->bitmap, &bitmap);
315+
}
308316
}
309317

310318
PyPositionedBitmap(PyPositionedBitmap& other) = delete; // Non-copyable.
@@ -1668,9 +1676,19 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
16681676
.def_readonly("top", &PyPositionedBitmap::top)
16691677
.def_property_readonly(
16701678
"buffer", [](PyPositionedBitmap &self) -> py::array {
1671-
return {{self.bitmap.rows, self.bitmap.width},
1679+
if (self.bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1680+
return {
1681+
py::array::ShapeContainer({self.bitmap.rows, self.bitmap.width, 4}),
1682+
py::array::StridesContainer({self.bitmap.pitch, 4, 1}),
1683+
self.bitmap.buffer
1684+
};
1685+
} else {
1686+
return {
1687+
{self.bitmap.rows, self.bitmap.width},
16721688
{self.bitmap.pitch, 1},
1673-
self.bitmap.buffer};
1689+
self.bitmap.buffer
1690+
};
1691+
}
16741692
})
16751693
;
16761694

@@ -1913,16 +1931,6 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
19131931
FT_CHECK(FT_Render_Glyph, face->glyph, render_mode);
19141932
return PyPositionedBitmap{face->glyph};
19151933
})
1916-
.def("_render_glyphs",
1917-
[](PyFT2Font *self, double x, double y, FT_Render_Mode render_mode) {
1918-
auto origin = FT_Vector{std::lround(x * 64), std::lround(y * 64)};
1919-
auto pbs = std::vector<PyPositionedBitmap>{};
1920-
for (auto &g: self->get_glyphs()) {
1921-
FT_CHECK(FT_Glyph_To_Bitmap, &g, render_mode, &origin, 1);
1922-
pbs.emplace_back(reinterpret_cast<FT_BitmapGlyph>(g));
1923-
}
1924-
return pbs;
1925-
})
19261934
;
19271935

19281936
m.attr("__freetype_version__") = version_string;

0 commit comments

Comments
 (0)