Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5a4c578

Browse files
committed
Simplify mathtext code so it doesn't require cyclical/weak references anymore. Alas, still no luck solving the mysterious FT2Image memory leak.
1 parent 590992e commit 5a4c578

2 files changed

Lines changed: 59 additions & 94 deletions

File tree

lib/matplotlib/mathtext.py

Lines changed: 59 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from sets import Set as set
3131
import unicodedata
3232
from warnings import warn
33-
import weakref
3433

3534
from numpy import inf, isinf
3635
import numpy as np
@@ -57,8 +56,6 @@
5756
latex_to_standard, tex2uni, latex_to_cmex, stix_virtual_fonts
5857
from matplotlib import get_data_path, rcParams
5958

60-
61-
6259
import matplotlib.colors as mcolors
6360
import matplotlib._png as _png
6461
####################
@@ -119,7 +116,9 @@ class MathtextBackend(object):
119116
- :meth:`get_hinting_type`
120117
"""
121118
def __init__(self):
122-
self.fonts_object = None
119+
self.width = 0
120+
self.height = 0
121+
self.depth = 0
123122

124123
def set_canvas_size(self, w, h, d):
125124
'Dimension the drawing canvas'
@@ -154,111 +153,84 @@ def get_hinting_type(self):
154153
"""
155154
return LOAD_NO_HINTING
156155

157-
class MathtextBackendBbox(MathtextBackend):
156+
class MathtextBackendAgg(MathtextBackend):
158157
"""
159-
A backend whose only purpose is to get a precise bounding box.
160-
Only required for the Agg backend.
158+
Render glyphs and rectangles to an FTImage buffer, which is later
159+
transferred to the Agg image by the Agg backend.
161160
"""
162-
163-
def __init__(self, real_backend):
164-
MathtextBackend.__init__(self)
161+
def __init__(self):
162+
self.ox = 0
163+
self.oy = 0
164+
self.image = None
165+
self.mode = 'bbox'
165166
self.bbox = [0, 0, 0, 0]
166-
self.real_backend = real_backend
167+
MathtextBackend.__init__(self)
167168

168169
def _update_bbox(self, x1, y1, x2, y2):
169170
self.bbox = [min(self.bbox[0], x1),
170171
min(self.bbox[1], y1),
171172
max(self.bbox[2], x2),
172173
max(self.bbox[3], y2)]
173174

175+
def set_canvas_size(self, w, h, d):
176+
MathtextBackend.set_canvas_size(self, w, h, d)
177+
if self.mode != 'bbox':
178+
self.image = FT2Image(ceil(w), ceil(h + d))
179+
174180
def render_glyph(self, ox, oy, info):
175-
self._update_bbox(ox + info.metrics.xmin,
176-
oy - info.metrics.ymax,
177-
ox + info.metrics.xmax,
178-
oy - info.metrics.ymin)
181+
if self.mode == 'bbox':
182+
self._update_bbox(ox + info.metrics.xmin,
183+
oy - info.metrics.ymax,
184+
ox + info.metrics.xmax,
185+
oy - info.metrics.ymin)
186+
else:
187+
info.font.draw_glyph_to_bitmap(
188+
self.image, ox, oy - info.metrics.iceberg, info.glyph)
179189

180190
def render_rect_filled(self, x1, y1, x2, y2):
181-
self._update_bbox(x1, y1, x2, y2)
191+
if self.mode == 'bbox':
192+
self._update_bbox(x1, y1, x2, y2)
193+
else:
194+
height = max(int(y2 - y1) - 1, 0)
195+
if height == 0:
196+
center = (y2 + y1) / 2.0
197+
y = int(center - (height + 1) / 2.0)
198+
else:
199+
y = int(y1)
200+
self.image.draw_rect_filled(int(x1), y, ceil(x2), y + height)
182201

