Skip to content

Commit aa583e4

Browse files
committed
Fixed the webagg backend to handle figures with transparency.
1 parent 1b96117 commit aa583e4

File tree

4 files changed

+32
-38
lines changed

4 files changed

+32
-38
lines changed

lib/matplotlib/backends/backend_nbagg.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
"""Interactive figures in the IPython notebook"""
2+
# Note: There is a notebook in
3+
# lib/matplotlib/backends/web_backend/nbagg_uat.ipynb to help verify
4+
# that changes made maintain expected behaviour.
5+
26
from base64 import b64encode
37
from contextlib import contextmanager
48
import json
@@ -33,8 +37,8 @@ def __call__(self, block=None):
3337
for manager in managers:
3438
manager.show()
3539

36-
if not is_interactive() and manager in Gcf._activeQue:
37-
Gcf._activeQue.remove(manager)
40+
if not is_interactive() and manager in Gcf._activeQue:
41+
Gcf._activeQue.remove(manager)
3842

3943

4044
show = Show()
@@ -189,19 +193,6 @@ def start_event_loop(self, timeout):
189193
def stop_event_loop(self):
190194
FigureCanvasBase.stop_event_loop_default(self)
191195

192-
def draw(self):
193-
renderer = self.get_renderer()
194-
195-
self._png_is_old = True
196-
197-
backend_agg.RendererAgg.lock.acquire()
198-
try:
199-
self.figure.draw(renderer)
200-
finally:
201-
backend_agg.RendererAgg.lock.release()
202-
# Swap the frames
203-
self.manager.refresh_all()
204-
205196

206197
def new_figure_manager(num, *args, **kwargs):
207198
"""

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def show(self):
7171
show()
7272

7373
def draw(self):
74-
renderer = self.get_renderer()
74+
renderer = self.get_renderer(cleared=True)
7575

7676
self._png_is_old = True
7777

@@ -91,21 +91,25 @@ def get_diff_image(self):
9191
# The buffer is created as type uint32 so that entire
9292
# pixels can be compared in one numpy call, rather than
9393
# needing to compare each plane separately.
94-
buff = np.frombuffer(
95-
self.get_renderer().buffer_rgba(), dtype=np.uint32)
96-
buff.shape = (
97-
self._renderer.height, self._renderer.width)
94+
renderer = self.get_renderer()
95+
buff = np.frombuffer(renderer.buffer_rgba(), dtype=np.uint32)
9896

99-
if not self._force_full:
100-
last_buffer = np.frombuffer(
101-
self._last_renderer.buffer_rgba(), dtype=np.uint32)
102-
last_buffer.shape = (
103-
self._renderer.height, self._renderer.width)
97+
buff.shape = (renderer.height, renderer.width)
98+
99+
# If any pixels have transparency, we need to force a full draw
100+
# as we cannot overlay new on top of old.
101+
pixels = buff.view(dtype=np.uint8).reshape(buff.shape + (4,))
102+
some_transparency = np.any(pixels[:, :, 3] != 255)
103+
104+
output = buff
105+
106+
if not self._force_full and not some_transparency:
107+
last_buffer = np.frombuffer(self._last_renderer.buffer_rgba(),
108+
dtype=np.uint32)
109+
last_buffer.shape = (renderer.height, renderer.width)
104110

105111
diff = buff != last_buffer
106112
output = np.where(diff, buff, 0)
107-
else:
108-
output = buff
109113

110114
# Clear out the PNG data buffer rather than recreating it
111115
# each time. This reduces the number of memory
@@ -122,7 +126,7 @@ def get_diff_image(self):
122126

123127
# Swap the renderer frames
124128
self._renderer, self._last_renderer = (
125-
self._last_renderer, self._renderer)
129+
self._last_renderer, renderer)
126130
self._force_full = False
127131
self._png_is_old = False
128132
return self._png_buffer.getvalue()
@@ -147,6 +151,9 @@ def get_renderer(self, cleared=None):
147151
w, h, self.figure.dpi)
148152
self._lastKey = key
149153

154+
elif cleared:
155+
self._renderer.clear()
156+
150157
return self._renderer
151158

152159
def handle_event(self, event):

lib/matplotlib/backends/web_backend/mpl.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ mpl.figure = function(figure_id, websocket, ondownload, parent_element) {
6060
}
6161

6262
this.imageObj.onload = function() {
63+
fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);
6364
fig.context.drawImage(fig.imageObj, 0, 0);
6465
fig.waiting = false;
6566
};
@@ -322,6 +323,7 @@ mpl.figure.prototype._make_on_message_function = function(fig) {
322323
(window.URL || window.webkitURL).revokeObjectURL(
323324
fig.imageObj.src);
324325
}
326+
325327
fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(
326328
evt.data);
327329
fig.updated_canvas_event();

lib/matplotlib/backends/web_backend/nbagg_uat.ipynb

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"metadata": {
33
"name": "",
4-
"signature": "sha256:34c3a5a9d82d5a3c433f0c03f0717d43e1d14eaf78f88a64f094d104fa636ddc"
4+
"signature": "sha256:1d491e506b54b126f6971897d3249a9e6f96f9d50bf4e4ba8d179c6b7b1aefa8"
55
},
66
"nbformat": 3,
77
"nbformat_minor": 0,
@@ -24,7 +24,6 @@
2424
"import matplotlib\n",
2525
"reload(matplotlib)\n",
2626
"\n",
27-
"\n",
2827
"matplotlib.use('nbagg')\n",
2928
"\n",
3029
"import matplotlib.backends.backend_nbagg\n",
@@ -47,6 +46,9 @@
4746
"cell_type": "code",
4847
"collapsed": false,
4948
"input": [
49+
"import matplotlib.backends.backend_webagg_core\n",
50+
"reload(matplotlib.backends.backend_webagg_core)\n",
51+
"\n",
5052
"import matplotlib.pyplot as plt\n",
5153
"plt.interactive(False)\n",
5254
"\n",
@@ -382,14 +384,6 @@
382384
"language": "python",
383385
"metadata": {},
384386
"outputs": []
385-
},
386-
{
387-
"cell_type": "code",
388-
"collapsed": false,
389-
"input": [],
390-
"language": "python",
391-
"metadata": {},
392-
"outputs": []
393387
}
394388
],
395389
"metadata": {}

0 commit comments

Comments
 (0)