Fixes Agg/Cairo antialiased rendering of contourf/pcolor/pcolormesh by using a blend group by ayshih · Pull Request #32256 · matplotlib/matplotlib · GitHub
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/api/next_api_changes/behavior/32256-AYS.rst
35 changes: 9 additions & 26 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5534,7 +5534,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
def hexbin(self, x, y, C=None, gridsize=100, bins=None,
xscale='linear', yscale='linear', extent=None,
cmap=None, norm=None, vmin=None, vmax=None,
alpha=None, linewidths=None, edgecolors='face',
alpha=None, linewidths=None, edgecolors='none',
reduce_C_function=np.mean, mincnt=None, marginals=False,
colorizer=None, **kwargs):
"""
Expand Down Expand Up @@ -5654,12 +5654,11 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None,
linewidths : float, default: *None*
If *None*, defaults to :rc:`patch.linewidth`.

edgecolors : {'face', 'none', *None*} or color, default: 'face'
edgecolors : {'face', 'none', *None*} or color, default: 'none'
The color of the hexagon edges. Possible values are:

- 'face': Draw the edges in the same color as the fill color.
- 'none': No edges are drawn. This can sometimes lead to unsightly
unpainted pixels between the hexagons.
- 'none': No edges are drawn.
- *None*: Draw outlines in the default color.
- An explicit color.

Expand Down Expand Up @@ -5833,6 +5832,7 @@ def reduce_C_function(C: array) -> float
offsets=offsets,
offset_transform=mtransforms.AffineDeltaTransform(self.transData)
)
collection._treat_patches_as_contiguous = True

# Set normalizer if bins is 'log'
if cbook._str_equal(bins, 'log'):
Expand Down Expand Up @@ -5913,7 +5913,8 @@ def reduce_C_function(C: array) -> float

trans = getattr(self, f"get_{zname}axis_transform")(which="grid")
bar = mcoll.PolyCollection(
verts, transform=trans, edgecolors="face")
verts, transform=trans, edgecolors="none")
bar._treat_patches_as_contiguous = True
bar.set_array(values)
bar.set_cmap(cmap)
bar.set_norm(norm)
Expand Down Expand Up @@ -6693,15 +6694,6 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,

Other Parameters
----------------
antialiaseds : bool, default: False
The default *antialiaseds* is False if the default
*edgecolors*\ ="none" is used. This eliminates artificial lines
at patch boundaries, and works regardless of the value of alpha.
If *edgecolors* is not "none", then the default *antialiaseds*
is taken from :rc:`patch.antialiased`.
Stroking the edges may be preferred if *alpha* is 1, but will
cause artifacts otherwise.

data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER

Expand Down Expand Up @@ -6761,15 +6753,6 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
kwargs['edgecolors'] = kwargs.pop('edgecolor')
ec = kwargs.setdefault('edgecolors', 'none')

# aa setting will default via collections to patch.antialiased
# unless the boundary is not stroked, in which case the
# default will be False; with unstroked boundaries, aa
# makes artifacts that are often disturbing.
if 'antialiaseds' in kwargs:
kwargs['antialiased'] = kwargs.pop('antialiaseds')
if 'antialiased' not in kwargs and cbook._str_lower_equal(ec, "none"):
kwargs['antialiased'] = False

kwargs.setdefault('snap', False)

if np.ma.isMaskedArray(X) or np.ma.isMaskedArray(Y):
Expand All @@ -6796,7 +6779,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
@_preprocess_data()
@_docstring.interpd
def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
vmax=None, colorizer=None, shading=None, antialiased=False,
vmax=None, colorizer=None, shading=None,
**kwargs):
"""
Create a pseudocolor plot with a non-regular rectangular grid.
Expand Down Expand Up @@ -7007,7 +6990,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
kwargs.setdefault('snap', mpl.rcParams['pcolormesh.snap'])

collection = mcoll.QuadMesh(
coords, antialiased=antialiased, shading=shading,
coords, shading=shading,
array=C, colorizer=colorizer, alpha=alpha, **kwargs)
collection._scale_norm(norm, vmin, vmax)

Expand Down Expand Up @@ -7206,7 +7189,7 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
collection = mcoll.QuadMesh(
coords, array=C,
alpha=alpha, cmap=cmap, norm=norm, colorizer=colorizer,
antialiased=False, edgecolors="none")
edgecolors="none")
self.add_collection(collection, autolim=False)
xl, xr, yb, yt = x.min(), x.max(), y.min(), y.max()
ret = collection
Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ class RendererBase:
* `draw_path_collection`
* `draw_quad_mesh`
"""
_supports_isolated_group_and_plus_blend_mode = False

def __init__(self):
super().__init__()
self._texmanager = None
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class RendererAgg(RendererBase):
The renderer handles all the drawing primitives using a graphics
context instance that controls the colors/styles
"""
_supports_isolated_group_and_plus_blend_mode = True

def __init__(self, width, height, dpi):
super().__init__()
Expand Down
28 changes: 24 additions & 4 deletions lib/matplotlib/backends/backend_cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ def attr(field):


class RendererCairo(RendererBase):
_supports_isolated_group_and_plus_blend_mode = True

def __init__(self, dpi):
self.dpi = dpi
self.gc = GraphicsContextCairo(renderer=self)
Expand Down Expand Up @@ -375,13 +377,31 @@ def close_blend_group(self):
if group_state.blend_mode is not None:
ctx = self.gc.ctx
group = ctx.pop_group()

ctx.save()
self.gc.set_blend_mode(group_state.blend_mode)
ctx.set_source(group)
if group_state.alpha != 1:
ctx.paint_with_alpha(group_state.alpha)

if group_state.blend_mode in {'knockout', 'clear'}:
mask_surface = ctx.get_target().create_similar_image(
cairo.FORMAT_A1, self.width, self.height)
mask_ctx = cairo.Context(mask_surface)
mask_ctx.set_source(group)
mask_ctx.paint()
mask_pattern = cairo.SurfacePattern(mask_surface)

group_surface = ctx.get_target().create_similar_image(
cairo.FORMAT_ARGB32, self.width, self.height)
group_ctx = cairo.Context(group_surface)
group_ctx.set_source(group)
group_ctx.paint_with_alpha(group_state.alpha)

ctx.set_source_surface(group_surface)
ctx.mask(mask_pattern)

else:
ctx.paint()
ctx.set_source(group)
ctx.paint_with_alpha(group_state.alpha)

ctx.restore()


Expand Down
108 changes: 74 additions & 34 deletions lib/matplotlib/collections.py
Loading
Loading