diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index a692504afb23..25619104134f 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -30,14 +30,6 @@ def __init__(self, figure): FigureCanvasBase.__init__(self, figure) width, height = self.get_width_height() _macosx.FigureCanvas.__init__(self, width, height) - self._dpi_ratio = 1.0 - - def _set_device_scale(self, value): - if self._dpi_ratio != value: - # Need the new value in place before setting figure.dpi, which - # will trigger a resize - self._dpi_ratio, old_value = value, self._dpi_ratio - self.figure.dpi = self.figure.dpi / old_value * self._dpi_ratio def set_cursor(self, cursor): # docstring inherited @@ -60,12 +52,11 @@ def blit(self, bbox=None): self.draw_idle() def resize(self, width, height): - dpi = self.figure.dpi - width /= dpi - height /= dpi - self.figure.set_size_inches(width * self._dpi_ratio, - height * self._dpi_ratio, - forward=False) + # Size from macOS is logical pixels, dpi is physical. + scale = self.figure.dpi / self.device_pixel_ratio + width /= scale + height /= scale + self.figure.set_size_inches(width, height, forward=False) FigureCanvasBase.resize_event(self) self.draw_idle() diff --git a/src/_macosx.m b/src/_macosx.m index ef999ca9ea2d..9eeeab46eb5b 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -237,6 +237,8 @@ @interface View : NSView } - (void)dealloc; - (void)drawRect:(NSRect)rect; +- (void)updateDevicePixelRatio:(double)scale; +- (void)windowDidChangeBackingProperties:(NSNotification*)notification; - (void)windowDidResize:(NSNotification*)notification; - (View*)initWithFrame:(NSRect)rect; - (void)setCanvas: (PyObject*)newCanvas; @@ -706,6 +708,7 @@ static CGFloat _get_device_scale(CGContextRef cr) [window setDelegate: view]; [window makeFirstResponder: view]; [[window contentView] addSubview: view]; + [view updateDevicePixelRatio: [window backingScaleFactor]]; return 0; } @@ -801,6 +804,9 @@ static CGFloat _get_device_scale(CGContextRef cr) Window* window = self->window; if(window) { + CGFloat device_pixel_ratio = [window backingScaleFactor]; + width /= device_pixel_ratio; + height /= device_pixel_ratio; // 36 comes from hard-coded size of toolbar later in code [window setContentSize: NSMakeSize(width, height + 36.)]; } @@ -1654,15 +1660,6 @@ -(void)drawRect:(NSRect)rect CGContextRef cr = [[NSGraphicsContext currentContext] CGContext]; - double new_device_scale = _get_device_scale(cr); - - if (device_scale != new_device_scale) { - device_scale = new_device_scale; - if (!PyObject_CallMethod(canvas, "_set_device_scale", "d", device_scale, NULL)) { - PyErr_Print(); - goto exit; - } - } if (!(renderer = PyObject_CallMethod(canvas, "_draw", "", NULL)) || !(renderer_buffer = PyObject_GetAttrString(renderer, "_renderer"))) { PyErr_Print(); @@ -1683,6 +1680,33 @@ -(void)drawRect:(NSRect)rect PyGILState_Release(gstate); } +- (void)updateDevicePixelRatio:(double)scale +{ + PyObject* change = NULL; + PyGILState_STATE gstate = PyGILState_Ensure(); + + device_scale = scale; + if (!(change = PyObject_CallMethod(canvas, "_set_device_pixel_ratio", "d", device_scale, NULL))) { + PyErr_Print(); + goto exit; + } + if (PyObject_IsTrue(change)) { + [self setNeedsDisplay: YES]; + } + + exit: + Py_XDECREF(change); + + PyGILState_Release(gstate); +} + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification +{ + Window* window = [notification object]; + + [self updateDevicePixelRatio: [window backingScaleFactor]]; +} + - (void)windowDidResize: (NSNotification*)notification { int width, height;