|
37 | 37 | from matplotlib.patches import ( |
38 | 38 | FancyBboxPatch, FancyArrowPatch, bbox_artist as mbbox_artist) |
39 | 39 | from matplotlib.transforms import Bbox, BboxBase, TransformedBbox |
| 40 | +from matplotlib.backends.backend_mixed import MixedModeRenderer |
40 | 41 |
|
41 | 42 |
|
42 | 43 | DEBUG = False |
43 | 44 |
|
44 | 45 |
|
45 | | -def _is_vector_renderer(renderer): |
46 | | - # 1. Check the renderer name |
47 | | - vector_bases = {'RendererPdf', 'RendererSVG', 'RendererPS'} |
48 | | - # If it's MixedModeRenderer, get the actual renderer recursively |
49 | | - actual_renderer = renderer |
50 | | - while type(actual_renderer).__name__ == 'MixedModeRenderer': |
51 | | - actual_renderer = getattr(actual_renderer, '_renderer', actual_renderer) |
52 | | - renderer_name = type(actual_renderer).__name__ |
53 | | - if renderer_name in vector_bases: |
54 | | - return True |
55 | | - # 3. For unknown renderers, assume they are not vector |
56 | | - return False |
57 | | - |
58 | | - |
59 | 46 | def _compat_get_offset(meth): |
60 | 47 | """ |
61 | 48 | Decorator for the get_offset method of OffsetBox and subclasses, that |
@@ -709,26 +696,29 @@ def draw(self, renderer): |
709 | 696 | [self.width, 0]]), |
710 | 697 | self.get_transform()) |
711 | 698 | for c in self._children: |
712 | | - is_rasterized = getattr(c, 'get_rasterized', lambda: False)() |
713 | | - if is_rasterized and _is_vector_renderer(renderer): |
714 | | - # PDF/SVG backend uses 72 dpi in display units, |
715 | | - # so we need to scale the rasterized content accordingly |
716 | | - target_dpi = getattr(renderer, 'dpi', 72) or 72 |
717 | | - dpi_correction = target_dpi / 72 |
718 | | - # New transform to apply the dpi correction |
719 | | - corrected_transform = self.get_transform() \ |
720 | | - + mtransforms.Affine2D().scale(dpi_correction) |
721 | | - orig_transform = c.get_transform() \ |
722 | | - if hasattr(c, 'get_transform') else None |
723 | | - c.set_transform(corrected_transform) |
724 | | - if self._clip_children and not (c.clipbox or c._clippath): |
725 | | - c.set_clip_path(tpath) |
| 699 | + if self._clip_children and not (c.clipbox or c._clippath): |
| 700 | + c.set_clip_path(tpath) |
| 701 | + if c.get_rasterized() and isinstance(renderer, MixedModeRenderer): |
| 702 | + # When using MixedModeRenderer (PDF/SVG/PS), the figure DPI is |
| 703 | + # set to the vector renderer's DPI (typically 72 pt/inch), so |
| 704 | + # dpi_cor ≈ 1.0. But rasterized children are drawn into a |
| 705 | + # RendererAgg buffer at renderer.dpi (the user's requested DPI). |
| 706 | + # The child's transform is dpi_transform + offset_transform, |
| 707 | + # where offset_transform is a translation in vector-DPI display |
| 708 | + # units. Both must be scaled to raster-DPI pixel coordinates. |
| 709 | + mag = renderer.get_image_magnification() |
| 710 | + raster_dpi_cor = dpi_cor * mag |
| 711 | + off_mat = self.offset_transform.get_matrix().copy() |
| 712 | + scaled_off = off_mat.copy() |
| 713 | + scaled_off[:2, 2] *= mag # scale tx and ty to raster DPI |
| 714 | + self.dpi_transform.clear() |
| 715 | + self.dpi_transform.scale(raster_dpi_cor) |
| 716 | + self.offset_transform.set_matrix(scaled_off) |
726 | 717 | c.draw(renderer) |
727 | | - if orig_transform is not None: |
728 | | - c.set_transform(orig_transform) |
| 718 | + self.dpi_transform.clear() |
| 719 | + self.dpi_transform.scale(dpi_cor) |
| 720 | + self.offset_transform.set_matrix(off_mat) |
729 | 721 | else: |
730 | | - if self._clip_children and not (c.clipbox or c._clippath): |
731 | | - c.set_clip_path(tpath) |
732 | 722 | c.draw(renderer) |
733 | 723 |
|
734 | 724 | _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) |
@@ -930,7 +920,26 @@ def get_bbox(self, renderer): |
930 | 920 | def draw(self, renderer): |
931 | 921 | # docstring inherited |
932 | 922 | for c in self._children: |
933 | | - c.draw(renderer) |
| 923 | + if c.get_rasterized() and isinstance(renderer, MixedModeRenderer): |
| 924 | + # offset_transform and ref_offset_transform store display-unit |
| 925 | + # translations computed at vector DPI (typically 72 pt/inch for |
| 926 | + # PDF/SVG/PS). When rasterizing, the RendererAgg buffer uses |
| 927 | + # renderer.dpi (user DPI). Scale the translation components so |
| 928 | + # the child draws at the correct position in the raster buffer. |
| 929 | + mag = renderer.get_image_magnification() |
| 930 | + off_mat = self.offset_transform.get_matrix().copy() |
| 931 | + ref_mat = self.ref_offset_transform.get_matrix().copy() |
| 932 | + scaled_off = off_mat.copy() |
| 933 | + scaled_off[:2, 2] *= mag # scale tx and ty only |
| 934 | + scaled_ref = ref_mat.copy() |
| 935 | + scaled_ref[:2, 2] *= mag |
| 936 | + self.offset_transform.set_matrix(scaled_off) |
| 937 | + self.ref_offset_transform.set_matrix(scaled_ref) |
| 938 | + c.draw(renderer) |
| 939 | + self.offset_transform.set_matrix(off_mat) |
| 940 | + self.ref_offset_transform.set_matrix(ref_mat) |
| 941 | + else: |
| 942 | + c.draw(renderer) |
934 | 943 | _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) |
935 | 944 | self.stale = False |
936 | 945 |
|
|
0 commit comments