183-
def get_results(self, box):
202+
def get_results(self, box, used_characters):
203+
self.mode = 'bbox'
184204
orig_height = box.height
185205
orig_depth = box.depth
186206
ship(0, 0, box)
187207
bbox = self.bbox
188208
bbox = [bbox[0] - 1, bbox[1] - 1, bbox[2] + 1, bbox[3] + 1]
189-
self._switch_to_real_backend()
190-
self.fonts_object().set_canvas_size(
209+
self.mode = 'render'
210+
self.set_canvas_size(
191211
bbox[2] - bbox[0],
192212
(bbox[3] - bbox[1]) - orig_depth,
193213
(bbox[3] - bbox[1]) - orig_height)
194214
ship(-bbox[0], -bbox[1], box)
195-
return self.fonts_object().get_results(box)
196-
197-
def get_hinting_type(self):
198-
return self.real_backend.get_hinting_type()
199-
200-
def _switch_to_real_backend(self):
201-
self.fonts_object().mathtext_backend = weakref.ref(self.real_backend)
202-
self.real_backend.fonts_object = self.fonts_object
203-
self.real_backend.ox = self.bbox[0]
204-
self.real_backend.oy = self.bbox[1]
205-
206-
class MathtextBackendAggRender(MathtextBackend):
207-
"""
208-
Render glyphs and rectangles to an FTImage buffer, which is later
209-
transferred to the Agg image by the Agg backend.
210-
"""
211-
def __init__(self):
212-
self.ox = 0
213-
self.oy = 0
214-
self.image = None
215-
MathtextBackend.__init__(self)
216-
217-
def set_canvas_size(self, w, h, d):
218-
MathtextBackend.set_canvas_size(self, w, h, d)
219-
self.image = FT2Image(ceil(w), ceil(h + d))
220-
221-
def render_glyph(self, ox, oy, info):
222-
info.font.draw_glyph_to_bitmap(
223-
self.image, ox, oy - info.metrics.iceberg, info.glyph)
224-
225-
def render_rect_filled(self, x1, y1, x2, y2):
226-
height = max(int(y2 - y1) - 1, 0)
227-
if height == 0:
228-
center = (y2 + y1) / 2.0
229-
y = int(center - (height + 1) / 2.0)
230-
else:
231-
y = int(y1)
232-
self.image.draw_rect_filled(int(x1), y, ceil(x2), y + height)
233-
234-
def get_results(self, box):
235215
return (self.ox,
236216
self.oy,
237217
self.width,
238218
self.height + self.depth,
239219
self.depth,
240220
self.image,
241-
self.fonts_object().get_used_characters())
221+
used_characters)
242222

243223
def get_hinting_type(self):
244224
if rcParams['text.hinting']:
245225
return LOAD_FORCE_AUTOHINT
246226
else:
247227
return LOAD_NO_HINTING
248228

249-
def MathtextBackendAgg():
250-
return MathtextBackendBbox(MathtextBackendAggRender())
251-
252-
class MathtextBackendBitmapRender(MathtextBackendAggRender):
253-
def get_results(self, box):
254-
return self.image, self.depth
255-
256-
def MathtextBackendBitmap():
257-
"""
258-
A backend to generate standalone mathtext images. No additional
259-
matplotlib backend is required.
260-
"""
261-
return MathtextBackendBbox(MathtextBackendBitmapRender())
229+
class MathtextBackendBitmap(MathtextBackendAgg):
230+
def get_results(self, box, used_characters):
231+
ox, oy, width, height, depth, image, characters = \
232+
MathtextBackendAgg(self, box, used_characters)
233+
return image, depth
262234

263235
class MathtextBackendPs(MathtextBackend):
264236
"""
@@ -292,14 +264,14 @@ def render_rect_filled(self, x1, y1, x2, y2):
292264
ps = "%f %f %f %f rectfill\n" % (x1, self.height - y2, x2 - x1, y2 - y1)
293265
self.pswriter.write(ps)
294266

295-
def get_results(self, box):
267+
def get_results(self, box, used_characters):
296268
ship(0, -self.depth, box)
297269
#print self.depth
298270
return (self.width,
299271
self.height + self.depth,
300272
self.depth,
301273
self.pswriter,
302-
self.fonts_object().get_used_characters())
274+
used_characters)
303275

304276
class MathtextBackendPdf(MathtextBackend):
305277
"""
@@ -320,14 +292,14 @@ def render_glyph(self, ox, oy, info):
320292
def render_rect_filled(self, x1, y1, x2, y2):
321293
self.rects.append((x1, self.height - y2, x2 - x1, y2 - y1))
322294

323-
def get_results(self, box):
295+
def get_results(self, box, used_characters):
324296
ship(0, -self.depth, box)
325297
return (self.width,
326298
self.height + self.depth,
327299
self.depth,
328300
self.glyphs,
329301
self.rects,
330-
self.fonts_object().get_used_characters())
302+
used_characters)
331303

332304
class MathtextBackendSvg(MathtextBackend):
333305
"""
@@ -348,15 +320,15 @@ def render_rect_filled(self, x1, y1, x2, y2):
348320
self.svg_rects.append(
349321
(x1, self.height - y1 + 1, x2 - x1, y2 - y1))
350322

351-
def get_results(self, box):
323+
def get_results(self, box, used_characters):
352324
ship(0, -self.depth, box)
353325
svg_elements = Bunch(svg_glyphs = self.svg_glyphs,
354326
svg_rects = self.svg_rects)
355327
return (self.width,
356328
self.height + self.depth,
357329
self.depth,
358330
svg_elements,
359-
self.fonts_object().get_used_characters())
331+
used_characters)
360332

361333
class MathtextBackendPath(MathtextBackend):
362334
"""
@@ -378,7 +350,7 @@ def render_rect_filled(self, x1, y1, x2, y2):
378350
self.rects.append(
379351
(x1, self.height-y2 , x2 - x1, y2 - y1))
380352

