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

Skip to content

Commit 04e22f3

Browse files
committed
Fix gtk3agg alpha channel.
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.
1 parent a3d1c46 commit 04e22f3

File tree

1 file changed

+20
-9
lines changed

1 file changed

+20
-9
lines changed

lib/matplotlib/backends/backend_gtk3agg.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
import six
55

6-
import numpy as np
6+
import sys
77
import warnings
88

9+
import numpy as np
10+
911
from . import backend_agg, backend_gtk3
1012
from .backend_cairo import cairo, HAS_CAIRO_CFFI
1113
from .backend_gtk3 import _BackendGTK3
@@ -48,20 +50,29 @@ def on_draw_event(self, widget, ctx):
4850
incref=True)
4951

5052
for bbox in bbox_queue:
51-
area = self.copy_from_bbox(bbox)
52-
buf = np.fromstring(area.to_string_argb(), dtype='uint8')
53-
5453
x = int(bbox.x0)
5554
y = h - int(bbox.y1)
5655
width = int(bbox.x1) - int(bbox.x0)
5756
height = int(bbox.y1) - int(bbox.y0)
5857

59-
if HAS_CAIRO_CFFI:
60-
image = cairo.ImageSurface.create_for_data(
61-
buf.data, cairo.FORMAT_ARGB32, width, height)
58+
buf = (np.fromstring(self.copy_from_bbox(bbox).to_string_argb(),
59+
dtype='uint8')
60+
.reshape((width, height, 4)))
61+
# cairo wants premultiplied alpha. Only bother doing the
62+
# conversion when the alpha channel is not fully opaque, as the
63+
# cost is not negligible. (The unsafe cast is needed to do the
64+
# multiplication in-place in an integer buffer.)
65+
if sys.byteorder == "little":
66+
rgb24 = buf[..., :-1]
67+
alpha8 = buf[..., -1:]
6268
else:
63-
image = cairo.ImageSurface.create_for_data(
64-
buf, cairo.FORMAT_ARGB32, width, height)
69+
alpha8 = buf[..., :1]
70+
rgb24 = buf[..., 1:]
71+
if alpha8.min() != 0xff:
72+
np.multiply(rgb24, alpha8 / 0xff, out=rgb24, casting="unsafe")
73+
74+
image = cairo.ImageSurface.create_for_data(
75+
buf.ravel().data, cairo.FORMAT_ARGB32, width, height)
6576
ctx.set_source_surface(image, x, y)
6677
ctx.paint()
6778

0 commit comments

Comments
 (0)