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

Add blend modes and blend groups for compositing #31162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Sign up for GitHub

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
story645 merged 10 commits into matplotlib:main from ayshih:agg_compositing
Aug 27, 2026
+4,734 −51
Merged

Add blend modes and blend groups for compositing #31162

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
10 commits
Select commit Hold shift + click to select a range
5ad8cab
Add support for specifying the blend mode for an artist
ayshih Feb 15, 2026
9199535
Add Agg code for computing HSL blend modes
ayshih Feb 17, 2026
aebc329
Add support for blend modes in non-PS backends
ayshih May 5, 2026
2ccbbd3
Fix precision loss with Agg compositing
ayshih Feb 17, 2026
22091f7
Add support for Gouraud shading in Cairo-based backends
ayshih Feb 20, 2026
7301134
Fix ability to specify scalar alpha to Agg draw_image
ayshih Mar 4, 2026
ced4c64
Add support for blend groups (aka transparency groups)
ayshih May 5, 2026
fcef182
Add documentation pages and whatsnew entry
ayshih Feb 19, 2026
8c0fee9
Fix Agg rendering of Gouraud shading
ayshih Mar 4, 2026
a848ba4
Add image tests for blend modes and blend groups
ayshih May 6, 2026
File filter
Filter by extension


Clear filters
Conversations
Failed to load comments.
Loading

Uh oh!

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

Jump to
Jump to file
Failed to load files.
Loading

Uh oh!

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

Diff view
Diff view

There are no files selected for viewing

3 changes: 3 additions & 0 deletions 3 doc/api/artist_api.rst
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ Drawing

Artist.set_alpha
Artist.get_alpha
Artist.set_blend_mode
Artist.get_blend_mode
Artist.set_snap
Artist.get_snap
Artist.set_visible
Expand Down Expand Up @@ -211,3 +213,4 @@ Helper classes

ArtistInspector
ArtistList
BlendMode
1 change: 1 addition & 0 deletions 1 doc/api/typing_api.rst
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Color
Artist styles
=============

.. autodata:: matplotlib.typing.BlendModeType
.. autodata:: matplotlib.typing.LineStyleType
.. autodata:: matplotlib.typing.DrawStyleType
.. autodata:: matplotlib.typing.MarkEveryType
Expand Down
6 changes: 3 additions & 3 deletions 6 doc/missing-references.json
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.set_text:1"
],
"matplotlib.axes._base._AxesBase": [
"doc/api/artist_api.rst:213"
"doc/api/artist_api.rst:216"
],
"matplotlib.backend_bases._Backend": [
"lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.ShowBase:1"
Expand All @@ -21,7 +21,7 @@
"lib/matplotlib/backends/backend_tkcairo.py:docstring of matplotlib.backends.backend_tkcairo.FigureCanvasTkCairo:1"
],
"matplotlib.image._ImageBase": [
"doc/api/artist_api.rst:213",
"doc/api/artist_api.rst:216",
"lib/matplotlib/image.py:docstring of matplotlib.image.AxesImage:1",
"lib/matplotlib/image.py:docstring of matplotlib.image.BboxImage:1",
"lib/matplotlib/image.py:docstring of matplotlib.image.FigureImage:1"
Expand Down Expand Up @@ -70,7 +70,7 @@
"lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.MollweideAxes.MollweideTransform:1"
],
"matplotlib.text._AnnotationBase": [
"doc/api/artist_api.rst:213",
"doc/api/artist_api.rst:216",
"lib/matplotlib/offsetbox.py:docstring of matplotlib.offsetbox.AnnotationBbox:1",
"lib/matplotlib/text.py:docstring of matplotlib.text.Annotation:1"
],
Expand Down
13 changes: 13 additions & 0 deletions 13 doc/release/next_whats_new/blend_modes.rst
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Blending and compositing artists
--------------------------------

In addition to normal alpha blending, there are now alternative options for
blending and compositing artists on top of previously drawn artists. The
behavior is controlled by the artist's ``blend_mode`` property. See
:ref:`blend-modes` for a gallery and for a table of supporting backends.

Furthermore, there is support for blend groups, also known as transparency
groups, which can be isolated, knockout, or both. For example, isolated blend
groups allow multiple artists to be rendered together in a separate buffer,
which is subsequently blended into the primary buffer. See
:ref:`blend-groups` for more details and for a table of supporting backends.
98 changes: 98 additions & 0 deletions 98 extern/agg24-svn/include/agg_color_rgba.h
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,104 @@ namespace agg
*this = from_wavelength(wavelen, gamma);
}

#ifdef MPL_ADD_AGG_HSL_BLEND_MODES
// The following functions are used for the non-separable blend modes
// They are near-literal implementations of pseudocode provided in the
// PDF specification (e.g., pages 326-327 of the PDF 1.7 specification,
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf)

