Revision: 4398
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4398&view=rev
Author: mdboom
Date: 2007-11-20 13:00:20 -0800 (Tue, 20 Nov 2007)
Log Message:
-----------
Support mixed-mode rendering the PDF backend. This allows some things
to be rendered as vectors and some as rasters. At the moment, mostly
as a proof-of-concept, all quadmeshes are rendered as rasters with the
PDF backend.
Also make PDF backend resolution independent.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_pdf.py
branches/transforms/lib/matplotlib/collections.py
Added Paths:
-----------
branches/transforms/lib/matplotlib/backends/backend_mixed.py
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-11-20 20:21:53 UTC
(rev 4397)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-11-20 21:00:20 UTC
(rev 4398)
@@ -9,6 +9,7 @@
import numpy as npy
import matplotlib.cbook as cbook
import matplotlib.colors as colors
+import matplotlib._image as _image
import matplotlib.path as path
import matplotlib.transforms as transforms
import matplotlib.widgets as widgets
@@ -328,7 +329,13 @@
def strip_math(self, s):
return cbook.strip_math(s)
+ def start_rasterizing(self):
+ pass
+ def stop_rasterizing(self):
+ pass
+
+
class GraphicsContextBase:
"""An abstract base class that provides color, line styles, etc...
"""
Added: branches/transforms/lib/matplotlib/backends/backend_mixed.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_mixed.py
(rev 0)
+++ branches/transforms/lib/matplotlib/backends/backend_mixed.py
2007-11-20 21:00:20 UTC (rev 4398)
@@ -0,0 +1,142 @@
+from matplotlib._image import frombuffer
+from matplotlib.backends.backend_agg import RendererAgg
+
+class MixedModeRenderer(object):
+ """
+ A helper class to implement a renderer that switches between
+ vector and raster drawing. An example may be a PDF writer, where
+ most things are drawn with PDF vector commands, but some very
+ complex objects, such as quad meshes, are rasterised and then
+ output as images.
+ """
+ def __init__(self, width, height, dpi, vector_renderer,
raster_renderer_class=None):
+ """
+ width: The width of the canvas in logical units
+
+ height: The height of the canvas in logical units
+
+ dpi: The dpi of the canvas
+
+ vector_renderer: An instance of a subclass of RendererBase
+ that will be used for the vector drawing.
+
+ raster_renderer_class: The renderer class to use for the
+ raster drawing. If not provided, this will use the Agg
+ backend (which is currently the only viable option anyway.)
+ """
+ if raster_renderer_class is None:
+ raster_renderer_class = RendererAgg
+
+ self._raster_renderer_class = raster_renderer_class
+ self._width = width
+ self._height = height
+ self._dpi = dpi
+
+ assert not vector_renderer.option_image_nocomposite()
+ self._vector_renderer = vector_renderer
+ vector_renderer.start_rasterizing = self.start_rasterizing
+ vector_renderer.stop_rasterizing = self.stop_rasterizing
+
+ self._raster_renderer = None
+ self._rasterizing = False
+
+ self._renderer = self._vector_renderer
+
+ def start_rasterizing(self):
+ """
+ Enter "raster" mode. All subsequent drawing commands (until
+ stop_rasterizing is called) will be drawn with the raster
+ backend.
+
+ If start_rasterizing is called multiple times before
+ stop_rasterizing is called, this method has no effect.
+ """
+ if not self._rasterizing:
+ self._raster_renderer = self._raster_renderer_class(
+ self._width*self._dpi, self._height*self._dpi, self._dpi)
+ self._raster_renderer.start_rasterizing = self.start_rasterizing
+ self._raster_renderer.stop_rasterizing = self.stop_rasterizing
+ self._renderer = self._raster_renderer
+ self._rasterizing = True
+
+ def stop_rasterizing(self):
+ """
+ Exit "raster" mode. All of the drawing that was done since
+ the last start_rasterizing command will be copied to the
+ vector backend by calling draw_image.
+
+ If stop_rasterizing is called multiple times before
+ start_rasterizing is called, this method has no effect.
+ """
+ if self._rasterizing:
+ width, height = self._width * self._dpi, self._height * self._dpi
+ buffer = self._raster_renderer.buffer_rgba(0, 0)
+ image = frombuffer(buffer, width, height, True)
+ image.is_grayscale = False
+
+ self._renderer = self._vector_renderer
+ self._renderer.draw_image(0, 0, image, None)
+ self._raster_renderer = None
+ self._rasterizing = False
+
+ def get_canvas_width_height(self):
+ 'return the canvas width and height in display coords'
+ return self._width, self._height
+
+ # The rest of this methods simply delegate to the currently active
+ # rendering backend.
+
+ def open_group(self, *args, **kwargs):
+ return self._renderer.open_group(*args, **kwargs)
+
+ def close_group(self, *args, **kwargs):
+ return self._renderer.close_group(*args, **kwargs)
+
+ def draw_path(self, *args, **kwargs):
+ return self._renderer.draw_path(*args, **kwargs)
+
+ def draw_markers(self, *args, **kwargs):
+ return self._renderer.draw_markers(*args, **kwargs)
+
+ def draw_path_collection(self, *args, **kwargs):
+ return self._renderer.draw_path_collection(*args, **kwargs)
+
+ def draw_quad_mesh(self, *args, **kwargs):
+ return self._renderer.draw_quad_mesh(*args, **kwargs)
+
+ def get_image_magnification(self, *args, **kwargs):
+ return self._renderer.get_image_magnification(*args, **kwargs)
+
+ def draw_image(self, *args, **kwargs):
+ return self._renderer.draw_image(*args, **kwargs)
+
+ def draw_tex(self, *args, **kwargs):
+ return self._renderer.draw_tex(*args, **kwargs)
+
+ def draw_text(self, *args, **kwargs):
+ return self._renderer.draw_text(*args, **kwargs)
+
+ def flipy(self, *args, **kwargs):
+ return self._renderer.flipy(*args, **kwargs)
+
+ def option_image_nocomposite(self, *args, **kwargs):
+ return self._vector_renderer.option_image_nocomposite(*args, **kwargs)
+
+ def get_texmanager(self, *args, **kwargs):
+ return self._renderer.get_texmanager(*args, **kwargs)
+
+ def get_text_width_height_descent(self, *args, **kwargs):
+ return self._renderer.get_text_width_height_descent(*args, **kwargs)
+
+ def new_gc(self, *args, **kwargs):
+ return self._renderer.new_gc(*args, **kwargs)
+
+ def points_to_pixels(self, *args, **kwargs):
+ return self._renderer.points_to_pixels(*args, **kwargs)
+
+ def strip_math(self, *args, **kwargs):
+ return self._renderer(*args, **kwargs)
+
+ def finalize(self, *args, **kwargs):
+ return self._renderer.finalize(*args, **kwargs)
+
Modified: branches/transforms/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-11-20
20:21:53 UTC (rev 4397)
+++ branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-11-20
21:00:20 UTC (rev 4398)
@@ -20,10 +20,11 @@
from sets import Set
import matplotlib
-from matplotlib import __version__, rcParams, agg, get_data_path
+from matplotlib import __version__, rcParams, get_data_path
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
FigureManagerBase, FigureCanvasBase
+from matplotlib.backends.backend_mixed import MixedModeRenderer
from matplotlib.cbook import Bunch, enumerate, is_string_like, reverse_dict, \
get_realpath_and_stat, is_writable_file_like
from matplotlib.figure import Figure
@@ -327,8 +328,9 @@
class PdfFile:
"""PDF file with one page."""
- def __init__(self, width, height, filename):
+ def __init__(self, width, height, dpi, filename):
self.width, self.height = width, height
+ self.dpi = dpi
self.nextObject = 1 # next free object id
self.xrefTable = [ [0, 65535, 'the zero object'] ]
self.passed_in_file_object = False
@@ -379,7 +381,7 @@
thePage = { 'Type': Name('Page'),
'Parent': pagesObject,
'Resources': resourceObject,
- 'MediaBox': [ 0, 0, 72*width, 72*height ],
+ 'MediaBox': [ 0, 0, dpi*width, dpi*height ],
'Contents': contentObject }
self.writeObject(thePageObject, thePage)
@@ -1003,8 +1005,9 @@
rgba = npy.fromstring(s, npy.uint8)
rgba.shape = (h, w, 4)
- rgb = rgba[:,:,:4]
- return h, w, rgb.tostring()
+ rgb = rgba[:,:,:3]
+ a = rgba[:,:,3:]
+ return h, w, rgb.tostring(), a.tostring()
def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
rgbat = im.as_rgba_str()
@@ -1022,20 +1025,36 @@
img.flipud_out()
if img.is_grayscale:
height, width, data = self._gray(img)
- colorspace = Name('DeviceGray')
+ self.beginStream(
+ pair[1].id,
+ self.reserveObject('length of image stream'),
+ {'Type': Name('XObject'), 'Subtype': Name('Image'),
+ 'Width': width, 'Height': height,
+ 'ColorSpace': Name('DeviceGray'), 'BitsPerComponent': 8 })
+ self.currentstream.write(data) # TODO: predictors (i.e.,
output png)
+ self.endStream()
else:
- height, width, data = self._rgb(img)
- colorspace = Name('DeviceRGB')
+ height, width, data, adata = self._rgb(img)
+ smaskObject = self.reserveObject("smask")
+ stream = self.beginStream(
+ smaskObject.id,
+ self.reserveObject('length of smask stream'),
+ {'Type': Name('XObject'), 'Subtype': Name('Image'),
+ 'Width': width, 'Height': height,
+ 'ColorSpace': Name('DeviceGray'), 'BitsPerComponent': 8 })
+ self.currentstream.write(adata) # TODO: predictors (i.e.,
output png)
+ self.endStream()
- self.beginStream(
- pair[1].id,
- self.reserveObject('length of image stream'),
- {'Type': Name('XObject'), 'Subtype': Name('Image'),
- 'Width': width, 'Height': height,
- 'ColorSpace': colorspace, 'BitsPerComponent': 8 })
- self.currentstream.write(data) # TODO: predictors (i.e., output
png)
- self.endStream()
-
+ self.beginStream(
+ pair[1].id,
+ self.reserveObject('length of image stream'),
+ {'Type': Name('XObject'), 'Subtype': Name('Image'),
+ 'Width': width, 'Height': height,
+ 'ColorSpace': Name('DeviceRGB'), 'BitsPerComponent': 8,
+ 'SMask': smaskObject})
+ self.currentstream.write(data) # TODO: predictors (i.e.,
output png)
+ self.endStream()
+
img.flipud_out()
def markerObject(self, path, trans, fillp, lw):
@@ -1152,7 +1171,7 @@
self.afm_font_cache = {}
self.file.used_characters = self.used_characters = {}
self.mathtext_parser = MathTextParser("Pdf")
- self.image_magnification = dpi/72.0
+ self.dpi = dpi
self.tex_font_map = None
def finalize(self):
@@ -1194,19 +1213,16 @@
stat_key, (realpath, Set()))
used_characters[1].update(set)
- def get_image_magnification(self):
- return self.image_magnification
-
def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
#print >>sys.stderr, "draw_image called"
# MGDTODO: Support clippath here
gc = self.new_gc()
- gc.set_clip_rectangle(bbox.bounds)
+ if bbox is not None:
+ gc.set_clip_rectangle(bbox)
self.check_gc(gc)
h, w = im.get_size_out()
- h, w = h/self.image_magnification, w/self.image_magnification
imob = self.file.imageObject(im)
self.file.output(Op.gsave, w, 0, 0, h, x, y, Op.concat_matrix,
imob, Op.use_xobject, Op.grestore)
@@ -1246,7 +1262,7 @@
def draw_mathtext(self, gc, x, y, s, prop, angle):
# TODO: fix positioning and encoding
width, height, descent, glyphs, rects, used_characters = \
- self.mathtext_parser.parse(s, 72, prop)
+ self.mathtext_parser.parse(s, self.dpi, prop)
self.merge_used_characters(used_characters)
# When using Type 3 fonts, we can't use character codes higher
@@ -1311,7 +1327,7 @@
texmanager = self.get_texmanager()
fontsize = prop.get_size_in_points()
dvifile = texmanager.make_dvi(s, fontsize)
- dvi = dviread.Dvi(dvifile, 72)
+ dvi = dviread.Dvi(dvifile, self.dpi)
page = iter(dvi).next()
dvi.close()
@@ -1539,13 +1555,13 @@
texmanager = self.get_texmanager()
fontsize = prop.get_size_in_points()
dvifile = texmanager.make_dvi(s, fontsize)
- dvi = dviread.Dvi(dvifile, 72)
+ dvi = dviread.Dvi(dvifile, self.dpi)
page = iter(dvi).next()
dvi.close()
return page.width, page.height, page.descent
if ismath:
w, h, d, glyphs, rects, used_characters = \
- self.mathtext_parser.parse(s, 72, prop)
+ self.mathtext_parser.parse(s, self.dpi, prop)
elif rcParams['pdf.use14corefonts']:
font = self._get_font_afm(prop)
@@ -1583,14 +1599,14 @@
font = FT2Font(str(filename))
self.truetype_font_cache[key] = font
font.clear()
- font.set_size(prop.get_size_in_points(), 72.0)
+ font.set_size(prop.get_size_in_points(), self.dpi)
return font
def flipy(self):
return False
def get_canvas_width_height(self):
- return self.file.width / 72.0, self.file.height / 72.0
+ return self.file.width / self.dpi, self.file.height / self.dpi
def new_gc(self):
return GraphicsContextPdf(self.file)
@@ -1830,11 +1846,12 @@
return 'pdf'
def print_pdf(self, filename, **kwargs):
- dpi = kwargs.get('dpi', None)
- self.figure.set_dpi(72) # Override the dpi kwarg
+ dpi = kwargs.get('dpi', 72)
+ self.figure.set_dpi(dpi) # Override the dpi kwarg
width, height = self.figure.get_size_inches()
- file = PdfFile(width, height, filename)
- renderer = RendererPdf(file, dpi)
+ file = PdfFile(width, height, dpi, filename)
+ renderer = MixedModeRenderer(
+ width, height, dpi, RendererPdf(file, dpi))
self.figure.draw(renderer)
renderer.finalize()
file.close()
Modified: branches/transforms/lib/matplotlib/collections.py
===================================================================
--- branches/transforms/lib/matplotlib/collections.py 2007-11-20 20:21:53 UTC
(rev 4397)
+++ branches/transforms/lib/matplotlib/collections.py 2007-11-20 21:00:20 UTC
(rev 4398)
@@ -453,11 +453,13 @@
offsets = transOffset.transform_non_affine(offsets)
transOffset = transOffset.get_affine()
+ renderer.start_rasterizing()
renderer.draw_quad_mesh(
transform.frozen(), self.clipbox, clippath, clippath_trans,
self._meshWidth, self._meshHeight, self._coordinates,
offsets, transOffset, self._facecolors, self._antialiased,
self._showedges)
+ renderer.stop_rasterizing()
renderer.close_group(self.__class__.__name__)
class PolyCollection(Collection):
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins