|
4 | 4 | from matplotlib.externals import six
|
5 | 5 |
|
6 | 6 | import os
|
7 |
| -import numpy |
8 | 7 |
|
9 | 8 | from matplotlib._pylab_helpers import Gcf
|
10 |
| -from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ |
11 |
| - FigureManagerBase, FigureCanvasBase, NavigationToolbar2, TimerBase |
| 9 | +from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase, \ |
| 10 | + NavigationToolbar2, TimerBase |
12 | 11 | from matplotlib.backend_bases import ShowBase
|
13 | 12 |
|
14 |
| -from matplotlib.cbook import maxdict |
15 | 13 | from matplotlib.figure import Figure
|
16 |
| -from matplotlib.path import Path |
17 |
| -from matplotlib.mathtext import MathTextParser |
18 |
| -from matplotlib.colors import colorConverter |
19 | 14 | from matplotlib import rcParams
|
20 | 15 |
|
21 | 16 | from matplotlib.widgets import SubplotTool
|
22 | 17 |
|
23 | 18 | import matplotlib
|
24 | 19 | from matplotlib.backends import _macosx
|
25 | 20 |
|
| 21 | +from .backend_agg import RendererAgg |
| 22 | + |
26 | 23 |
|
27 | 24 | class Show(ShowBase):
|
28 | 25 | def mainloop(self):
|
29 | 26 | _macosx.show()
|
30 | 27 | show = Show()
|
31 | 28 |
|
32 | 29 |
|
33 |
| -class RendererMac(RendererBase): |
34 |
| - """ |
35 |
| - The renderer handles drawing/rendering operations. Most of the renderer's |
36 |
| - methods forward the command to the renderer's graphics context. The |
37 |
| - renderer does not wrap a C object and is written in pure Python. |
38 |
| - """ |
39 |
| - |
40 |
| - texd = maxdict(50) # a cache of tex image rasters |
41 |
| - |
42 |
| - def __init__(self, dpi, width, height): |
43 |
| - RendererBase.__init__(self) |
44 |
| - self.dpi = dpi |
45 |
| - self.width = width |
46 |
| - self.height = height |
47 |
| - self.gc = GraphicsContextMac() |
48 |
| - self.gc.set_dpi(self.dpi) |
49 |
| - self.mathtext_parser = MathTextParser('MacOSX') |
50 |
| - |
51 |
| - def set_width_height (self, width, height): |
52 |
| - self.width, self.height = width, height |
53 |
| - |
54 |
| - def draw_path(self, gc, path, transform, rgbFace=None): |
55 |
| - if rgbFace is not None: |
56 |
| - rgbFace = tuple(rgbFace) |
57 |
| - linewidth = gc.get_linewidth() |
58 |
| - gc.draw_path(path, transform, linewidth, rgbFace) |
59 |
| - |
60 |
| - def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): |
61 |
| - if rgbFace is not None: |
62 |
| - rgbFace = tuple(rgbFace) |
63 |
| - linewidth = gc.get_linewidth() |
64 |
| - gc.draw_markers(marker_path, marker_trans, path, trans, linewidth, rgbFace) |
65 |
| - |
66 |
| - def draw_path_collection(self, gc, master_transform, paths, all_transforms, |
67 |
| - offsets, offsetTrans, facecolors, edgecolors, |
68 |
| - linewidths, linestyles, antialiaseds, urls, |
69 |
| - offset_position): |
70 |
| - if offset_position=='data': |
71 |
| - offset_position = True |
72 |
| - else: |
73 |
| - offset_position = False |
74 |
| - path_ids = [] |
75 |
| - for path, transform in self._iter_collection_raw_paths( |
76 |
| - master_transform, paths, all_transforms): |
77 |
| - path_ids.append((path, transform)) |
78 |
| - master_transform = master_transform.get_matrix() |
79 |
| - offsetTrans = offsetTrans.get_matrix() |
80 |
| - gc.draw_path_collection(master_transform, path_ids, all_transforms, |
81 |
| - offsets, offsetTrans, facecolors, edgecolors, |
82 |
| - linewidths, linestyles, antialiaseds, |
83 |
| - offset_position) |
84 |
| - |
85 |
| - def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, |
86 |
| - coordinates, offsets, offsetTrans, facecolors, |
87 |
| - antialiased, edgecolors): |
88 |
| - gc.draw_quad_mesh(master_transform.get_matrix(), |
89 |
| - meshWidth, |
90 |
| - meshHeight, |
91 |
| - coordinates, |
92 |
| - offsets, |
93 |
| - offsetTrans.get_matrix(), |
94 |
| - facecolors, |
95 |
| - antialiased, |
96 |
| - edgecolors) |
97 |
| - |
98 |
| - def new_gc(self): |
99 |
| - self.gc.save() |
100 |
| - self.gc.set_hatch(None) |
101 |
| - self.gc._alpha = 1.0 |
102 |
| - self.gc._forced_alpha = False # if True, _alpha overrides A from RGBA |
103 |
| - return self.gc |
104 |
| - |
105 |
| - def draw_gouraud_triangle(self, gc, points, colors, transform): |
106 |
| - points = transform.transform(points) |
107 |
| - gc.draw_gouraud_triangle(points, colors) |
108 |
| - |
109 |
| - def get_image_magnification(self): |
110 |
| - return self.gc.get_image_magnification() |
111 |
| - |
112 |
| - def draw_image(self, gc, x, y, im): |
113 |
| - nrows, ncols, data = im.as_rgba_str() |
114 |
| - gc.draw_image(x, y, nrows, ncols, data) |
115 |
| - |
116 |
| - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): |
117 |
| - # todo, handle props, angle, origins |
118 |
| - scale = self.gc.get_image_magnification() |
119 |
| - size = prop.get_size_in_points() |
120 |
| - texmanager = self.get_texmanager() |
121 |
| - key = s, size, self.dpi, angle, texmanager.get_font_config() |
122 |
| - im = self.texd.get(key) # Not sure what this does; just copied from backend_agg.py |
123 |
| - if im is None: |
124 |
| - Z = texmanager.get_grey(s, size, self.dpi*scale) |
125 |
| - Z = numpy.array(255.0 - Z * 255.0, numpy.uint8) |
126 |
| - |
127 |
| - gc.draw_mathtext(x, y, angle, Z) |
128 |
| - |
129 |
| - def _draw_mathtext(self, gc, x, y, s, prop, angle): |
130 |
| - scale = self.gc.get_image_magnification() |
131 |
| - ox, oy, width, height, descent, image, used_characters = \ |
132 |
| - self.mathtext_parser.parse(s, self.dpi*scale, prop) |
133 |
| - descent /= scale |
134 |
| - xd = descent * numpy.sin(numpy.deg2rad(angle)) |
135 |
| - yd = descent * numpy.cos(numpy.deg2rad(angle)) |
136 |
| - x = numpy.round(x + ox + xd) |
137 |
| - y = numpy.round(y + oy - yd) |
138 |
| - gc.draw_mathtext(x, y, angle, 255 - image.as_array()) |
139 |
| - |
140 |
| - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): |
141 |
| - if ismath: |
142 |
| - self._draw_mathtext(gc, x, y, s, prop, angle) |
143 |
| - else: |
144 |
| - family = prop.get_family() |
145 |
| - weight = prop.get_weight() |
146 |
| - # transform weight into string for the native backend |
147 |
| - if weight >= 700: |
148 |
| - weight = 'bold' |
149 |
| - else: |
150 |
| - weight = 'normal' |
151 |
| - style = prop.get_style() |
152 |
| - points = prop.get_size_in_points() |
153 |
| - size = self.points_to_pixels(points) |
154 |
| - gc.draw_text(x, y, six.text_type(s), family, size, weight, style, angle) |
155 |
| - |
156 |
| - def get_text_width_height_descent(self, s, prop, ismath): |
157 |
| - if ismath=='TeX': |
158 |
| - # todo: handle props |
159 |
| - texmanager = self.get_texmanager() |
160 |
| - fontsize = prop.get_size_in_points() |
161 |
| - w, h, d = texmanager.get_text_width_height_descent(s, fontsize, |
162 |
| - renderer=self) |
163 |
| - return w, h, d |
164 |
| - if ismath: |
165 |
| - ox, oy, width, height, descent, fonts, used_characters = \ |
166 |
| - self.mathtext_parser.parse(s, self.dpi, prop) |
167 |
| - return width, height, descent |
168 |
| - family = prop.get_family() |
169 |
| - weight = prop.get_weight() |
170 |
| - # transform weight into string for the native backend |
171 |
| - if weight >= 700: |
172 |
| - weight = 'bold' |
173 |
| - else: |
174 |
| - weight = 'normal' |
175 |
| - style = prop.get_style() |
176 |
| - points = prop.get_size_in_points() |
177 |
| - size = self.points_to_pixels(points) |
178 |
| - width, height, descent = self.gc.get_text_width_height_descent( |
179 |
| - six.text_type(s), family, size, weight, style) |
180 |
| - return width, height, descent |
181 |
| - |
182 |
| - def flipy(self): |
183 |
| - return False |
184 |
| - |
185 |
| - def points_to_pixels(self, points): |
186 |
| - return points/72.0 * self.dpi |
187 |
| - |
188 |
| - def option_image_nocomposite(self): |
189 |
| - return True |
190 |
| - |
191 |
| - |
192 |
| -class GraphicsContextMac(_macosx.GraphicsContext, GraphicsContextBase): |
193 |
| - """ |
194 |
| - The GraphicsContext wraps a Quartz graphics context. All methods |
195 |
| - are implemented at the C-level in macosx.GraphicsContext. These |
196 |
| - methods set drawing properties such as the line style, fill color, |
197 |
| - etc. The actual drawing is done by the Renderer, which draws into |
198 |
| - the GraphicsContext. |
199 |
| - """ |
200 |
| - def __init__(self): |
201 |
| - GraphicsContextBase.__init__(self) |
202 |
| - _macosx.GraphicsContext.__init__(self) |
203 |
| - |
204 |
| - def set_alpha(self, alpha): |
205 |
| - GraphicsContextBase.set_alpha(self, alpha) |
206 |
| - _alpha = self.get_alpha() |
207 |
| - _macosx.GraphicsContext.set_alpha(self, _alpha, self.get_forced_alpha()) |
208 |
| - rgb = self.get_rgb() |
209 |
| - _macosx.GraphicsContext.set_foreground(self, rgb) |
210 |
| - |
211 |
| - def set_foreground(self, fg, isRGBA=False): |
212 |
| - GraphicsContextBase.set_foreground(self, fg, isRGBA) |
213 |
| - rgb = self.get_rgb() |
214 |
| - _macosx.GraphicsContext.set_foreground(self, rgb) |
215 |
| - |
216 |
| - def set_graylevel(self, fg): |
217 |
| - GraphicsContextBase.set_graylevel(self, fg) |
218 |
| - _macosx.GraphicsContext.set_graylevel(self, fg) |
219 |
| - |
220 |
| - def set_clip_rectangle(self, box): |
221 |
| - GraphicsContextBase.set_clip_rectangle(self, box) |
222 |
| - if not box: return |
223 |
| - _macosx.GraphicsContext.set_clip_rectangle(self, box.bounds) |
224 |
| - |
225 |
| - def set_clip_path(self, path): |
226 |
| - GraphicsContextBase.set_clip_path(self, path) |
227 |
| - if not path: return |
228 |
| - path = path.get_fully_transformed_path() |
229 |
| - _macosx.GraphicsContext.set_clip_path(self, path) |
230 |
| - |
231 |
| - |
232 | 30 | ########################################################################
|
233 | 31 | #
|
234 | 32 | # The following functions and classes are for pylab and implement
|
@@ -311,14 +109,42 @@ class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase):
|
311 | 109 | def __init__(self, figure):
|
312 | 110 | FigureCanvasBase.__init__(self, figure)
|
313 | 111 | width, height = self.get_width_height()
|
314 |
| - self.renderer = RendererMac(figure.dpi, width, height) |
315 | 112 | _macosx.FigureCanvas.__init__(self, width, height)
|
316 | 113 |
|
| 114 | + @property |
| 115 | + def renderer(self): |
| 116 | + l, b, w, h = self.figure.bbox.bounds |
| 117 | + key = w, h, self.figure.dpi |
| 118 | + try: |
| 119 | + self._lastKey, self._renderer |
| 120 | + except AttributeError: |
| 121 | + need_new_renderer = True |
| 122 | + else: |
| 123 | + need_new_renderer = (self._lastKey != key) |
| 124 | + |
| 125 | + if need_new_renderer: |
| 126 | + self._renderer = RendererAgg(w, h, self.figure.dpi) |
| 127 | + self._lastKey = key |
| 128 | + |
| 129 | + return self._renderer |
| 130 | + |
| 131 | + def _draw(self, device_scale): |
| 132 | + figure = self.figure |
| 133 | + |
| 134 | + orig_dpi = figure.dpi |
| 135 | + try: |
| 136 | + figure.dpi *= device_scale |
| 137 | + renderer = self.renderer |
| 138 | + figure.draw(renderer) |
| 139 | + finally: |
| 140 | + figure.dpi = orig_dpi |
| 141 | + |
| 142 | + return renderer |
| 143 | + |
317 | 144 | def draw_idle(self, *args, **kwargs):
|
318 | 145 | self.invalidate()
|
319 | 146 |
|
320 | 147 | def resize(self, width, height):
|
321 |
| - self.renderer.set_width_height(width, height) |
322 | 148 | dpi = self.figure.dpi
|
323 | 149 | width /= dpi
|
324 | 150 | height /= dpi
|
|
0 commit comments