I’m working on a game for the iPhone platform. The levels will be quite big, and on top of that I need them in 2 different resolutions (because of the iPhone 4 retina display). Having them in the bundle in a bitmap based format would make the final bundle several hundred megabytes big, which is unacceptable. So I figured that it would be better to store them in a vector based format instead, since I create the levels in InkScape anyway.
At first I was thinking about the PDF format. But then I realized that I could use the same vector data to construct the physics world in Box2D. And for this task, the SVG format is brilliant. It’s an easy-to-parse XML format and it can have arbitrary text-based data associated with each object. That means I can put implementation specific data in the file directly from InkScape (like if an object should be static or dynamic in Box2D for example).
As it turns out, though, CoreGraphics does not support the SVG format. Bummer. Sure, UIWebView can render SVG’s, but I could not find a good way to use that for off-screen rendering. So I went and started looking for an open source library that could render an SVG into a pixel buffer. I found only one; librsvg. And then a few others, but they all seemed to depend on librsvg.
There are two problems with librsvg:
1. It’s LGPL, which if I’ve understood correctly will require me to release my game under a compatible licence unless I link librsvg dynamically. Unfortunatly, this cannot be done in the iPhone SDK.
2. After hours of trying, I could not get librsvg to compile for the iPhone.
So I gave up on 3rd party libs and began writing my own SVG renderer in Obj-c. Turns out it wasn’t very hard to get the most basic stuff up and running.
And this is it. SVGQuartzRender. It’s by no means a complete implementation of the SVG specification, but it can render the most basic stuff found in an SVG, and it can conveniently render it to a CGImageRef.
This is how to use it, in a nutshell:
Somewhere in your class, the init method for example:
SVGQuartzRenderer *svgRenderer = [[SVGQuartzRenderer alloc] init]; // A delegate must be specified [svgRenderer setDelegate:self];
You also need to implement the <SVGQuartzRendererDelegate> protocol:
// This is called before rendering begins. Here you need to supply the CGContext where you want the
//rendering to be done. If you just want an empty, off-screen bitmap context, you can use the
//convenience method -(CGContextRef)createBitmapContext
- (CGContextRef)svgRenderer:(id)renderer
requestedCGContextWithSize:(CGSize)size
{
return [renderer createBitmapContext];;
}
// Will be called when rendering is complete
- (void)svgRenderer:(id)renderer
didFinnishRenderingFile:(NSString *)file
inCGContext:(CGContextRef)context
{
// Here's your CGImageRef!
CGImageRef svgDrawing = CGBitmapContextCreateImage(context);
}
In the XCode project, there is a simple Cocoa app that demostrates more thoroughly how it is used.
I’ve put up a GitHub repo for SVGQuartzRenderer. If you want it, you should get it from there.
Feel free to use this code how ever you like, commercially or non-commercially. Should you make any improvements I’d love to get a patch! :)
Tags: Development, iPhone Dev, Objective-C, OSX, SVG
By the way, for anyone who is looking for a way to construct Box2D worlds from SVG, you could simply copy the code that parses the path nodes in SVGQuartzRenderer.m, and make a b2Vec array instead of CoreGraphics lines. And then simply feed that array to the DecomposeConvexAndAddTo method (from the ConvexDecomposition util included with Box2D). Works fairly well, but you usually need to tweak your paths at certian points to avoid gaps and holes in the final body.
Hi there. How is the performance of this vs UIWebView for rendering?
I think performance is slightly worse than UIWebView thus far, or at least not any faster. Compatibility with the SVG spec is also not at the same level as UIWebView.
The strength of SVGQuartzRenderer vs UIWebView is that it allows you to render to an off-screen context, so that you can use the result as a texture or save it in an other format, etc. As far as I know, there is no way you can do off-screen rendering with UIWebView.
But if you only intend to display the SVG on screen, then you are probably better of using UIWebView.
Great. Thanks.
Hi Stardust,
Great post.
I’ve thought of doing the same many time, but I always thought it foreboding as well.
I tried it out and it’s quite impressive.
I’m under the impression that the LGPL allow complete usage without having to give up modifications or code that binds to it. It is very similar to the BSD licence that allowed Apple to use BSD without providing changes back or releasing source.
Hi !
This seems to be an appropriate answer to my problem. I want to generate PDF file from an iOS app. In this file I need to include some vectorial pictures. This is always the same pictures (technical drawing). So I am looking to convert a SVG file to quartz data I then can reuse. Does SVGQuartzRenderer can achieve this ?
Hi!
You would have to modify it slightly in order to return the CGPathRefs instead of simply a CGImageRef which is currently the case (this is pixel based).
However, in your case it might be better to store the pictures as PDF files instead of SVG, and rely on Quartz’s built-in PDF support.