GeezerVison is showing github.com — Fix the unintended blending of fill/hatch/stroke when drawing patches by ayshih · Pull Request #32343 · matplotlib/matplotlib
Conversation
ayshih
commented
Sep 12, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
PR summary
This PR fixes a bug in the Agg/Cairo/PGF backends where applying a non-"normal" blend mode to a patch unintentionally resulted in blending the fill/hatch/stroke with each other using that same blend mode. Instead, the correct behavior – which the PDF/SVG backends already do – is for the fill/hatch/stroke to always be blended with each other using the "normal" blend mode, and then the result is blended with the backdrop using the non-"normal" blend mode. This PR makes the Agg/Cairo/PGF backends match the PDF/SVG backends.
The changes to the Cairo/PGF backends are straightforward because we can use open_blend_group()/close_blend_group() to create an isolated blend group for the fill/hatch/stroke. The blend group is used only if an non-"normal" blend mode is specified and if more than one of fill/hatch/stroke is involved. (If there is only one of fill/hatch/stroke, no unintended blending can happen.) The changes to the Agg backend are a little trickier since the drawing function is at a deeper code layer than those renderer methods, so we have to mimic an isolated blend group by manually swapping a fresh buffer in and out of the C++ renderer.
Since this PR is related to blend modes (#31162), it is not to be backported.
In the plots below, the first row is "normal" blend mode, and the second row is "multiply" blend mode. Note how in the "before this PR" image, the part of the stroke that overlaps the fill is being "multiply" blended instead of "normal" blended, and the star hatching plus fill is a different color even over the white backdrop because the stars are getting "multiply" blended with the fill.
Before this PR
After this PR
Generating code
import matplotlib
#matplotlib.use('TkCairo')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
data = np.arange(35).reshape((7, 5)) % 7
fc_list = ['none', 'none', 'none', 'none', 'm', 'm', 'm', 'm']
ec_list = ['none', 'c', 'none', 'c', 'none', 'c', 'none', 'c']
lw_list = [0, 5, 0, 5, 0, 5, 0, 5]
hatch_list = [None, None, '*', '*', None, None, '*', '*']
hatchcolor_list = [None, None, 'y', 'y', None, None, 'y', 'y']
fig, axs = plt.subplots(2, 8, figsize=(8, 2), layout='constrained')
for i, blend_mode in enumerate(['normal', 'multiply']):
for ax, fc, ec, lw, hatch, hatchcolor in zip(axs[i, :], fc_list, ec_list, lw_list,
hatch_list, hatchcolor_list):
ax.imshow(data, cmap='jet')
circle = Circle((3, 3), 3, fc=fc, ec=ec, lw=lw,
hatch=hatch, hatchcolor=hatchcolor, blend_mode=blend_mode)
ax.add_patch(circle)
ax.autoscale_view()
ax.set_axis_off()
plt.show()
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
- [N/A] 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.
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.
Approved. I'm a bit uneasy with the PGF format/backend, so others may want to examine that in more detail.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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.
Since you are now specifying stroke and fill in every call, should the default values be removed? I find the defaults of True/False to be a bit confusing.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
ayshih
Sep 14, 2026
•
edited
Loading
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.
Ah, good idea! I did in fact get tripped up by those weird defaults when writing this PR. I have now made them keyword-only with no defaults.
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.
@ayshih can self-merge when CI is green.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Reviewers
iccir
iccir approved these changes
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.
|