当前位置:网站首页>Xamarin版的C# SVG路径解析器
Xamarin版的C# SVG路径解析器
2022-04-23 06:27:00 【大可山人】
Xamarin版的C# SVG路径解析器,对SVG的Path路径进行解析,其中包括:
主程序SvgPathParser.cs,
相关接口定义:ISourceFormatter.cs,
辅助类:FormatterRocks.cs,
从接口派生的CSharpCoreGraphicsFormatter.cs。
//主程序SvgPathParser.cs:
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012-2013 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/xaml.cpp
// Copyright 2007 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
namespace Poupou.SvgPathConverter {
public class SvgPathParser {
static int i;
public ISourceFormatter Formatter { get; set; }
public void Parse (string svgPath, string name = null)
{
if (Formatter == null)
throw new InvalidOperationException ("Missing formatter");
if (name == null)
name = "Unnamed_" + (++i).ToString ();
Parse (svgPath, name, Formatter);
}
static void Advance (string s, ref int pos)
{
if (pos >= s.Length)
return;
char c = s [pos];
while (!Char.IsLetterOrDigit (c) && c != '.' && c!= '-' && c != '+') {
if (++pos == s.Length)
return;
c = s [pos];
}
}
static int FindNonFloat (string s, int pos)
{
char c = s [pos];
while ((Char.IsNumber (c) || c == '.' || c == '-' || c == '+')) {
if (++pos == s.Length)
return pos;
c = s [pos];
}
return pos;
}
static bool MorePointsAvailable (string s, int pos)
{
if (pos >= s.Length)
return false;
char c = s [pos];
while (Char.IsWhiteSpace (c) || c == ',')
c = s [++pos];
return Char.IsDigit (c) || c == '.' || c == '-' || c == '+';
}
static float GetFloat (string svg, ref int pos)
{
int end = FindNonFloat (svg, pos);
string s = svg.Substring (pos, end - pos);
float f = Single.Parse (s, CultureInfo.InvariantCulture);
pos = end;
return f;
}
static PointF GetPoint (string svg, ref int pos)
{
while (Char.IsWhiteSpace (svg [pos]))
pos++;
float x = GetFloat (svg, ref pos);
while (Char.IsWhiteSpace (svg [pos]))
pos++;
if (svg [pos] == ',')
pos++;
while (Char.IsWhiteSpace (svg [pos]))
pos++;
float y = GetFloat (svg, ref pos);
return new PointF (x, y);
}
static PointF MakeRelative (PointF c, PointF m)
{
return new PointF (m.X + c.X, m.Y + c.Y);
}
static void Parse (string svg, string name, ISourceFormatter formatter)
{
formatter.Prologue (name);
PointF start;
PointF cp = new PointF (0, 0);
PointF cp1, cp2, cp3;
PointF qbzp, cbzp;
int fill_rule = 0;
int pos = 0;
bool cbz = false;
bool qbz = false;
while (pos < svg.Length) {
char c = svg [pos++];
if (Char.IsWhiteSpace (c))
continue;
bool relative = false;
switch (c) {
case 'f':
case 'F':
c = svg [pos++];
if (c == '0')
fill_rule = 0;
else if (c == '1')
fill_rule = 1;
else
throw new FormatException ();
break;
case 'h':
relative = true;
goto case 'H';
case 'H':
float x = GetFloat (svg, ref pos);
if (relative)
x += cp.X;
cp = new PointF (x, cp.Y);
formatter.LineTo (cp);
cbz = qbz = false;
break;
case 'm':
relative = true;
goto case 'M';
case 'M':
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
formatter.MoveTo (cp1);
start = cp = cp1;
Advance (svg, ref pos);
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
formatter.LineTo (cp1);
}
cp = cp1;
cbz = qbz = false;
break;
case 'l':
relative = true;
goto case 'L';
case 'L':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
formatter.LineTo (cp1);
cp = cp1;
}
cbz = qbz = false;
break;
case 'a':
relative = true;
goto case 'A';
case 'A':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
// this is a width and height so it's not made relative to cp
Advance (svg, ref pos);
float angle = GetFloat (svg, ref pos);
Advance (svg, ref pos);
bool is_large = GetFloat (svg, ref pos) != 0.0f;
Advance (svg, ref pos);
bool positive_sweep = GetFloat (svg, ref pos) != 0.0f;
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
formatter.ArcTo (cp1, angle, is_large, positive_sweep, cp2, cp);
cp = cp2;
Advance (svg, ref pos);
}
qbz = false;
cbz = false;
break;
case 'q':
relative = true;
goto case 'Q';
case 'Q':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
formatter.QuadCurveTo (cp1, cp2);
cp = cp2;
Advance (svg, ref pos);
}
qbz = true;
qbzp = cp1;
cbz = false;
break;
case 'c':
relative = true;
goto case 'C';
case 'C':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
cp3 = GetPoint (svg, ref pos);
if (relative)
cp3 = MakeRelative (cp, cp3);
Advance (svg, ref pos);
formatter.CurveTo (cp1, cp2, cp3);
cp1 = cp3;
}
cp = cp3;
cbz = true;
cbzp = cp2;
qbz = false;
break;
case 't':
relative = true;
goto case 'T';
case 'T':
while (MorePointsAvailable (svg, pos)) {
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
if (qbz) {
cp1.X = 2 * cp.X - qbzp.X;
cp1.Y = 2 * cp.Y - qbzp.Y;
} else {
cp1 = cp;
}
formatter.QuadCurveTo (cp1, cp2);
qbz = true;
qbzp = cp1;
cp = cp2;
Advance (svg, ref pos);
}
cbz = false;
break;
case 's':
relative = true;
goto case 'S';
case 'S':
while (MorePointsAvailable (svg, pos)) {
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
cp3 = GetPoint (svg, ref pos);
if (relative)
cp3 = MakeRelative (cp, cp3);
if (cbz) {
cp1.X = 2 * cp.X - cbzp.X;
cp1.Y = 2 * cp.Y - cbzp.Y;
} else {
cp1 = cp;
}
formatter.CurveTo (cp1, cp2, cp3);
cbz = true;
cbzp = cp2;
cp = cp3;
Advance (svg, ref pos);
}
qbz = false;
break;
case 'v':
relative = true;
goto case 'V';
case 'V':
float y = GetFloat (svg, ref pos);
if (relative)
y += cp.Y;
cp = new PointF (cp.X, y);
formatter.LineTo (cp);
cbz = qbz = false;
break;
case 'z':
case 'Z':
formatter.ClosePath ();
formatter.MoveTo (start);
cp = start;
cbz = qbz = false;
break;
default:
throw new FormatException (c.ToString ());
}
}
formatter.Epilogue ();
}
}
}
//相关接口定义:ISourceFormatter.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
namespace Poupou.SvgPathConverter {
public interface ISourceFormatter {
void Prologue (string name);
void Epilogue ();
void MoveTo (PointF pt);
void LineTo (PointF pt);
void QuadCurveTo (PointF pt1, PointF pt2);
void CurveTo (PointF pt1, PointF pt2, PointF pt3);
void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF ep, PointF sp);
void ClosePath ();
}
}
//辅助类:FormatterRocks.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/moon-path.cpp
// Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
using System.IO;
namespace Poupou.SvgPathConverter {
public static class FormatterRocks {
static bool IsNearZero (float value)
{
return Math.Abs (value) < 0.000019;
}
// The SVG Arc is a bit more complex than others - and also quite different than many existing API
// This implementation will use a ISourceFormatter's CurveTo method to draw the arc
public static void ArcHelper (this ISourceFormatter formatter, PointF size, float anglef,
bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
{
if (IsNearZero (endPoint.X - startPoint.X) && IsNearZero (endPoint.Y - startPoint.Y))
return;
// Correction of out-of-range radii, see F6.6 (step 1)
if (IsNearZero (size.X) || IsNearZero (size.Y)) {
// treat this as a straight line (to end point)
formatter.LineTo (endPoint);
return;
}
// Correction of out-of-range radii, see F6.6.1 (step 2)
float rx = Math.Abs (size.X);
float ry = Math.Abs (size.Y);
// convert angle into radians
double angle = anglef * Math.PI / 180.0f;
// variables required for F6.3.1
double cos_phi = Math.Cos (angle);
double sin_phi = Math.Sin (angle);
double dx2 = (startPoint.X - endPoint.X) / 2.0;
double dy2 = (startPoint.Y - endPoint.Y) / 2.0;
double x1p = cos_phi * dx2 + sin_phi * dy2;
double y1p = cos_phi * dy2 - sin_phi * dx2;
double x1p2 = x1p * x1p;
double y1p2 = y1p * y1p;
float rx2 = rx * rx;
float ry2 = ry * ry;
// Correction of out-of-range radii, see F6.6.2 (step 4)
double lambda = (x1p2 / rx2) + (y1p2 / ry2);
if (lambda > 1.0) {
// see F6.6.3
float lambda_root = (float) Math.Sqrt (lambda);
rx *= lambda_root;
ry *= lambda_root;
// update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
}
double cxp, cyp, cx, cy;
double c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
// check if there is no possible solution (i.e. we can't do a square root of a negative value)
if (c < 0.0) {
// scale uniformly until we have a single solution (see F6.2) i.e. when c == 0.0
float scale = (float) Math.Sqrt (1.0 - c / (rx2 * ry2));
rx *= scale;
ry *= scale;
// update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
// step 2 (F6.5.2) - simplified since c == 0.0
cxp = 0.0;
cyp = 0.0;
// step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
cx = 0.0;
cy = 0.0;
} else {
// complete c calculation
c = Math.Sqrt (c / ((rx2 * y1p2) + (ry2 * x1p2)));
// inverse sign if Fa == Fs
if (isLarge == sweep)
c = -c;
// step 2 (F6.5.2)
cxp = c * ( rx * y1p / ry);
cyp = c * (-ry * x1p / rx);
// step 3 (F6.5.3 first part)
cx = cos_phi * cxp - sin_phi * cyp;
cy = sin_phi * cxp + cos_phi * cyp;
}
// step 3 (F6.5.3 second part) we now have the center point of the ellipse
cx += (startPoint.X + endPoint.X) / 2.0;
cy += (startPoint.Y + endPoint.Y) / 2.0;
// step 4 (F6.5.4)
// we dont' use arccos (as per w3c doc), see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
// note: atan2 (0.0, 1.0) == 0.0
double at = Math.Atan2 (((y1p - cyp) / ry), ((x1p - cxp) / rx));
double theta1 = (at < 0.0) ? 2.0 * Math.PI + at : at;
double nat = Math.Atan2 (((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
double delta_theta = (nat < at) ? 2.0 * Math.PI - at + nat : nat - at;
if (sweep) {
// ensure delta theta < 0 or else add 360 degrees
if (delta_theta < 0.0)
delta_theta += 2.0 * Math.PI;
} else {
// ensure delta theta > 0 or else substract 360 degrees
if (delta_theta > 0.0)
delta_theta -= 2.0 * Math.PI;
}
// add several cubic bezier to approximate the arc (smaller than 90 degrees)
// we add one extra segment because we want something smaller than 90deg (i.e. not 90 itself)
int segments = (int) (Math.Abs (delta_theta / Math.PI)) + 1;
double delta = delta_theta / segments;
// http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
float bcp = (float) (4.0 / 3 * (1 - Math.Cos (delta / 2)) / Math.Sin (delta / 2));
double cos_phi_rx = cos_phi * rx;
double cos_phi_ry = cos_phi * ry;
double sin_phi_rx = sin_phi * rx;
double sin_phi_ry = sin_phi * ry;
double cos_theta1 = Math.Cos (theta1);
double sin_theta1 = Math.Sin (theta1);
PointF c1, c2;
int i;
for (i = 0; i < segments; ++i) {
// end angle (for this segment) = current + delta
double theta2 = theta1 + delta;
double cos_theta2 = Math.Cos (theta2);
double sin_theta2 = Math.Sin (theta2);
// first control point (based on start point sx,sy)
c1.X = startPoint.X - bcp * (float) (cos_phi_rx * sin_theta1 + sin_phi_ry * cos_theta1);
c1.Y = startPoint.Y + bcp * (float) (cos_phi_ry * cos_theta1 - sin_phi_rx * sin_theta1);
// end point (for this segment)
endPoint.X = (float) (cx + (cos_phi_rx * cos_theta2 - sin_phi_ry * sin_theta2));
endPoint.Y = (float) (cy + (sin_phi_rx * cos_theta2 + cos_phi_ry * sin_theta2));
// second control point (based on end point ex,ey)
c2.X = endPoint.X + bcp * (float) (cos_phi_rx * sin_theta2 + sin_phi_ry * cos_theta2);
c2.Y = endPoint.Y + bcp * (float) (sin_phi_rx * sin_theta2 - cos_phi_ry * cos_theta2);
formatter.CurveTo (c1, c2, endPoint);
// next start point is the current end point (same for angle)
startPoint = endPoint;
theta1 = theta2;
// avoid recomputations
cos_theta1 = cos_theta2;
sin_theta1 = sin_theta2;
}
}
}
}
// 从接口派生的CSharpCoreGraphicsFormatter.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012-2013 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
namespace Poupou.SvgPathConverter {
public class CSharpCoreGraphicsFormatter : ISourceFormatter {
TextWriter writer;
public CSharpCoreGraphicsFormatter (TextWriter textWriter)
{
writer = textWriter;
}
public void Prologue (string name)
{
writer.WriteLine ("\tstatic void {0} (CGContext c)", name);
writer.WriteLine ("\t{");
}
public void Epilogue ()
{
writer.WriteLine ("\t\tc.FillPath ();");
writer.WriteLine ("\t\tc.StrokePath ();");
writer.WriteLine ("\t}");
writer.WriteLine ();
}
public void MoveTo (PointF pt)
{
writer.WriteLine ("\t\tc.MoveTo ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
pt.Y.ToString (CultureInfo.InvariantCulture));
}
public void LineTo (PointF pt)
{
writer.WriteLine ("\t\tc.AddLineToPoint ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
pt.Y.ToString (CultureInfo.InvariantCulture));
}
public void ClosePath ()
{
writer.WriteLine ("\t\tc.ClosePath ();");
}
public void QuadCurveTo (PointF pt1, PointF pt2)
{
writer.WriteLine ("\t\tc.AddQuadCurveToPoint ({0}f, {1}f, {2}f, {3}f);",
pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture));
}
public void CurveTo (PointF pt1, PointF pt2, PointF pt3)
{
writer.WriteLine ("\t\tc.AddCurveToPoint ({0}f, {1}f, {2}f, {3}f, {4}f, {5}f);",
pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture),
pt3.X.ToString (CultureInfo.InvariantCulture), pt3.Y.ToString (CultureInfo.InvariantCulture));
}
public void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
{
this.ArcHelper (size, angle, isLarge, sweep, endPoint, startPoint);
}
}
}
版权声明
本文为[大可山人]所创,转载请带上原文链接,感谢
https://blog.csdn.net/johnsuna/article/details/77596145
边栏推荐
- 基于NLP的软件安全研究(一)
- SAP PI / Po rfc2restful Publishing RFC interface as restful examples (proxy indirect)
- 公共依赖模块common的处理
- SAP ECC连接SAP PI系统配置
- Processing of common dependency module
- Reflection on the systematic design of Android audio and video caching mechanism
- SAP CR传输请求顺序、依赖检查
- Common DOS commands
- What are the total number of all courses of Mr. Tang Xiaoyang, who is very popular in CSDN (question mark)
- 反转链表练习
猜你喜欢

Install and configure Taobao image NPM (cnpm)

页面动态显示时间(升级版)

canvas学习第一篇

中间人环境mitmproxy搭建

FSM finite state machine

ABAP 7.4 SQL Window Expression

SAP pi / PO rfc2restful publishing RFC interface is a restful example (proxy indirect method)

如何判断点是否在多边形内(包含复杂多边形或者多边形数量很多的情况)

页面实时显示当前时间

Super classic & Programming Guide (red and blue book) - Reading Notes
随机推荐
SAP PI/PO rfc2RESTful 發布rfc接口為RESTful示例(Proxy間接法)
C# 多个矩形围成的多边形标注位置的问题
Reflect on the limitations of event bus and the design and implementation of communication mechanism in component development process
11.表和库的管理
promise all的实现
定位、修饰样式
数组扁平化
【TED系列】如何与内心深处的批评家相处?
公共依赖模块common的处理
SAP CR传输请求顺序、依赖检查
Methods of database query optimization
对js中argumens的简单理解
h5本地存储数据sessionStorage、localStorage
10.更新操作
js之作用域、作用域链、全局变量和局部变量
Design optimization of MySQL database
页面实时显示当前时间
Custom time format (yyyy-mm-dd HH: mm: SS week x)
移动Web(字体图标、平面转换、颜色渐变)
Two threads print odd and even numbers interactively