CL游戏引擎 - 图形 - SVG [完整实现]

SVG

我们在这里只简单的对SVG文件一些基本支持,我的目的并非制作完整的SVG图像查看器。SVG的支持主要目的是获得诸如矩形和贝塞尔路径之类的形状。这些可用于Level布局,AI路径或各种其他目的。

SVG解析器支持组,路径,rects,线,圆,椭圆,多边形,折线和图像。可以嵌入图像,可通过URL或内容文件夹访问。还包括调试渲染器组件,用于测试SVG文件。您只需将其添加到场景中的实体即可使用它,如下所示:

var svgEntity = createEntity( "svg" );
svgEntity.addComponent( new SvgDebugComponent( "mySvgFile.svg" ) );

为什么支持SVG

原因是PCL无法访问System.Drawing,因此使用了使用relection的ISvgPathBuilder。

如何直接解析路径

您也可以从SVG文件中仅访问路径,而无需解析整个文件。 为此,您只需要从SVG文件中获取’path’元素中’d’属性的内容。 这使您可以快速轻松地获得bezier和其他路径数据。 示例代码如下:

var svgPath = new SvgPath();
svgPath.d = "SVG文件中'd'属性的内容";
var points = svgPath.getTransformedDrawingPoints( new SvgPathBuilder() );

了解以上的内容后,我们开始实现SVG支持的代码。

  • 所有SVG元素的基类。 有一些帮助解析颜色和处理变换。
public abstract class SvgElement
{
	[XmlAttribute( "id" )]
	public string id;

	[XmlAttribute( "stroke" )]
	public string strokeAttribute
	{
		get { return null; }
		set
		{
			if( value.StartsWith( "#" ) )
				strokeColor = ColorExt.hexToColor( value.Substring( 1 ) );
		}
	}

	public Color strokeColor = Color.Red;

	[XmlAttribute( "fill" )]
	public string fillAttribute
	{
		get { return null; }
		set
		{
			if( value.StartsWith( "#" ) )
				fillColor = ColorExt.hexToColor( value.Substring( 1 ) );
		}
	}

	public Color fillColor;

	[XmlAttribute( "stroke-width" )]
	public string strokeWidthAttribute
	{
		get { return null; }
		set { float.TryParse( value, out strokeWidth ); }
	}

	public float strokeWidth = 1;

	[XmlAttribute( "transform" )]
	public string transformAttribute
	{
		get { return null; }
		set { _transforms = SvgTransformConverter.parseTransforms( value ); }
	}

	protected List<SvgTransform> _transforms;


	public Matrix2D getCombinedMatrix()
	{
		var m = Matrix2D.identity;
		if( _transforms != null && _transforms.Count > 0 )
		{
			foreach( var trans in _transforms )
				m = Matrix2D.multiply( m, trans.matrix );
		}

		return m;
	}

	/// <summary>
	/// 只是循环遍历所有变换的辅助属性,如果有一个SvgRotate变换,它将返回该角度
	/// </summary>
	public float rotationDegrees
	{
		get
		{
			if( _transforms == null )
				return 0;

			for( var i = 0; i < _transforms.Count; i++ )
			{
				if( _transforms[i] is SvgRotate )
					return ( _transforms[i] as SvgRotate ).angle;
			}

			return 0;
		}
	}

}
  • 处理解析组,路径,rects,线,圆,椭圆,多边形,折线和图像。 这只是SVG规范的一小部分! 仅解析基础知识,因为它不是设计为图像查看器。
[XmlRoot( ElementName = "svg", Namespace = "http://www.w3.org/2000/svg" )]
public class SvgDocument : SvgGroup
{
	[XmlAttribute( "width" )]
	public string widthAttribute
	{
		get { return null; }
		set { width = int.Parse( Regex.Replace( value, @"[^\d]", string.Empty ) ); }
	}
	public int width;

	[XmlAttribute( "height" )]
	public string heightAttribute
	{
		get { return null; }
		set { height = int.Parse( Regex.Replace( value, @"[^\d]", string.Empty ) ); }
	}
	public int height;



