GeezerVison is showing github.com — Fixes Agg/Cairo antialiased rendering of contourf/pcolor/pcolormesh by using a blend group by ayshih · Pull Request #32256 · matplotlib/matplotlib
Conversation
ayshih
commented
Aug 28, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
PR summary
This PR fixes Agg/Cairo antialiased rendering of contourf/pcolor/pcolormesh by using a blend group (see #31162, where these fixes were separated out of). In the examples below, look at the right-most panel and see how the faint lines between antialiased patches are fixed by this PR. The middle panel shows why it is desirable to turn on antialiasing when alpha < 1.
Since these fixes require #31162, this PR cannot be backported to 3.11.x.
contourf
import matplotlib
#matplotlib.use('TkCairo')
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(1, 6)
y = x.reshape(-1, 1)
data = (x * y).astype(float)
data[2, 2] = np.nan
fig, axs = plt.subplots(1, 3, figsize=(5, 2), layout="constrained")
for i, antialiased in enumerate([None, False, True]):
kwargs = {'cmap': 'jet', 'alpha': 0.5}
if antialiased is not None:
kwargs['antialiased'] = antialiased
axs[i].contourf(data, levels=np.arange(1, 25, 1), extend="both", **kwargs)
axs[i].set_aspect("equal")
axs[i].set_axis_off()
axs[i].set_title(f"antialiased={antialiased}" if antialiased is not None else "default")
plt.show()
Before this PR
After this PR
pcolor/pcolormesh
import matplotlib
#matplotlib.use('TkCairo')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
N = 5
data = np.arange(N**2, dtype=float).reshape((N, N))
data[2, 2] = np.nan
x, y = np.meshgrid(np.arange(N + 1), np.arange(N + 1))
rotation = Affine2D().rotate(1)
fig, axs = plt.subplots(2, 3, figsize=(5, 3.5), layout="constrained")
for i, antialiased in enumerate([None, False, True]):
kwargs = {'cmap': 'jet', 'alpha': 0.5}
if antialiased is not None:
kwargs['antialiased'] = antialiased
axs[0, i].pcolormesh(x, y, data, transform=rotation + axs[0, i].transData,
**kwargs)
axs[1, i].pcolor(x, y, data, transform=rotation + axs[1, i].transData, **kwargs)
for j in range(2):
axs[j, i].set_aspect("equal")
axs[j, i].set_axis_off()
axs[0, i].set_title(f"antialiased={antialiased}" if antialiased is not None else "default")
plt.show()
Before this PR
After this PR
AI Disclosure
No AI was used
PR quality check
- Use an expressive title, e.g. "Fix title font property precedence"
- New and changed code is tested
- [N/A] Plotting related features are demonstrated in an example
- New features and API changes have release notes
- [N/A] Documentation complies with general and docstring guidelines
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
rcomer reacted with heart emoji
|
Would it make sense/work to apply this to all Collections?
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Can you explain what exactly is happening and why it's fixed via a plus blend mode? I think that may have gotten buried in the other PR.
My first reaction was "this makes basic sense, but I need to look up the formula for Plus again."
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment about why "plus" was chosen, but there are also existing comments in the source base about this + intermediate buffers.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
The pcolor docstring states
|
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. |
Those artificial lines are now gone, so this explanation no longer makes sense. Should we change the default? The default for pcolormesh and contourfis alsoFalse, but I have not found statements about why for those.
|
iccir reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Those artificial lines are now gone, so this explanation no longer makes sense. Should we change the default? The default for pcolormesh and contourfis alsoFalse, but I have not found statements about why for those.
I'm +1 on "default to the more-correct-looking option", unless there are other concerns.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
ayshih
commented
Aug 31, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Can you explain what exactly is happening and why it's fixed via a plus blend mode?
It's because the alpha at the edge of an antialiased patch represents how much the edge fills the pixel. When two patches are exactly adjacent and should fill the pixel entirely to a resulting alpha_combined, the alphas ought to be added up (alpha_1 + alpha_2 = alpha_combined). However, standard blend modes treat the alphas as unrelated opacities, so the resulting blended alpha is alpha_1 + alpha_2 - alpha_1 * alpha_2, which is less than alpha_combined. That's why the existing code produces lighter lines at patch edges; the alpha is smaller than it should be. So, the "plus" blend mode, which is actually a Porter-Duff compositing operator, is exactly what is needed to get the correct result.
Maybe add a comment about why "plus" was chosen, but there are also existing comments in the source base about this + intermediate buffers.
I've added (fairly wordy) comments in the code, which should help future maintainers.
Would it make sense/work to apply this to all Collections?
No. I believe these are the only collections where the patches are expressly constructed to be abutting with no overlaps. A collection in general could have overlapping patches, and the "plus" blend mode would give a bad result.
Those artificial lines are now gone, so this explanation no longer makes sense. Should we change the default? The default for pcolormesh and contourfis alsoFalse, but I have not found statements about why for those.
I've changed all three so they no longer forcefully disable antialiasing by default, and now the default is simply rcParams["patch.antialiased"].
|
iccir reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
rcomer
commented
Aug 31, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Would it make sense/work to apply this to all Collections?
No. I believe these are the only collections where the patches are expressly constructed to be abutting with no overlaps. A collection in general could have overlapping patches, and the "plus" blend mode would give a bad result.
Is there a way that downstream libraries could take advantage of this functionality for their own collections? For example, Cartopy has a collection that enables drawing choropleth maps.
https://cartopy.readthedocs.io/stable/gallery/scalar_data/geometry_data.html
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
ayshih
commented
Aug 31, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
I'm dropping this PR back to draft in order to revise the approach. I realized that Axes.tricontourf() and Axes.tripcolor() also need these fixes, plus as @rcomer noted, it'd be nice if an external package can indicate that this type of rendering is appropriate for a collection.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This branch has not been deployed
No deployments
Reviewers
iccir
Awaiting requested review from iccir
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|