From 0cc8287226b04534b05a9019706c1babfeb27035 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 3 Sep 2013 21:25:16 +0200 Subject: [PATCH 1/2] Implement draw_markers in the cairo backend. This commit increases the speed of marker drawing in the caior backend. It directly uses cairo paths for this. If the marker is only stroked (and not filled) then only one drawing operation is used. If it is filled then each marker is filled and stroked separately. --- lib/matplotlib/backends/backend_cairo.py | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index f8328b5c7e7..0a0e78d51bc 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -152,6 +152,47 @@ def draw_path(self, gc, path, transform, rgbFace=None): self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha()) + def draw_markers(self, gc, marker_path, marker_trans, path, transform, rgbFace=None): + ctx = gc.ctx + + ctx.new_path() + # Create the path for the marker; it needs to be flipped here already! + self.convert_path(ctx, marker_path, marker_trans + Affine2D().scale(1.0, -1.0)) + marker_path = ctx.copy_path_flat() + + # Figure out whether the path has a fill + x1, y1, x2, y2 = ctx.fill_extents() + if x1 == 0 and y1 == 0 and x2 == 0 and y2 == 0: + filled = False + # No fill, just unset this (so we don't try to fill it later on) + rgbFace = None + else: + filled = True + + transform = transform + \ + Affine2D().scale(1.0, -1.0).translate(0, self.height) + + ctx.new_path() + for vertices, codes in path.iter_segments(transform, simplify=False): + if len(vertices): + x, y = vertices[-2:] + ctx.save() + + # Translate and apply path + ctx.translate(x, y) + ctx.append_path(marker_path) + + # Slower code path if there is a fill; we need to draw + # the fill and stroke for each marker at the same time. + if filled: + self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha()) + + ctx.restore() + + # Fast path, if there is no fill, draw everything in one step + if not filled: + self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha()) + def draw_image(self, gc, x, y, im): # bbox - not currently used if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name())) From c52e357b849bf318024bc2f330a9e03522879ce5 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 3 Sep 2013 23:44:07 +0200 Subject: [PATCH 2/2] cairo backend: Flush out mark drawing every 1000 marks. This is done to prevent the path that is passed to cairo to become way too large, as very long paths may impact performance. --- lib/matplotlib/backends/backend_cairo.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index 0a0e78d51bc..81269706ec9 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -173,7 +173,7 @@ def draw_markers(self, gc, marker_path, marker_trans, path, transform, rgbFace=N Affine2D().scale(1.0, -1.0).translate(0, self.height) ctx.new_path() - for vertices, codes in path.iter_segments(transform, simplify=False): + for i, (vertices, codes) in enumerate(path.iter_segments(transform, simplify=False)): if len(vertices): x, y = vertices[-2:] ctx.save() @@ -182,13 +182,15 @@ def draw_markers(self, gc, marker_path, marker_trans, path, transform, rgbFace=N ctx.translate(x, y) ctx.append_path(marker_path) + ctx.restore() + # Slower code path if there is a fill; we need to draw # the fill and stroke for each marker at the same time. - if filled: + # Also flush out the drawing every once in a while to + # prevent the paths from getting way too long. + if filled or i % 1000 == 0: self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha()) - ctx.restore() - # Fast path, if there is no fill, draw everything in one step if not filled: self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha())