Skip to content

Commit ac64e25

Browse files
committed
Tighten a bit the RendererAgg API.
The get_content_extents and tostring_rgba_minimized methods of RendererAgg are used to implement 1) agg_filter and 2) rasterization in MixedModeRenderer (i.e. for vector backends). They can easily instead be implemented at the Python level (i.e., via _get_nonzero_slices), which makes it easier to plug in an alternative renderer (e.g., mplcairo) for rasterization in MixedModeRenderer. I'm not convinced _get_nonzero_slices needs to be exposed as public API (it's quite simple anyways), but it could be if we absolutely want to present an alternative for the deprecated methods.
1 parent 8e7c7ab commit ac64e25

File tree

4 files changed

+42
-20
lines changed

4 files changed

+42
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``RendererAgg.get_content_extents``, ``RendererAgg.tostring_rgba_minimized``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
... are deprecated.

lib/matplotlib/backends/backend_agg.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,12 @@ def _update_methods(self):
119119
# self.draw_path_collection = self._renderer.draw_path_collection
120120
self.draw_quad_mesh = self._renderer.draw_quad_mesh
121121
self.copy_from_bbox = self._renderer.copy_from_bbox
122-
self.get_content_extents = self._renderer.get_content_extents
123122

123+
@cbook.deprecated("3.4") # Also needs to be removed at C-level.
124+
def get_content_extents(self):
125+
return self._renderer.get_content_extents()
126+
127+
@cbook.deprecated("3.4")
124128
def tostring_rgba_minimized(self):
125129
extents = self.get_content_extents()
126130
bbox = [[extents[0], self.height - (extents[1] + extents[3])],
@@ -367,23 +371,21 @@ def post_processing(image, dpi):
367371
The saved renderer is restored and the returned image from
368372
post_processing is plotted (using draw_image) on it.
369373
"""
370-
371-
width, height = int(self.width), int(self.height)
372-
373-
buffer, (l, b, w, h) = self.tostring_rgba_minimized()
374+
buf = np.asarray(self.buffer_rgba())
375+
sly, slx = cbook._get_nonzero_slices(buf[..., 3])
376+
buf_min = buf[sly, slx]
374377

375378
self._renderer = self._filter_renderers.pop()
376379
self._update_methods()
377380

378-
if w > 0 and h > 0:
379-
img = np.frombuffer(buffer, np.uint8)
380-
img, ox, oy = post_processing(img.reshape((h, w, 4)) / 255.,
381-
self.dpi)
381+
if buf_min.size:
382+
img, ox, oy = post_processing(buf_min / 255, self.dpi)
382383
gc = self.new_gc()
383384
if img.dtype.kind == 'f':
384385
img = np.asarray(img * 255., np.uint8)
385-
img = img[::-1]
386-
self._renderer.draw_image(gc, l + ox, height - b - h + oy, img)
386+
self._renderer.draw_image(
387+
gc, slx.start + ox, int(self.height) - sly.stop + oy,
388+
img[::-1])
387389

388390

389391
class FigureCanvasAgg(FigureCanvasBase):

lib/matplotlib/backends/backend_mixed.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import numpy as np
22

3+
from matplotlib import cbook
34
from matplotlib.backends.backend_agg import RendererAgg
45
from matplotlib.tight_bbox import process_figure_for_rasterizing
56

@@ -105,21 +106,19 @@ def stop_rasterizing(self):
105106
self._renderer = self._vector_renderer
106107

107108
height = self._height * self.dpi
108-
buffer, bounds = self._raster_renderer.tostring_rgba_minimized()
109-
l, b, w, h = bounds
110-
if w > 0 and h > 0:
111-
image = np.frombuffer(buffer, dtype=np.uint8)
112-
image = image.reshape((h, w, 4))
113-
image = image[::-1]
109+
buf = np.asarray(self._raster_renderer.buffer_rgba())
110+
sly, slx = cbook._get_nonzero_slices(buf[..., 3])
111+
buf_min = buf[sly, slx]
112+
if buf_min.size:
114113
gc = self._renderer.new_gc()
115114
# TODO: If the mixedmode resolution differs from the figure's
116115
# dpi, the image must be scaled (dpi->_figdpi). Not all
117116
# backends support this.
118117
self._renderer.draw_image(
119118
gc,
120-
l * self._figdpi / self.dpi,
121-
(height-b-h) * self._figdpi / self.dpi,
122-
image)
119+
slx.start * self._figdpi / self.dpi,
120+
(height-sly.stop) * self._figdpi / self.dpi,
121+
buf_min[::-1])
123122
self._raster_renderer = None
124123
self._rasterizing = False
125124

lib/matplotlib/cbook/__init__.py

+18
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,24 @@ def _unmultiplied_rgba8888_to_premultiplied_argb32(rgba8888):
21622162
return argb32
21632163

21642164

2165+
def _get_nonzero_slices(buf):
2166+
"""
2167+
Return the bounds of the nonzero region of a 2D array as a pair of slices.
2168+
2169+
``buf[_get_nonzero_slices(buf)]`` is the smallest sub-rectangle in *buf*
2170+
that encloses all non-zero entries in *buf*. If *buf* is fully zero, then
2171+
``(slice(0, 0), slice(0, 0))`` is returned.
2172+
"""
2173+
x_nz, = buf.any(axis=0).nonzero()
2174+
y_nz, = buf.any(axis=1).nonzero()
2175+
if len(x_nz) and len(y_nz):
2176+
l, r = x_nz[[0, -1]]
2177+
b, t = y_nz[[0, -1]]
2178+
return slice(b, t + 1), slice(l, r + 1)
2179+
else:
2180+
return slice(0, 0), slice(0, 0)
2181+
2182+
21652183
def _pformat_subprocess(command):
21662184
"""Pretty-format a subprocess command for printing/logging purposes."""
21672185
return (command if isinstance(command, str)

0 commit comments

Comments
 (0)