Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
BUGFIX: use true bbox for rasters in backend_mixed#17182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,8 +1,9 @@ | ||||||
import numpy as np | ||||||
from matplotlib import cbook | ||||||
from matplotlib._tight_bbox import process_figure_for_rasterizing | ||||||
from matplotlib.backends.backend_agg import RendererAgg | ||||||
from matplotlib.transforms import Bbox, Affine2D, IdentityTransform | ||||||
class MixedModeRenderer: | ||||||
@@ -68,6 +69,71 @@ def __getattr__(self, attr): | ||||||
# to the underlying C implementation). | ||||||
return getattr(self._renderer, attr) | ||||||
# need to wrap each drawing function that might be called on the rasterized | ||||||
# version of the renderer to save what the "true" bbox is for scaling the | ||||||
# output correctly | ||||||
# the functions we might want to overwrite are: | ||||||
# `draw_path`, `draw_image`, `draw_gouraud_triangle`, `draw_text`, | ||||||
# `draw_markers`, `draw_path_collection`, `draw_quad_mesh` | ||||||
def _update_true_bbox(self, bbox, transform=None): | ||||||
"""Convert to real units and update""" | ||||||
if transform is None: | ||||||
transform = IdentityTransform() | ||||||
bbox = bbox.transformed(transform + Affine2D().scale( | ||||||
self._figdpi / self.dpi)) | ||||||
if self._true_bbox is None: | ||||||
self._true_bbox = bbox | ||||||
else: | ||||||
self._true_bbox = Bbox.union([self._true_bbox, bbox]) | ||||||
def draw_path(self, gc, path, transform, rgbFace=None): | ||||||
if self._rasterizing > 0: | ||||||
bbox = Bbox.null() | ||||||
bbox.update_from_path(path, ignore=True) | ||||||
self._update_true_bbox(bbox, transform) | ||||||
return self._renderer.draw_path(gc, path, transform, rgbFace) | ||||||
def draw_path_collection(self, gc, master_transform, paths, all_transforms, | ||||||
offsets, offsetTrans, facecolors, edgecolors, | ||||||
linewidths, linestyles, antialiaseds, urls, | ||||||
offset_position): | ||||||
if self._rasterizing > 0: | ||||||
bbox = Bbox.null() | ||||||
# TODO probably faster to merge all coordinates from path using | ||||||
# numpy for large lists of paths, such as the one produced by the | ||||||
# test case tests/test_backed_pgf.py:test_mixed_mode | ||||||
for path in paths: | ||||||
bbox.update_from_path(path, ignore=False) | ||||||
self._update_true_bbox(bbox, master_transform) | ||||||
return self._renderer.draw_path_collection( | ||||||
gc, master_transform, paths, all_transforms, offsets, | ||||||
offsetTrans, facecolors, edgecolors, linewidths, linestyles, | ||||||
antialiaseds, urls, offset_position) | ||||||
def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, | ||||||
coordinates, offsets, offsetTrans, facecolors, | ||||||
antialiased, edgecolors): | ||||||
if self._rasterizing > 0: | ||||||
# TODO should check if this is always Bbox.unit for efficiency | ||||||
bbox = Bbox.null() | ||||||
cshape = coordinates.shape | ||||||
flat_coords = coordinates.reshape((cshape[0]*cshape[1], cshape[2])) | ||||||
bbox.update_from_data_xy(flat_coords, ignore=True) | ||||||
self._update_true_bbox(bbox, master_transform) | ||||||
return self._renderer.draw_quad_mesh( | ||||||
gc, master_transform, meshWidth, meshHeight, coordinates, | ||||||
offsets, offsetTrans, facecolors, antialiased, edgecolors) | ||||||
def draw_gouraud_triangle(self, gc, points, colors, transform): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This should probably be matplotlib/lib/matplotlib/backend_bases.py Lines 291 to 292 ind77801e
https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.7.0.html#draw-gouraud-triangle | ||||||
if self._rasterizing > 0: | ||||||
bbox = Bbox.null() | ||||||
bbox.update_from_data_xy(points, ignore=True) | ||||||
self._update_true_bbox(bbox, transform) | ||||||
return self._renderer.draw_gouraud_triangle( | ||||||
gc, points, colors, transform) | ||||||
def start_rasterizing(self): | ||||||
""" | ||||||
Enter "raster" mode. All subsequent drawing commands (until | ||||||
@@ -83,6 +149,7 @@ def start_rasterizing(self): | ||||||
self._raster_renderer = self._raster_renderer_class( | ||||||
self._width*self.dpi, self._height*self.dpi, self.dpi) | ||||||
self._renderer = self._raster_renderer | ||||||
self._true_bbox = None | ||||||
def stop_rasterizing(self): | ||||||
""" | ||||||
@@ -92,21 +159,35 @@ def stop_rasterizing(self): | ||||||
""" | ||||||
self._renderer = self._vector_renderer | ||||||
height = self._height * self.dpi | ||||||
# these bounds are in pixels, relative to the figure when pixelated | ||||||
# at the requested DPI. However, the vectorized backends draw at a | ||||||
# fixed DPI of 72, and typically aren't snapped to the | ||||||
# requested-DPI pixel grid, so we have to grab the actual bounds to | ||||||
# put the image into some other way | ||||||
if self._true_bbox is not None: | ||||||
# raise NotImplementedError( | ||||||
# "Something was drawn using a method not wrapped by " | ||||||
# "MixedModeRenderer.") | ||||||
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. | ||||||
# because rasterizing will have rounded size to nearest | ||||||
# pixel, we need to rescale our drawing to fit the original | ||||||
# intended Bbox. This results in a slightly different DPI than | ||||||
# requested, but that's better than the drawing not fitting | ||||||
# into the space requested, see Issue #6827 | ||||||
self._renderer.draw_image( | ||||||
gc, self._true_bbox.x0, self._true_bbox.y0, cropped_img[::-1], | ||||||
true_size=(self._true_bbox.width, self._true_bbox.height) | ||||||
) | ||||||
self._raster_renderer = None | ||||||
# restore the figure dpi. | ||||||
Uh oh!
There was an error while loading.Please reload this page.