Browse Source

Merge branch 'relative-path-fixes' into binding-updates2

pull/691/head
Steven Kirk 10 years ago
parent
commit
b89ec02f57
  1. 110
      src/Avalonia.SceneGraph/Media/PathMarkupParser.cs
  2. 214
      tests/Avalonia.RenderTests/Shapes/PathTests.cs
  3. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Arc_Absolute.expected.png
  4. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Arc_Relative.expected.png
  5. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/CubicBezier_Absolute.expected.png
  6. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/CubicBezier_Relative.expected.png
  7. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/HorizontalLine_Absolute.expected.png
  8. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/HorizontalLine_Relative.expected.png
  9. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Line_Absolute.expected.png
  10. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Line_Relative.expected.png
  11. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/VerticalLine_Absolute.expected.png
  12. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/VerticalLine_Relative.expected.png

110
src/Avalonia.SceneGraph/Media/PathMarkupParser.cs

@ -17,21 +17,13 @@ namespace Avalonia.Media
private static readonly Dictionary<char, Command> Commands = new Dictionary<char, Command> private static readonly Dictionary<char, Command> Commands = new Dictionary<char, Command>
{ {
{ 'F', Command.FillRule }, { 'F', Command.FillRule },
{ 'f', Command.FillRule },
{ 'M', Command.Move }, { 'M', Command.Move },
{ 'm', Command.MoveRelative },
{ 'L', Command.Line }, { 'L', Command.Line },
{ 'l', Command.LineRelative },
{ 'H', Command.HorizontalLine }, { 'H', Command.HorizontalLine },
{ 'h', Command.HorizontalLineRelative },
{ 'V', Command.VerticalLine }, { 'V', Command.VerticalLine },
{ 'v', Command.VerticalLineRelative },
{ 'C', Command.CubicBezierCurve }, { 'C', Command.CubicBezierCurve },
{ 'c', Command.CubicBezierCurveRelative },
{ 'A', Command.Arc }, { 'A', Command.Arc },
{ 'a', Command.Arc },
{ 'Z', Command.Close }, { 'Z', Command.Close },
{ 'z', Command.Close },
}; };
private static readonly Dictionary<char, FillRule> FillRules = new Dictionary<char, FillRule> private static readonly Dictionary<char, FillRule> FillRules = new Dictionary<char, FillRule>
@ -63,18 +55,12 @@ namespace Avalonia.Media
None, None,
FillRule, FillRule,
Move, Move,
MoveRelative,
Line, Line,
LineRelative,
HorizontalLine, HorizontalLine,
HorizontalLineRelative,
VerticalLine, VerticalLine,
VerticalLineRelative,
CubicBezierCurve, CubicBezierCurve,
CubicBezierCurveRelative,
Arc, Arc,
Close, Close,
Eof,
} }
/// <summary> /// <summary>
@ -87,11 +73,11 @@ namespace Avalonia.Media
using (StringReader reader = new StringReader(s)) using (StringReader reader = new StringReader(s))
{ {
Command lastCommand = Command.None; Command command = Command.None;
Command command;
Point point = new Point(); Point point = new Point();
bool relative = false;
while ((command = ReadCommand(reader, lastCommand)) != Command.Eof) while (ReadCommand(reader, ref command, ref relative))
{ {
switch (command) switch (command)
{ {
@ -100,72 +86,58 @@ namespace Avalonia.Media
break; break;
case Command.Move: case Command.Move:
case Command.MoveRelative:
if (openFigure) if (openFigure)
{ {
_context.EndFigure(false); _context.EndFigure(false);
} }
point = command == Command.Move ? point = ReadPoint(reader, point, relative);
ReadPoint(reader) :
ReadRelativePoint(reader, point);
_context.BeginFigure(point, true); _context.BeginFigure(point, true);
openFigure = true; openFigure = true;
break; break;
case Command.Line: case Command.Line:
point = ReadPoint(reader); point = ReadPoint(reader, point, relative);
_context.LineTo(point);
break;
case Command.LineRelative:
point = ReadRelativePoint(reader, point);
_context.LineTo(point); _context.LineTo(point);
break; break;
case Command.HorizontalLine: case Command.HorizontalLine:
point = point.WithX(ReadDouble(reader)); if (!relative)
_context.LineTo(point); {
break; point = point.WithX(ReadDouble(reader));
}
else
{
point = new Point(point.X + ReadDouble(reader), point.Y);
}
case Command.HorizontalLineRelative:
point = new Point(point.X + ReadDouble(reader), point.Y);
_context.LineTo(point); _context.LineTo(point);
break; break;
case Command.VerticalLine: case Command.VerticalLine:
point = point.WithY(ReadDouble(reader)); if (!relative)
_context.LineTo(point); {
break; point = point.WithY(ReadDouble(reader));
}
else
{
point = new Point(point.X, point.Y + ReadDouble(reader));
}
case Command.VerticalLineRelative:
point = new Point(point.X, point.Y + ReadDouble(reader));
_context.LineTo(point); _context.LineTo(point);
break; break;
case Command.CubicBezierCurve: case Command.CubicBezierCurve:
{ {
Point point1 = ReadPoint(reader); Point point1 = ReadPoint(reader, point, relative);
Point point2 = ReadPoint(reader); Point point2 = ReadPoint(reader, point, relative);
point = ReadPoint(reader); point = ReadPoint(reader, point, relative);
_context.CubicBezierTo(point1, point2, point); _context.CubicBezierTo(point1, point2, point);
break; break;
} }
case Command.CubicBezierCurveRelative:
{
Point point1 = ReadRelativePoint(reader, point);
Point point2 = ReadRelativePoint(reader, point);
_context.CubicBezierTo(point, point1, point2);
point = point2;
break;
}
case Command.Arc: case Command.Arc:
{ {
//example: A10,10 0 0,0 10,20
//format - size rotationAngle isLargeArcFlag sweepDirectionFlag endPoint
Size size = ReadSize(reader); Size size = ReadSize(reader);
ReadSeparator(reader); ReadSeparator(reader);
double rotationAngle = ReadDouble(reader); double rotationAngle = ReadDouble(reader);
@ -173,7 +145,7 @@ namespace Avalonia.Media
bool isLargeArc = ReadBool(reader); bool isLargeArc = ReadBool(reader);
ReadSeparator(reader); ReadSeparator(reader);
SweepDirection sweepDirection = ReadBool(reader) ? SweepDirection.Clockwise : SweepDirection.CounterClockwise; SweepDirection sweepDirection = ReadBool(reader) ? SweepDirection.Clockwise : SweepDirection.CounterClockwise;
point = ReadPoint(reader); point = ReadPoint(reader, point, relative);
_context.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection); _context.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection);
break; break;
@ -187,8 +159,6 @@ namespace Avalonia.Media
default: default:
throw new NotSupportedException("Unsupported command"); throw new NotSupportedException("Unsupported command");
} }
lastCommand = command;
} }
if (openFigure) if (openFigure)
@ -198,7 +168,10 @@ namespace Avalonia.Media
} }
} }
private static Command ReadCommand(StringReader reader, Command lastCommand) private static bool ReadCommand(
StringReader reader,
ref Command command,
ref bool relative)
{ {
ReadWhitespace(reader); ReadWhitespace(reader);
@ -206,19 +179,19 @@ namespace Avalonia.Media
if (i == -1) if (i == -1)
{ {
return Command.Eof; return false;
} }
else else
{ {
char c = (char)i; char c = (char)i;
Command command = Command.None; Command next = Command.None;
if (!Commands.TryGetValue(c, out command)) if (!Commands.TryGetValue(char.ToUpperInvariant(c), out next))
{ {
if ((char.IsDigit(c) || c == '.' || c == '+' || c == '-') && if ((char.IsDigit(c) || c == '.' || c == '+' || c == '-') &&
(lastCommand != Command.None)) (command != Command.None))
{ {
return lastCommand; return true;
} }
else else
{ {
@ -226,8 +199,10 @@ namespace Avalonia.Media
} }
} }
command = next;
relative = char.IsLower(c);
reader.Read(); reader.Read();
return command; return true;
} }
} }
@ -297,12 +272,17 @@ namespace Avalonia.Media
return double.Parse(b.ToString(), CultureInfo.InvariantCulture); return double.Parse(b.ToString(), CultureInfo.InvariantCulture);
} }
private static Point ReadPoint(StringReader reader) private static Point ReadPoint(StringReader reader, Point current, bool relative)
{ {
if (!relative)
{
current = new Point();
}
ReadWhitespace(reader); ReadWhitespace(reader);
double x = ReadDouble(reader); double x = current.X + ReadDouble(reader);
ReadSeparator(reader); ReadSeparator(reader);
double y = ReadDouble(reader); double y = current.Y + ReadDouble(reader);
return new Point(x, y); return new Point(x, y);
} }

