GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add blend modes and blend groups for compositing by ayshih · Pull Request #31162 · matplotlib/matplotlib

Conversation

@ayshih

@ayshih ayshih commented Feb 15, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

PR summary

This PR adds support for blend modes beyond alpha blending (e.g., "screen" or "hard light"), so closes #6210. With this PR, all artists can specify blend_mode, and they are supported by Agg-based and Cairo-based backends, and mostly supported by SVG/PDF/PGF backends.

Of course, mplcairo provides access to these blend modes, but this PR provides blend-mode support without needing cairo.

Update: This PR uses this functionality to fix a long-standing bug (e.g., fixes #27016) with Agg rendering of Gouraud shading, where the edges of triangles would become visible when transparency is involved.

Update: This PR adds support for blend groups, which can be isolated, knockout, or both.


✅ = supported, 🟡 = supported through rasterization, ❌ = not supported

Blend modes Agg Cairo SVG PDF PGF PS
normal ✅ ✅ ✅ ✅ ✅ ✅
multiply, screen, overlay, darken, lighten,
color dodge, color burn, hard light, soft light,
difference, exclusion
✅ ✅ ✅ ✅ ✅ 🟡
hue, saturation, color, luminosity ✅ ✅* ✅ ✅ ✅ 🟡
knockout, erase, clear, atop, xor, plus ✅ ✅ 🟡 🟡 🟡 🟡
  • "normal" is the normal alpha blending (also known as "over" or "source over")
  • "multiply" through "exclusion" are separable blend modes (color channels are independent)
  • "hue" through "luminosity" are non-separable blend modes
  • "knockout" through "plus" are Porter Duff compositing operators, where "knockout" is also known as "source" and "erase" is also known as "destination out"
  • * Text artists disappear on some combinations of Cairo version and platform, likely a Cairo bug
Blend groups Agg Cairo SVG PDF PGF PS
neither isolated nor knockout (same as no group) ✅ ✅ ✅ ✅ ✅ ✅
isolated only ✅ ✅ ✅ ✅ ✅ 🟡
isolated and knockout ✅ ✅ 🟡 ✅ ✅ 🟡
knockout only ❌ ❌ ❌ ✅ ✅ ❌

Agg showcase

  • Gouraud shading can look wrong with some blend modes, for the same reason it can look wrong under normal blend mode when alpha < 1, due to overlapping triangles Now fixed
agg

Cairo showcase

  • With some combinations of Cairo version and platform, text is missing in non-separable blend modes, which is presumably a bug in Cairo
  • Gouraud shading is apparently not supported by the Cairo backend, so I commented out the pcolormesh call I added support for Gouraud shading

Windows

cairo

macOS

blend_modes_cairo_macos

SVG showcase

  • These results may not render as intended depending on the SVG renderer (try non-mobile web browsers)
  • The Porter Duff compositing operators normally use a different mechanism to command the renderer, which is inaccessible through SVG XML, so are currently disabled (and fall back to "normal" with a warning)
Figure_1

PDF showcase

  • I haven't figured out how to implement the Porter Duff compositing operators

Figure_1.pdf

PGF showcase

  • The PGF and PGF->PDF output looks fine, but the PGF->PNG output via pdftocairo (on Windows) appears to screw up some colors for the non-separable blend modes
  • Gouraud shading is apparently not supported by the PGF backend, so I commented out the pcolormesh call
  • No Porter Duff compositing operators yet again

Figure_1.pgf.pdf

Generating code

import matplotlib
#matplotlib.use('TkCairo')

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle

N = 10
data = np.arange(N**2).reshape((N, N)) % (N-1)

fig, axs = plt.subplots(3, 8, figsize=(10, 5.5), layout="tight")
axs = axs.flatten()
fig.set_facecolor("none")

blend_modes = ["normal", "multiply", "screen", "overlay",
               "darken", "lighten", "color dodge", "color burn",
               "hard light", "soft light", "difference", "exclusion",
               "hue", "saturation", "color", "luminosity",
               "knockout", "erase", "clear", "atop", "xor", "plus"]

for ax in axs:
    ax.set_facecolor("none")
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1.2)
    ax.set_axis_off()

