Skip to content

Commit 6f2bb54

Browse files
committed
Fix gtk3_native with nonzero origin.
1 parent 9cd1fb4 commit 6f2bb54

File tree

6 files changed

+47
-15
lines changed

6 files changed

+47
-15
lines changed

lib/mplcairo/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ def __init__(self, width, height, dpi):
7777
RendererBase.__init__(self)
7878

7979
@classmethod
80-
def from_pycairo_ctx(cls, ctx, width, height, dpi):
80+
def from_pycairo_ctx(cls, ctx, width, height, dpi, orig_scale):
8181
obj = _mplcairo.GraphicsContextRendererCairo.__new__(
82-
cls, ctx, width, height, dpi)
82+
cls, ctx, width, height, dpi, orig_scale)
8383
_mplcairo.GraphicsContextRendererCairo.__init__(
84-
obj, ctx, width, height, dpi)
84+
obj, ctx, width, height, dpi, orig_scale)
8585
RendererBase.__init__(obj)
8686
return obj
8787

lib/mplcairo/gtk_native.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def on_draw_event(self, widget, ctx):
2929
# a RecordingSurface).
3030
renderer = self._get_cached_or_new_renderer(
3131
GraphicsContextRendererCairo.from_pycairo_ctx,
32-
ctx, figure.bbox.width, figure.bbox.height, figure.dpi)
32+
ctx, figure.bbox.width, figure.bbox.height, figure.dpi, prev_scale)
3333
figure.draw(renderer)
3434
surface.set_device_scale(*prev_scale)
3535

src/_mplcairo.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ GraphicsContextRenderer::AdditionalContext::AdditionalContext(
171171
if (auto const& rectangle = state.clip_rectangle) {
172172
auto const& [x, y, w, h] = *rectangle;
173173
cairo_save(cr);
174-
cairo_identity_matrix(cr);
174+
restore_init_matrix(cr);
175175
cairo_new_path(cr);
176176
cairo_rectangle(cr, x, state.height - h - y, w, h);
177177
cairo_restore(cr);
@@ -192,6 +192,7 @@ GraphicsContextRenderer::AdditionalContext::AdditionalContext(
192192
"cairo_tag_begin requires cairo>=1.15.4");
193193
}
194194
}
195+
restore_init_matrix(cr);
195196
}
196197

197198
GraphicsContextRenderer::AdditionalContext::~AdditionalContext()
@@ -313,7 +314,8 @@ GraphicsContextRenderer::GraphicsContextRenderer(
313314
std::floor(width), std::floor(height), dpi}
314315
{}
315316

316-
cairo_t* GraphicsContextRenderer::cr_from_pycairo_ctx(py::object ctx)
317+
cairo_t* GraphicsContextRenderer::cr_from_pycairo_ctx(
318+
py::object ctx, std::tuple<double, double> device_scales)
317319
{
318320
if (!detail::has_pycairo) {
319321
throw std::runtime_error{"pycairo is not available"};
@@ -326,12 +328,24 @@ cairo_t* GraphicsContextRenderer::cr_from_pycairo_ctx(py::object ctx)
326328
auto const& cr = PycairoContext_GET(ctx.ptr());
327329
CAIRO_CHECK(cairo_status, cr);
328330
cairo_reference(cr);
331+
// With native Gtk3, the context may have a nonzero initial translation (if
332+
// the drawn area is not at the top left of the window); this needs to be
333+
// taken into account by the path loader (which works in absolute coords).
334+
auto mtx = new cairo_matrix_t{};
335+
cairo_get_matrix(cr, mtx);
336+
auto const& [sx, sy] = device_scales;
337+
mtx->x0 *= sx; mtx->y0 *= sy;
338+
CAIRO_CHECK_SET_USER_DATA(
339+
cairo_set_user_data, cr, &detail::INIT_MATRIX_KEY, mtx,
340+
[](void* data) -> void { delete static_cast<cairo_matrix_t*>(data); });
329341
return cr;
330342
}
331343

332344
GraphicsContextRenderer::GraphicsContextRenderer(
333-
py::object ctx, double width, double height, double dpi) :
334-
GraphicsContextRenderer{cr_from_pycairo_ctx(ctx), width, height, dpi}
345+
py::object ctx, double width, double height, double dpi,
346+
std::tuple<double, double> device_scales) :
347+
GraphicsContextRenderer{
348+
cr_from_pycairo_ctx(ctx, device_scales), width, height, dpi}
335349
{}
336350