214
tests/Avalonia.RenderTests/Shapes/PathTests.cs

@ -24,6 +24,220 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
{ {
} }
[Fact]
public void Line_Absolute()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M 10,190 L 190,10 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void Line_Relative()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M10,190 l190,-190 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void HorizontalLine_Absolute()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,100 H10 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void HorizontalLine_Relative()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,100 h-180 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void VerticalLine_Absolute()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M100,190 V10 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void VerticalLine_Relative()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M100,190 V-180 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void CubicBezier_Absolute()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Fill = Brushes.Gray,
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,0 C10,10 190,190 10,190 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void CubicBezier_Relative()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Fill = Brushes.Gray,
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,0 c-180,10 0,190 -180,190 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void Arc_Absolute()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Fill = Brushes.Gray,
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,100 A90,90 0 1,0 10,100 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void Arc_Relative()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Path
{
Fill = Brushes.Gray,
Stroke = Brushes.Red,
StrokeThickness = 1,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M190,100 a90,90 0 1,0 -180,0 M0,0M200,200"),
}
};
RenderToFile(target);
CompareImages();
}
[Fact] [Fact]
public void Path_100px_Triangle_Centered() public void Path_100px_Triangle_Centered()
{ {

BIN
tests/TestFiles/Direct2D1/Shapes/Path/Arc_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Path/Arc_Relative.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Path/CubicBezier_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Path/CubicBezier_Relative.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Path/HorizontalLine_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

BIN
tests/TestFiles/Direct2D1/Shapes/Path/HorizontalLine_Relative.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

BIN
tests/TestFiles/Direct2D1/Shapes/Path/Line_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

BIN
tests/TestFiles/Direct2D1/Shapes/Path/Line_Relative.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

BIN
tests/TestFiles/Direct2D1/Shapes/Path/VerticalLine_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

BIN
tests/TestFiles/Direct2D1/Shapes/Path/VerticalLine_Relative.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Loading…
Cancel
Save