double max_rgb() const
{
double max_rg = ((r > g) ? r : g);
return (max_rg > b) ? max_rg : b;
}

double min_rgb() const
{
double min_rg = ((r < g) ? r : g);
return (min_rg < b) ? min_rg : b;
}

double luminosity() const
{
return 0.3*r + 0.59*g + 0.11*b;
Comment thread
story645 marked this conversation as resolved.
Resolved

Uh oh!

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

}

rgba& clip_color()
{
double L = luminosity();
double N = min_rgb();
double X = max_rgb();
Comment thread
story645 marked this conversation as resolved.
Resolved

Uh oh!

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

if (N < 0.)
{
r = L + (((r - L) * L) / (L - N));
g = L + (((g - L) * L) / (L - N));
b = L + (((b - L) * L) / (L - N));
}
if (X > 1.)
{
r = L + (((r - L) * (1 - L)) / (X - L));
g = L + (((g - L) * (1 - L)) / (X - L));
b = L + (((b - L) * (1 - L)) / (X - L));
}
return *this;
}

rgba& set_luminosity(double L)
{
double D = L - luminosity();
r += D;
g += D;
b += D;
return clip_color();
}

double saturation() const
{
return max_rgb() - min_rgb();
}

// Helper method to get pointers to the min/mid/max color channels in that order
std::array<double*, 3> get_min_mid_max_pointers()
{
std::array<double*, 3> out = {&r, &g, &b};

// Do a bubble sort on the three pointers based on the values
if (*out[0] > *out[1])
{
std::swap(out[0], out[1]);
}
if (*out[1] > *out[2])
{
std::swap(out[1], out[2]);

// We need to perform the third check only if the second swap happened
if (*out[0] > *out[1])
{
std::swap(out[0], out[1]);
}
}
return out;
}

rgba& set_saturation(double S)
{
auto [cmin, cmid, cmax] = get_min_mid_max_pointers();
if (*cmax > *cmin)
{
*cmid = ((*cmid - *cmin) * S) / (*cmax - *cmin);
Comment thread
story645 marked this conversation as resolved.
Resolved

Uh oh!

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

*cmax = S;
}
else
{
*cmid = 0;
*cmax = 0;
}
*cmin = 0;
return *this;
}
#endif

};

inline rgba operator+(const rgba& a, const rgba& b)
Expand Down
111 changes: 111 additions & 0 deletions 111 extern/agg24-svn/include/agg_pixfmt_rgba.h
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,102 @@ namespace agg
};
#endif

#ifdef MPL_ADD_AGG_HSL_BLEND_MODES
// These four blend modes are implemented per the PDF specification
Comment thread
ayshih marked this conversation as resolved.
Resolved

Uh oh!

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

// (e.g., pages 327-328 of Section 11.3.5 of the PDF 1.7 specification,
Comment thread
story645 marked this conversation as resolved.
Resolved

Uh oh!

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

// which is formally ISO 32000-1:2008, with a free version available at
// https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf)
Comment thread
ayshih marked this conversation as resolved.
Resolved

Uh oh!

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

// For the code below, the two colors have been renamed: C_s -> s and C_b -> d

//=====================================================comp_op_rgba_hsl_hue
template<class ColorT, class Order>
Comment thread
QuLogic marked this conversation as resolved.
Resolved

Uh oh!

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

struct comp_op_rgba_hsl_hue : blender_base<ColorT, Order>
Comment thread
story645 marked this conversation as resolved.
Resolved

Uh oh!

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

{
typedef ColorT color_type;
typedef typename color_type::value_type value_type;
using blender_base<ColorT, Order>::get;
using blender_base<ColorT, Order>::set;

static AGG_INLINE void blend_pix(value_type* p,
value_type r, value_type g, value_type b, value_type a, cover_type cover)
{
rgba s = get(r, g, b, a, cover).demultiply();
rgba d = get(p).demultiply();
rgba blend = rgba(s);
blend.set_saturation(d.saturation()).set_luminosity(d.luminosity());
rgba comp = s * s.a * (1 - d.a) + blend * s.a * d.a + d * (1 - s.a) * d.a;
comp.a = s.a + d.a - s.a * d.a;
set(p, comp);
}
};

