From bf441fe32985add39c9b231bd6247dd76a54d2a8 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 2 Jun 2022 01:22:44 +0200 Subject: [PATCH] Simplify webagg blitting. - Directly reuse FigureCanvasAgg.get_renderer. We don't need to init _last_buff there as we can just use buff.copy() in get_diff_image() (If we really care about reusing the same array whenever possible, we could add a shape check before the removed copyto() and reuse the old array if the shapes match, but I doubt it matters.) Instead, init it once to a non-valid shape in the constructor. Note that the comments in the function were outdated since 78c182d. - First creating `pixels` and then viewing it as `buff` is slighly simpler than doing it the other way round. --- .../backends/backend_webagg_core.py | 48 +++++-------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index fd90984c347c..3ca4e6906d2a 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -171,6 +171,9 @@ def __init__(self, *args, **kwargs): # sent to the clients will be a full frame. self._force_full = True + # The last buffer, for diff mode. + self._last_buff = np.empty((0, 0)) + # Store the current image mode so that at any point, clients can # request the information. This should be changed by calling # self.set_image_mode(mode) so that the notification can be given @@ -227,17 +230,18 @@ def get_diff_image(self): if self._png_is_old: renderer = self.get_renderer() + pixels = np.asarray(renderer.buffer_rgba()) # The buffer is created as type uint32 so that entire # pixels can be compared in one numpy call, rather than # needing to compare each plane separately. - buff = (np.frombuffer(renderer.buffer_rgba(), dtype=np.uint32) - .reshape((renderer.height, renderer.width))) - - # If any pixels have transparency, we need to force a full - # draw as we cannot overlay new on top of old. - pixels = buff.view(dtype=np.uint8).reshape(buff.shape + (4,)) - - if self._force_full or np.any(pixels[:, :, 3] != 255): + buff = pixels.view(np.uint32).squeeze(2) + + if (self._force_full + # If the buffer has changed size we need to do a full draw. + or buff.shape != self._last_buff.shape + # If any pixels have transparency, we need to force a full + # draw as we cannot overlay new on top of old. + or (pixels[:, :, 3] != 255).any()): self.set_image_mode('full') output = buff else: @@ -246,7 +250,7 @@ def get_diff_image(self): output = np.where(diff, buff, 0) # Store the current buffer so we can compute the next diff. - np.copyto(self._last_buff, buff) + self._last_buff = buff.copy() self._force_full = False self._png_is_old = False @@ -255,32 +259,6 @@ def get_diff_image(self): Image.fromarray(data).save(png, format="png") return png.getvalue() - @_api.delete_parameter("3.6", "cleared", alternative="renderer.clear()") - def get_renderer(self, cleared=None): - # Mirrors super.get_renderer, but caches the old one so that we can do - # things such as produce a diff image in get_diff_image. - w, h = self.figure.bbox.size.astype(int) - key = w, h, self.figure.dpi - try: - self._lastKey, self._renderer - except AttributeError: - need_new_renderer = True - else: - need_new_renderer = (self._lastKey != key) - - if need_new_renderer: - self._renderer = backend_agg.RendererAgg( - w, h, self.figure.dpi) - self._lastKey = key - self._last_buff = np.copy(np.frombuffer( - self._renderer.buffer_rgba(), dtype=np.uint32 - ).reshape((self._renderer.height, self._renderer.width))) - - elif cleared: - self._renderer.clear() - - return self._renderer - def handle_event(self, event): e_type = event['type'] handler = getattr(self, 'handle_{0}'.format(e_type),