Skip to content

Use Agg for rendering in the Mac OSX backend #6178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/matplotlib/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,8 @@ def _blit_clear(self, artists, bg_cache):
# cache and restore.
axes = set(a.axes for a in artists)
for a in axes:
a.figure.canvas.restore_region(bg_cache[a])
if a in bg_cache:
a.figure.canvas.restore_region(bg_cache[a])

def _setup_blit(self):
# Setting up the blit requires: a cache of the background for the
Expand Down
12 changes: 7 additions & 5 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ def print_raw(self, filename_or_obj, *args, **kwargs):
finally:
if close:
filename_or_obj.close()
renderer.dpi = original_dpi
renderer.dpi = original_dpi
print_rgba = print_raw

def print_png(self, filename_or_obj, *args, **kwargs):
Expand All @@ -529,16 +529,18 @@ def print_png(self, filename_or_obj, *args, **kwargs):
finally:
if close:
filename_or_obj.close()
renderer.dpi = original_dpi
renderer.dpi = original_dpi

def print_to_buffer(self):
FigureCanvasAgg.draw(self)
renderer = self.get_renderer()
original_dpi = renderer.dpi
renderer.dpi = self.figure.dpi
result = (renderer._renderer.buffer_rgba(),
(int(renderer.width), int(renderer.height)))
renderer.dpi = original_dpi
try:
result = (renderer._renderer.buffer_rgba(),
(int(renderer.width), int(renderer.height)))
finally:
renderer.dpi = original_dpi
return result

if _has_pil:
Expand Down
300 changes: 51 additions & 249 deletions lib/matplotlib/backends/backend_macosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,231 +4,29 @@
from matplotlib.externals import six

import os
import numpy

from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
FigureManagerBase, FigureCanvasBase, NavigationToolbar2, TimerBase
from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase, \
NavigationToolbar2, TimerBase
from matplotlib.backend_bases import ShowBase

from matplotlib.cbook import maxdict
from matplotlib.figure import Figure
from matplotlib.path import Path
from matplotlib.mathtext import MathTextParser
from matplotlib.colors import colorConverter
from matplotlib import rcParams

from matplotlib.widgets import SubplotTool

import matplotlib
from matplotlib.backends import _macosx

from .backend_agg import RendererAgg, FigureCanvasAgg


class Show(ShowBase):
def mainloop(self):
_macosx.show()
show = Show()


class RendererMac(RendererBase):
"""
The renderer handles drawing/rendering operations. Most of the renderer's
methods forward the command to the renderer's graphics context. The
renderer does not wrap a C object and is written in pure Python.
"""

texd = maxdict(50) # a cache of tex image rasters

def __init__(self, dpi, width, height):
RendererBase.__init__(self)
self.dpi = dpi
self.width = width
self.height = height
self.gc = GraphicsContextMac()
self.gc.set_dpi(self.dpi)
self.mathtext_parser = MathTextParser('MacOSX')

def set_width_height (self, width, height):
self.width, self.height = width, height

def draw_path(self, gc, path, transform, rgbFace=None):
if rgbFace is not None:
rgbFace = tuple(rgbFace)
linewidth = gc.get_linewidth()
gc.draw_path(path, transform, linewidth, rgbFace)

def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
if rgbFace is not None:
rgbFace = tuple(rgbFace)
linewidth = gc.get_linewidth()
gc.draw_markers(marker_path, marker_trans, path, trans, linewidth, rgbFace)

def draw_path_collection(self, gc, master_transform, paths, all_transforms,
offsets, offsetTrans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position):
if offset_position=='data':
offset_position = True
else:
offset_position = False
path_ids = []
for path, transform in self._iter_collection_raw_paths(
master_transform, paths, all_transforms):
path_ids.append((path, transform))
master_transform = master_transform.get_matrix()
offsetTrans = offsetTrans.get_matrix()
gc.draw_path_collection(master_transform, path_ids, all_transforms,
offsets, offsetTrans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds,
offset_position)

def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight,
coordinates, offsets, offsetTrans, facecolors,
antialiased, edgecolors):
gc.draw_quad_mesh(master_transform.get_matrix(),
meshWidth,
meshHeight,
coordinates,
offsets,
offsetTrans.get_matrix(),
facecolors,
antialiased,
edgecolors)

