21
21
* integrate screen dpi w/ ppi and text
22
22
"""
23
23
from __future__ import division
24
-
24
+ import threading
25
25
import numpy as np
26
26
27
27
from matplotlib import verbose , rcParams
@@ -46,11 +46,24 @@ class RendererAgg(RendererBase):
46
46
context instance that controls the colors/styles
47
47
"""
48
48
debug = 1
49
+
50
+ # we want to cache the fonts at the class level so that when
51
+ # multiple figures are created we can reuse them. This helps with
52
+ # a bug on windows where the creation of too many figures leads to
53
+ # too many open file handles. However, storing them at the class
54
+ # level is not thread safe. The solution here is to let the
55
+ # FigureCanvas acquire a lock on the fontd at the start of the
56
+ # draw, and release it when it is done. This allows multiple
57
+ # renderers to share the cached fonts, but only one figure can
58
+ # draw at at time and so the font cache is used by only one
59
+ # renderer at a time
60
+
61
+ lock = threading .Lock ()
62
+ _fontd = maxdict (50 )
49
63
def __init__ (self , width , height , dpi ):
50
64
if __debug__ : verbose .report ('RendererAgg.__init__' , 'debug-annoying' )
51
65
RendererBase .__init__ (self )
52
66
self .texd = maxdict (50 ) # a cache of tex image rasters
53
- self ._fontd = maxdict (50 )
54
67
55
68
self .dpi = dpi
56
69
self .width = width
@@ -69,6 +82,7 @@ def __init__(self, width, height, dpi):
69
82
if __debug__ : verbose .report ('RendererAgg.__init__ done' ,
70
83
'debug-annoying' )
71
84
85
+
72
86
def _get_hinting_flag (self ):
73
87
if rcParams ['text.hinting' ]:
74
88
return LOAD_FORCE_AUTOHINT
@@ -82,7 +96,7 @@ def draw_markers(self, *kl, **kw):
82
96
83
97
def draw_path_collection (self , * kl , ** kw ):
84
98
return self ._renderer .draw_path_collection (* kl , ** kw )
85
-
99
+
86
100
def _update_methods (self ):
87
101
#self.draw_path = self._renderer.draw_path # see below
88
102
#self.draw_markers = self._renderer.draw_markers
@@ -215,15 +229,16 @@ def _get_agg_font(self, prop):
215
229
'debug-annoying' )
216
230
217
231
key = hash (prop )
218
- font = self ._fontd .get (key )
232
+ font = RendererAgg ._fontd .get (key )
219
233
220
234
if font is None :
221
235
fname = findfont (prop )
222
- font = self ._fontd .get (fname )
236
+ font = RendererAgg ._fontd .get (fname )
223
237
if font is None :
224
238
font = FT2Font (str (fname ))
225
- self ._fontd [fname ] = font
226
- self ._fontd [key ] = font
239
+ RendererAgg ._fontd [fname ] = font
240
+
241
+ RendererAgg ._fontd [key ] = font
227
242
228
243
font .clear ()
229
244
size = prop .get_size_in_points ()
@@ -358,6 +373,7 @@ def post_processing(image, dpi):
358
373
image )
359
374
360
375
376
+
361
377
def new_figure_manager (num , * args , ** kwargs ):
362
378
"""
363
379
Create a new figure manager instance
@@ -398,7 +414,15 @@ def draw(self):
398
414
if __debug__ : verbose .report ('FigureCanvasAgg.draw' , 'debug-annoying' )
399
415
400
416
self .renderer = self .get_renderer ()
401
- self .figure .draw (self .renderer )
417
+ # acquire a lock on the shared font cache
418
+ RendererAgg .lock .acquire ()
419
+
420
+ try :
421
+ self .figure .draw (self .renderer )
422
+ finally :
423
+ RendererAgg .lock .release ()
424
+
425
+
402
426
403
427
def get_renderer (self ):
404
428
l , b , w , h = self .figure .bbox .bounds
0 commit comments