Traktor/myenv/Lib/site-packages/matplotlib/backends/backend_mixed.py
2024-05-23 01:57:24 +02:00

120 lines
4.6 KiB
Python

import numpy as np
from matplotlib import cbook
from .backend_agg import RendererAgg
from matplotlib._tight_bbox import process_figure_for_rasterizing
class MixedModeRenderer:
"""
A helper class to implement a renderer that switches between
vector and raster drawing. An example may be a PDF writer, where
most things are drawn with PDF vector commands, but some very
complex objects, such as quad meshes, are rasterised and then
output as images.
"""
def __init__(self, figure, width, height, dpi, vector_renderer,
raster_renderer_class=None,
bbox_inches_restore=None):
"""
Parameters
----------
figure : `~matplotlib.figure.Figure`
The figure instance.
width : scalar
The width of the canvas in logical units
height : scalar
The height of the canvas in logical units
dpi : float
The dpi of the canvas
vector_renderer : `~matplotlib.backend_bases.RendererBase`
An instance of a subclass of
`~matplotlib.backend_bases.RendererBase` that will be used for the
vector drawing.
raster_renderer_class : `~matplotlib.backend_bases.RendererBase`
The renderer class to use for the raster drawing. If not provided,
this will use the Agg backend (which is currently the only viable
option anyway.)
"""
if raster_renderer_class is None:
raster_renderer_class = RendererAgg
self._raster_renderer_class = raster_renderer_class
self._width = width
self._height = height
self.dpi = dpi
self._vector_renderer = vector_renderer
self._raster_renderer = None
# A reference to the figure is needed as we need to change
# the figure dpi before and after the rasterization. Although
# this looks ugly, I couldn't find a better solution. -JJL
self.figure = figure
self._figdpi = figure.dpi
self._bbox_inches_restore = bbox_inches_restore
self._renderer = vector_renderer
def __getattr__(self, attr):
# Proxy everything that hasn't been overridden to the base
# renderer. Things that *are* overridden can call methods
# on self._renderer directly, but must not cache/store
# methods (because things like RendererAgg change their
# methods on the fly in order to optimise proxying down
# to the underlying C implementation).
return getattr(self._renderer, attr)
def start_rasterizing(self):
"""
Enter "raster" mode. All subsequent drawing commands (until
`stop_rasterizing` is called) will be drawn with the raster backend.
"""
# change the dpi of the figure temporarily.
self.figure.dpi = self.dpi
if self._bbox_inches_restore: # when tight bbox is used
r = process_figure_for_rasterizing(self.figure,
self._bbox_inches_restore)
self._bbox_inches_restore = r
self._raster_renderer = self._raster_renderer_class(
self._width*self.dpi, self._height*self.dpi, self.dpi)
self._renderer = self._raster_renderer
def stop_rasterizing(self):
"""
Exit "raster" mode. All of the drawing that was done since
the last `start_rasterizing` call will be copied to the
vector backend by calling draw_image.
"""
self._renderer = self._vector_renderer
height = self._height * self.dpi
img = np.asarray(self._raster_renderer.buffer_rgba())
slice_y, slice_x = cbook._get_nonzero_slices(img[..., 3])
cropped_img = img[slice_y, slice_x]
if cropped_img.size:
gc = self._renderer.new_gc()
# TODO: If the mixedmode resolution differs from the figure's
# dpi, the image must be scaled (dpi->_figdpi). Not all
# backends support this.
self._renderer.draw_image(
gc,
slice_x.start * self._figdpi / self.dpi,
(height - slice_y.stop) * self._figdpi / self.dpi,
cropped_img[::-1])
self._raster_renderer = None
# restore the figure dpi.
self.figure.dpi = self._figdpi
if self._bbox_inches_restore: # when tight bbox is used
r = process_figure_for_rasterizing(self.figure,
self._bbox_inches_restore,
self._figdpi)
self._bbox_inches_restore = r