//=====================================================comp_op_rgba_hsl_saturation
template<class ColorT, class Order>
struct comp_op_rgba_hsl_saturation : blender_base<ColorT, Order>
{
typedef ColorT color_type;
typedef typename color_type::value_type value_type;
using blender_base<ColorT, Order>::get;
using blender_base<ColorT, Order>::set;

static AGG_INLINE void blend_pix(value_type* p,
value_type r, value_type g, value_type b, value_type a, cover_type cover)
{
rgba s = get(r, g, b, a, cover).demultiply();
rgba d = get(p).demultiply();
rgba blend = rgba(d);
blend.set_saturation(s.saturation()).set_luminosity(d.luminosity());
rgba comp = s * s.a * (1 - d.a) + blend * s.a * d.a + d * (1 - s.a) * d.a;
comp.a = s.a + d.a - s.a * d.a;
set(p, comp);
}
};

//=====================================================comp_op_rgba_hsl_color
template<class ColorT, class Order>
struct comp_op_rgba_hsl_color : blender_base<ColorT, Order>
{
typedef ColorT color_type;
typedef typename color_type::value_type value_type;
using blender_base<ColorT, Order>::get;
using blender_base<ColorT, Order>::set;

static AGG_INLINE void blend_pix(value_type* p,
value_type r, value_type g, value_type b, value_type a, cover_type cover)
{
rgba s = get(r, g, b, a, cover).demultiply();
rgba d = get(p).demultiply();
rgba blend = rgba(s);
blend.set_luminosity(d.luminosity());
rgba comp = s * s.a * (1 - d.a) + blend * s.a * d.a + d * (1 - s.a) * d.a;
comp.a = s.a + d.a - s.a * d.a;
set(p, comp);
}
};

//=====================================================comp_op_rgba_hsl_luminosity
template<class ColorT, class Order>
struct comp_op_rgba_hsl_luminosity : blender_base<ColorT, Order>
{
typedef ColorT color_type;
typedef typename color_type::value_type value_type;
using blender_base<ColorT, Order>::get;
using blender_base<ColorT, Order>::set;

static AGG_INLINE void blend_pix(value_type* p,
value_type r, value_type g, value_type b, value_type a, cover_type cover)
{
rgba s = get(r, g, b, a, cover).demultiply();
rgba d = get(p).demultiply();
rgba blend = rgba(d);
blend.set_luminosity(s.luminosity());
rgba comp = s * s.a * (1 - d.a) + blend * s.a * d.a + d * (1 - s.a) * d.a;
comp.a = s.a + d.a - s.a * d.a;
set(p, comp);
}
};
#endif


//======================================================comp_op_table_rgba
template<class ColorT, class Order> struct comp_op_table_rgba
Expand Down Expand Up @@ -1207,6 +1303,14 @@ namespace agg
//comp_op_rgba_contrast <ColorT,Order>::blend_pix,
//comp_op_rgba_invert <ColorT,Order>::blend_pix,
//comp_op_rgba_invert_rgb <ColorT,Order>::blend_pix,

#ifdef MPL_ADD_AGG_HSL_BLEND_MODES
comp_op_rgba_hsl_hue <ColorT,Order>::blend_pix,
comp_op_rgba_hsl_saturation <ColorT,Order>::blend_pix,
comp_op_rgba_hsl_color <ColorT,Order>::blend_pix,
comp_op_rgba_hsl_luminosity <ColorT,Order>::blend_pix,
#endif

0
};

Expand Down Expand Up @@ -1243,6 +1347,13 @@ namespace agg
//comp_op_invert, //----comp_op_invert
//comp_op_invert_rgb, //----comp_op_invert_rgb

#ifdef MPL_ADD_AGG_HSL_BLEND_MODES
comp_op_hsl_hue,
comp_op_hsl_saturation,
comp_op_hsl_color,
comp_op_hsl_luminosity,
#endif

end_of_comp_op_e
};

Expand Down
1 change: 1 addition & 0 deletions 1 galleries/users_explain/artists/artist_intro.rst
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ We can interrogate the full list of settable properties with
animated = False
antialiased or aa = True
bbox = Bbox(x0=0.004013842290585101, y0=0.013914221641967...
blend_mode = normal
children = []
clip_box = TransformedBbox( Bbox(x0=0.0, y0=0.0, x1=1.0, ...
clip_on = True
Expand Down
9 changes: 5 additions & 4 deletions 9 galleries/users_explain/colors/GALLERY_HEADER.rst
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
Colors
------

Matplotlib has support for visualizing information with a wide array
of colors and colormaps. These tutorials cover the basics of how
these colormaps look, how you can create your own, and how you can
customize colormaps for your use case.
Matplotlib has support for visualizing information with a wide array of colors
and colormaps. The color tutorials cover the basics of specifying colors, as
well as the range of options for blending the colors of overlapping artists.
The colormap tutorials cover the basics of how colormaps look, how you can
create your own, and how you can customize colormaps for your use case.

For even more information see the :ref:`examples page <color_examples>`.
Loading
Oops, something went wrong.

Uh oh!

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

Loading
Oops, something went wrong.

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.
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.