Skip to content

Commit ff62e9e

Browse files
committed
Pick simplest way to support for colored text.
1 parent 98404c1 commit ff62e9e

File tree

2 files changed

+43
-89
lines changed

2 files changed

+43
-89
lines changed

mpl_cairo/__init__.py

+3-17
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,10 @@
22

33
from matplotlib.backend_bases import GraphicsContextBase, RendererBase
44
from matplotlib.backends import backend_cairo
5-
from matplotlib.font_manager import ttfFontProperty
6-
from matplotlib.mathtext import MathtextBackendCairo, MathTextParser
5+
from matplotlib.mathtext import MathTextParser
76
from . import _mpl_cairo
87

98

10-
class MathtextBackendCairo2(MathtextBackendCairo):
11-
def render_glyph(self, ox, oy, info):
12-
self.glyphs.append(
13-
# Convert to ttfFontProperty here.
14-
(ttfFontProperty(info.font),
15-
info.fontsize,
16-
chr(info.num),
17-
ox,
18-
oy - info.offset - self.height))
19-
20-
21-
MathTextParser._backend_mapping["cairo2"] = MathtextBackendCairo2
22-
23-
249
class GraphicsContextRendererCairo(
2510
_mpl_cairo.GraphicsContextRendererCairo,
2611
GraphicsContextBase,
@@ -50,7 +35,8 @@ def _swap(class_pairs):
5035
for old, new in class_pairs:
5136
for cls in old.__subclasses__():
5237
idx = cls.__bases__.index(old)
53-
cls.__bases__ = cls.__bases__[:idx] + (new,) + cls.__bases__[idx + 1:]
38+
cls.__bases__ = (
39+
cls.__bases__[:idx] + (new,) + cls.__bases__[idx + 1:])
5440
setattr(backend_cairo, new.__name__, new)
5541

5642

src/_mpl_cairo.cpp

+40-72
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ struct GraphicsContextRenderer {
2525
double dpi_;
2626
std::optional<double> alpha_;
2727

28+
// Not exposed.
2829
double points_to_pixels(double points);
30+
rgba_t get_rgba(void);
2931

3032
GraphicsContextRenderer(double dpi);
3133
~GraphicsContextRenderer();
@@ -82,6 +84,19 @@ double GraphicsContextRenderer::points_to_pixels(double points) {
8284
return points * dpi_ / 72;
8385
}
8486

87+
rgba_t GraphicsContextRenderer::get_rgba(void) {
88+
double r, g, b, a;
89+
auto status = cairo_pattern_get_rgba(cairo_get_source(cr_), &r, &g, &b, &a);
90+
if (status != CAIRO_STATUS_SUCCESS) {
91+
throw std::runtime_error("Could not retrieve color from pattern: "
92+
+ std::string{cairo_status_to_string(status)});
93+
}
94+
if (alpha_) {
95+
a = *alpha_;
96+
}
97+
return {r, g, b, a};
98+
}
99+
85100
int GraphicsContextRenderer::get_width(void) {
86101
if (!cr_) {
87102
return 0;
@@ -198,12 +213,7 @@ void GraphicsContextRenderer::set_linewidth(double lw) {
198213
}
199214

200215
rgb_t GraphicsContextRenderer::get_rgb(void) {
201-
double r, g, b, a;
202-
auto status = cairo_pattern_get_rgba(cairo_get_source(cr_), &r, &g, &b, &a);
203-
if (status != CAIRO_STATUS_SUCCESS) {
204-
throw std::runtime_error("Could not retrieve color from pattern: "
205-
+ std::string{cairo_status_to_string(status)});
206-
}
216+
auto [r, g, b, a] = get_rgba();
207217
return {r, g, b};
208218
}
209219

@@ -396,86 +406,44 @@ void GraphicsContextRenderer::draw_text(
396406
return;
397407
}
398408
cairo_save(cr_);
399-
cairo_translate(cr_, int(x), int(y));
400-
cairo_rotate(cr_, -angle * M_PI / 180);
409+
// FIXME If angle == 0, we need to round x and y to avoid additional
410+
// aliasing on top of the one already provided by freetype. Perhaps we
411+
// should let it know about the destination subpixel position?
412+
// If angle != 0, all hope is lost anyways.
413+
if (angle) {
414+
cairo_translate(cr_, x, y);
415+
cairo_rotate(cr_, -angle * M_PI / 180);
416+
} else {
417+
cairo_translate(cr_, round(x), round(y));
418+
}
401419
if (ismath) {
402-
403-
// // FIXME cairo2 parser
404-
// auto [width, height, descent, glyphs, rects] =
405-
// py::cast(this).attr("mathtext_parser").attr("parse")(s, dpi_, prop)
406-
// .cast<std::tuple<double, double, double,
407-
// std::vector<py::object>, std::vector<py::object>>>();
408-
// for (auto glyph: glyphs) {
409-
// auto [prop, fontsize, c, ox, oy] = glyph
410-
// .cast<std::tuple<py::object, double, std::string, double, double>>();
411-
// cairo_move_to(cr_, ox, oy);
412-
// cairo_select_font_face(
413-
// cr_,
414-
// prop.attr("name").cast<std::string>().c_str(),
415-
// CAIRO_FONT_SLANT_NORMAL,
416-
// CAIRO_FONT_WEIGHT_NORMAL); // FIXME
417-
// cairo_set_font_size(
418-
// cr_,
419-
// points_to_pixels(fontsize));
420-
// cairo_show_text(cr_, c.c_str());
421-
// }
422-
// for (auto rect: rects) {
423-
// auto [ox, oy, w, h] = rect.cast<rectangle_t>();
424-
// cairo_new_path(cr_);
425-
// cairo_rectangle(cr_, ox, oy, w, h);
426-
// cairo_set_source_rgb(cr_, 0, 0, 0);
427-
// cairo_fill(cr_);
428-
// }
429-
430-
// FIXME agg parser, two versions
431420
auto [ox, oy, width, height, descent, image, chars] =
432421
py::cast(this).attr("mathtext_parser").attr("parse")(s, dpi_, prop)
433422
.cast<std::tuple<double, double, double, double, double,
434423
py::object, py::object>>();
435424
auto im_raw = py::array_t<uint8_t, py::array::c_style>{image}.mutable_unchecked<2>();
436425
auto ni = im_raw.shape(0), nj = im_raw.shape(1);
437-
438-
// // FIXME agg parser, alpha
439-
auto stride = cairo_format_stride_for_width(CAIRO_FORMAT_A8, nj);
440-
std::unique_ptr<uint8_t[]> buf;
441-
uint8_t* ptr;
442-
if (stride == nj) {
443-
ptr = im_raw.mutable_data(0, 0); // cairo is non-const.
444-
} else {
445-
auto pix = im_raw.data(0, 0);
446-
buf = std::make_unique<uint8_t[]>(ni * stride);
447-
for (size_t i = 0; i < ni; ++i) {
448-
ptr = buf.get() + i * stride;
449-
for (size_t j = 0; j < nj; ++j) {
450-
*(ptr++) = *(pix++);
451-
}
426+
// Recompute the colors. Trying to use an A8 image seems just as
427+
// complicated (http://cairo.cairographics.narkive.com/ijgxr19T/alpha-masks).
428+
auto stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, nj);
429+
auto pix = im_raw.data(0, 0);
430+
std::unique_ptr<uint8_t[]> buf{new uint8_t[ni * stride]};
431+
auto [r, g, b, a] = get_rgba();
432+
for (size_t i = 0; i < ni; ++i) {
433+
auto ptr = reinterpret_cast<uint32_t*>(buf.get() + i * stride);
434+
for (size_t j = 0; j < nj; ++j) {
435+
auto val = *(pix++);
436+
*(ptr++) =
437+
(uint8_t(a * val) << 24) + (uint8_t(a * val * r) << 16)
438+
+ (uint8_t(a * val * g) << 8) + (uint8_t(a * val * b));
452439
}
453-
ptr = buf.get();
454440
}
455441
auto surface = cairo_image_surface_create_for_data(
456-
ptr, CAIRO_FORMAT_A8, nj, ni, stride);
457-
458-
// FIXME agg parser, grayscale
459-
// auto stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, nj);
460-
// auto pix = im_raw.data(0, 0);
461-
// std::unique_ptr<uint8_t[]> buf{new uint8_t[ni * stride]};
462-
// for (size_t i = 0; i < ni; ++i) {
463-
// auto ptr = reinterpret_cast<uint32_t*>(buf.get() + i * stride);
464-
// for (size_t j = 0; j < nj; ++j) {
465-
// *(ptr++) = 0xff000000 + (0xff - *(pix++)) * 0x010101;
466-
// }
467-
// }
468-
// auto surface = cairo_image_surface_create_for_data(
469-
// buf.get(), CAIRO_FORMAT_ARGB32, nj, ni, stride);
470-
442+
buf.get(), CAIRO_FORMAT_ARGB32, nj, ni, stride);
471443
auto pattern = cairo_pattern_create_for_surface(surface);
472444
cairo_matrix_t matrix{1, 0, 0, 1, -ox, ni - oy};
473445
cairo_pattern_set_matrix(pattern, &matrix);
474446
cairo_set_source(cr_, pattern);
475-
476-
// // FIXME Already antialiased by freetype, don't do it twice?
477-
// cairo_set_antialias(cr_, CAIRO_ANTIALIAS_NONE);
478-
479447
cairo_paint(cr_);
480448
cairo_pattern_destroy(pattern);
481449
cairo_surface_destroy(surface);

0 commit comments

Comments
 (0)