for i, blend_mode in enumerate(blend_modes):
    axs[i].imshow(data, cmap='Reds', alpha=0.75, extent=(0, 0.8, 0, 0.8))
    axs[i].imshow(data[::-1, :], cmap='Blues', alpha=0.75, extent=(0.2, 1, 0.4, 1.2),
                  blend_mode=blend_mode)
    axs[i].pcolormesh(*np.meshgrid(np.linspace(0.6, 0.9, 5), np.linspace(0.7, 1, 5)),
                      data[:5, :5], cmap='Spectral', alpha=0.75, shading='gouraud',
                      blend_mode=blend_mode)
    axs[i].text(0.05, 0.15, "Horizontal", weight="bold", color="c",
                blend_mode=blend_mode)
    axs[i].text(0.35, 0.10, "Tilted", weight="bold", color="m", rotation=45,
                blend_mode=blend_mode)
    axs[i].plot([0.1, 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.2], [0.7, 0.8, 0.9, 1, 0.7, 0.8, 0.9, 1],
                'p', markersize=15, markeredgecolor="orange", markerfacecolor="purple", alpha=0.75,
                blend_mode=blend_mode)
    axs[i].plot([0, 1], [1.2, 0], color="y",
                blend_mode=blend_mode)
    circ = Circle((.65, 0.5), .3, facecolor='g', alpha=0.5,
                  blend_mode=blend_mode, zorder=2)
    axs[i].add_artist(circ)

    rect = Rectangle((0, 1.2), 1, .3, facecolor='lightgray', clip_on=False)
    axs[i].add_artist(rect)
    axs[i].set_title(blend_mode)

plt.show()

Put off to future work:

  • Change the way pcolormesh.snap behaves when the mesh edges are not horizontal/vertical
  • Change the default antialiasing behavior of contourf()/pcolor()/pcolormesh() to be True
  • Agg: investigate alpha edge around Gouraud shading

PR checklist

  • "closes #0000" is in the body of the PR description to link the related issue
  • new and changed code is tested
  • Plotting related features are demonstrated in an example
  • New Features and API Changes are noted with a directive and release note
  • Documentation complies with general and docstring guidelines

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

story645, yuzie007, and daniilS reacted with heart emoji
All reactions
  • ❤️ 3 reactions
@github-actions github-actions Bot added topic: text backend: agg topic: images topic: collections and mappables topic: canvas and figure manager labels Feb 15, 2026
@ayshih
ayshih force-pushed the agg_compositing branch from dee65fa to 37b0b9c Compare February 15, 2026 19:14
@ayshih ayshih changed the title WIP: Add blend modes for compositing, supported by Agg backend WIP: Add blend modes for compositing, supported by Agg-based backends Feb 15, 2026
@github-actions github-actions Bot added topic: mpl_toolkit Documentation: API files in lib/ and doc/api labels Feb 15, 2026
@timhoffm

timhoffm commented Feb 15, 2026

Copy link
Copy Markdown
Member

This looks interesting. Thanks for working on it.

Since I’m not into the topic I can dare to ask the stupid questions:

  • Is it correct that an Artist and its blend mode define completely how they blend with “the background” I.e. all previously drawn artists? In particular, this does not depend on the blend mode of the other artists.
  • Are all these blend modes parameter-less?
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih

ayshih commented Feb 15, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author
  • Is it correct that an Artist and its blend mode define completely how they blend with “the background” I.e. all previously drawn artists? In particular, this does not depend on the blend mode of the other artists.

Yup, that is correct: the history of how that "background" was constructed has no bearing on how the next Artist is blended in using its specific blend mode.

It's also important to remember that that the "empty" background of an Axes is solid white, and thus not actually empty as far as these blend modes are concerned. For example, using "screen" to blend in an image on a truly empty background will just return the image, but on a white background will return solid white, which can make it look instead like the image call failed. That's why I turn off the face colors in my example above.

What I still need to investigate is how my changes interact with collections of Artists. A user may want "over" blending (the default) within the collection before using a different blend mode for the collection as a whole.

  • Are all these blend modes parameter-less?

Yes. In principle, the transformation functions for the hue/saturation/color/luminosity operators could have more than one possibility, but in practice I think everyone has simply used the same functions for decades (as defined in the PDF specification).

timhoffm reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih
ayshih force-pushed the agg_compositing branch from 37b0b9c to bb554e7 Compare February 16, 2026 00:14
@github-actions github-actions Bot added the backend: svg label Feb 16, 2026
@ayshih
ayshih force-pushed the agg_compositing branch 2 times, most recently from 2221d69 to 6d49bcf Compare February 16, 2026 04:43
story645
story645 reviewed Feb 16, 2026
View reviewed changes
Comment thread lib/matplotlib/artist.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@anntzer

