8000 Merge pull request #6178 from mdboom/macagg · bearstrong/matplotlib@e21eec2 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e21eec2

Browse files
efiringbearstrong
authored andcommitted
Merge pull request matplotlib#6178 from mdboom/macagg
Use Agg for rendering in the Mac OSX backend
1 parent fde356b commit e21eec2

File tree

5 files changed

+273
-3671
lines changed

5 files changed

+273
-3671
lines changed

lib/matplotlib/animation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,8 @@ def _blit_clear(self, artists, bg_cache):
895895
# cache and restore.
896896
axes = set(a.axes for a in artists)
897897
for a in axes:
898-
a.figure.canvas.restore_region(bg_cache[a])
898+
if a in bg_cache:
899+
a.figure.canvas.restore_region(bg_cache[a])
899900

900901
def _setup_blit(self):
901902
# Setting up the blit requires: a cache of the background for the

lib/matplotlib/backends/backend_agg.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ def print_raw(self, filename_or_obj, *args, **kwargs):
509509
finally:
510510
if close:
511511
filename_or_obj.close()
512-
renderer.dpi = original_dpi
512+
renderer.dpi = original_dpi
513513
print_rgba = print_raw
514514

515515
def print_png(self, filename_or_obj, *args, **kwargs):
@@ -528,16 +528,18 @@ def print_png(self, filename_or_obj, *args, **kwargs):
528528
finally:
529529
if close:
530530
filename_or_obj.close()
531-
renderer.dpi = original_dpi
531+
renderer.dpi = original_dpi
532532

533533
def print_to_buffer(self):
534534
FigureCanvasAgg.draw(self)
535535
renderer = self.get_renderer()
536536
original_dpi = renderer.dpi
537537
renderer.dpi = self.figure.dpi
538-
result = (renderer._renderer.buffer_rgba(),
539-
(int(renderer.width), int(renderer.height)))
540-
renderer.dpi = original_dpi
538+
try:
539+
result = (renderer._renderer.buffer_rgba(),
540+
(int(renderer.width), int(renderer.height)))
541+
finally:
542+
renderer.dpi = original_dpi
541543
return result
542544

543545
if _has_pil:
< F438 code class="diff-text-cell hunk">

lib/matplotlib/backends/backend_macosx.py

Lines changed: 52 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -4,231 +4,29 @@
44
from matplotlib.externals import six
55

66
import os
7-
import numpy
87

98
from 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
1211
from matplotlib.backend_bases import ShowBase
1312

14-
from matplotlib.cbook import maxdict
1513
from matplotlib.figure import Figure
16-
from matplotlib.path import Path
17-
from matplotlib.mathtext import MathTextParser
18-
from matplotlib.colors import colorConverter
1914
from matplotlib import rcParams
2015

2116
from matplotlib.widgets import SubplotTool
2217

2318
import matplotlib
2419
from matplotlib.backends import _macosx
2520

21+
from .backend_agg import RendererAgg, FigureCanvasAgg
22+
2623

2724
class Show(ShowBase):
2825
def mainloop(self):
2926
_macosx.show()
3027
show = 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, 10000 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

Comments
 (0)
0