Yet Another [à compléter]

SVG With Mono for Android

One of the aspect of Android development is that your application ultimately run on a plethora of different devices.

Since it would be too easy, each of these devices usually sports its own combination of screen size, resolution and pixel density.

Technically, this would mean that as a developper you would need to run your app on a large enough device subset having a triplet of these parameters to ensure everything looks good.

Of course when we talk about pixels, the main problem arise with the use of bitmap-based drawable which need to be tailored to the kind of device you are running on.

Android makes it a bit easier by defining a set of density “standards” in the form of a different suffix for your drawable directory.

(Side note: if you don’t want your bitmaps to be altered due to that, put them in a drawable-nodpi directory).

The downside is that you now have to produce and maintain several version of the same graphic to accomodate this system.

A good way to avoid this problem is to use vector-based graphics to dynamically generate bitmap drawable that are suited to the screen size and density but unfortunately Android only support it’s own bitmap-that-can-extend-itself format called 9-patch which works great for buttons but is a bit limiting for anything else.

Thankfully, people have developped librairies that are able to load SVG and map it to Android’s Picture API. One of the main advantage of SVG is that it has great editors like Inkscape.

To use that support from Mono for Android, I cooked up a jar using code from svg-android and AndEngine which I then simply imported into a Mono for Android Binding project.

You can download the project structure ready to include into your solution as a zip file here.

To use it, here is a little tutorial. In one of my project, I’m using two SVG images that I need to composite together in a single bitmap.

One is a flag bearer:

And the other is an actual flag:

Notice that the flag bearer has a gradient rectangle placeholder where the flag will appear so that we can create a shadow effect when we composite the two images together.

In Inkscape, open up the SVG and be sure to save them as Optimized SVG so that they have the maximum compatibility with the library:

That will bring a dialog with several optimizations, I use this kind of combination:

Put the resulting SVG under a new folder called for instance raw under Resources. Then in code, you can reference your SVG with Resource.Raw.the_svg_name.

Following is the code I use to composite the two SVGs together, you really just have to use one type from the library to work with your graphics:

// At the top
using SVGParser = Org.Anddev.Andengine.Extension.Svg.SVGParser;

// The actual method
// width and height corresponds to the final dimensions of the resulting bitmap
// The stream instance is the flag SVG file to use
public static Bitmap GetFlagGuyWithFlagStream (Android.Content.Res.Resources resources,
                                               System.IO.Stream stream,
                                               int width, int height) 
{
	// This is the default widthxheight of my flag guy
	const int DefaultCanvasDimension = 264;
	// This is the default dimensions of the flag
	const int DefaultPlaceholderWidth = 146;
	const int DefaultPlaceholderHeight = 109;
	
	var guyFlag = SVGParser.ParseSVGFromResource (resources, Resource.Raw.flag_guy);
	var flag = SVGParser.ParseSVGFromInputStream (flag , null);
	
	var bmp = Bitmap.CreateBitmap (width, height, Bitmap.Config.Argb8888);
	using (var c = new Canvas (bmp)) {
		// The "complex" math is here
		var computedWidth = width * DefaultPlaceholderWidth / ((float)DefaultCanvasDimension);
		var computedHeight = height * DefaultPlaceholderHeight / ((float)DefaultCanvasDimension);

		// To draw your SVG you simply use the equivalent Picture object created
		// by the parser and the DrawPicture method of the Canvas API
		var dstF = new RectF (width - computedWidth, 0, width, computedHeight);
		c.DrawPicture (flag.Picture, dstF);

		// The size of the SVG is controlled by the dst rectangle you pass
		// to DrawPicture
		var dst = new Rect (0, 0, width, height);
		c.DrawPicture (guyFlag.Picture, dst);
	}

	// I create a new bitmap here so that it's returned as an immutable variant
	return Bitmap.CreateBitmap (bmp);
}

Final result in app:

PS: the Optimized SVG option in Inkscape is actually a Python script that you can directly use if you want to do bulk conversion of existing SVG. It’s located in Inkscape.app/Contents/Resources/extensions/scour.py, issue --help on the script to see the command-line equivalents of the GUI checkboxes.