44from matplotlib .externals import six
55
66import os
7- import numpy
87
98from 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
1211from matplotlib .backend_bases import ShowBase
1312
14- from matplotlib .cbook import maxdict
1513from matplotlib .figure import Figure
16- from matplotlib .path import Path
17- from matplotlib .mathtext import MathTextParser
18- from matplotlib .colors import colorConverter
1914from matplotlib import rcParams
2015
2116from matplotlib .widgets import SubplotTool
2217
2318import matplotlib
2419from matplotlib .backends import _macosx
2520
21+ from .backend_agg import RendererAgg , FigureCanvasAgg
22+
2623
2724class Show (ShowBase ):
2825 def mainloop (self ):
2926 _macosx .show ()
3027show = Show ()
3128
3229
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-
23230########################################################################
23331#
23432# The following functions and classes are for pylab and implement
@@ -285,7 +83,7 @@ class TimerMac(_macosx.Timer, TimerBase):
28583 # completely implemented at the C-level (in _macosx.Timer)
28684
28785
288- class FigureCanvasMac (_macosx .FigureCanvas , FigureCanvasBase ):
86+ class FigureCanvasMac (_macosx .FigureCanvas , FigureCanvasAgg ):
28987 """
29088 The canvas the figure renders into. Calls the draw and print fig
29189 methods, creates the renderers, etc...
@@ -300,61 +98,66 @@ class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase):
30098 key_press_event, and key_release_event are called from there.
30199 """
302100
303- filetypes = FigureCanvasBase .filetypes .copy ()
304- filetypes ['bmp' ] = 'Windows bitmap'
305- filetypes ['jpeg' ] = 'JPEG'
306- filetypes ['jpg' ] = 'JPEG'
307- filetypes ['gif' ] = 'Graphics Interchange Format'
308- filetypes ['tif' ] = 'Tagged Image Format File'
309- filetypes ['tiff' ] = 'Tagged Image Format File'
310-
311101 def __init__ (self , figure ):
312102 FigureCanvasBase .__init__ (self , figure )
313103 width , height = self .get_width_height ()
314- self .renderer = RendererMac (figure .dpi , width , height )
315104 _macosx .FigureCanvas .__init__ (self , width , height )
105+ self ._needs_draw = True
106+ self ._device_scale = 1.0
107+
108+ def _set_device_scale (self , value ):
109+ if self ._device_scale != value :
110+ self .figure .dpi = self .figure .dpi / self ._device_scale * value
111+ self ._device_scale = value
112+
113+ def get_renderer (self , cleared = False ):
114+ l , b , w , h = self .figure .bbox .bounds
115+ key = w , h , self .figure .dpi
116+ try :
117+ self ._lastKey , self ._renderer
118+ except AttributeError :
119+ need_new_renderer = True
120+ else :
121+ need_new_renderer = (self ._lastKey != key )
316122
317- def draw_idle (self , * args , ** kwargs ):
318- self .invalidate ()
123+ if need_new_renderer :
124+ self ._renderer = RendererAgg (w , h , self .figure .dpi )
125+ self ._lastKey = key
126+ elif cleared :
127+ self ._renderer .clear ()
319128
320- def resize (self , width , height ):
321- self .renderer .set_width_height (width , height )
322- dpi = self .figure .dpi
323- width /= dpi
324- height /= dpi
325- self .figure .set_size_inches (width , height , forward = False )
326- FigureCanvasBase .resize_event (self )
129+ return self ._renderer
327130
328- def _print_bitmap (self , filename , * args , ** kwargs ):
329- # In backend_bases.py, print_figure changes the dpi of the figure.
330- # But since we are essentially redrawing the picture, we need the
331- # original dpi. Pick it up from the renderer.
332- dpi = kwargs ['dpi' ]
333- old_dpi = self .figure .dpi
334- self .figure .dpi = self .renderer .dpi
335- width , height = self .figure .get_size_inches ()
336- width , height = width * dpi , height * dpi
337- filename = six .text_type (filename )
338- self .write_bitmap (filename , width , height , dpi )
339- self .figure .dpi = old_dpi
131+ def _draw (self ):
132+ renderer = self .get_renderer ()
340133
341- def print_bmp ( self , filename , * args , ** kwargs ) :
342- self . _print_bitmap ( filename , * args , ** kwargs )
134+ if not self . _needs_draw :
135+ return renderer
343136
344- def print_jpg (self , filename , * args , ** kwargs ):
345- self ._print_bitmap (filename , * args , ** kwargs )
137+ self .figure .draw (renderer )
138+ self ._needs_draw = False
139+ return renderer
346140
347- def print_jpeg (self , filename , * args , ** kwargs ):
348- self ._print_bitmap (filename , * args , ** kwargs )
141+ def draw (self ):
142+ self ._draw ()
143+ self .invalidate ()
349144
350- def print_tif (self , filename , * args , ** kwargs ):
351- self ._print_bitmap (filename , * args , ** kwargs )
145+ def draw_idle (self , * args , ** kwargs ):
146+ self ._needs_draw = True
147+ self .invalidate ()
352148
353- def print_tiff (self , filename , * args , ** kwargs ):
354- self ._print_bitmap ( filename , * args , ** kwargs )
149+ def blit (self , bbox ):
150+ self .invalidate ( )
355151
356- def print_gif (self , filename , * args , ** kwargs ):
357- self ._print_bitmap (filename , * args , ** kwargs )
152+ def resize (self , width , height ):
153+ dpi = self .figure .dpi
154+ width /= dpi
155+ height /= dpi
156+ self .figure .set_size_inches (width * self ._device_scale ,
157+ height * self ._device_scale ,
158+ forward = False )
159+ FigureCanvasBase .resize_event (self )
160+ self .draw_idle ()
358161
359162 def new_timer (self , * args , ** kwargs ):
360163 """
0 commit comments