381-
def get_results(self, box):
353+
def get_results(self, box, used_characters):
382354
ship(0, -self.depth, box)
383355
return (self.width,
384356
self.height + self.depth,
@@ -406,7 +378,7 @@ def render_rect_filled(self, x1, y1, x2, y2):
406378
self.rects.append(
407379
(x1, y1 - self.height, x2 - x1, y2 - y1))
408380

409-
def get_results(self, box):
381+
def get_results(self, box, used_characters):
410382
ship(0, -self.depth, box)
411383
return (self.width,
412384
self.height + self.depth,
@@ -434,9 +406,7 @@ def __init__(self, default_font_prop, mathtext_backend):
434406
used to delegate the actual rendering.
435407
"""
436408
self.default_font_prop = default_font_prop
437-
self.mathtext_backend = weakref.ref(mathtext_backend)
438-
# Make these classes doubly-linked
439-
mathtext_backend.fonts_object = weakref.ref(self)
409+
self.mathtext_backend = mathtext_backend
440410
self.used_characters = {}
441411

442412
def destroy(self):
@@ -502,7 +472,7 @@ def set_canvas_size(self, w, h, d):
502472
Only really necessary for the bitmap backends.
503473
"""
504474
self.width, self.height, self.depth = ceil(w), ceil(h), ceil(d)
505-
self.mathtext_backend().set_canvas_size(self.width, self.height, self.depth)
475+
self.mathtext_backend.set_canvas_size(self.width, self.height, self.depth)
506476

507477
def render_glyph(self, ox, oy, facename, font_class, sym, fontsize, dpi):
508478
"""
@@ -525,13 +495,13 @@ def render_glyph(self, ox, oy, facename, font_class, sym, fontsize, dpi):
525495
used_characters = self.used_characters.setdefault(
526496
stat_key, (realpath, set()))
527497
used_characters[1].add(info.num)
528-
self.mathtext_backend().render_glyph(ox, oy, info)
498+
self.mathtext_backend.render_glyph(ox, oy, info)
529499

530500
def render_rect_filled(self, x1, y1, x2, y2):
531501
"""
532502
Draw a filled rectangle from (*x1*, *y1*) to (*x2*, *y2*).
533503
"""
534-
self.mathtext_backend().render_rect_filled(x1, y1, x2, y2)
504+
self.mathtext_backend.render_rect_filled(x1, y1, x2, y2)
535505

536506
def get_xheight(self, font, fontsize, dpi):
537507
"""
@@ -559,7 +529,7 @@ def get_results(self, box):
559529
Get the data needed by the backend to render the math
560530
expression. The return value is backend-specific.
561531
"""
562-
return self.mathtext_backend().get_results(box)
532+
return self.mathtext_backend.get_results(box, self.get_used_characters())
563533

564534
def get_sized_alternatives_for_symbol(self, fontname, sym):
565535
"""
@@ -632,7 +602,7 @@ def _get_info(self, fontname, font_class, sym, fontsize, dpi):
632602
font.set_size(fontsize, dpi)
633603
glyph = font.load_char(
634604
num,
635-
flags=self.mathtext_backend().get_hinting_type())
605+
flags=self.mathtext_backend.get_hinting_type())
636606

637607
xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox]
638608
offset = self._get_offset(cached_font, glyph, fontsize, dpi)

src/ft2font.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ FT2Image::FT2Image(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwd
6262
}
6363

6464
FT2Image::~FT2Image() {
65-
printf("~FT2Image");
6665
delete [] _buffer;
6766
_buffer = NULL;
6867
}
@@ -1905,10 +1904,6 @@ FT2Image::init_type(void)
19051904
FT2Image::as_array__doc__);
19061905
PYCXX_ADD_VARARGS_METHOD(as_str, py_as_str,
19071906
FT2Image::as_str__doc__);
1908-
PYCXX_ADD_VARARGS_METHOD(as_rgb_str, py_as_rgb_str,
1909-
FT2Image::as_rgb_str__doc__);
1910-
PYCXX_ADD_VARARGS_METHOD(as_rgba_str, py_as_rgba_str,
1911-
FT2Image::as_rgba_str__doc__);
19121907
PYCXX_ADD_VARARGS_METHOD(get_width, py_get_width,
19131908
"Returns the width of the image");
19141909
PYCXX_ADD_VARARGS_METHOD(get_height, py_get_height,

0 commit comments

Comments
 (0)