Skip to content

Agg alpha image compositor differs from Matplotlib's software image compositor #8847

Closed

Description

@anntzer

On master.

The following test is taken from test_images.test_image_composite_alpha:

from pylab import *

fig = plt.figure()
ax = fig.add_subplot(111)
arr = np.zeros((11, 21, 4))
arr[:, :, 0] = 1
arr[:, :, 3] = np.concatenate((np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))
arr2 = np.zeros((21, 11, 4))
arr2[:, :, 0] = 1
arr2[:, :, 1] = 1
arr2[:, :, 3] = np.concatenate((np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))[:, np.newaxis]
ax.imshow(arr, extent=[1, 2, 5, 0], alpha=0.3)
ax.imshow(arr, extent=[2, 3, 5, 0], alpha=0.6)
ax.imshow(arr, extent=[3, 4, 5, 0])
ax.imshow(arr2, extent=[0, 5, 1, 2])
ax.imshow(arr2, extent=[0, 5, 2, 3], alpha=0.6)
ax.imshow(arr2, extent=[0, 5, 3, 4], alpha=0.3)
ax.set_facecolor((0, 0.5, 0, 1))
ax.set_xlim([0, 5])
ax.set_ylim([5, 0])

show()

By default, Agg produces the following result:
aggcomposed

Matplotlib can also force software composition of overlapping images, which is, I guess, faster for vector backends (only one image ends up written to the file). For Agg, this is disabled; the Agg renderer does the compositing itself. But you can switch Agg back to use software composition with the following patch:

diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py
index c6af41b19..580cf0cf8 100644
--- a/lib/matplotlib/backends/backend_agg.py
+++ b/lib/matplotlib/backends/backend_agg.py
@@ -302,7 +302,7 @@ class RendererAgg(RendererBase):
         # It is generally faster to composite each image directly to
         # the Figure, and there's no file size benefit to compositing
         # with the Agg backend
-        return True
+        return False
 
     def option_scale_image(self):
         """

With the patch on, the result is clearly different.
mplcomposed

I haven't looked much into which one is correct (it should be easy to manually compute the result), although https://github.com/anntzer/mpl_cairo seems to agree visually with Matplotlib, not Agg.

Activity

  1. added this to the milestone on Jul 8, 2017
  2. github-actions commented on Apr 14, 2023

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  3. anntzer commented on Apr 14, 2023

    @anntzer
    ContributorAuthor

    I have verified that the discrepancy is still present.

  4. added
    keepItems to be ignored by the “Stale” Github Action
    on Apr 14, 2023
  5. anntzer commented on Mar 17, 2025

    @anntzer
    ContributorAuthor

    I think I figured part of this out while investigating #29711 (comment) (this only took me 7.5 years...).

    Currently, RGBA blending is implementing using a "fixed" blender (fixed_blender_rgba_plain in agg_workaround.h) forward-ported from an even older version of Agg in #3777, apparently because the upstream version (blender_rgba_plain in agg_pixfmt_rgba.h) has lower precision. But the two blenders don't actually implement the same blending formulas! blender_rgba_plain blends in premultiplied alpha space (before backconverting to straight alpha at the end, see the final call to demultiply() in blend_pix()) whereas fixed_blender_rgba_plain blends in straight rgba space. These blenders are used in two different places (_image_resample.h and _backend_agg.h).

    Note that the software-compositing option provided by matplotlib fundamentally relies on the ability to composite multiple images together before compositing them against the background, rather than compositing them one at a time against the background (see the explanation in _draw_list_compositing_images()), i.e. composition should be associative. But I believe(?) that only composition in premultiplied alpha space is associative, not composition in straight alpha space(?).

    So the fix is likely to switch the blenders to use the formulas of blender_rgba_plain (possibly tweaking them to improve their precision?). We should probably check carefully the consequences of switching _image_resample.h and _backend_agg.h separately...


    Edit: Actually no, looks like the formulas in agg_workaround.h are correct too? But switching the blenders does change some results. Still to be investigated.

  6. QuLogic commented on Jul 23, 2026

    @QuLogic
    Member

    @ayshih Not sure if you've seen this over your blending/compositing mode PR.

  7. ayshih commented on Jul 23, 2026

    @ayshih
    Contributor

    Ah, thanks for putting this issue on my radar! I think I know where to look

  8. removed
    keepItems to be ignored by the “Stale” Github Action
    on Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions