2121 * integrate screen dpi w/ ppi and text
2222"""
2323from __future__ import division
24-
24+ import threading
2525import numpy as np
2626
2727from matplotlib import verbose , rcParams
@@ -59,11 +59,24 @@ class RendererAgg(RendererBase):
5959 context instance that controls the colors/styles
6060 """
6161 debug = 1
62+
63+ # we want to cache the fonts at the class level so that when
64+ # multiple figures are created we can reuse them. This helps with
65+ # a bug on windows where the creation of too many figures leads to
66+ # too many open file handles. However, storing them at the class
67+ # level is not thread safe. The solution here is to let the
68+ # FigureCanvas acquire a lock on the fontd at the start of the
69+ # draw, and release it when it is done. This allows multiple
70+ # renderers to share the cached fonts, but only one figure can
71+ # draw at at time and so the font cache is used by only one
72+ # renderer at a time
73+
74+ lock = threading .Lock ()
75+ _fontd = maxdict (50 )
6276 def __init__ (self , width , height , dpi ):
6377 if __debug__ : verbose .report ('RendererAgg.__init__' , 'debug-annoying' )
6478 RendererBase .__init__ (self )
6579 self .texd = maxdict (50 ) # a cache of tex image rasters
66- self ._fontd = maxdict (50 )
6780
6881 self .dpi = dpi
6982 self .width = width
@@ -82,6 +95,12 @@ def __init__(self, width, height, dpi):
8295 if __debug__ : verbose .report ('RendererAgg.__init__ done' ,
8396 'debug-annoying' )
8497
98+ def _get_hinting_flag (self ):
99+ if rcParams ['text.hinting' ]:
100+ return LOAD_FORCE_AUTOHINT
101+ else :
102+ return LOAD_NO_HINTING
103+
85104 # for filtering to work with rasterization, methods needs to be wrapped.
86105 # maybe there is better way to do it.
87106 def draw_markers (self , * kl , ** kw ):
@@ -221,17 +240,17 @@ def _get_agg_font(self, prop):
221240 'debug-annoying' )
222241
223242 key = hash (prop )
224- font = self ._fontd .get (key )
243+ font = RendererAgg ._fontd .get (key )
225244
226245 if font is None :
227246 fname = findfont (prop )
228- font = self ._fontd .get (fname )
247+ font = RendererAgg ._fontd .get (fname )
229248 if font is None :
230249 font = FT2Font (
231250 str (fname ),
232251 hinting_factor = rcParams ['text.hinting_factor' ])
233- self ._fontd [fname ] = font
234- self ._fontd [key ] = font
252+ RendererAgg ._fontd [fname ] = font
253+ RendererAgg ._fontd [key ] = font
235254
236255 font .clear ()
237256 size = prop .get_size_in_points ()
@@ -366,6 +385,7 @@ def post_processing(image, dpi):
366385 image )
367386
368387
388+
369389def new_figure_manager (num , * args , ** kwargs ):
370390 """
371391 Create a new figure manager instance
@@ -406,7 +426,15 @@ def draw(self):
406426 if __debug__ : verbose .report ('FigureCanvasAgg.draw' , 'debug-annoying' )
407427
408428 self .renderer = self .get_renderer ()
409- self .figure .draw (self .renderer )
429+ # acquire a lock on the shared font cache
430+ RendererAgg .lock .acquire ()
431+
432+ try :
433+ self .figure .draw (self .renderer )
434+ finally :
435+ RendererAgg .lock .release ()
436+
437+
410438
411439 def get_renderer (self ):
412440 l , b , w , h = self .figure .bbox .bounds
0 commit comments