Skip to content

Commit df67aaf

Browse files
authored
Merge pull request #17478 from anntzer/qt5cblit
Add support for blitting in qt5cairo.
2 parents a1c51a0 + 7e57660 commit df67aaf

File tree

3 files changed

+49
-12
lines changed

3 files changed

+49
-12
lines changed

lib/matplotlib/backends/backend_cairo.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88

99
import gzip
10+
import math
1011

1112
import numpy as np
1213

@@ -402,8 +403,47 @@ def set_linewidth(self, w):
402403
self.ctx.set_line_width(self.renderer.points_to_pixels(w))
403404

404405

406+
class _CairoRegion:
407+
def __init__(self, slices, data):
408+
self._slices = slices
409+
self._data = data
410+
411+
405412
class FigureCanvasCairo(FigureCanvasBase):
406413

414+
def copy_from_bbox(self, bbox):
415+
surface = self._renderer.gc.ctx.get_target()
416+
if not isinstance(surface, cairo.ImageSurface):
417+
raise RuntimeError(
418+
"copy_from_bbox only works when rendering to an ImageSurface")
419+
sw = surface.get_width()
420+
sh = surface.get_height()
421+
x0 = math.ceil(bbox.x0)
422+
x1 = math.floor(bbox.x1)
423+
y0 = math.ceil(sh - bbox.y1)
424+
y1 = math.floor(sh - bbox.y0)
425+
if not (0 <= x0 and x1 <= sw and bbox.x0 <= bbox.x1
426+
and 0 <= y0 and y1 <= sh and bbox.y0 <= bbox.y1):
427+
raise ValueError("Invalid bbox")
428+
sls = slice(y0, y0 + max(y1 - y0, 0)), slice(x0, x0 + max(x1 - x0, 0))
429+
data = (np.frombuffer(surface.get_data(), np.uint32)
430+
.reshape((sh, sw))[sls].copy())
431+
return _CairoRegion(sls, data)
432+
433+
def restore_region(self, region):
434+
surface = self._renderer.gc.ctx.get_target()
435+
if not isinstance(surface, cairo.ImageSurface):
436+
raise RuntimeError(
437+
"restore_region only works when rendering to an ImageSurface")
438+
surface.flush()
439+
sw = surface.get_width()
440+
sh = surface.get_height()
441+
sly, slx = region._slices
442+
(np.frombuffer(surface.get_data(), np.uint32)
443+
.reshape((sh, sw))[sly, slx]) = region._data
444+
surface.mark_dirty_rectangle(
445+
slx.start, sly.start, slx.stop - slx.start, sly.stop - sly.start)
446+
407447
def print_png(self, fobj, *args, **kwargs):
408448
self._get_printed_image_surface().write_to_png(fobj)
409449

lib/matplotlib/backends/backend_qt5.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,15 @@ def draw_idle(self):
460460
self._draw_pending = True
461461
QtCore.QTimer.singleShot(0, self._draw_idle)
462462

463+
def blit(self, bbox=None):
464+
# docstring inherited
465+
if bbox is None and self.figure:
466+
bbox = self.figure.bbox # Blit the entire canvas if bbox is None.
467+
# repaint uses logical pixels, not physical pixels like the renderer.
468+
l, b, w, h = [pt / self._dpi_ratio for pt in bbox.bounds]
469+
t = b + h
470+
self.repaint(l, self.rect().height() - t, w, h)
471+
463472
def _draw_idle(self):
464473
with self._idle_draw_cntx():
465474
if not self._draw_pending:

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,6 @@ def paintEvent(self, event):
7878

7979
painter.end()
8080

81-
def blit(self, bbox=None):
82-
# docstring inherited
83-
# If bbox is None, blit the entire canvas. Otherwise
84-
# blit only the area defined by the bbox.
85-
if bbox is None and self.figure:
86-
bbox = self.figure.bbox
87-
88-
# repaint uses logical pixels, not physical pixels like the renderer.
89-
l, b, w, h = [pt / self._dpi_ratio for pt in bbox.bounds]
90-
t = b + h
91-
self.repaint(l, self.renderer.height / self._dpi_ratio - t, w, h)
92-
9381
def print_figure(self, *args, **kwargs):
9482
super().print_figure(*args, **kwargs)
9583
self.draw()

0 commit comments

Comments
 (0)