"""
A gd backend http://newcenturycomputers.net/projects/gdmodule.html
"""
from __future__ import division
import sys, os, math, warnings
try: import gd
except ImportError:
print >>sys.stderr, 'You must first install the gd module http://newcenturycomputers.net/projects/gdmodule.html'
sys.exit()
from matplotlib.backend_bases import RendererBase, \
GraphicsContextBase, FigureManagerBase, FigureCanvasBase
from matplotlib import verbose
from matplotlib._pylab_helpers import Gcf
from matplotlib.cbook import enumerate, pieces, is_string_like
from matplotlib.colors import colorConverter
from matplotlib.figure import Figure
from matplotlib.transforms import Bbox
from matplotlib.font_manager import fontManager
from matplotlib.numerix import ones, array, nx, asarray
# support old font names
if (os.environ.has_key('GDFONTPATH') and not
os.environ.has_key('TTFPATH')):
os.environ['TTFPATH'] = os.environ['GDFONTPATH']
PIXELS_PER_INCH = 96 # constant GD uses for screen DPI
def round(x):
return int(math.floor(x+0.5))
class RendererGD(RendererBase):
"""
The renderer handles all the drawing primitives using a graphics
context instance that controls the colors/styles
"""
# todo: can gd support cap and join styles?
def __init__(self, im, dpi):
"Initialize the renderer with a gd image instance"
self.im = im
self._cached = {} # a map from get_color args to colors
self.width, self.height = im.size()
self.dpi = dpi
def get_canvas_width_height(self):
'return the canvas width and height in display coords'
return self.width, self.height
def get_text_width_height(self, s, prop, ismath):
"""
get the width and height in display coords of the string s
with fontsize in points
"""
size = prop.get_size_in_points()
font = fontManager.findfont(prop)
scale = self.get_text_scale()
try:
llx, lly, lrx, lry, urx, ury, ulx, uly = \
self.im.get_bounding_rect(
font, scale*size, 0.0, (0,0), s)
except ValueError:
raise RuntimeError('Could not load font %s. Try setting TTFFONTPATH to include this font' % fontname)
w = abs(lrx - llx)
h = abs(lly - uly)
return w, h
def flipy(self):
'return true if y small numbers are top for renderer'
return True
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation):
"""
Draw an arc centered at x,y with width and height and angles
from 0.0 to 360.0
"""
center = int(x), self.height-int(y)
wh = int(width), int(height)
a1, a2 = int(angle1), int(angle2)
if rgbFace is not None:
color = self.get_gd_color( rgbFace )
self.im.filledEllipse(
center, wh, color)
color = self.get_gd_color( gc.get_rgb() )
self.im.arc(center, wh, a1, a2, color)
self.flush_clip()
def draw_line(self, gc, x1, y1, x2, y2):
"""
Draw a single line from x1,y1 to x2,y2
"""
self.draw_lines(gc, array([x1, x2]), array([y1, y2]))
def draw_lines(self, gc, x, y):
"""
x and y are equal length arrays, draw lines connecting each
point in x, y
"""
x = x.astype(nx.Int16)
y = self.height*ones(y.shape, nx.Int16) - y.astype(nx.Int16)
style = self._set_gd_style(gc)
self.im.lines( zip(x,y), style)
self.flush_clip()
def draw_point(self, gc, x, y):
"""
Draw a single point at x,y
"""
self.im.setPixel((int(x),self.height-int(y)),
self.get_gd_color( gc.get_rgb() ))
self.flush_clip()
def draw_polygon(self, gc, rgbFace, points):
"""
Draw a polygon. points is a len vertices tuple, each element
giving the x,y coords a vertex
"""
edgecolor = self.get_gd_color( gc.get_rgb() )
points = [(int(x), self.height-int(y)) for x,y in points]
if rgbFace is not None:
facecolor = self.get_gd_color( rgbFace )
self.im.filledPolygon(points, facecolor)
else: facecolor = None
if edgecolor != facecolor:
self.im.polygon(points, edgecolor)
self.flush_clip()
def draw_rectangle(self, gc, rgbFace, x, y, width, height):
"""
Draw a rectangle at lower left x,y with width and height
If filled=True, fill the rectangle with the gc foreground
gc is a GraphicsContext instance
"""
lb = int(x), self.height-int(y)
ur = int(x+width), self.height-int((y+height))
edgecolor = self.get_gd_color( gc.get_rgb() )
if rgbFace is not None:
facecolor = self.get_gd_color( rgbFace )
self.im.filledRectangle(ur, lb, facecolor)
else: facecolor = None
if edgecolor != facecolor:
self.im.rectangle(ur, lb, edgecolor)
self.flush_clip()
def draw_text(self, gc, x, y, s, prop, angle, ismath):
"""
Render the text using the RendererGD instance
"""
size = prop.get_size_in_points()
font = fontManager.findfont(prop)
x = int(x)
y = int(y)
color = self.get_gd_color( gc.get_rgb() )
angle *= math.pi/180.0
scale = self.get_text_scale()
self.im.string_ft(font, scale*size, angle,
(x, y), s, color)
self.flush_clip()
def finish(self):
pass
#self.im.writePng( file('xx.png', 'w') )
def flush_clip(self):
imw, imh = self.im.size()
lb = 0, 0
ur = imw, imh
self.im.setClip(ur, lb)
def get_gd_color(self, rgb):
"""
RGB is a unit RGB tuple, return a gd color
"""
r,g,b = rgb
rgbi = (int(r*255),int(g*255),int(b*255))
try: return self._cached[rgbi]
except KeyError: pass
color = self.im.colorAllocate( rgbi )
if color==-1:
warnings.warn('Unable to allocate color %1.3f, %1.3f, %1.3f; using nearest neighbor' % rgb)
color = self.im.colorClosest(rgbi)
self._cached[rgbi] = color
return color
def get_text_scale(self):
"""
Return the scale factor for fontsize taking screendpi and pixels per
inch into account
"""
return self.dpi.get()/PIXELS_PER_INCH
def new_gc(self):
"""
Return an instance of a GraphicsContextGD
"""
return GraphicsContextGD( self.im, self )
def _set_gd_style(self, gc):
color = self.get_gd_color( gc.get_rgb() )
offset, dashes = gc.get_dashes()
if dashes is not None:
pixels = self.points_to_pixels(dashes)
style = []
for on, off in pieces(pixels):
if on<1: on = 1
else: on = round(on)
if off<1: off = 1
else: off = round(off)
style.extend([color]*on)
style.extend([gd.gdTransparent]*off)
self.im.setStyle(style)
return gd.gdStyled
else:
if gc.get_antialiased():
self.im.setAntiAliased(color)
return gd.gdAntiAliased
else:
self.im.setStyle([color])
return gd.gdStyled
def points_to_pixels(self, points):
"""
convert point measures to pixes using dpi and the pixels per
inch of the display
"""
return asarray(points)*(PIXELS_PER_INCH/72.0*self.dpi.get()/72.0)
class GraphicsContextGD(GraphicsContextBase):
"""
The graphics context provides the color, line styles, etc... See
the gtk and postscript backends for examples of mapping the
graphics context attributes (cap styles, join styles, line widths,
colors) to a particular backend. """
def __init__(self, im, renderer):
"""
Initialize with a gd image
"""
GraphicsContextBase.__init__(self)
self.im = im
self.renderer = renderer
def set_clip_rectangle(self, rectangle):
GraphicsContextBase.set_clip_rectangle(self, rectangle)
x,y,w,h = rectangle
imw, imh = self.im.size()
lb = int(x), imh-int(y)
ur = int(x+w), imh-int(y+h)
self.im.setClip(ur, lb)
def set_linestyle(self, style):
GraphicsContextBase.set_linestyle(self, style)
offset, dashes = self.dashd[style]
self.set_dashes(offset, dashes)
def set_linewidth(self, lw):
GraphicsContextBase.set_linewidth(self, lw)
pixels = self.renderer.points_to_pixels(lw)
if pixels<1: pixels = 1
else: pixels = round(pixels)
self.im.setThickness(pixels)
########################################################################
#
# The following functions and classes are for matlab compatibility
# mode (pylab) and implement figure managers, etc...
#
########################################################################
def new_figure_manager(num, *args, **kwargs):
"""
Add a new figure num (default autoincrement). For GUI
backends, you'll need to instantiate a new window and embed
the figure in it.
"""
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass(*args, **kwargs)
canvas = FigureCanvasGD(thisFig)
manager = FigureManagerGD(canvas, num)
return manager
class FigureCanvasGD(FigureCanvasBase):
def print_figure(self, filename, dpi=150, facecolor='w', edgecolor='w',
orientation='portrait', **kwargs):
"""
Render the figure to hardcopy using self.renderer as the
renderer if neccessary
filename can be a string filename or writable file instance
"""
origDPI = self.figure.dpi.get()
origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()
self.figure.dpi.set(dpi)
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)
im = self.draw()
if is_string_like(filename):
basename, ext = os.path.splitext(filename)
if not len(ext): filename += '.png'
im.writePng( filename )
self.figure.dpi.set(origDPI)
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
def draw(self):
"""
Draw to a gd image and return the image instance
"""
left, bottom, width, height = self.figure.bbox.get_bounds()
im = gd.image((int(width), int(height)))
if not hasattr(im, 'setAntiAliased'):
raise RuntimeError('gd_requirements_failed')
renderer = RendererGD(im, self.figure.dpi)
self.figure.draw(renderer)
renderer.finish()
return im
class FigureManagerGD(FigureManagerBase):
"""
This class manages all the figures for matlab mode
"""
pass
########################################################################
#
# Now just provide the standard names that backend.__init__ is expecting
#
########################################################################
FigureManager = FigureManagerGD
syntax highlighted by Code2HTML, v. 0.9.1