Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix gtk3agg alpha channel. #10297

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

Merged
merged 1 commit into from
Jul 1, 2018
Merged

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Jan 23, 2018

The gtk3agg backend works by drawing the ARGB32 buffer (from Agg) onto a
cairo context (passed by gtk3). However, cairo wants a premultiplied
ARGB32 buffer (i.e., where 100% blue with 50% transparency is
represented by (r=0%, g=0%, b=50%, a=50%) instead of (r=0%, g=0%,
b=100%, a=50%), which we didn't do before.

This is only apparent if the entire buffer contains some transparency,
e.g. if the figure background is transparent. Consider e.g. under
gtk3agg:

from pylab import *
rcParams["figure.facecolor"] = (0, 0, 0, 0)
gca()
show()

Without the patch, the area surrounding the axes is white (because of
the misinterpretation of premultiplied alpha). With the patch, it is
(correctly) gray, which is the background color of the gtk widget.
(Note that when running the example under qt5agg or tkagg, the situation
is complicated by the fact that these backends themselves set the widget
background color to white rather than gray.)

As a comparison point, qt5agg builds the QImage using Format_ARGB32,
instead of Format_ARB32_Premultiplied; i.e. Qt provides its own
conversion from non-premultiplied to premultiplied.

The premultiplication step involves allocating a full new buffer, so
check whether there is actually any transparency before doing it.

PR Summary

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@jklymak
Copy link
Member

jklymak commented Jan 23, 2018

Is there a test for this?

@anntzer
Copy link
Contributor Author

anntzer commented Jan 23, 2018

Basically run the example code yourself and observe the background color of the figure as explained above... (not sure it can be automated in any reasonable way)

if sys.byteorder == "little":
alpha = buf[..., -1:]
if alpha.min() != 0xff:
buf = (np.dstack([buf[..., :3] * (alpha / 0xff), alpha])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to make a copy? Isn't buf an independent thing, so can you do an in-place multiply?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was a bit more tricky to write correctly but fixed it

@anntzer anntzer force-pushed the gtk3agg-alpha branch 2 times, most recently from 9d62bb0 to fd9da34 Compare February 6, 2018 09:46
@QuLogic
Copy link
Member

QuLogic commented Feb 8, 2018

When I try out your example, I get an error:

Traceback (most recent call last):
  File "/home/elliott/code/matplotlib/lib/matplotlib/backends/backend_gtk3agg.py", line 76, in on_draw_event
    buf.data, cairo.FORMAT_ARGB32, width, height)
  File "/usr/lib/python3.6/site-packages/cairocffi/surfaces.py", line 660, in create_for_data
    return cls(format, width, height, data, stride)
  File "/usr/lib/python3.6/site-packages/cairocffi/surfaces.py", line 649, in __init__
    % (length, stride * height))
ValueError: Got a 640 bytes buffer, needs at least 1228800.

@anntzer
Copy link
Contributor Author

anntzer commented Feb 8, 2018

Ah, that's because I use pycairo (which actually works with py3 now...) rather than cairocffi. Ravel()ing the buffer before passing it to create_for_data fixes the problem and works for both pycairo and cairocffi, pushed that.

@QuLogic
Copy link
Member

QuLogic commented Feb 17, 2018

which actually works with py3 now...

Should we remove the warning then?

@anntzer
Copy link
Contributor Author

anntzer commented Feb 17, 2018

Yup. Have another PR in the works for that.

@anntzer
Copy link
Contributor Author

anntzer commented Mar 8, 2018

rebased

The gtk3agg backend works by drawing the ARGB32 buffer (from Agg) onto a
cairo context (passed by gtk3).  However, cairo wants a *premultiplied*
ARGB32 buffer (i.e., where 100% blue with 50% transparency is
represented by (r=0%, g=0%, b=50%, a=50%) instead of (r=0%, g=0%,
b=100%, a=50%), which we didn't do before.

This is only apparent if the entire buffer contains some transparency,
e.g. if the figure background is transparent.  Consider e.g. under
gtk3agg:

    from pylab import *
    rcParams["figure.facecolor"] = (0, 0, 0, 0)
    gca()
    show()

Without the patch, the area surrounding the axes is white (because of
the misinterpretation of premultiplied alpha).  With the patch, it is
(correctly) gray, which is the background color of the gtk widget.
(Note that when running the example under qt5agg or tkagg, the situation
is complicated by the fact that these backends themselves set the widget
background color to white rather than gray.)

As a comparison point, qt5agg builds the QImage using Format_ARGB32,
instead of Format_ARB32_Premultiplied; i.e. Qt provides its own
conversion from non-premultiplied to premultiplied.

The premultiplication step involves allocating a full new buffer, so
check whether there is actually any transparency before doing it.
@tacaswell tacaswell added this to the v3.0 milestone Jul 1, 2018
@tacaswell tacaswell merged commit 766f03a into matplotlib:master Jul 1, 2018
@anntzer anntzer deleted the gtk3agg-alpha branch July 1, 2018 05:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants