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

Reply via email to