337351
cairo_t* GraphicsContextRenderer::cr_from_fileformat_args(
@@ -2065,7 +2079,7 @@ Only intended for debugging purposes.
20652079
// The RendererAgg signature, which is also expected by MixedModeRenderer
20662080
// (with doubles!).
20672081
.def(py::init<double, double, double>())
2068-
.def(py::init<py::object, double, double, double>())
2082+
.def(py::init<py::object, double, double, double, std::tuple<double, double>>())
20692083
.def(py::init<StreamSurfaceType, py::object, double, double, double>())
20702084
.def(
20712085
py::pickle(

src/_mplcairo.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ class GraphicsContextRenderer {
5151

5252
static cairo_t* cr_from_image_args(int width, int height);
5353
GraphicsContextRenderer(double width, double height, double dpi);
54-
static cairo_t* cr_from_pycairo_ctx(py::object ctx);
54+
static cairo_t* cr_from_pycairo_ctx(
55+
py::object ctx, std::tuple<double, double> device_scales);
5556
GraphicsContextRenderer(
5657
py::object ctx,
57-
double width, double height, double dpi);
58+
double width, double height, double dpi,
59+
std::tuple<double, double> device_scales);
5860
static cairo_t* cr_from_fileformat_args(
5961
StreamSurfaceType type, py::object file,
6062
double width, double height, double dpi);

src/_util.cpp

+18-4
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ ITER_CAIRO_OPTIONAL_API(DEFINE_API)
4545

4646
// Other useful values.
4747
std::unordered_map<std::string, cairo_font_face_t*> FONT_CACHE{};
48-
cairo_user_data_key_t const
49-
REFS_KEY{}, STATE_KEY{}, FT_KEY{}, FEATURES_KEY{}, IS_COLOR_FONT_KEY{};
48+
cairo_user_data_key_t const REFS_KEY{},
49+
STATE_KEY{},
50+
INIT_MATRIX_KEY{},
51+
FT_KEY{},
52+
FEATURES_KEY{},
53+
IS_COLOR_FONT_KEY{};
5054
py::object RC_PARAMS{},
5155
PIXEL_MARKER{},
5256
UNIT_CIRCLE{};
@@ -191,6 +195,16 @@ AdditionalState& get_additional_state(cairo_t* cr)
191195
return stack.top();
192196
}
193197

198+
void restore_init_matrix(cairo_t* cr)
199+
{
200+
auto const& data = cairo_get_user_data(cr, &detail::INIT_MATRIX_KEY);
201+
if (!data) {
202+
cairo_identity_matrix(cr);
203+
} else {
204+
cairo_set_matrix(cr, static_cast<cairo_matrix_t*>(data));
205+
}
206+
}
207+
194208
// Set the current path of `cr` to `path`, after transformation by `matrix`,
195209
// ignoring the CTM ("exact").
196210
//
@@ -226,7 +240,7 @@ struct LoadPathContext {
226240
snap{!has_vector_surface(cr) && get_additional_state(cr).snap}
227241
{
228242
cairo_get_matrix(cr, &ctm);
229-
cairo_identity_matrix(cr);
243+
restore_init_matrix(cr);
230244
cairo_new_path(cr);
231245
auto const& lw = cairo_get_line_width(cr);
232246
snapper =
@@ -567,7 +581,7 @@ void fill_and_stroke_exact(
567581
load_path_exact(cr, path, matrix);
568582
path_loaded = true;
569583
}
570-
cairo_identity_matrix(cr); // Dashes are interpreted using the CTM.
584+
restore_init_matrix(cr); // Dashes are interpreted using the CTM.
571585
cairo_stroke_preserve(cr);
572586
}
573587
cairo_restore(cr);

src/_util.h

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extern std::unordered_map<std::string, cairo_font_face_t*> FONT_CACHE;
7070
extern cairo_user_data_key_t const
7171
REFS_KEY, // cairo_t -> kept alive Python objects.
7272
STATE_KEY, // cairo_t -> additional state.
73+
INIT_MATRIX_KEY, // cairo_t -> cairo_matrix_t.
7374
FT_KEY, // cairo_font_face_t -> FT_Face.
7475
FEATURES_KEY, // cairo_font_face_t -> OpenType features.
7576
IS_COLOR_FONT_KEY; // cairo_font_face_t -> non-null if a color font.
@@ -136,6 +137,7 @@ cairo_matrix_t matrix_from_transform(
136137
py::object transform, cairo_matrix_t const* master_matrix);
137138
bool has_vector_surface(cairo_t* cr);
138139
AdditionalState& get_additional_state(cairo_t* cr);
140+
void restore_init_matrix(cairo_t* cr);
139141
void load_path_exact(
140142
cairo_t* cr, py::object path, cairo_matrix_t const* matrix);
141143
void load_path_exact(

0 commit comments

Comments
 (0)