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.
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
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
.build
(1)
.cpp
(1)
.h
(5)
.json
(1)
.pdf
(5)
.png
(6)
.py
(23)
.pyi
(2)
.rst
(5)
.svg
(3)
.toml
(1)
.txt
(1)
All 12 file types selected
Only manifest files
Viewed files
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
Hide whitespace
Diff view
Hide whitespace
There are no files selected for viewing
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
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
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
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.
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
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
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
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.