зеркало из https://github.com/SixLabors/Svg.git
improved path/shape support
This commit is contained in:
Родитель
f41d627f7e
Коммит
aafea14480
|
@ -1,6 +1,10 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
<?xml version="1.0" standalone="no"?>
|
||||||
<svg width="400" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
<svg width="400" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="red" >
|
||||||
|
<rect x="10" y="100" width="90" height="90" stroke="black" stroke-width="15"/>
|
||||||
|
<rect x="160" y="100" rx="20" ry="20" width="90" height="90" fill-opacity="0.5" stroke="black" stroke-width="15"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
<rect x="10" y="10" width="90" height="90" stroke="black" fill="none" stroke-width="15"/>
|
<rect x="10" y="10" width="90" height="90" stroke="black" fill="none" stroke-width="15"/>
|
||||||
<rect x="160" y="10" rx="20" ry="20" width="90" height="90" fill="none" stroke="black" stroke-width="15"/>
|
<rect x="160" y="10" rx="20" ry="20" width="90" height="90" fill="none" stroke="black" stroke-width="15"/>
|
||||||
|
|
||||||
|
@ -14,5 +18,5 @@
|
||||||
<polygon points="50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180"
|
<polygon points="50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180"
|
||||||
stroke="green" fill="transparent" stroke-width="5"/>
|
stroke="green" fill="transparent" stroke-width="5"/>
|
||||||
|
|
||||||
<path d="M20,230 Q40,205 50,230 T90,230" fill="none" stroke="blue" stroke-width="5"/>
|
<path d="M20,230 Q40,205 50,230 T90,230" fill="red" stroke="blue" stroke-width="5"/>
|
||||||
</svg>
|
</svg>
|
|
@ -0,0 +1,86 @@
|
||||||
|
using AngleSharp.Svg.Dom;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.Shapes;
|
||||||
|
using AngleSharp.Dom;
|
||||||
|
using System.Linq;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
|
namespace SixLabors.Svg.Dom
|
||||||
|
{
|
||||||
|
internal static class Extensions
|
||||||
|
{
|
||||||
|
public static IPath GenerateStroke<TPixel>(this IPath path, Image<TPixel> img, ISvgShape shape) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
var strokeWidth = shape.StrokeWidth.AsPixelXAxis(img);
|
||||||
|
if (strokeWidth == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Outliner.GenerateOutline(path, strokeWidth, shape.StrokeLineJoin.Style, shape.StrokeLineCap.Style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAttributeValueSelfOrGroup(this ISvgElement element, string attribute)
|
||||||
|
{
|
||||||
|
var val = element.Attributes[attribute]?.Value;
|
||||||
|
|
||||||
|
if (val == null)
|
||||||
|
{
|
||||||
|
val = element.Ancestors<ISvgElement>().Where(x => x.TagName == "g").FirstOrDefault()?.GetAttributeValueSelfOrGroup(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgPaint GetPaint(this ISvgElement element, string attribute, string defaultPaintValue, string defaultOpacityValue)
|
||||||
|
{
|
||||||
|
var paint = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultPaintValue;
|
||||||
|
var opacity = element.GetAttributeValueSelfOrGroup(attribute + "-opacity") ?? defaultOpacityValue;
|
||||||
|
|
||||||
|
return SvgPaint.Parse(paint, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgUnitValue GetUnitValue(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.TryGetUnitValue(attribute, defaultValue);
|
||||||
|
|
||||||
|
return val ?? SvgUnitValue.Unset;
|
||||||
|
}
|
||||||
|
public static SvgUnitValue? TryGetUnitValue(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
|
||||||
|
|
||||||
|
return SvgUnitValue.Parse(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgLineCap GetLineCap(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.TryGetLineCap(attribute, defaultValue);
|
||||||
|
|
||||||
|
return val ?? SvgLineCap.Unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgLineCap? TryGetLineCap(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
|
||||||
|
|
||||||
|
return SvgLineCap.Parse(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static SvgLineJoin GetLineJoin(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.TryGetLineJoin(attribute, defaultValue);
|
||||||
|
|
||||||
|
return val ?? SvgLineJoin.Unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgLineJoin? TryGetLineJoin(this ISvgElement element, string attribute, string defaultValue = null)
|
||||||
|
{
|
||||||
|
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
|
||||||
|
|
||||||
|
return SvgLineJoin.Parse(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace SixLabors.Svg.Dom
|
||||||
|
{
|
||||||
|
internal interface ISvgShape
|
||||||
|
{
|
||||||
|
SvgPaint Fill { get; }
|
||||||
|
SvgPaint Stroke { get; }
|
||||||
|
SvgUnitValue StrokeWidth { get; }
|
||||||
|
SvgLineCap StrokeLineCap { get; }
|
||||||
|
SvgLineJoin StrokeLineJoin { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,10 +30,10 @@ namespace SixLabors.Svg.Dom
|
||||||
{
|
{
|
||||||
var document = new SvgDocument()
|
var document = new SvgDocument()
|
||||||
{
|
{
|
||||||
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
|
X = element.GetUnitValue("x", "0"),
|
||||||
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
|
Y = element.GetUnitValue("y", "0"),
|
||||||
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
|
Width = element.GetUnitValue("width"),
|
||||||
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
|
Height = element.GetUnitValue("height")
|
||||||
};
|
};
|
||||||
|
|
||||||
var children = element.Children.OfType<ISvgElement>();
|
var children = element.Children.OfType<ISvgElement>();
|
||||||
|
|
|
@ -21,6 +21,11 @@ namespace SixLabors.Svg.Dom
|
||||||
case "circle":
|
case "circle":
|
||||||
case "ellipse":
|
case "ellipse":
|
||||||
return await SvgEllipse.LoadAsync(element);
|
return await SvgEllipse.LoadAsync(element);
|
||||||
|
case "line":
|
||||||
|
case "polyline":
|
||||||
|
case "polygon":
|
||||||
|
case "path":
|
||||||
|
return await SvgPath.LoadAsync(element);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ using System.Threading.Tasks;
|
||||||
using AngleSharp.Svg.Dom;
|
using AngleSharp.Svg.Dom;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using SixLabors.Shapes;
|
|
||||||
|
|
||||||
namespace SixLabors.Svg.Dom
|
namespace SixLabors.Svg.Dom
|
||||||
{
|
{
|
||||||
internal sealed class SvgEllipse : SvgElement
|
internal sealed class SvgEllipse : SvgElement, ISvgShape
|
||||||
{
|
{
|
||||||
|
|
||||||
public SvgUnitValue X { get; private set; }
|
public SvgUnitValue X { get; private set; }
|
||||||
|
@ -17,25 +16,30 @@ namespace SixLabors.Svg.Dom
|
||||||
public SvgPaint Fill { get; private set; }
|
public SvgPaint Fill { get; private set; }
|
||||||
public SvgPaint Stroke { get; private set; }
|
public SvgPaint Stroke { get; private set; }
|
||||||
public SvgUnitValue StrokeWidth { get; private set; }
|
public SvgUnitValue StrokeWidth { get; private set; }
|
||||||
|
public SvgLineCap StrokeLineCap { get; private set; }
|
||||||
|
public SvgLineJoin StrokeLineJoin { get; private set; }
|
||||||
|
|
||||||
public static Task<SvgElement> LoadAsync(ISvgElement element)
|
public static Task<SvgElement> LoadAsync(ISvgElement element)
|
||||||
{
|
{
|
||||||
var ellipse = new SvgEllipse()
|
var ellipse = new SvgEllipse()
|
||||||
{
|
{
|
||||||
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
|
Fill = element.GetPaint("fill", "Black", "1"),
|
||||||
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
|
Stroke = element.GetPaint("stroke", "None", "1"),
|
||||||
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
|
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
|
||||||
X = SvgUnitValue.Parse(element.Attributes["cx"]?.Value),
|
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
|
||||||
Y = SvgUnitValue.Parse(element.Attributes["cy"]?.Value)
|
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
|
||||||
|
X = element.GetUnitValue("cx"),
|
||||||
|
Y = element.GetUnitValue("cy"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (element.TagName == "circle")
|
if (element.TagName == "circle")
|
||||||
{
|
{
|
||||||
ellipse.RadiusY = ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["r"]?.Value ?? "0");
|
ellipse.RadiusY = ellipse.RadiusX = element.GetUnitValue("r", "0");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ellipse.RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? "0");
|
ellipse.RadiusX = element.GetUnitValue("rx", "0");
|
||||||
ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? "0");
|
ellipse.RadiusY = element.GetUnitValue("ry", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult<SvgElement>(ellipse);
|
return Task.FromResult<SvgElement>(ellipse);
|
||||||
|
@ -47,17 +51,19 @@ namespace SixLabors.Svg.Dom
|
||||||
|
|
||||||
var fillBrush = Fill.AsBrush<TPixel>();
|
var fillBrush = Fill.AsBrush<TPixel>();
|
||||||
var strokeBrush = Stroke.AsBrush<TPixel>();
|
var strokeBrush = Stroke.AsBrush<TPixel>();
|
||||||
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
|
|
||||||
image.Mutate(x =>
|
image.Mutate(x =>
|
||||||
{
|
{
|
||||||
if (fillBrush != null)
|
if (fillBrush != null)
|
||||||
{
|
{
|
||||||
x = x.Fill(fillBrush, rect);
|
x = x.Fill(fillBrush, rect);
|
||||||
}
|
}
|
||||||
if (strokeBrush != null && strokeWidth > 0)
|
if (strokeBrush != null)
|
||||||
{
|
{
|
||||||
var outline = Outliner.GenerateOutline(rect, strokeWidth);
|
var outline = rect.GenerateStroke(image, this);
|
||||||
x = x.Fill(strokeBrush, outline);
|
if (outline != null)
|
||||||
|
{
|
||||||
|
x = x.Fill(strokeBrush, outline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SixLabors.Svg.Dom
|
||||||
|
{
|
||||||
|
internal struct SvgPaint
|
||||||
|
{
|
||||||
|
public static readonly SvgPaint Unset = default(SvgPaint);
|
||||||
|
|
||||||
|
internal SvgPaint(string value, float opacity)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Opacity = opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
public float Opacity { get; }
|
||||||
|
|
||||||
|
public static SvgPaint Parse(string val, string opacity)
|
||||||
|
{
|
||||||
|
val = val?.Trim() ?? "";
|
||||||
|
opacity = opacity?.Trim() ?? "";
|
||||||
|
|
||||||
|
float opacityVal = 1;
|
||||||
|
if (float.TryParse(opacity, out float op))
|
||||||
|
{
|
||||||
|
opacityVal = op;
|
||||||
|
if (opacityVal < 0)
|
||||||
|
{
|
||||||
|
opacityVal = 0;
|
||||||
|
}
|
||||||
|
if (opacityVal > 1)
|
||||||
|
{
|
||||||
|
opacityVal = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SvgPaint(val, opacityVal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TPixel? GetNamedColor<TPixel>(string name) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, TPixel>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["AliceBlue"] = NamedColors<TPixel>.AliceBlue,
|
||||||
|
["MistyRose"] = NamedColors<TPixel>.MistyRose,
|
||||||
|
["Moccasin"] = NamedColors<TPixel>.Moccasin,
|
||||||
|
["NavajoWhite"] = NamedColors<TPixel>.NavajoWhite,
|
||||||
|
["Navy"] = NamedColors<TPixel>.Navy,
|
||||||
|
["OldLace"] = NamedColors<TPixel>.OldLace,
|
||||||
|
["Olive"] = NamedColors<TPixel>.Olive,
|
||||||
|
["MintCream"] = NamedColors<TPixel>.MintCream,
|
||||||
|
["OliveDrab"] = NamedColors<TPixel>.OliveDrab,
|
||||||
|
["OrangeRed"] = NamedColors<TPixel>.OrangeRed,
|
||||||
|
["Orchid"] = NamedColors<TPixel>.Orchid,
|
||||||
|
["PaleGoldenrod"] = NamedColors<TPixel>.PaleGoldenrod,
|
||||||
|
["PaleGreen"] = NamedColors<TPixel>.PaleGreen,
|
||||||
|
["PaleTurquoise"] = NamedColors<TPixel>.PaleTurquoise,
|
||||||
|
["PaleVioletRed"] = NamedColors<TPixel>.PaleVioletRed,
|
||||||
|
["Orange"] = NamedColors<TPixel>.Orange,
|
||||||
|
["PapayaWhip"] = NamedColors<TPixel>.PapayaWhip,
|
||||||
|
["MidnightBlue"] = NamedColors<TPixel>.MidnightBlue,
|
||||||
|
["MediumTurquoise"] = NamedColors<TPixel>.MediumTurquoise,
|
||||||
|
["LightSteelBlue"] = NamedColors<TPixel>.LightSteelBlue,
|
||||||
|
["LightYellow"] = NamedColors<TPixel>.LightYellow,
|
||||||
|
["Lime"] = NamedColors<TPixel>.Lime,
|
||||||
|
["LimeGreen"] = NamedColors<TPixel>.LimeGreen,
|
||||||
|
["Linen"] = NamedColors<TPixel>.Linen,
|
||||||
|
["Magenta"] = NamedColors<TPixel>.Magenta,
|
||||||
|
["MediumVioletRed"] = NamedColors<TPixel>.MediumVioletRed,
|
||||||
|
["Maroon"] = NamedColors<TPixel>.Maroon,
|
||||||
|
["MediumBlue"] = NamedColors<TPixel>.MediumBlue,
|
||||||
|
["MediumOrchid"] = NamedColors<TPixel>.MediumOrchid,
|
||||||
|
["MediumPurple"] = NamedColors<TPixel>.MediumPurple,
|
||||||
|
["MediumSeaGreen"] = NamedColors<TPixel>.MediumSeaGreen,
|
||||||
|
["MediumSlateBlue"] = NamedColors<TPixel>.MediumSlateBlue,
|
||||||
|
["MediumSpringGreen"] = NamedColors<TPixel>.MediumSpringGreen,
|
||||||
|
["MediumAquamarine"] = NamedColors<TPixel>.MediumAquamarine,
|
||||||
|
["LightSlateGray"] = NamedColors<TPixel>.LightSlateGray,
|
||||||
|
["PeachPuff"] = NamedColors<TPixel>.PeachPuff,
|
||||||
|
["Pink"] = NamedColors<TPixel>.Pink,
|
||||||
|
["SpringGreen"] = NamedColors<TPixel>.SpringGreen,
|
||||||
|
["SteelBlue"] = NamedColors<TPixel>.SteelBlue,
|
||||||
|
["Tan"] = NamedColors<TPixel>.Tan,
|
||||||
|
["Teal"] = NamedColors<TPixel>.Teal,
|
||||||
|
["Thistle"] = NamedColors<TPixel>.Thistle,
|
||||||
|
["Tomato"] = NamedColors<TPixel>.Tomato,
|
||||||
|
["Snow"] = NamedColors<TPixel>.Snow,
|
||||||
|
["Transparent"] = NamedColors<TPixel>.Transparent,
|
||||||
|
["Violet"] = NamedColors<TPixel>.Violet,
|
||||||
|
["Wheat"] = NamedColors<TPixel>.Wheat,
|
||||||
|
["White"] = NamedColors<TPixel>.White,
|
||||||
|
["WhiteSmoke"] = NamedColors<TPixel>.WhiteSmoke,
|
||||||
|
["Yellow"] = NamedColors<TPixel>.Yellow,
|
||||||
|
["YellowGreen"] = NamedColors<TPixel>.YellowGreen,
|
||||||
|
["Turquoise"] = NamedColors<TPixel>.Turquoise,
|
||||||
|
["Peru"] = NamedColors<TPixel>.Peru,
|
||||||
|
["SlateGray"] = NamedColors<TPixel>.SlateGray,
|
||||||
|
["SkyBlue"] = NamedColors<TPixel>.SkyBlue,
|
||||||
|
["Plum"] = NamedColors<TPixel>.Plum,
|
||||||
|
["PowderBlue"] = NamedColors<TPixel>.PowderBlue,
|
||||||
|
["Purple"] = NamedColors<TPixel>.Purple,
|
||||||
|
["RebeccaPurple"] = NamedColors<TPixel>.RebeccaPurple,
|
||||||
|
["Red"] = NamedColors<TPixel>.Red,
|
||||||
|
["RosyBrown"] = NamedColors<TPixel>.RosyBrown,
|
||||||
|
["SlateBlue"] = NamedColors<TPixel>.SlateBlue,
|
||||||
|
["RoyalBlue"] = NamedColors<TPixel>.RoyalBlue,
|
||||||
|
["Salmon"] = NamedColors<TPixel>.Salmon,
|
||||||
|
["SandyBrown"] = NamedColors<TPixel>.SandyBrown,
|
||||||
|
["SeaGreen"] = NamedColors<TPixel>.SeaGreen,
|
||||||
|
["SeaShell"] = NamedColors<TPixel>.SeaShell,
|
||||||
|
["Sienna"] = NamedColors<TPixel>.Sienna,
|
||||||
|
["Silver"] = NamedColors<TPixel>.Silver,
|
||||||
|
["SaddleBrown"] = NamedColors<TPixel>.SaddleBrown,
|
||||||
|
["LightSkyBlue"] = NamedColors<TPixel>.LightSkyBlue,
|
||||||
|
["LightSeaGreen"] = NamedColors<TPixel>.LightSeaGreen,
|
||||||
|
["LightSalmon"] = NamedColors<TPixel>.LightSalmon,
|
||||||
|
["Crimson"] = NamedColors<TPixel>.Crimson,
|
||||||
|
["Cyan"] = NamedColors<TPixel>.Cyan,
|
||||||
|
["DarkBlue"] = NamedColors<TPixel>.DarkBlue,
|
||||||
|
["DarkCyan"] = NamedColors<TPixel>.DarkCyan,
|
||||||
|
["DarkGoldenrod"] = NamedColors<TPixel>.DarkGoldenrod,
|
||||||
|
["DarkGray"] = NamedColors<TPixel>.DarkGray,
|
||||||
|
["Cornsilk"] = NamedColors<TPixel>.Cornsilk,
|
||||||
|
["DarkGreen"] = NamedColors<TPixel>.DarkGreen,
|
||||||
|
["DarkMagenta"] = NamedColors<TPixel>.DarkMagenta,
|
||||||
|
["DarkOliveGreen"] = NamedColors<TPixel>.DarkOliveGreen,
|
||||||
|
["DarkOrange"] = NamedColors<TPixel>.DarkOrange,
|
||||||
|
["DarkOrchid"] = NamedColors<TPixel>.DarkOrchid,
|
||||||
|
["DarkRed"] = NamedColors<TPixel>.DarkRed,
|
||||||
|
["DarkSalmon"] = NamedColors<TPixel>.DarkSalmon,
|
||||||
|
["DarkKhaki"] = NamedColors<TPixel>.DarkKhaki,
|
||||||
|
["DarkSeaGreen"] = NamedColors<TPixel>.DarkSeaGreen,
|
||||||
|
["CornflowerBlue"] = NamedColors<TPixel>.CornflowerBlue,
|
||||||
|
["Chocolate"] = NamedColors<TPixel>.Chocolate,
|
||||||
|
["AntiqueWhite"] = NamedColors<TPixel>.AntiqueWhite,
|
||||||
|
["Aqua"] = NamedColors<TPixel>.Aqua,
|
||||||
|
["Aquamarine"] = NamedColors<TPixel>.Aquamarine,
|
||||||
|
["Azure"] = NamedColors<TPixel>.Azure,
|
||||||
|
["Beige"] = NamedColors<TPixel>.Beige,
|
||||||
|
["Bisque"] = NamedColors<TPixel>.Bisque,
|
||||||
|
["Coral"] = NamedColors<TPixel>.Coral,
|
||||||
|
["Black"] = NamedColors<TPixel>.Black,
|
||||||
|
["Blue"] = NamedColors<TPixel>.Blue,
|
||||||
|
["BlueViolet"] = NamedColors<TPixel>.BlueViolet,
|
||||||
|
["Brown"] = NamedColors<TPixel>.Brown,
|
||||||
|
["BurlyWood"] = NamedColors<TPixel>.BurlyWood,
|
||||||
|
["CadetBlue"] = NamedColors<TPixel>.CadetBlue,
|
||||||
|
["Chartreuse"] = NamedColors<TPixel>.Chartreuse,
|
||||||
|
["BlanchedAlmond"] = NamedColors<TPixel>.BlanchedAlmond,
|
||||||
|
["DarkSlateBlue"] = NamedColors<TPixel>.DarkSlateBlue,
|
||||||
|
["DarkSlateGray"] = NamedColors<TPixel>.DarkSlateGray,
|
||||||
|
["DarkTurquoise"] = NamedColors<TPixel>.DarkTurquoise,
|
||||||
|
["Indigo"] = NamedColors<TPixel>.Indigo,
|
||||||
|
["Ivory"] = NamedColors<TPixel>.Ivory,
|
||||||
|
["Khaki"] = NamedColors<TPixel>.Khaki,
|
||||||
|
["Lavender"] = NamedColors<TPixel>.Lavender,
|
||||||
|
["LavenderBlush"] = NamedColors<TPixel>.LavenderBlush,
|
||||||
|
["LawnGreen"] = NamedColors<TPixel>.LawnGreen,
|
||||||
|
["IndianRed"] = NamedColors<TPixel>.IndianRed,
|
||||||
|
["LemonChiffon"] = NamedColors<TPixel>.LemonChiffon,
|
||||||
|
["LightCoral"] = NamedColors<TPixel>.LightCoral,
|
||||||
|
["LightCyan"] = NamedColors<TPixel>.LightCyan,
|
||||||
|
["LightGoldenrodYellow"] = NamedColors<TPixel>.LightGoldenrodYellow,
|
||||||
|
["LightGray"] = NamedColors<TPixel>.LightGray,
|
||||||
|
["LightGreen"] = NamedColors<TPixel>.LightGreen,
|
||||||
|
["LightPink"] = NamedColors<TPixel>.LightPink,
|
||||||
|
["LightBlue"] = NamedColors<TPixel>.LightBlue,
|
||||||
|
["HotPink"] = NamedColors<TPixel>.HotPink,
|
||||||
|
["Honeydew"] = NamedColors<TPixel>.Honeydew,
|
||||||
|
["GreenYellow"] = NamedColors<TPixel>.GreenYellow,
|
||||||
|
["DarkViolet"] = NamedColors<TPixel>.DarkViolet,
|
||||||
|
["DeepPink"] = NamedColors<TPixel>.DeepPink,
|
||||||
|
["DeepSkyBlue"] = NamedColors<TPixel>.DeepSkyBlue,
|
||||||
|
["DimGray"] = NamedColors<TPixel>.DimGray,
|
||||||
|
["DodgerBlue"] = NamedColors<TPixel>.DodgerBlue,
|
||||||
|
["Firebrick"] = NamedColors<TPixel>.Firebrick,
|
||||||
|
["FloralWhite"] = NamedColors<TPixel>.FloralWhite,
|
||||||
|
["ForestGreen"] = NamedColors<TPixel>.ForestGreen,
|
||||||
|
["Fuchsia"] = NamedColors<TPixel>.Fuchsia,
|
||||||
|
["Gainsboro"] = NamedColors<TPixel>.Gainsboro,
|
||||||
|
["GhostWhite"] = NamedColors<TPixel>.GhostWhite,
|
||||||
|
["Gold"] = NamedColors<TPixel>.Gold,
|
||||||
|
["Goldenrod"] = NamedColors<TPixel>.Goldenrod,
|
||||||
|
["Gray"] = NamedColors<TPixel>.Gray,
|
||||||
|
["Green"] = NamedColors<TPixel>.Green,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dict.TryGetValue(name, out var pixel))
|
||||||
|
{
|
||||||
|
return pixel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public IBrush<TPixel> AsBrush<TPixel>() where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
// lets asume the brush is a color for now
|
||||||
|
// TODO update ColourBuilder to expose named colors
|
||||||
|
var value = this.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace(value) || value.Equals("None", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// if a null prush is returned we should skip
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var color = GetNamedColor<TPixel>(Value) ?? ColorBuilder<TPixel>.FromHex(value);
|
||||||
|
|
||||||
|
|
||||||
|
if(Opacity == 0)
|
||||||
|
{
|
||||||
|
// no-op push here to efficienty sake
|
||||||
|
color = NamedColors<TPixel>.Transparent;
|
||||||
|
} else if (Opacity != 1)
|
||||||
|
{
|
||||||
|
var vector = color.ToScaledVector4();
|
||||||
|
vector.W *= Opacity;
|
||||||
|
color.FromScaledVector4(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidBrush<TPixel>(color);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Svg.Dom;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using SixLabors.Shapes;
|
||||||
|
|
||||||
|
namespace SixLabors.Svg.Dom
|
||||||
|
{
|
||||||
|
internal sealed class SvgPath : SvgElement, ISvgShape
|
||||||
|
{
|
||||||
|
public IPath Path { get; private set; }
|
||||||
|
|
||||||
|
public SvgPaint Fill { get; private set; }
|
||||||
|
public SvgPaint Stroke { get; private set; }
|
||||||
|
public SvgUnitValue StrokeWidth { get; private set; }
|
||||||
|
public SvgLineCap StrokeLineCap { get; private set; }
|
||||||
|
public SvgLineJoin StrokeLineJoin { get; private set; }
|
||||||
|
|
||||||
|
public IEnumerable<SvgPathOperation> PathOperations { get; private set; }
|
||||||
|
public static Task<SvgElement> LoadAsync(ISvgElement element)
|
||||||
|
{
|
||||||
|
var path = new SvgPath()
|
||||||
|
{
|
||||||
|
Fill = element.GetPaint("fill", "Black", "1"),
|
||||||
|
Stroke = element.GetPaint("stroke", "None", "1"),
|
||||||
|
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
|
||||||
|
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
|
||||||
|
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (element.TagName == "line")
|
||||||
|
{
|
||||||
|
path.PathOperations = new[] {
|
||||||
|
SvgPathOperation.MoveTo(element.GetUnitValue("x1", "0"), element.GetUnitValue("y1", "0"), false),
|
||||||
|
SvgPathOperation.LineTo(element.GetUnitValue("x2", "0"), element.GetUnitValue("y2", "0"), false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (element.TagName == "polyline" || element.TagName == "polygon")
|
||||||
|
{
|
||||||
|
var pathData = element.GetAttributeValueSelfOrGroup("points");
|
||||||
|
path.PathOperations = ParsePoints(pathData, element.TagName == "polygon");
|
||||||
|
}
|
||||||
|
else if (element.TagName == "path")
|
||||||
|
{
|
||||||
|
var pathData = element.GetAttributeValueSelfOrGroup("d");
|
||||||
|
path.PathOperations = SvgPathOperation.Parse(pathData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.PathOperations == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult<SvgElement>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<SvgElement>(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<SvgPathOperation> ParsePoints(string pathData, bool closePath)
|
||||||
|
{
|
||||||
|
if (pathData != null && !pathData.Equals("none", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var parts = pathData.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x =>
|
||||||
|
{
|
||||||
|
if (int.TryParse(x, out var i))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return (int?)null; }
|
||||||
|
}).Where(x => x != null).Select(x => x.Value).ToArray();
|
||||||
|
if (parts.Length > 4)
|
||||||
|
{
|
||||||
|
var len = parts.Length / 2;
|
||||||
|
if (closePath)
|
||||||
|
{
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ops = new SvgPathOperation[len];
|
||||||
|
ops[0] = SvgPathOperation.MoveTo(new SvgUnitValue(parts[0], SvgUnitValue.Units.undefined), new SvgUnitValue(parts[1], SvgUnitValue.Units.undefined), false);
|
||||||
|
|
||||||
|
for (var i = 2; i < parts.Length; i += 2)
|
||||||
|
{
|
||||||
|
ops[i / 2] = SvgPathOperation.LineTo(new SvgUnitValue(parts[i], SvgUnitValue.Units.undefined), new SvgUnitValue(parts[i + 1], SvgUnitValue.Units.undefined), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closePath)
|
||||||
|
{
|
||||||
|
ops[len - 1] = SvgPathOperation.ClosePath();
|
||||||
|
}
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void RenderTo<TPixel>(Image<TPixel> image)
|
||||||
|
{
|
||||||
|
var rect = this.PathOperations.GeneratePath(image);
|
||||||
|
|
||||||
|
var fillBrush = Fill.AsBrush<TPixel>();
|
||||||
|
var strokeBrush = Stroke.AsBrush<TPixel>();
|
||||||
|
image.Mutate(x =>
|
||||||
|
{
|
||||||
|
if (fillBrush != null)
|
||||||
|
{
|
||||||
|
x = x.Fill(fillBrush, rect);
|
||||||
|
}
|
||||||
|
if (strokeBrush != null)
|
||||||
|
{
|
||||||
|
var outline = rect.GenerateStroke(image, this);
|
||||||
|
if (outline != null)
|
||||||
|
{
|
||||||
|
x = x.Fill(strokeBrush, outline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,401 @@
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.Primitives;
|
||||||
|
using SixLabors.Shapes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SixLabors.Svg.Dom
|
||||||
|
{
|
||||||
|
internal struct SvgPathOperation
|
||||||
|
{
|
||||||
|
public Operations Op { get; private set; }
|
||||||
|
public bool IsRelative { get; private set; }
|
||||||
|
|
||||||
|
public SvgUnitValue? X { get; private set; }
|
||||||
|
public SvgUnitValue? Y { get; private set; }
|
||||||
|
|
||||||
|
public SvgUnitValue? X1 { get; private set; }
|
||||||
|
public SvgUnitValue? Y1 { get; private set; }
|
||||||
|
|
||||||
|
public SvgUnitValue? X2 { get; private set; }
|
||||||
|
public SvgUnitValue? Y2 { get; private set; }
|
||||||
|
|
||||||
|
public static IEnumerable<SvgPathOperation> Parse(string path)
|
||||||
|
{
|
||||||
|
List<SvgPathOperation> operations = new List<SvgPathOperation>();
|
||||||
|
char cmd = ' ';
|
||||||
|
for (var i = 0; i < path.Length; i++)
|
||||||
|
{
|
||||||
|
switch (path[i])
|
||||||
|
{
|
||||||
|
case 'Z':
|
||||||
|
case 'z':
|
||||||
|
{
|
||||||
|
operations.Add(SvgPathOperation.ClosePath());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 'm';
|
||||||
|
if (!TryReadFloat(in path, ref i, out var x) || !TryReadFloat(in path, ref i, out var y))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.Add(SvgPathOperation.MoveTo(x, y, relative));
|
||||||
|
while (TryReadFloat(in path, ref i, out x) && TryReadFloat(in path, ref i, out y))
|
||||||
|
{
|
||||||
|
operations.Add(SvgPathOperation.LineTo(x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
case 'l':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 'l';
|
||||||
|
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
|
||||||
|
{
|
||||||
|
operations.Add(SvgPathOperation.LineTo(x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
case 'q':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 'q';
|
||||||
|
while (TryReadFloat(in path, ref i, out var x1) && TryReadFloat(in path, ref i, out var y1)
|
||||||
|
&& TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
|
||||||
|
{
|
||||||
|
operations.Add(SvgPathOperation.QuadraticTo(x1, y1, x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
case 't':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 't';
|
||||||
|
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
|
||||||
|
{
|
||||||
|
SvgUnitValue x1;
|
||||||
|
SvgUnitValue y1;
|
||||||
|
var last = operations.Last();
|
||||||
|
|
||||||
|
if (last.Op != Operations.QuadraticTo)
|
||||||
|
{
|
||||||
|
x1 = last.X.Value;
|
||||||
|
y1 = last.Y.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x1 = new SvgUnitValue(last.X.Value.Value + (last.X.Value.Value - last.X1.Value.Value), SvgUnitValue.Units.undefined);
|
||||||
|
y1 = new SvgUnitValue(last.Y.Value.Value + (last.Y.Value.Value - last.Y1.Value.Value), SvgUnitValue.Units.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.Add(SvgPathOperation.QuadraticTo(x1, y1, x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
case 'c':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 'c';
|
||||||
|
while (TryReadFloat(in path, ref i, out var x1) && TryReadFloat(in path, ref i, out var y1)
|
||||||
|
&& TryReadFloat(in path, ref i, out var x2) && TryReadFloat(in path, ref i, out var y2)
|
||||||
|
&& TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
|
||||||
|
{
|
||||||
|
operations.Add(SvgPathOperation.CubicTo(x1, y1, x2, y2, x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
var relative = path[i] == 's';
|
||||||
|
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y)
|
||||||
|
&& TryReadFloat(in path, ref i, out var x2) && TryReadFloat(in path, ref i, out var y2))
|
||||||
|
{
|
||||||
|
SvgUnitValue x1;
|
||||||
|
SvgUnitValue y1;
|
||||||
|
var last = operations.Last();
|
||||||
|
|
||||||
|
if (last.Op != Operations.CubicTo)
|
||||||
|
{
|
||||||
|
x1 = last.X.Value;
|
||||||
|
y1 = last.Y.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x1 = new SvgUnitValue(last.X.Value.Value + (last.X.Value.Value - last.X2.Value.Value), SvgUnitValue.Units.undefined);
|
||||||
|
y1 = new SvgUnitValue(last.Y.Value.Value + (last.Y.Value.Value - last.Y2.Value.Value), SvgUnitValue.Units.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.Add(SvgPathOperation.CubicTo(x1, y1, x2, y2, x, y, relative));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (char.IsWhiteSpace(path[i]) || path[i] == ',' || path[i] == ' ')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadFloat(in string str, ref int position, out SvgUnitValue value)
|
||||||
|
{
|
||||||
|
// look for the first character that can be ins float
|
||||||
|
int floatStart = 0;
|
||||||
|
|
||||||
|
for (position++; position < str.Length; position++)
|
||||||
|
{
|
||||||
|
if (floatStart == 0)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(str[position]) || str[position] == ',')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// we can skip 'whitespace'
|
||||||
|
}
|
||||||
|
if (char.IsDigit(str[position]) || str[position] == '-' || str[position] == '.')
|
||||||
|
{
|
||||||
|
if (floatStart == 0) { floatStart = position; }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floatStart == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
value = SvgUnitValue.Unset;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position == str.Length)
|
||||||
|
{
|
||||||
|
value = new SvgUnitValue(float.Parse(str.Substring(floatStart)), SvgUnitValue.Units.undefined);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = new SvgUnitValue(float.Parse(str.Substring(floatStart, position - floatStart + 1)), SvgUnitValue.Units.undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum Operations
|
||||||
|
{
|
||||||
|
MoveTo,
|
||||||
|
LineTo,
|
||||||
|
ClosePath,
|
||||||
|
QuadraticTo,
|
||||||
|
CubicTo
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SvgPathOperation CubicTo(SvgUnitValue x1, SvgUnitValue y1, SvgUnitValue x2, SvgUnitValue y2, SvgUnitValue x, SvgUnitValue y, bool relative)
|
||||||
|
{
|
||||||
|
return new SvgPathOperation()
|
||||||
|
{
|
||||||
|
IsRelative = relative,
|
||||||
|
Op = Operations.CubicTo,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
X1 = x1,
|
||||||
|
Y1 = y1,
|
||||||
|
X2 = x2,
|
||||||
|
Y2 = y2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private static SvgPathOperation QuadraticTo(SvgUnitValue x1, SvgUnitValue y1, SvgUnitValue x, SvgUnitValue y, bool relative)
|
||||||
|
{
|
||||||
|
return new SvgPathOperation()
|
||||||
|
{
|
||||||
|
IsRelative = relative,
|
||||||
|
Op = Operations.QuadraticTo,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
X1 = x1,
|
||||||
|
Y1 = y1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgPathOperation MoveTo(SvgUnitValue x, SvgUnitValue y, bool relative)
|
||||||
|
{
|
||||||
|
return new SvgPathOperation()
|
||||||
|
{
|
||||||
|
IsRelative = relative,
|
||||||
|
Op = Operations.MoveTo,
|
||||||
|
X = x,
|
||||||
|
Y = y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static SvgPathOperation LineTo(SvgUnitValue x, SvgUnitValue y, bool relative)
|
||||||
|
{
|
||||||
|
return new SvgPathOperation()
|
||||||
|
{
|
||||||
|
IsRelative = relative,
|
||||||
|
Op = Operations.LineTo,
|
||||||
|
X = x,
|
||||||
|
Y = y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SvgPathOperation ClosePath()
|
||||||
|
{
|
||||||
|
return new SvgPathOperation()
|
||||||
|
{
|
||||||
|
Op = Operations.ClosePath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class SvgPathOperationExtensions
|
||||||
|
{
|
||||||
|
private static PointF Point<TPixel>(Image<TPixel> img, PointF currentPoint, SvgUnitValue x, SvgUnitValue y) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
var p = new PointF(x.AsPixelXAxis(img), y.AsPixelYAxis(img));
|
||||||
|
return currentPoint + p;
|
||||||
|
}
|
||||||
|
public static PointF PointMain<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
return Point(img, operation.IsRelative ? currentPoint : default, operation.X.Value, operation.Y.Value);
|
||||||
|
}
|
||||||
|
public static PointF Point1<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
return Point(img, operation.IsRelative ? currentPoint : default, operation.X1.Value, operation.Y1.Value);
|
||||||
|
}
|
||||||
|
public static PointF Point2<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
return Point(img, operation.IsRelative ? currentPoint : default, operation.X2.Value, operation.Y2.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPath GeneratePath<TPixel>(this IEnumerable<SvgPathOperation> operations, Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
|
||||||
|
{
|
||||||
|
var pb = new PathRenderer();
|
||||||
|
|
||||||
|
foreach (var op in operations)
|
||||||
|
{
|
||||||
|
switch (op.Op)
|
||||||
|
{
|
||||||
|
case SvgPathOperation.Operations.MoveTo:
|
||||||
|
pb.MoveTo(op.PointMain(img, pb.CurrentPoint));
|
||||||
|
break;
|
||||||
|
case SvgPathOperation.Operations.LineTo:
|
||||||
|
pb.LineTo(op.PointMain(img, pb.CurrentPoint));
|
||||||
|
break;
|
||||||
|
case SvgPathOperation.Operations.QuadraticTo:
|
||||||
|
pb.QuadraticBezierTo(op.Point1(img, pb.CurrentPoint), op.PointMain(img, pb.CurrentPoint));
|
||||||
|
break;
|
||||||
|
case SvgPathOperation.Operations.CubicTo:
|
||||||
|
pb.CubicBezierTo(op.Point1(img, pb.CurrentPoint), op.Point2(img, pb.CurrentPoint), op.PointMain(img, pb.CurrentPoint));
|
||||||
|
break;
|
||||||
|
case SvgPathOperation.Operations.ClosePath:
|
||||||
|
pb.Close();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb.Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PathRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The builder. TODO: Should this be a property?
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private readonly PathBuilder builder;
|
||||||
|
private readonly List<IPath> paths = new List<IPath>();
|
||||||
|
private PointF currentPoint = default(PointF);
|
||||||
|
private PointF initalPoint = default(PointF);
|
||||||
|
|
||||||
|
public PointF CurrentPoint => currentPoint;
|
||||||
|
public PointF InitalPoint => initalPoint;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BaseGlyphBuilder"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public PathRenderer()
|
||||||
|
{
|
||||||
|
// glyphs are renderd realative to bottom left so invert the Y axis to allow it to render on top left origin surface
|
||||||
|
this.builder = new PathBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the paths that have been rendered by this.
|
||||||
|
/// </summary>
|
||||||
|
public IPath Path()
|
||||||
|
{
|
||||||
|
return this.builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a cubic bezier from the current point to the <paramref name="point"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secondControlPoint">The second control point.</param>
|
||||||
|
/// <param name="thirdControlPoint">The third control point.</param>
|
||||||
|
/// <param name="point">The point.</param>
|
||||||
|
public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
|
||||||
|
{
|
||||||
|
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
|
||||||
|
this.currentPoint = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a line from the current point to the <paramref name="point"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point.</param>
|
||||||
|
public void LineTo(PointF point)
|
||||||
|
{
|
||||||
|
this.builder.AddLine(this.currentPoint, point);
|
||||||
|
this.currentPoint = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves to current point to the supplied vector.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point.</param>
|
||||||
|
public void MoveTo(PointF point)
|
||||||
|
{
|
||||||
|
this.builder.StartFigure();
|
||||||
|
this.currentPoint = point;
|
||||||
|
this.initalPoint = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a quadratics bezier from the current point to the <paramref name="point"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secondControlPoint">The second control point.</param>
|
||||||
|
/// <param name="point">The point.</param>
|
||||||
|
public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
|
||||||
|
{
|
||||||
|
this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
|
||||||
|
this.currentPoint = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Close()
|
||||||
|
{
|
||||||
|
this.LineTo(initalPoint);
|
||||||
|
//this.builder.CloseFigure();
|
||||||
|
//this.currentPoint = this.initalPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,38 +2,45 @@
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Svg.Dom;
|
using AngleSharp.Svg.Dom;
|
||||||
|
using AngleSharp.Css.Dom;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using SixLabors.Shapes;
|
using SixLabors.Shapes;
|
||||||
|
|
||||||
namespace SixLabors.Svg.Dom
|
namespace SixLabors.Svg.Dom
|
||||||
{
|
{
|
||||||
internal sealed class SvgRect : SvgElement
|
|
||||||
{
|
|
||||||
|
|
||||||
|
internal sealed class SvgRect : SvgElement, ISvgShape
|
||||||
|
{
|
||||||
public SvgUnitValue X { get; private set; }
|
public SvgUnitValue X { get; private set; }
|
||||||
public SvgUnitValue Y { get; private set; }
|
public SvgUnitValue Y { get; private set; }
|
||||||
public SvgUnitValue Width { get; private set; }
|
public SvgUnitValue Width { get; private set; }
|
||||||
public SvgUnitValue Height { get; private set; }
|
public SvgUnitValue Height { get; private set; }
|
||||||
public SvgPaint Fill { get; private set; }
|
public SvgPaint Fill { get; private set; }
|
||||||
public SvgPaint Stroke { get; private set; }
|
public SvgPaint Stroke { get; private set; }
|
||||||
|
public SvgLineCap StrokeLineCap { get; private set; }
|
||||||
|
public SvgLineJoin StrokeLineJoin { get; private set; }
|
||||||
public SvgUnitValue StrokeWidth { get; private set; }
|
public SvgUnitValue StrokeWidth { get; private set; }
|
||||||
public SvgUnitValue RadiusX { get; private set; }
|
public SvgUnitValue RadiusX { get; private set; }
|
||||||
public SvgUnitValue RadiusY { get; private set; }
|
public SvgUnitValue RadiusY { get; private set; }
|
||||||
|
|
||||||
public static Task<SvgElement> LoadAsync(ISvgElement element)
|
public static Task<SvgElement> LoadAsync(ISvgElement element)
|
||||||
{
|
{
|
||||||
|
var rx = element.TryGetUnitValue("rx");
|
||||||
|
var ry = element.TryGetUnitValue("ry");
|
||||||
return Task.FromResult<SvgElement>(new SvgRect()
|
return Task.FromResult<SvgElement>(new SvgRect()
|
||||||
{
|
{
|
||||||
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
|
Fill = element.GetPaint("fill", "Black", "1"),
|
||||||
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
|
Stroke = element.GetPaint("stroke", "None", "1"),
|
||||||
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
|
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
|
||||||
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
|
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
|
||||||
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
|
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
|
||||||
RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? element.Attributes["ry"]?.Value ?? "0"),
|
X = element.GetUnitValue("x"),
|
||||||
RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? element.Attributes["rx"]?.Value ?? "0"),
|
Y = element.GetUnitValue("y"),
|
||||||
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
|
RadiusX = rx ?? ry ?? SvgUnitValue.Zero,
|
||||||
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
|
RadiusY = ry ?? rx ?? SvgUnitValue.Zero,
|
||||||
|
Width = element.GetUnitValue("width"),
|
||||||
|
Height = element.GetUnitValue("height")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,17 +58,19 @@ namespace SixLabors.Svg.Dom
|
||||||
|
|
||||||
var fillBrush = Fill.AsBrush<TPixel>();
|
var fillBrush = Fill.AsBrush<TPixel>();
|
||||||
var strokeBrush = Stroke.AsBrush<TPixel>();
|
var strokeBrush = Stroke.AsBrush<TPixel>();
|
||||||
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
|
|
||||||
image.Mutate(x =>
|
image.Mutate(x =>
|
||||||
{
|
{
|
||||||
if (fillBrush != null)
|
if (fillBrush != null)
|
||||||
{
|
{
|
||||||
x = x.Fill(fillBrush, rect);
|
x = x.Fill(fillBrush, rect);
|
||||||
}
|
}
|
||||||
if (strokeBrush != null && strokeWidth > 0)
|
if (strokeBrush != null)
|
||||||
{
|
{
|
||||||
var outline = Outliner.GenerateOutline(rect, strokeWidth);
|
var outline = rect.GenerateStroke(image, this);
|
||||||
x = x.Fill(strokeBrush, outline);
|
if (outline != null)
|
||||||
|
{
|
||||||
|
x = x.Fill(strokeBrush, outline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -83,19 +92,15 @@ namespace SixLabors.Svg.Dom
|
||||||
//lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
|
//lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
|
||||||
var center = new Vector2(imageWidth / 2F, imageHeight / 2F);
|
var center = new Vector2(imageWidth / 2F, imageHeight / 2F);
|
||||||
|
|
||||||
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1;
|
float rightPos = imageWidth - cornerToptLeft.Bounds.Width ;
|
||||||
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
|
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height ;
|
||||||
|
|
||||||
// move it across the widthof the image - the width of the shape
|
// move it across the widthof the image - the width of the shape
|
||||||
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos - 0.5f, 0);
|
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
|
||||||
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos - 0.5f);
|
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
|
||||||
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos - 0.5f, bottomPos - 0.5f);
|
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
|
||||||
|
|
||||||
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
|
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,226 +1,18 @@
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.Shapes;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace SixLabors.Svg.Dom
|
namespace SixLabors.Svg.Dom
|
||||||
{
|
{
|
||||||
internal struct SvgPaint
|
|
||||||
{
|
|
||||||
public static readonly SvgPaint Unset = default(SvgPaint);
|
|
||||||
|
|
||||||
internal SvgPaint(string value, float opacity)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
Opacity = opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public float Opacity { get; }
|
|
||||||
|
|
||||||
public static SvgPaint Parse(string val, string opacity)
|
|
||||||
{
|
|
||||||
val = val?.Trim() ?? "";
|
|
||||||
opacity = opacity?.Trim() ?? "";
|
|
||||||
|
|
||||||
float opacityVal = 1;
|
|
||||||
if (float.TryParse(opacity, out float op))
|
|
||||||
{
|
|
||||||
opacityVal = op;
|
|
||||||
if (opacityVal < 0)
|
|
||||||
{
|
|
||||||
opacityVal = 0;
|
|
||||||
}
|
|
||||||
if (opacityVal > 1)
|
|
||||||
{
|
|
||||||
opacityVal = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SvgPaint(val, opacityVal);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private TPixel? GetNamedColor<TPixel>(string name) where TPixel : struct, IPixel<TPixel>
|
|
||||||
{
|
|
||||||
var dict = new Dictionary<string, TPixel>(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
["AliceBlue"] = NamedColors<TPixel>.AliceBlue,
|
|
||||||
["MistyRose"] = NamedColors<TPixel>.MistyRose,
|
|
||||||
["Moccasin"] = NamedColors<TPixel>.Moccasin,
|
|
||||||
["NavajoWhite"] = NamedColors<TPixel>.NavajoWhite,
|
|
||||||
["Navy"] = NamedColors<TPixel>.Navy,
|
|
||||||
["OldLace"] = NamedColors<TPixel>.OldLace,
|
|
||||||
["Olive"] = NamedColors<TPixel>.Olive,
|
|
||||||
["MintCream"] = NamedColors<TPixel>.MintCream,
|
|
||||||
["OliveDrab"] = NamedColors<TPixel>.OliveDrab,
|
|
||||||
["OrangeRed"] = NamedColors<TPixel>.OrangeRed,
|
|
||||||
["Orchid"] = NamedColors<TPixel>.Orchid,
|
|
||||||
["PaleGoldenrod"] = NamedColors<TPixel>.PaleGoldenrod,
|
|
||||||
["PaleGreen"] = NamedColors<TPixel>.PaleGreen,
|
|
||||||
["PaleTurquoise"] = NamedColors<TPixel>.PaleTurquoise,
|
|
||||||
["PaleVioletRed"] = NamedColors<TPixel>.PaleVioletRed,
|
|
||||||
["Orange"] = NamedColors<TPixel>.Orange,
|
|
||||||
["PapayaWhip"] = NamedColors<TPixel>.PapayaWhip,
|
|
||||||
["MidnightBlue"] = NamedColors<TPixel>.MidnightBlue,
|
|
||||||
["MediumTurquoise"] = NamedColors<TPixel>.MediumTurquoise,
|
|
||||||
["LightSteelBlue"] = NamedColors<TPixel>.LightSteelBlue,
|
|
||||||
["LightYellow"] = NamedColors<TPixel>.LightYellow,
|
|
||||||
["Lime"] = NamedColors<TPixel>.Lime,
|
|
||||||
["LimeGreen"] = NamedColors<TPixel>.LimeGreen,
|
|
||||||
["Linen"] = NamedColors<TPixel>.Linen,
|
|
||||||
["Magenta"] = NamedColors<TPixel>.Magenta,
|
|
||||||
["MediumVioletRed"] = NamedColors<TPixel>.MediumVioletRed,
|
|
||||||
["Maroon"] = NamedColors<TPixel>.Maroon,
|
|
||||||
["MediumBlue"] = NamedColors<TPixel>.MediumBlue,
|
|
||||||
["MediumOrchid"] = NamedColors<TPixel>.MediumOrchid,
|
|
||||||
["MediumPurple"] = NamedColors<TPixel>.MediumPurple,
|
|
||||||
["MediumSeaGreen"] = NamedColors<TPixel>.MediumSeaGreen,
|
|
||||||
["MediumSlateBlue"] = NamedColors<TPixel>.MediumSlateBlue,
|
|
||||||
["MediumSpringGreen"] = NamedColors<TPixel>.MediumSpringGreen,
|
|
||||||
["MediumAquamarine"] = NamedColors<TPixel>.MediumAquamarine,
|
|
||||||
["LightSlateGray"] = NamedColors<TPixel>.LightSlateGray,
|
|
||||||
["PeachPuff"] = NamedColors<TPixel>.PeachPuff,
|
|
||||||
["Pink"] = NamedColors<TPixel>.Pink,
|
|
||||||
["SpringGreen"] = NamedColors<TPixel>.SpringGreen,
|
|
||||||
["SteelBlue"] = NamedColors<TPixel>.SteelBlue,
|
|
||||||
["Tan"] = NamedColors<TPixel>.Tan,
|
|
||||||
["Teal"] = NamedColors<TPixel>.Teal,
|
|
||||||
["Thistle"] = NamedColors<TPixel>.Thistle,
|
|
||||||
["Tomato"] = NamedColors<TPixel>.Tomato,
|
|
||||||
["Snow"] = NamedColors<TPixel>.Snow,
|
|
||||||
["Transparent"] = NamedColors<TPixel>.Transparent,
|
|
||||||
["Violet"] = NamedColors<TPixel>.Violet,
|
|
||||||
["Wheat"] = NamedColors<TPixel>.Wheat,
|
|
||||||
["White"] = NamedColors<TPixel>.White,
|
|
||||||
["WhiteSmoke"] = NamedColors<TPixel>.WhiteSmoke,
|
|
||||||
["Yellow"] = NamedColors<TPixel>.Yellow,
|
|
||||||
["YellowGreen"] = NamedColors<TPixel>.YellowGreen,
|
|
||||||
["Turquoise"] = NamedColors<TPixel>.Turquoise,
|
|
||||||
["Peru"] = NamedColors<TPixel>.Peru,
|
|
||||||
["SlateGray"] = NamedColors<TPixel>.SlateGray,
|
|
||||||
["SkyBlue"] = NamedColors<TPixel>.SkyBlue,
|
|
||||||
["Plum"] = NamedColors<TPixel>.Plum,
|
|
||||||
["PowderBlue"] = NamedColors<TPixel>.PowderBlue,
|
|
||||||
["Purple"] = NamedColors<TPixel>.Purple,
|
|
||||||
["RebeccaPurple"] = NamedColors<TPixel>.RebeccaPurple,
|
|
||||||
["Red"] = NamedColors<TPixel>.Red,
|
|
||||||
["RosyBrown"] = NamedColors<TPixel>.RosyBrown,
|
|
||||||
["SlateBlue"] = NamedColors<TPixel>.SlateBlue,
|
|
||||||
["RoyalBlue"] = NamedColors<TPixel>.RoyalBlue,
|
|
||||||
["Salmon"] = NamedColors<TPixel>.Salmon,
|
|
||||||
["SandyBrown"] = NamedColors<TPixel>.SandyBrown,
|
|
||||||
["SeaGreen"] = NamedColors<TPixel>.SeaGreen,
|
|
||||||
["SeaShell"] = NamedColors<TPixel>.SeaShell,
|
|
||||||
["Sienna"] = NamedColors<TPixel>.Sienna,
|
|
||||||
["Silver"] = NamedColors<TPixel>.Silver,
|
|
||||||
["SaddleBrown"] = NamedColors<TPixel>.SaddleBrown,
|
|
||||||
["LightSkyBlue"] = NamedColors<TPixel>.LightSkyBlue,
|
|
||||||
["LightSeaGreen"] = NamedColors<TPixel>.LightSeaGreen,
|
|
||||||
["LightSalmon"] = NamedColors<TPixel>.LightSalmon,
|
|
||||||
["Crimson"] = NamedColors<TPixel>.Crimson,
|
|
||||||
["Cyan"] = NamedColors<TPixel>.Cyan,
|
|
||||||
["DarkBlue"] = NamedColors<TPixel>.DarkBlue,
|
|
||||||
["DarkCyan"] = NamedColors<TPixel>.DarkCyan,
|
|
||||||
["DarkGoldenrod"] = NamedColors<TPixel>.DarkGoldenrod,
|
|
||||||
["DarkGray"] = NamedColors<TPixel>.DarkGray,
|
|
||||||
["Cornsilk"] = NamedColors<TPixel>.Cornsilk,
|
|
||||||
["DarkGreen"] = NamedColors<TPixel>.DarkGreen,
|
|
||||||
["DarkMagenta"] = NamedColors<TPixel>.DarkMagenta,
|
|
||||||
["DarkOliveGreen"] = NamedColors<TPixel>.DarkOliveGreen,
|
|
||||||
["DarkOrange"] = NamedColors<TPixel>.DarkOrange,
|
|
||||||
["DarkOrchid"] = NamedColors<TPixel>.DarkOrchid,
|
|
||||||
["DarkRed"] = NamedColors<TPixel>.DarkRed,
|
|
||||||
["DarkSalmon"] = NamedColors<TPixel>.DarkSalmon,
|
|
||||||
["DarkKhaki"] = NamedColors<TPixel>.DarkKhaki,
|
|
||||||
["DarkSeaGreen"] = NamedColors<TPixel>.DarkSeaGreen,
|
|
||||||
["CornflowerBlue"] = NamedColors<TPixel>.CornflowerBlue,
|
|
||||||
["Chocolate"] = NamedColors<TPixel>.Chocolate,
|
|
||||||
["AntiqueWhite"] = NamedColors<TPixel>.AntiqueWhite,
|
|
||||||
["Aqua"] = NamedColors<TPixel>.Aqua,
|
|
||||||
["Aquamarine"] = NamedColors<TPixel>.Aquamarine,
|
|
||||||
["Azure"] = NamedColors<TPixel>.Azure,
|
|
||||||
["Beige"] = NamedColors<TPixel>.Beige,
|
|
||||||
["Bisque"] = NamedColors<TPixel>.Bisque,
|
|
||||||
["Coral"] = NamedColors<TPixel>.Coral,
|
|
||||||
["Black"] = NamedColors<TPixel>.Black,
|
|
||||||
["Blue"] = NamedColors<TPixel>.Blue,
|
|
||||||
["BlueViolet"] = NamedColors<TPixel>.BlueViolet,
|
|
||||||
["Brown"] = NamedColors<TPixel>.Brown,
|
|
||||||
["BurlyWood"] = NamedColors<TPixel>.BurlyWood,
|
|
||||||
["CadetBlue"] = NamedColors<TPixel>.CadetBlue,
|
|
||||||
["Chartreuse"] = NamedColors<TPixel>.Chartreuse,
|
|
||||||
["BlanchedAlmond"] = NamedColors<TPixel>.BlanchedAlmond,
|
|
||||||
["DarkSlateBlue"] = NamedColors<TPixel>.DarkSlateBlue,
|
|
||||||
["DarkSlateGray"] = NamedColors<TPixel>.DarkSlateGray,
|
|
||||||
["DarkTurquoise"] = NamedColors<TPixel>.DarkTurquoise,
|
|
||||||
["Indigo"] = NamedColors<TPixel>.Indigo,
|
|
||||||
["Ivory"] = NamedColors<TPixel>.Ivory,
|
|
||||||
["Khaki"] = NamedColors<TPixel>.Khaki,
|
|
||||||
["Lavender"] = NamedColors<TPixel>.Lavender,
|
|
||||||
["LavenderBlush"] = NamedColors<TPixel>.LavenderBlush,
|
|
||||||
["LawnGreen"] = NamedColors<TPixel>.LawnGreen,
|
|
||||||
["IndianRed"] = NamedColors<TPixel>.IndianRed,
|
|
||||||
["LemonChiffon"] = NamedColors<TPixel>.LemonChiffon,
|
|
||||||
["LightCoral"] = NamedColors<TPixel>.LightCoral,
|
|
||||||
["LightCyan"] = NamedColors<TPixel>.LightCyan,
|
|
||||||
["LightGoldenrodYellow"] = NamedColors<TPixel>.LightGoldenrodYellow,
|
|
||||||
["LightGray"] = NamedColors<TPixel>.LightGray,
|
|
||||||
["LightGreen"] = NamedColors<TPixel>.LightGreen,
|
|
||||||
["LightPink"] = NamedColors<TPixel>.LightPink,
|
|
||||||
["LightBlue"] = NamedColors<TPixel>.LightBlue,
|
|
||||||
["HotPink"] = NamedColors<TPixel>.HotPink,
|
|
||||||
["Honeydew"] = NamedColors<TPixel>.Honeydew,
|
|
||||||
["GreenYellow"] = NamedColors<TPixel>.GreenYellow,
|
|
||||||
["DarkViolet"] = NamedColors<TPixel>.DarkViolet,
|
|
||||||
["DeepPink"] = NamedColors<TPixel>.DeepPink,
|
|
||||||
["DeepSkyBlue"] = NamedColors<TPixel>.DeepSkyBlue,
|
|
||||||
["DimGray"] = NamedColors<TPixel>.DimGray,
|
|
||||||
["DodgerBlue"] = NamedColors<TPixel>.DodgerBlue,
|
|
||||||
["Firebrick"] = NamedColors<TPixel>.Firebrick,
|
|
||||||
["FloralWhite"] = NamedColors<TPixel>.FloralWhite,
|
|
||||||
["ForestGreen"] = NamedColors<TPixel>.ForestGreen,
|
|
||||||
["Fuchsia"] = NamedColors<TPixel>.Fuchsia,
|
|
||||||
["Gainsboro"] = NamedColors<TPixel>.Gainsboro,
|
|
||||||
["GhostWhite"] = NamedColors<TPixel>.GhostWhite,
|
|
||||||
["Gold"] = NamedColors<TPixel>.Gold,
|
|
||||||
["Goldenrod"] = NamedColors<TPixel>.Goldenrod,
|
|
||||||
["Gray"] = NamedColors<TPixel>.Gray,
|
|
||||||
["Green"] = NamedColors<TPixel>.Green,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dict.TryGetValue(name, out var pixel))
|
|
||||||
{
|
|
||||||
return pixel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public IBrush<TPixel> AsBrush<TPixel>() where TPixel : struct, IPixel<TPixel>
|
|
||||||
{
|
|
||||||
// lets asume the brush is a color for now
|
|
||||||
// TODO update ColourBuilder to expose named colors
|
|
||||||
var value = this.Value;
|
|
||||||
if (string.IsNullOrWhiteSpace(value) || value.Equals("None", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
// if a null prush is returned we should skip
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var color = GetNamedColor<TPixel>(Value) ?? ColorBuilder<TPixel>.FromHex(value);
|
|
||||||
|
|
||||||
|
|
||||||
return new SolidBrush<TPixel>(color);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal struct SvgUnitValue
|
internal struct SvgUnitValue
|
||||||
{
|
{
|
||||||
public static readonly SvgUnitValue Unset = default(SvgUnitValue);
|
public static readonly SvgUnitValue Unset = default(SvgUnitValue);
|
||||||
|
public static readonly SvgUnitValue Zero = new SvgUnitValue(0, Units.undefined);
|
||||||
|
public static readonly SvgUnitValue One = new SvgUnitValue(1, Units.undefined);
|
||||||
|
|
||||||
public bool IsSet { get; }
|
public bool IsSet { get; }
|
||||||
public float Value { get; }
|
public float Value { get; }
|
||||||
|
@ -272,7 +64,7 @@ namespace SixLabors.Svg.Dom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SvgUnitValue Parse(string val)
|
public static SvgUnitValue? Parse(string val)
|
||||||
{
|
{
|
||||||
val = val?.Trim() ?? "";
|
val = val?.Trim() ?? "";
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
if (!string.IsNullOrWhiteSpace(val))
|
||||||
|
@ -314,7 +106,7 @@ namespace SixLabors.Svg.Dom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unset;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Units
|
public enum Units
|
||||||
|
@ -333,4 +125,71 @@ namespace SixLabors.Svg.Dom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal struct SvgLineCap
|
||||||
|
{
|
||||||
|
public static readonly SvgLineCap Unset = default(SvgLineCap);
|
||||||
|
public static readonly SvgLineCap Butt = new SvgLineCap(EndCapStyle.Butt);
|
||||||
|
public static readonly SvgLineCap Round = new SvgLineCap(EndCapStyle.Round);
|
||||||
|
public static readonly SvgLineCap Square = new SvgLineCap(EndCapStyle.Square);
|
||||||
|
|
||||||
|
public bool IsSet { get; }
|
||||||
|
|
||||||
|
public EndCapStyle Style { get; private set; }
|
||||||
|
|
||||||
|
public SvgLineCap(EndCapStyle style)
|
||||||
|
{
|
||||||
|
this.IsSet = true;
|
||||||
|
this.Style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgLineCap? Parse(string val)
|
||||||
|
{
|
||||||
|
val = (val?.Trim().ToLower() ?? "");
|
||||||
|
switch (val)
|
||||||
|
{
|
||||||
|
case "butt":
|
||||||
|
return SvgLineCap.Butt;
|
||||||
|
case "round":
|
||||||
|
return SvgLineCap.Round;
|
||||||
|
case "square":
|
||||||
|
return SvgLineCap.Square;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal struct SvgLineJoin
|
||||||
|
{
|
||||||
|
public static readonly SvgLineJoin Unset = default(SvgLineJoin);
|
||||||
|
public static readonly SvgLineJoin Miter = new SvgLineJoin(JointStyle.Miter);
|
||||||
|
public static readonly SvgLineJoin Round = new SvgLineJoin(JointStyle.Round);
|
||||||
|
public static readonly SvgLineJoin Square = new SvgLineJoin(JointStyle.Square);
|
||||||
|
|
||||||
|
public bool IsSet { get; }
|
||||||
|
|
||||||
|
public JointStyle Style { get; private set; }
|
||||||
|
|
||||||
|
public SvgLineJoin(JointStyle style)
|
||||||
|
{
|
||||||
|
this.IsSet = true;
|
||||||
|
this.Style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SvgLineJoin? Parse(string val)
|
||||||
|
{
|
||||||
|
val = (val?.Trim().ToLower() ?? "");
|
||||||
|
switch (val)
|
||||||
|
{
|
||||||
|
case "miter":
|
||||||
|
return SvgLineJoin.Miter;
|
||||||
|
case "round":
|
||||||
|
return SvgLineJoin.Round;
|
||||||
|
case "bevel":
|
||||||
|
return SvgLineJoin.Square;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard1.3</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>7.2</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.10.1" />
|
<PackageReference Include="AngleSharp" Version="0.10.1" />
|
||||||
|
<PackageReference Include="AngleSharp.Css" Version="0.10.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-dev002362" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-dev002362" />
|
||||||
|
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000112" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -1,7 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<LangVersion>7.2</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче