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
@@ -51,19 +51,19 @@ class RendererAgg(RendererBase):
51
51
# multiple figures are created we can reuse them. This helps with
52
52
# a bug on windows where the creation of too many figures leads to
53
53
# too many open file handles. However, storing them at the class
54
- # level is not thread safe. The solution here is to cache the
55
- # fonts at class level, but for a given renderer to slurp them
56
- # down into the instance cache "_fontd_instance" from the
57
- # "_fontd_class" in a call to pre_draw_hook (managed by the
58
- # FigureCanvas) and to restore them to the fontd_class in the
59
- # post_draw_hook.
60
- _fontd_class = maxdict (50 )
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 )
61
63
def __init__ (self , width , height , dpi ):
62
64
if __debug__ : verbose .report ('RendererAgg.__init__' , 'debug-annoying' )
63
65
RendererBase .__init__ (self )
64
66
self .texd = maxdict (50 ) # a cache of tex image rasters
65
- self ._fontd_instance = maxdict (50 )
66
-
67
67
68
68
self .dpi = dpi
69
69
self .width = width
@@ -82,15 +82,6 @@ def __init__(self, width, height, dpi):
82
82
if __debug__ : verbose .report ('RendererAgg.__init__ done' ,
83
83
'debug-annoying' )
84
84
85
- def pre_draw_hook (self ):
86
- 'called by FigureCanvas right before draw; slurp in the class level cache'
87
- self ._fontd_instance = RendererAgg ._fontd_class
88
- RendererAgg ._fontd_class = {}
89
-
90
- def post_draw_hook (self ):
91
- 'called by FigureCanvas right after draw; restore the class level cache'
92
- RendererAgg ._fontd_class = self ._fontd_instance
93
- self ._fontd_instance = {}
94
85
95
86
def _get_hinting_flag (self ):
96
87
if rcParams ['text.hinting' ]:
@@ -238,16 +229,16 @@ def _get_agg_font(self, prop):
238
229
'debug-annoying' )
239
230
240
231
key = hash (prop )
241
- font = self . _fontd_instance .get (key )
232
+ font = RendererAgg . _fontd .get (key )
242
233
243
234
if font is None :
244
235
fname = findfont (prop )
245
- font = self . _fontd_instance .get (fname )
236
+ font = RendererAgg . _fontd .get (fname )
246
237
if font is None :
247
238
font = FT2Font (str (fname ))
248
- self . _fontd_instance [fname ] = font
239
+ RendererAgg . _fontd [fname ] = font
249
240
250
- self . _fontd_instance [key ] = font
241
+ RendererAgg . _fontd [key ] = font
251
242
252
243
font .clear ()
253
244
size = prop .get_size_in_points ()
@@ -423,9 +414,15 @@ def draw(self):
423
414
if __debug__ : verbose .report ('FigureCanvasAgg.draw' , 'debug-annoying' )
424
415
425
416
self .renderer = self .get_renderer ()
426
- self .renderer .pre_draw_hook ()
427
- self .figure .draw (self .renderer )
428
- self .renderer .post_draw_hook ()
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
+
429
426
430
427
def get_renderer (self ):
431
428
l , b , w , h = self .figure .bbox .bounds
0 commit comments