	public static SvgDocument open( Stream stream )
	{
		var serializer = new XmlSerializer( typeof( SvgDocument ) );
		return (SvgDocument)serializer.Deserialize( stream );
	}

}
  • SVG中的容器。 'g’XML标签。
public class SvgGroup : SvgElement
{
	[XmlElement( "title" )]
	public string title;

	[XmlElement( "g" )]
	public SvgGroup[] groups;

	[XmlElement( "path" )]
	public SvgPath[] paths;

	[XmlElement( "rect" )]
	public SvgRectangle[] rectangles;

	[XmlElement( "line" )]
	public SvgLine[] lines;

	[XmlElement( "circle" )]
	public SvgCircle[] circles;

	[XmlElement( "ellipse" )]
	public SvgEllipse[] ellipses;

	[XmlElement( "polygon" )]
	public SvgPolygon[] polygons;

	[XmlElement( "polyline" )]
	public SvgPolyline[] polylines;

	[XmlElement( "image" )]
	public SvgImage[] images;
}
public class SvgPolygon : SvgElement
{
	[XmlAttribute( "cx" )]
	public float centerX;

	[XmlAttribute( "cy" )]
	public float centerY;

	[XmlAttribute( "sides" )]
	public int sides;

	[XmlAttribute( "points" )]
	public string pointsAttribute
	{
		get { return null; }
		set { parsePoints( value ); }
	}

	public Vector2[] points;


	void parsePoints( string str )
	{
		var pairs = str.Split( new char[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries );
		points = new Vector2[pairs.Length];

		for( var i = 0; i < pairs.Length; i++ )
		{
			var parts = pairs[i].Split( new char[] { ',' }, System.StringSplitOptions.RemoveEmptyEntries );
			points[i] = new Vector2( float.Parse( parts[0] ), float.Parse( parts[1] ) );
		}
	}


	public Vector2[] getTransformedPoints()
	{
		var pts = new Vector2[points.Length];
		var mat = getCombinedMatrix();
		Vector2Ext.transform( points, ref mat, pts );

		return pts;
	}


	/// <summary>
	/// 获得相对于中心的点。 SVG默认使用点的绝对位置。
	/// </summary>
	public Vector2[] getRelativePoints()
	{
		var pts = new Vector2[points.Length];

		var center = new Vector2( centerX, centerY );
		for( var i = 0; i < points.Length; i++ )
			pts[i] = points[i] - center;

		return pts;
	}

}
public class SvgCircle : SvgElement
{
	[XmlAttribute( "r" )]
	public float radius;

	[XmlAttribute( "cy" )]
	public float centerY;

	[XmlAttribute( "cx" )]
	public float centerX;
}
public class SvgEllipse : SvgElement
{
	[XmlAttribute( "rx" )]
	public float radiusX;

	[XmlAttribute( "ry" )]
	public float radiusY;

	[XmlAttribute( "cy" )]
	public float centerY;

	[XmlAttribute( "cx" )]
	public float centerX;
}
  • 表示SVG文档中的图像标记。 此类将从href属性加载图像。 它将检查嵌入的图像,基于Web的图像,然后回退到使用href从ContentManager加载
public class SvgImage : SvgElement
{
	[XmlAttribute( "x" )]
	public float x;

	[XmlAttribute( "y" )]
	public float y;

	[XmlAttribute( "width" )]
	public float width;

	[XmlAttribute( "height" )]
	public float height;

	/// <summary>
	/// 包含此图像的矩形。 请注意,rect没有应用变换。
	/// </summary>
	public RectangleF rect { get { return new RectangleF( x, y, width, height ); } }

	[XmlAttribute( "href", Namespace = "http://www.w3.org/1999/xlink" )]
	public string href;

	/// <summary>
	/// 标志,确定我们是否尝试加载纹理。 我们只尝试加载一次。
	/// </summary>
	bool _didAttemptTextureLoad;

