Skip to content

Commit 83d2017

Browse files
committed
PDF backend can now do alpha on hatches.
More consistent behavior for edge/fill/hatch alpha vs. entire patch alpha, for both PDF and SVG. PathCollections now respect whole object alpha.
1 parent 9f821e4 commit 83d2017

File tree

16 files changed

+375
-257
lines changed

16 files changed

+375
-257
lines changed

doc/users/next_whats_new/2020-03-24-svg-hatch-alpha.rst

-6
This file was deleted.
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The SVG and PDF backends can now render hatches with transparency
2+
-----------------------------------------------------------------
3+
4+
The SVG and PDF backends now respect the hatch stroke alpha. Useful applications
5+
are, among others, semi-transparent hatches as a subtle way to differentiate
6+
columns in bar plots.

lib/matplotlib/backends/backend_pdf.py

+19-6
Original file line numberDiff line numberDiff line change
@@ -1288,14 +1288,18 @@ def _write_soft_mask_groups(self):
12881288
self.output(*content)
12891289
self.endStream()
12901290

1291-
def hatchPattern(self, hatch_style):
1291+
def hatchPattern(self, hatch_style, forced_alpha):
12921292
# The colors may come in as numpy arrays, which aren't hashable
12931293
if hatch_style is not None:
12941294
edge, face, hatch = hatch_style
12951295
if edge is not None:
12961296
edge = tuple(edge)
1297+
if forced_alpha: # reset alpha if forced
1298+
edge = edge[:3] + (1.0,)
12971299
if face is not None:
12981300
face = tuple(face)
1301+
if forced_alpha: # reset alpha if forced
1302+
face = face[:3] + (1.0,)
12991303
hatch_style = (edge, face, hatch)
13001304

13011305
pattern = self.hatchPatterns.get(hatch_style, None)
@@ -1310,10 +1314,14 @@ def writeHatches(self):
13101314
hatchDict = dict()
13111315
sidelen = 72.0
13121316
for hatch_style, name in self.hatchPatterns.items():
1317+
stroke_rgb, fill_rgb, path = hatch_style
13131318
ob = self.reserveObject('hatch pattern')
13141319
hatchDict[name] = ob
13151320
res = {'Procsets':
13161321
[Name(x) for x in "PDF Text ImageB ImageC ImageI".split()]}
1322+
if stroke_rgb[3] != 1.0:
1323+
res['ExtGState'] = self._extGStateObject
1324+
13171325
self.beginStream(
13181326
ob.id, None,
13191327
{'Type': Name('Pattern'),
@@ -1324,7 +1332,9 @@ def writeHatches(self):
13241332
# Change origin to match Agg at top-left.
13251333
'Matrix': [1, 0, 0, 1, 0, self.height * 72]})
13261334

1327-
stroke_rgb, fill_rgb, path = hatch_style
1335+
if stroke_rgb[3] != 1.0:
1336+
gstate = self.alphaState((stroke_rgb[3], fill_rgb[3]))
1337+
self.output(gstate, Op.setgstate)
13281338
self.output(stroke_rgb[0], stroke_rgb[1], stroke_rgb[2],
13291339
Op.setrgb_stroke)
13301340
if fill_rgb is not None:
@@ -2245,15 +2255,15 @@ def alpha_cmd(self, alpha, forced, effective_alphas):
22452255
name = self.file.alphaState(effective_alphas)
22462256
return [name, Op.setgstate]
22472257

2248-
def hatch_cmd(self, hatch, hatch_color):
2258+
def hatch_cmd(self, hatch, hatch_color, forced_alpha):
22492259
if not hatch:
22502260
if self._fillcolor is not None:
22512261
return self.fillcolor_cmd(self._fillcolor)
22522262
else:
22532263
return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
22542264
else:
22552265
hatch_style = (hatch_color, self._fillcolor, hatch)
2256-
name = self.file.hatchPattern(hatch_style)
2266+
name = self.file.hatchPattern(hatch_style, forced_alpha)
22572267
return [Name('Pattern'), Op.setcolorspace_nonstroke,
22582268
name, Op.setcolor_nonstroke]
22592269

@@ -2311,13 +2321,15 @@ def clip_cmd(self, cliprect, clippath):
23112321
(('_cliprect', '_clippath'), clip_cmd),
23122322
(('_alpha', '_forced_alpha', '_effective_alphas'), alpha_cmd),
23132323
(('_capstyle',), capstyle_cmd),
2324+
# If you change the next line also fix the check in delta
23142325
(('_fillcolor',), fillcolor_cmd),
23152326
(('_joinstyle',), joinstyle_cmd),
23162327
(('_linewidth',), linewidth_cmd),
23172328
(('_dashes',), dash_cmd),
23182329
(('_rgb',), rgb_cmd),
23192330
# must come after fillcolor and rgb
2320-
(('_hatch', '_hatch_color'), hatch_cmd),
2331+
# If you change the next line also fix the check in delta
2332+
(('_hatch', '_hatch_color', '_forced_alpha'), hatch_cmd),
23212333
)
23222334

23232335
def delta(self, other):
@@ -2346,7 +2358,8 @@ def delta(self, other):
23462358
break
23472359

23482360
# Need to update hatching if we also updated fillcolor
2349-
if params == ('_hatch', '_hatch_color') and fill_performed:
2361+
if (params == ('_hatch', '_hatch_color', '_forced_alpha')
2362+
and fill_performed):
23502363
different = True
23512364

23522365
if different:

lib/matplotlib/backends/backend_svg.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -350,12 +350,17 @@ def _get_hatch(self, gc, rgbFace):
350350
"""
351351
Create a new hatch pattern
352352
"""
353+
forced_alpha = gc.get_forced_alpha()
353354
if rgbFace is not None:
354355
rgbFace = tuple(rgbFace)
356+
if forced_alpha: # reset alpha if forced
357+
rgbFace = rgbFace[:3] + (1.0,)
355358
edge = gc.get_hatch_color()
356359
if edge is not None:
357360
edge = tuple(edge)
358-
dictkey = (gc.get_hatch(), rgbFace, edge)
361+
if forced_alpha: # reset alpha if forced
362+
edge = edge[:3] + (1.0,)
363+
dictkey = (gc.get_hatch(), rgbFace, edge, gc.get_forced_alpha())
359364
oid = self._hatchd.get(dictkey)
360365
if oid is None:
361366
oid = self._make_id('h', dictkey)
@@ -398,8 +403,8 @@ def _write_hatches(self):
398403
'stroke-linecap': 'butt',
399404
'stroke-linejoin': 'miter'
400405
}
401-
if stroke[3] < 1:
402-
hatch_style['stroke-opacity'] = str(stroke[3])
406+
if stroke[3] != 1.0:
407+
hatch_style['stroke-opacity'] = short_float_fmt(stroke[3])
403408
writer.element(
404409
'path',
405410
d=path_data,

lib/matplotlib/collections.py

+1
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ def draw(self, renderer):
299299
gc = renderer.new_gc()
300300
self._set_gc_clip(gc)
301301
gc.set_snap(self.get_snap())
302+
gc.set_alpha(self._alpha)
302303

303304
if self._hatch:
304305
gc.set_hatch(self._hatch)
Binary file not shown.

lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.svg

+40-18
Loading
Binary file not shown.

0 commit comments

Comments
 (0)