An update on my latest cairo adventures…
When cairo 1.10 comes out we’ll get a RecordingSurface so you can record commands and play them back to another surface, but how to do something similar now ?
Skip to the end if you just want the code, explanation of how I got there ahead:
My first advice was to try using PDFSurface, and set the file object to None.
# Record a red rectangle onto a surface. # # Create another surface and draw a background on it # Draw the first surface onto this surface # from cairo import PDFSurface, ImageSurface, FORMAT_ARGB32, Context recording = PDFSurface(None, 200, 200) target = ImageSurface(FORMAT_ARGB32, 200, 200) # Record a red rectangle cr = Context(recording) cr.set_source_rgb(1.0, 0.0, 1.0) cr.rectangle(20, 20, 10, 10) cr.fill_preserve() cr.set_source_rgb(0.0, 0.0, 1.0) cr.stroke() target_context = Context(target) # Draw background target_context.set_source_rgb(1.0, 1.0, 0) target_context.paint() # Replay recording to target target_context.set_source_surface(recording) target_context.paint() target.write_to_png('output.png')
That seems to work, except when I tried in Windows, when it complained that the file object was wrong.
OK, we can work round that:
def RecordingSurface(w, h): if os.name == 'nt': fobj = 'nul' else: fobj = None return PDFSurface(fobj, w, h)
This seems to be working, but my animation seemed slow… time for some benchmarking; I rendered 1000 frames and got a rough wall clock time:
Hmm… perhaps SVGSurface will be quicker:
This is much better, 20 seconds difference just by changing what kind of surface is returned!
Animation not smooth though
The animation still seemed jerky it occured to me that when the surfaces are disposed they will attempt to out their content to the file object !
Luckily, get_similar_surface() comes to the rescue; it returns a surface not associated with a file object. Using this the original surface can be kept around forever, and never output.
And here it is:
_svg_surface = None def RecordingSurface(*size): ''' We don't have RecordingSurfaces until cairo 1.10, so this kludge is used SVGSurfaces are created, but to stop them ever attempting to output, they are kept in a dict. When a surface is needed, create_similar is called to get a Surface from the SVGSurface of the same size ''' global _svg_surface if os.name == 'nt': fobj = 'nul' else: fobj = None if _svg_surface is None: _svg_surface = SVGSurface(fobj, 0, 0) return _svg_surface.create_similar(cairo.CONTENT_COLOR_ALPHA, *size)
This is really useful, you can record commands and play them back to other surfaces.