mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 03:18:23 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
272 lines
8.3 KiB
C#
272 lines
8.3 KiB
C#
using System;
|
|
using ShimSkiaSharp;
|
|
using Svg.Model.Drawables;
|
|
using Svg.Skia;
|
|
using SvgLib = Svg;
|
|
|
|
namespace Sandbox.Utility.Svg;
|
|
|
|
/// <summary>
|
|
/// How to determine which sections of the path are filled.
|
|
/// </summary>
|
|
public enum PathFillType
|
|
{
|
|
/// <summary>
|
|
/// Clockwise paths are filled, counter-clockwise are empty.
|
|
/// </summary>
|
|
Winding = 0,
|
|
|
|
/// <summary>
|
|
/// Regions that are enclosed by an odd number of paths are filled, other regions are empty.
|
|
/// </summary>
|
|
EvenOdd = 1
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls arc size in <see cref="ArcToPathCommand"/>.
|
|
/// </summary>
|
|
public enum PathArcSize
|
|
{
|
|
Small = 0,
|
|
Large = 1
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls arc direction in <see cref="ArcToPathCommand"/>.
|
|
/// </summary>
|
|
public enum PathDirection
|
|
{
|
|
Clockwise = 0,
|
|
CounterClockwise = 1
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base class for SVG path commands.
|
|
/// </summary>
|
|
public abstract record PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle"/>.
|
|
/// </summary>
|
|
public record AddCirclePathCommand( float X, float Y, float Radius ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse"/>.
|
|
/// </summary>
|
|
public record AddOvalPathCommand( Rect Rect ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline"/>, <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon"/>.
|
|
/// </summary>
|
|
public record AddPolyPathCommand( IReadOnlyList<Vector2> Points, bool Close ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect"/>.
|
|
/// </summary>
|
|
public record AddRectPathCommand( Rect Rect ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect"/>.
|
|
/// </summary>
|
|
public record AddRoundRectPathCommand( Rect Rect, float Rx, float Ry ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#arcs"/>.
|
|
/// </summary>
|
|
public record ArcToPathCommand( float Rx, float Ry, float XAxisRotate, PathArcSize LargeArc, PathDirection Sweep, float X, float Y ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#line_commands"/>.
|
|
/// </summary>
|
|
public record ClosePathCommand : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#b%C3%A9zier_curves"/>.
|
|
/// </summary>
|
|
public record CubicToPathCommand( float X0, float Y0, float X1, float Y1, float X2, float Y2 ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#line_commands"/>.
|
|
/// </summary>
|
|
public record LineToPathCommand( float X, float Y ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#line_commands"/>.
|
|
/// </summary>
|
|
public record MoveToPathCommand( float X, float Y ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#b%C3%A9zier_curves"/>.
|
|
/// </summary>
|
|
public record QuadToPathCommand( float X0, float Y0, float X1, float Y1 ) : PathCommand;
|
|
|
|
/// <summary>
|
|
/// A shape in a <see cref="SvgDocument"/>, described as a vector path.
|
|
/// </summary>
|
|
public class SvgPath
|
|
{
|
|
/// <summary>
|
|
/// How to determine which sections of the path are filled.
|
|
/// </summary>
|
|
public PathFillType FillType { get; }
|
|
|
|
/// <summary>
|
|
/// Description of how the path is constructed out of basic elements.
|
|
/// </summary>
|
|
public IReadOnlyList<PathCommand> Commands { get; }
|
|
|
|
/// <summary>
|
|
/// If true, this path has no commands.
|
|
/// </summary>
|
|
public bool IsEmpty => Commands.Count == 0;
|
|
|
|
/// <summary>
|
|
/// Enclosing bounding box for this path.
|
|
/// </summary>
|
|
public Rect Bounds { get; }
|
|
|
|
/// <summary>
|
|
/// Optional outline color for this path.
|
|
/// </summary>
|
|
public Color? StrokeColor { get; }
|
|
|
|
/// <summary>
|
|
/// Optional fill color for this path.
|
|
/// </summary>
|
|
public Color? FillColor { get; }
|
|
|
|
internal SvgPath( SKPath skPath, SKColor? strokeColor, SKColor? fillColor )
|
|
{
|
|
var commands = new List<PathCommand>();
|
|
|
|
FillType = (PathFillType)skPath.FillType;
|
|
Commands = commands;
|
|
Bounds = SvgDocument.ConvertRect( skPath.Bounds );
|
|
|
|
StrokeColor = SvgDocument.ConvertColor( strokeColor );
|
|
FillColor = SvgDocument.ConvertColor( fillColor );
|
|
|
|
if ( skPath.Commands == null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach ( var cmd in skPath.Commands )
|
|
{
|
|
switch ( cmd )
|
|
{
|
|
case ShimSkiaSharp.MoveToPathCommand moveToPathCommand:
|
|
commands.Add( new MoveToPathCommand( moveToPathCommand.X, moveToPathCommand.Y ) );
|
|
break;
|
|
case ShimSkiaSharp.LineToPathCommand lineToPathCommand:
|
|
commands.Add( new LineToPathCommand( lineToPathCommand.X, lineToPathCommand.Y ) );
|
|
break;
|
|
case ShimSkiaSharp.ArcToPathCommand arcToPathCommand:
|
|
commands.Add( new ArcToPathCommand( arcToPathCommand.Rx, arcToPathCommand.Ry,
|
|
arcToPathCommand.XAxisRotate, (PathArcSize)arcToPathCommand.LargeArc,
|
|
(PathDirection)arcToPathCommand.Sweep, arcToPathCommand.X, arcToPathCommand.Y ) );
|
|
break;
|
|
case ShimSkiaSharp.QuadToPathCommand quadToPathCommand:
|
|
commands.Add( new QuadToPathCommand( quadToPathCommand.X0, quadToPathCommand.Y0,
|
|
quadToPathCommand.X1, quadToPathCommand.Y1 ) );
|
|
break;
|
|
case ShimSkiaSharp.CubicToPathCommand cubicToPathCommand:
|
|
commands.Add( new CubicToPathCommand( cubicToPathCommand.X0, cubicToPathCommand.Y0,
|
|
cubicToPathCommand.X1, cubicToPathCommand.Y1,
|
|
cubicToPathCommand.X2, cubicToPathCommand.Y2 ) );
|
|
break;
|
|
case ShimSkiaSharp.ClosePathCommand:
|
|
commands.Add( new ClosePathCommand() );
|
|
break;
|
|
case ShimSkiaSharp.AddRectPathCommand addRectPathCommand:
|
|
commands.Add( new AddRectPathCommand( SvgDocument.ConvertRect( addRectPathCommand.Rect ) ) );
|
|
break;
|
|
case ShimSkiaSharp.AddRoundRectPathCommand addRoundRectPathCommand:
|
|
commands.Add( new AddRoundRectPathCommand( SvgDocument.ConvertRect( addRoundRectPathCommand.Rect ), addRoundRectPathCommand.Rx, addRoundRectPathCommand.Ry ) );
|
|
break;
|
|
case ShimSkiaSharp.AddOvalPathCommand addOvalPathCommand:
|
|
commands.Add( new AddOvalPathCommand( SvgDocument.ConvertRect( addOvalPathCommand.Rect ) ) );
|
|
break;
|
|
case ShimSkiaSharp.AddCirclePathCommand addCirclePathCommand:
|
|
commands.Add( new AddCirclePathCommand( addCirclePathCommand.X, addCirclePathCommand.Y, addCirclePathCommand.Radius ) );
|
|
break;
|
|
case ShimSkiaSharp.AddPolyPathCommand addPolyPathCommand:
|
|
commands.Add( new AddPolyPathCommand(
|
|
addPolyPathCommand.Points?.Select( x => new Vector2( x.X, x.Y ) ).ToList() ??
|
|
(IReadOnlyList<Vector2>)Array.Empty<Vector2>(), addPolyPathCommand.Close ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class for reading Scalable Vector Graphics files.
|
|
/// </summary>
|
|
public class SvgDocument
|
|
{
|
|
internal static Rect ConvertRect( SKRect rect )
|
|
{
|
|
return new Rect( rect.Left, rect.Top, rect.Width, rect.Height );
|
|
}
|
|
|
|
internal static Color? ConvertColor( SKColor? color )
|
|
{
|
|
if ( color is null )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Color.FromBytes( color.Value.Red, color.Value.Green, color.Value.Blue, color.Value.Alpha );
|
|
}
|
|
|
|
private static void ExtractPaths( SKDrawable drawable, List<SvgPath> outPaths )
|
|
{
|
|
switch ( drawable )
|
|
{
|
|
case DrawablePath { Path: { } path } drawablePath:
|
|
outPaths.Add( new SvgPath( path, drawablePath.Stroke?.Color, drawablePath.Fill?.Color ) );
|
|
break;
|
|
case DrawableContainer { ChildrenDrawables: { } children }:
|
|
{
|
|
foreach ( var child in children )
|
|
{
|
|
ExtractPaths( child, outPaths );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads an SVG document from the given string, returning a list of path elements
|
|
/// describing the shapes in the image.
|
|
/// </summary>
|
|
/// <param name="contents">SVG document contents.</param>
|
|
public static SvgDocument FromString( string contents )
|
|
{
|
|
var svgDoc = SvgLib.SvgDocument.FromSvg<SvgLib.SvgDocument>( contents );
|
|
|
|
using var svg = new SKSvg();
|
|
|
|
svg.FromSvgDocument( svgDoc );
|
|
|
|
var paths = new List<SvgPath>();
|
|
|
|
ExtractPaths( svg.Drawable, paths );
|
|
|
|
return new SvgDocument( paths );
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of all shapes in the document.
|
|
/// </summary>
|
|
public IReadOnlyList<SvgPath> Paths { get; }
|
|
|
|
private SvgDocument( IReadOnlyList<SvgPath> paths )
|
|
{
|
|
Paths = paths;
|
|
}
|
|
}
|