	/// <summary>
	/// 如果成功加载,则缓存纹理
	/// </summary>
	Texture2D _texture;


	/// <summary>
	/// 试图获得图像的纹理
	///  - 首先它会检查href是否有png文件名。 如果找到一个,它将使用传入的ContentManager加载它
	///  - 接下来它将看到href是否是一个url,如果是,它将加载它
	///  - 接下来它检查嵌入的base64图像。如果找到一个, 则它会加载
	/// </summary>
	public Texture2D getTexture( EngineContentManager content )
	{
		if( _didAttemptTextureLoad || _texture != null )
			return _texture;

		// 检查一个url
		if( href.StartsWith( "http" ) )
		{
			using( var client = new System.Net.Http.HttpClient() )
			{
				var stream = client.GetStreamAsync( href ).Result;
				_texture = Texture2D.FromStream( Core.graphicsDevice, stream );
			}
		}
		// 看看我们是否有href中png文件的路径
		else if( href.EndsWith( "png" ) )
		{
			// 在尝试加载之前检查是否存在! 
			try
			{
				if( content != null )
					_texture = content.Load<Texture2D>( href );
			}
			catch( ContentLoadException )
			{
				Debug.Debug.error( "无法从href加载SvgImage: {0}", href );
			}
		}
		// 尝试解析base64字符串(如果它嵌入在href中)
		else if( href.StartsWith( "data:" ) )
		{
			var startIndex = href.IndexOf( "base64,", StringComparison.OrdinalIgnoreCase ) + 7;
			var imageContents = href.Substring( startIndex );
			var bytes = Convert.FromBase64String( imageContents );

			using( var m = new MemoryStream() )
			{
				m.Write( bytes, 0, bytes.Length );
				m.Seek( 0, SeekOrigin.Begin );

				_texture = Texture2D.FromStream( Core.graphicsDevice, m );
			}
		}

		_didAttemptTextureLoad = true;
		return _texture;
	}

}
public class SvgLine : SvgElement
{
	[XmlAttribute( "x1" )]
	public float x1;

	[XmlAttribute( "y1" )]
	public float y1;

	[XmlAttribute( "x2" )]
	public float x2;

	[XmlAttribute( "y2" )]
	public float y2;

	public Vector2 start { get { return new Vector2( x1, y1 ); } }
	public Vector2 end { get { return new Vector2( x2, y2 ); } }


	public Vector2[] getTransformedPoints()
	{
		var pts = new Vector2[] { start, end };
		var mat = getCombinedMatrix();
		Vector2Ext.transform( pts, ref mat, pts );

		return pts;
	}

}
public class SvgPolyline : SvgElement
{
	[XmlAttribute( "points" )]
	public string pointsAttribute
	{
		get { return null; }
		set { parsePoints( value ); }
	}

	public Vector2[] points;


	void parsePoints( string str )
	{
		// 标准化逗号和空格,因为某些程序使用逗号分隔点而其他程序使用空格
		str = str.Replace( ',', ' ' );
		var pairs = str.Split( new char[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries );
		points = new Vector2[pairs.Length / 2];

		var pointIndex = 0;
		for( var i = 0; i < pairs.Length; i += 2 )
			points[pointIndex++] = new Vector2( float.Parse( pairs[i] ), float.Parse( pairs[i + 1] ) );
	}


	public Vector2[] getTransformedPoints()
	{
		var pts = new Vector2[points.Length];
		var mat = getCombinedMatrix();
		Vector2Ext.transform( points, ref mat, pts );

		return pts;
	}
}
public class SvgRectangle : SvgElement
{
	[XmlAttribute( "x" )]
	public float x;

	[XmlAttribute( "y" )]
	public float y;

	[XmlAttribute( "width" )]
	public float width;

	[XmlAttribute( "height" )]
	public float height;

	public Vector2 center { get { return new Vector2( x + width / 2, y + height / 2 ); } }