def new_gc(self):
self.gc.save()
self.gc.set_hatch(None)
self.gc._alpha = 1.0
self.gc._forced_alpha = False # if True, _alpha overrides A from RGBA
return self.gc

def draw_gouraud_triangle(self, gc, points, colors, transform):
points = transform.transform(points)
gc.draw_gouraud_triangle(points, colors)

def get_image_magnification(self):
return self.gc.get_image_magnification()

def draw_image(self, gc, x, y, im):
nrows, ncols, data = im.as_rgba_str()
gc.draw_image(x, y, nrows, ncols, data)

def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
# todo, handle props, angle, origins
scale = self.gc.get_image_magnification()
size = prop.get_size_in_points()
texmanager = self.get_texmanager()
key = s, size, self.dpi, angle, texmanager.get_font_config()
im = self.texd.get(key) # Not sure what this does; just copied from backend_agg.py
if im is None:
Z = texmanager.get_grey(s, size, self.dpi*scale)
Z = numpy.array(255.0 - Z * 255.0, numpy.uint8)

gc.draw_mathtext(x, y, angle, Z)

def _draw_mathtext(self, gc, x, y, s, prop, angle):
scale = self.gc.get_image_magnification()
ox, oy, width, height, descent, image, used_characters = \
self.mathtext_parser.parse(s, self.dpi*scale, prop)
descent /= scale
xd = descent * numpy.sin(numpy.deg2rad(angle))
yd = descent * numpy.cos(numpy.deg2rad(angle))
x = numpy.round(x + ox + xd)
y = numpy.round(y + oy - yd)
gc.draw_mathtext(x, y, angle, 255 - image.as_array())

def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
if ismath:
self._draw_mathtext(gc, x, y, s, prop, angle)
else:
family = prop.get_family()
weight = prop.get_weight()
# transform weight into string for the native backend
if weight >= 700:
weight = 'bold'
else:
weight = 'normal'
style = prop.get_style()
points = prop.get_size_in_points()
size = self.points_to_pixels(points)
gc.draw_text(x, y, six.text_type(s), family, size, weight, style, angle)

def get_text_width_height_descent(self, s, prop, ismath):
if ismath=='TeX':
# todo: handle props
texmanager = self.get_texmanager()
fontsize = prop.get_size_in_points()
w, h, d = texmanager.get_text_width_height_descent(s, fontsize,
renderer=self)
return w, h, d
if ismath:
ox, oy, width, height, descent, fonts, used_characters = \
self.mathtext_parser.parse(s, self.dpi, prop)
return width, height, descent
family = prop.get_family()
weight = prop.get_weight()
# transform weight into string for the native backend
if weight >= 700:
weight = 'bold'
else:
weight = 'normal'
style = prop.get_style()
points = prop.get_size_in_points()
size = self.points_to_pixels(points)
width, height, descent = self.gc.get_text_width_height_descent(
six.text_type(s), family, size, weight, style)
return width, height, descent

def flipy(self):
return False

def points_to_pixels(self, points):
return points/72.0 * self.dpi

def option_image_nocomposite(self):
return True


class GraphicsContextMac(_macosx.GraphicsContext, GraphicsContextBase):
"""
The GraphicsContext wraps a Quartz graphics context. All methods
are implemented at the C-level in macosx.GraphicsContext. These
methods set drawing properties such as the line style, fill color,
etc. The actual drawing is done by the Renderer, which draws into
the GraphicsContext.
"""
def __init__(self):
GraphicsContextBase.__init__(self)
_macosx.GraphicsContext.__init__(self)

def set_alpha(self, alpha):
GraphicsContextBase.set_alpha(self, alpha)
_alpha = self.get_alpha()
_macosx.GraphicsContext.set_alpha(self, _alpha, self.get_forced_alpha())
rgb = self.get_rgb()
_macosx.GraphicsContext.set_foreground(self, rgb)

def set_foreground(self, fg, isRGBA=False):
GraphicsContextBase.set_foreground(self, fg, isRGBA)
rgb = self.get_rgb()
_macosx.GraphicsContext.set_foreground(self, rgb)

