Skip to content

Commit 8fc0ba4

Browse files
committed
Support hatching in cairo backends.
Direct transposition of the mplcairo implementation (except for the lack of proper stacking, which was already present for the alpha state so it's not really "worse"), itself translated from the agg implementation.
1 parent 365c950 commit 8fc0ba4

File tree

1 file changed

+42
-13
lines changed

1 file changed

+42
-13
lines changed

lib/matplotlib/backends/backend_cairo.py

+42-13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
from matplotlib.transforms import Affine2D
3535

3636

37+
def _set_rgba(ctx, color, alpha, forced_alpha):
38+
if len(color) == 3 or forced_alpha:
39+
ctx.set_source_rgba(*color[:3], alpha)
40+
else:
41+
ctx.set_source_rgba(*color)
42+
43+
3744
def _append_path(ctx, path, transform, clip=None):
3845
for points, code in path.iter_segments(
3946
transform, remove_nans=True, clip=clip):
@@ -100,13 +107,11 @@ def set_context(self, ctx):
100107
self.gc.ctx = ctx
101108
self.width, self.height = size
102109

103-
def _fill_and_stroke(self, ctx, fill_c, alpha, alpha_overrides):
110+
@staticmethod
111+
def _fill_and_stroke(ctx, fill_c, alpha, alpha_overrides):
104112
if fill_c is not None:
105113
ctx.save()
106-
if len(fill_c) == 3 or alpha_overrides:
107-
ctx.set_source_rgba(fill_c[0], fill_c[1], fill_c[2], alpha)
108-
else:
109-
ctx.set_source_rgba(fill_c[0], fill_c[1], fill_c[2], fill_c[3])
114+
_set_rgba(ctx, fill_c, alpha, alpha_overrides)
110115
ctx.fill_preserve()
111116
ctx.restore()
112117
ctx.stroke()
@@ -122,8 +127,31 @@ def draw_path(self, gc, path, transform, rgbFace=None):
122127
+ Affine2D().scale(1, -1).translate(0, self.height))
123128
ctx.new_path()
124129
_append_path(ctx, path, transform, clip)
125-
self._fill_and_stroke(
126-
ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha())
130+
if rgbFace is not None:
131+
ctx.save()
132+
_set_rgba(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha())
133+
ctx.fill_preserve()
134+
ctx.restore()
135+
hatch_path = gc.get_hatch_path()
136+
if hatch_path:
137+
dpi = int(self.dpi)
138+
hatch_surface = ctx.get_target().create_similar(
139+
cairo.Content.COLOR_ALPHA, dpi, dpi)
140+
hatch_ctx = cairo.Context(hatch_surface)
141+
_append_path(hatch_ctx, hatch_path,
142+
Affine2D().scale(dpi, -dpi).translate(0, dpi),
143+
None)
144+
hatch_ctx.set_line_width(self.points_to_pixels(gc.get_hatch_linewidth()))
145+
hatch_ctx.set_source_rgba(*gc.get_hatch_color())
146+
hatch_ctx.fill_preserve()
147+
hatch_ctx.stroke()
148+
hatch_pattern = cairo.SurfacePattern(hatch_surface)
149+
hatch_pattern.set_extend(cairo.Extend.REPEAT)
150+
ctx.save()
151+
ctx.set_source(hatch_pattern)
152+
ctx.fill_preserve()
153+
ctx.restore()
154+
ctx.stroke()
127155

128156
def draw_markers(self, gc, marker_path, marker_trans, path, transform,
129157
rgbFace=None):
@@ -267,8 +295,13 @@ def get_text_width_height_descent(self, s, prop, ismath):
267295
def new_gc(self):
268296
# docstring inherited
269297
self.gc.ctx.save()
298+
# FIXME: The following doesn't properly implement a stack-like behavior
299+
# and relies instead on the (non-guaranteed) fact that artists never
300+
# rely on nesting gc states, so directly resetting the attributes (IOW
301+
# a single-level stack) is enough.
270302
self.gc._alpha = 1
271303
self.gc._forced_alpha = False # if True, _alpha overrides A from RGBA
304+
self.gc._hatch = None
272305
return self.gc
273306

274307
def points_to_pixels(self, points):
@@ -298,12 +331,8 @@ def restore(self):
298331

299332
def set_alpha(self, alpha):
300333
super().set_alpha(alpha)
301-
_alpha = self.get_alpha()
302-
rgb = self._rgb
303-
if self.get_forced_alpha():
304-
self.ctx.set_source_rgba(rgb[0], rgb[1], rgb[2], _alpha)
305-
else:
306-
self.ctx.set_source_rgba(rgb[0], rgb[1], rgb[2], rgb[3])
334+
_set_rgba(
335+
self.ctx, self._rgb, self.get_alpha(), self.get_forced_alpha())
307336

308337
def set_antialiased(self, b):
309338
self.ctx.set_antialias(

0 commit comments

Comments
 (0)