anntzer commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

This is pretty cool :-)

It's also important to remember that that the "empty" background of an Axes is solid white, and thus not actually empty as far as these blend modes are concerned. For example, using "screen" to blend in an image on a truly empty background will just return the image, but on a white background will return solid white, which can make it look instead like the image call failed. That's why I turn off the face colors in my example above.

What I still need to investigate is how my changes interact with collections of Artists. A user may want "over" blending (the default) within the collection before using a different blend mode for the collection as a whole.

Actually I suspect that another possibility is to want some nonstandard blending between multiple artists, then "over" blending of the result over the background.

In general I suspect this would be related to adding support for temporary, intermediate rendering buffers, which is also something that would be useful for other purposes e.g. contour label overplotting (#26971 (comment)).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih

ayshih commented Feb 16, 2026

Copy link
Copy Markdown
Contributor Author

By the way, I decided to rename "over" to "normal". That mode of blending is referred to as "normal" often enough, and it makes it readily apparent to users that it is the standard choice (and the default).

timhoffm reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih
ayshih force-pushed the agg_compositing branch from 6d49bcf to 63f53c9 Compare February 16, 2026 23:57
@github-actions github-actions Bot added the backend: pdf label Feb 16, 2026
@ayshih
ayshih force-pushed the agg_compositing branch from 63f53c9 to c9552d5 Compare February 17, 2026 00:24
@github-actions github-actions Bot added the backend: cairo label Feb 17, 2026
@ayshih
ayshih force-pushed the agg_compositing branch 4 times, most recently from f49e8d0 to f7af1e4 Compare February 17, 2026 14:15
@ayshih ayshih changed the title WIP: Add blend modes for compositing, supported by Agg-based backends WIP: Add blend modes for compositing, fully supported by Agg backend and mostly supported by others Feb 17, 2026
@ayshih
ayshih force-pushed the agg_compositing branch from f7af1e4 to d313054 Compare February 17, 2026 16:00
@ayshih ayshih changed the title WIP: Add blend modes for compositing, fully supported by Agg backend and mostly supported by others WIP: Add blend modes for compositing, fully supported by Agg backends and mostly supported by other major backends Feb 17, 2026
@ayshih
ayshih force-pushed the agg_compositing branch from d04f663 to c287d35 Compare February 18, 2026 03:52
@ayshih

ayshih commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

I have moved the fixes for contourf and pcolor/pcolormesh out of this PR to make this PR slightly more wieldy to review. The bugs are less apparent to users because it requires antialiasing to be turned on, which is not the default. I'll PR those fixes after this PR is merged.

I have retained the fix to Agg Gouraud shading because the bug is easier to be encountered by users and would be immediately seen in the blend-mode gallery.

story645 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

QuLogic
QuLogic reviewed Aug 13, 2026
View reviewed changes
Comment thread lib/matplotlib/artist.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/artist.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/_backend_agg.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/backends/backend_agg.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

QuLogic
QuLogic reviewed Aug 25, 2026
View reviewed changes
Comment thread lib/matplotlib/tests/test_backends_rendering.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@QuLogic

QuLogic commented Aug 25, 2026

Copy link
Copy Markdown
Member

Also, do you want to squash merge this when complete?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih

ayshih commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Also, do you want to squash merge this when complete?

This PR feels too hefty to be a single commit. Once the PR is approved, my inclination is to squash the review-triggered commits back into the first ~10 commits in the appropriate places.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

QuLogic
QuLogic approved these changes Aug 25, 2026
View reviewed changes
story645
story645 reviewed Aug 25, 2026
View reviewed changes
Comment thread galleries/users_explain/colors/blend_modes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

story645
story645 reviewed Aug 25, 2026
View reviewed changes
Comment thread galleries/users_explain/colors/blend_modes.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

story645
story645 reviewed Aug 25, 2026
View reviewed changes
Comment thread extern/agg24-svn/include/agg_pixfmt_rgba.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

story645
story645 approved these changes Aug 25, 2026
View reviewed changes

@story645 story645 left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

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.

Minor clarification on knockout and a bit confused on blend modes but otherwise this is awesome and thanks for your patience. Also sorry for the delay in getting back to you.

Also you might wanna make a blending mode :mpl_type:

matplotlib/lib/matplotlib/sphinxext/roles.py

Line 129 in e519c44

def _mpltype_role(name, rawtext, text, lineno, inliner, options=None, content=None):

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
story645
story645 reviewed Aug 25, 2026
View reviewed changes
Comment thread galleries/users_explain/colors/blend_groups.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

ayshih added 10 commits August 26, 2026 10:56
@ayshih
Add support for specifying the blend mode for an artist
5ad8cab
@ayshih
Add Agg code for computing HSL blend modes
9199535
@ayshih
Add support for blend modes in non-PS backends
aebc329
@ayshih
Fix precision loss with Agg compositing
2ccbbd3
@ayshih
Add support for Gouraud shading in Cairo-based backends
22091f7
@ayshih
Fix ability to specify scalar alpha to Agg draw_image
7301134
@ayshih
Add support for blend groups (aka transparency groups)
ced4c64
@ayshih
Add documentation pages and whatsnew entry
fcef182
@ayshih
Fix Agg rendering of Gouraud shading
8c0fee9
@ayshih
Add image tests for blend modes and blend groups
a848ba4
@ayshih

ayshih commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Also you might wanna make a blending mode :mpl_type:

Done!

This PR feels too hefty to be a single commit. Once the PR is approved, my inclination is to squash the review-triggered commits back into the first ~10 commits in the appropriate places.

I have now done this, so this PR is ready for (non-squashed) merging.

iccir reacted with hooray emoji
All reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jklymak
jklymak reviewed Aug 26, 2026
View reviewed changes
Comment thread galleries/users_explain/colors/blend_groups.py
Blending and compositing groups of artists
==========================================

An advanced technique of blending artists (see :ref:`blend-modes`) is to use a

@jklymak jklymak Aug 26, 2026

Copy link
Copy Markdown
Member

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.

Just a suggestion, but since blend groups is an advanced topic depending on blend modes, I'd suggest making this a subsection of an overall "blend modes and groups" page with two subsections.

  • Artist blending and compositing:
    • per artist blending and compositing
    • blend groups to create layers of composition.

I'm not sure a reader who doesn't know these terms would know which of these to drill down into.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@story645 story645 Aug 26, 2026

Copy link
Copy Markdown
Member

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.

I think it's fine as is b/c @ayshih structured it so the blend_modes page comes first in the order.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@story645

story645 commented Aug 27, 2026

Copy link
Copy Markdown
Member

Congrats @ayshih!

Appveyor and wasm failures are unrelated. Discussed on call and decided that doc changes can happen in follow ups as there are arguments for both approaches.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih

ayshih commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews and merge!

anntzer and scottshambaugh reacted with hooray emoji
All reactions
  • 🎉 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ayshih ayshih mentioned this pull request Aug 28, 2026
Fixes Agg/Cairo antialiased rendering of contourf/pcolor/pcolormesh by using a blend group #32256
Draft
3 tasks done
This was referenced Sep 9, 2026
Fix bug with an isolated blend group in Cairo with certain group blend modes #32326
Merged
Fix the unintended blending of fill/hatch/stroke when drawing patches #32343
Merged
@greglucas greglucas mentioned this pull request Sep 14, 2026
Implement Figure-level overlay architecture with two-pass drawing #32199
Open
1 of 5 tasks
@ayshih ayshih mentioned this pull request Oct 16, 2026
ci: Test running on Ubuntu 26.04 #32478
Draft
2 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@tacaswell tacaswell tacaswell left review comments
@iccir iccir iccir left review comments
@story645 story645 story645 approved these changes
@anntzer anntzer anntzer left review comments
@jklymak jklymak jklymak left review comments
@QuLogic QuLogic QuLogic approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

backend: agg backend: cairo backend: pdf backend: pgf backend: ps backend: svg CI: Run cibuildwheel Run wheel building tests on a PR Documentation: API files in lib/ and doc/api Documentation: user guide files in galleries/users_explain or doc/users topic: canvas and figure manager topic: collections and mappables topic: images topic: mpl_toolkit topic: sphinx extension topic: text

Projects

None yet

Milestone

v3.12.0

Development

Successfully merging this pull request may close these issues.

[Bug]: diagonal lines in pcolormesh with Gouraud shading and transparency Alternative compositing methods

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@ayshih @timhoffm @anntzer @story645 @tacaswell @QuLogic @iccir @jklymak
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.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.