// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to
// -----------------------------------------------------------------
// Uses the SVG Rendering Engine (http://svg.codeplex.com/) to extract Bezier
// curves from an SVG file in an XNA ContentImporter.
//
// See http://www.wam-games.com/2010/04/svg-in-xna/ for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Svg;
using System.Drawing.Drawing2D;
// Alias our output type -- A dictionary mapping a path name to a list of
// control points.
using PathDict = System.Collections.Generic.Dictionary>;
// The Svg library uses this matrix type not the XNA one:
using Matrix = System.Drawing.Drawing2D.Matrix;
namespace ContentPipeline {
[ContentImporter(".svg", DisplayName = "SVG Path Importer")]
public class SvgPathImporter : ContentImporter {
string filename;
public override PathDict Import(string filename, ContentImporterContext context) {
this.filename = filename;
SvgDocument doc = SvgDocument.Open(filename);
var paths = new PathDict();
collectPaths(ref paths, doc);
return paths;
}
static bool isBezier(SvgPath path) {
// All but the first point must be type '3'
const int bezierPointId = 3;
return path.Path.PathTypes.Skip(1).All(t => t == bezierPointId);
}
// Calculate the overall transform that applies to a node.
static Matrix getTransform(SvgElement node) {
var m = new Matrix();
while (node != null) {
if (node.Transforms != null && node.Transforms.Count > 0) {
var temp = new Matrix();
foreach (var trans in node.Transforms) {
temp.Multiply(trans.Matrix, MatrixOrder.Prepend);
}
m.Multiply(temp, MatrixOrder.Append);
}
node = node.Parent;
}
return m;
}
// Traverse the tree of SvgElement nodes recursively. If a node is of type
// SvgPath, and is a Bezier curve, then we process it and add it to our
// dictionary.
void collectPaths(ref PathDict paths, SvgElement e) {
SvgPath path = e as SvgPath;
if (path != null) {
if (!isBezier(path)) {
// Complain about any non-Bezier curves so we can fix them
// in the editor.
throw new InvalidContentException(
String.Format("Cubic Beziers only: {0}", path.ID),
new ContentIdentity(filename));
}
// Transform points in-place.
var pts = path.Path.PathPoints;
getTransform(e).TransformPoints(pts);
// Convert to Vector2.
var vpts = pts.Select(p => new Vector2(p.X, p.Y)).ToList();
paths.Add(path.ID, vpts);
}
foreach (var child in e.Children) {
collectPaths(ref paths, child);
}
}
}
}
// To load the path data in-game, use:
// var paths = Content.Load>>("test");