def set_graylevel(self, fg):
GraphicsContextBase.set_graylevel(self, fg)
_macosx.GraphicsContext.set_graylevel(self, fg)

def set_clip_rectangle(self, box):
GraphicsContextBase.set_clip_rectangle(self, box)
if not box: return
_macosx.GraphicsContext.set_clip_rectangle(self, box.bounds)

def set_clip_path(self, path):
GraphicsContextBase.set_clip_path(self, path)
if not path: return
path = path.get_fully_transformed_path()
_macosx.GraphicsContext.set_clip_path(self, path)


########################################################################
#
# The following functions and classes are for pylab and implement
Expand Down Expand Up @@ -285,7 +83,7 @@ class TimerMac(_macosx.Timer, TimerBase):
# completely implemented at the C-level (in _macosx.Timer)


class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase):
class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasAgg):
"""
The canvas the figure renders into. Calls the draw and print fig
methods, creates the renderers, etc...
Expand All @@ -300,61 +98,65 @@ class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase):
key_press_event, and key_release_event are called from there.
"""

filetypes = FigureCanvasBase.filetypes.copy()
filetypes['bmp'] = 'Windows bitmap'
filetypes['jpeg'] = 'JPEG'
filetypes['jpg'] = 'JPEG'
filetypes['gif'] = 'Graphics Interchange Format'
filetypes['tif'] = 'Tagged Image Format File'
filetypes['tiff'] = 'Tagged Image Format File'

def __init__(self, figure):
FigureCanvasBase.__init__(self, figure)
width, height = self.get_width_height()
self.renderer = RendererMac(figure.dpi, width, height)
_macosx.FigureCanvas.__init__(self, width, height)
self._needs_draw = True
self._device_scale = 1.0

def _set_device_scale(self, value):
if self._device_scale != value:
self.figure.dpi = self.figure.dpi / self._device_scale * value
self._device_scale = value

def get_renderer(self, cleared=False):
l, b, w, h = self.figure.bbox.bounds
key = w, h, self.figure.dpi
try:
self._lastKey, self._renderer
except AttributeError:
need_new_renderer = True
else:
need_new_renderer = (self._lastKey != key)

def draw_idle(self, *args, **kwargs):
self.invalidate()
if need_new_renderer:
self._renderer = RendererAgg(w, h, self.figure.dpi)
self._lastKey = key
elif cleared:
self._renderer.clear()

def resize(self, width, height):
self.renderer.set_width_height(width, height)
dpi = self.figure.dpi
width /= dpi
height /= dpi
self.figure.set_size_inches(width, height)
FigureCanvasBase.resize_event(self)
return self._renderer

def _print_bitmap(self, filename, *args, **kwargs):
# In backend_bases.py, print_figure changes the dpi of the figure.
# But since we are essentially redrawing the picture, we need the
# original dpi. Pick it up from the renderer.
dpi = kwargs['dpi']
old_dpi = self.figure.dpi
self.figure.dpi = self.renderer.dpi
width, height = self.figure.get_size_inches()
width, height = width*dpi, height*dpi
filename = six.text_type(filename)
self.write_bitmap(filename, width, height, dpi)
self.figure.dpi = old_dpi
def _draw(self):
renderer = self.get_renderer()

def print_bmp(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
if not self._needs_draw:
return renderer

def print_jpg(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
self.figure.draw(renderer)
self._needs_draw = False
return renderer

def print_jpeg(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
def draw(self):
self._draw()
self.invalidate()

def print_tif(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
def draw_idle(self, *args, **kwargs):
self._needs_draw = True
self.invalidate()

def print_tiff(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
def blit(self, bbox):
self.invalidate()

def print_gif(self, filename, *args, **kwargs):
self._print_bitmap(filename, *args, **kwargs)
def resize(self, width, height):
dpi = self.figure.dpi
width /= dpi
height /= dpi
self.figure.set_size_inches(width * self._device_scale,
height * self._device_scale)
FigureCanvasBase.resize_event(self)
self.draw_idle()

def new_timer(self, *args, **kwargs):
"""
Expand Down
Loading