	/// <summary>
	/// 获取应用了所有变换的矩形的点
	/// </summary>
	public Vector2[] getTransformedPoints()
	{
		var pts = new Vector2[] { new Vector2( x, y ), new Vector2( x + width, y ), new Vector2( x + width, y + height ), new Vector2( x, y + height ) };
		var mat = getCombinedMatrix();
		Vector2Ext.transform( pts, ref mat, pts );

		return pts;
	}

}
public abstract class SvgTransform
{
	public Matrix2D matrix;
}
public class SvgMatrix : SvgTransform
{
	List<float> _points;


	public SvgMatrix( List<float> points )
	{
		_points = points;
		matrix = new Matrix2D(
				_points[0],
				_points[1],
				_points[2],
				_points[3],
				_points[4],
				_points[5]
			);
	}


	public override string ToString()
	{
		return string.Format( CultureInfo.InvariantCulture, "matrix({0}, {1}, {2}, {3}, {4}, {5})",
			_points[0], _points[1], _points[2], _points[3], _points[4], _points[5] );
	}

}
public class SvgRotate : SvgTransform
{
	public float angle;
	public float centerX;
	public float centerY;


	public SvgRotate( float angle )
	{
		this.angle = angle;

		calculateMatrix();
	}


	public SvgRotate( float angle, float centerX, float centerY )
	{
		this.angle = angle;
		this.centerX = centerX;
		this.centerY = centerY;

		calculateMatrix();
	}


	void calculateMatrix()
	{
		var mat = Matrix2D.createTranslation( -centerX, -centerY );
		mat.multiplyRotation( angle * Mathf.deg2Rad );
		mat.multiplyTranslation( centerX, centerY );

		matrix = mat;
	}


	public override string ToString()
	{
		return string.Format( CultureInfo.InvariantCulture, "rotate({0}, {1}, {2})", angle, centerX, centerY );
	}
}
public class SvgScale : SvgTransform
{
	float _scaleX;
	float _scaleY;


	public SvgScale( float x, float y )
	{
		_scaleX = x;
		_scaleY = y;

		matrix = Matrix2D.createScale( _scaleX, _scaleY );
	}


	public SvgScale( float x ) : this( x, x )
	{}


	public override string ToString()
	{
		if( _scaleX == _scaleY )
			return string.Format( CultureInfo.InvariantCulture, "scale({0})", _scaleX );
		return string.Format( CultureInfo.InvariantCulture, "scale({0}, {1})", _scaleX, _scaleY );
	}

}
public class SvgShear : SvgTransform
{
	float _shearX;
	float _shearY;


	public SvgShear( float shearX, float shearY )
	{
		_shearX = shearX;
		_shearY = shearY;
		Debug.warn( "SvgSkew剪切未实现" );
	}


	public override string ToString()
	{
		return string.Format( CultureInfo.InvariantCulture, "shear({0}, {1})", _shearX, _shearY );
	}

}
public class SvgSkew : SvgTransform
{
	float _angleX;
	float _angleY;


	public SvgSkew( float angleX, float angleY )
	{
		_angleX = angleX;
		_angleY = angleY;

		Debug.warn( "SvgSkew矩阵未实现" );
		//matrix = Matrix2D.Shear(
		//	(float)System.Math.Tan( _angleX / 180 * MathHelper.Pi ),
		//	(float)System.Math.Tan( _angleY / 180 * MathHelper.Pi ) );
	}


	public override string ToString()
	{
		if( _angleY == 0 )
			return string.Format( CultureInfo.InvariantCulture, "skewX({0})", _angleX );

		return string.Format( CultureInfo.InvariantCulture, "skewY({0})", _angleY );
	}

}
public class SvgTranslate : SvgTransform
{
	float _x;
	float _y;


	public SvgTranslate( float x, float y = 0 )
	{
		_x = x;
		_y = y;

		matrix = Matrix2D.createTranslation( _x, _y );
	}


	public override string ToString()
	{
		return string.Format( CultureInfo.InvariantCulture, "translate({0}, {1})", _x, _y );
	}

}

猜你喜欢

转载自blog.csdn.net/u011488756/article/details/85168163
今日推荐