initial render of simple shapes to imagesharp canvas

This commit is contained in:
Scott Williams 2019-02-06 22:48:02 +00:00
Родитель 7c835d52e2
Коммит f41d627f7e
16 изменённых файлов: 731 добавлений и 239 удалений

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="myget.org imagesharp" value="https://www.myget.org/F/imagesharp/api/v3/index.json" />
<add key="myget.org imagesharp" value="https://www.myget.org/F/sixlabors/api/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
</packageSources>

Просмотреть файл

@ -1,12 +1,18 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
namespace Scratch
{
class Program
{
static void Main(string[] args)
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
using (var img = await SixLabors.Svg.SvgImage.LoadFromFileAsync<SixLabors.ImageSharp.PixelFormats.Rgba32>("source.svg"))
{
img.Save("source.png");
}
}
}
}

Просмотреть файл

@ -2,7 +2,18 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\SixLabors.Svg\SixLabors.Svg.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="source.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

18
Scratch/source.svg Normal file
Просмотреть файл

@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<svg width="400" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
<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"/>
<circle cx="25" cy="175" r="20" stroke="red" fill="transparent" stroke-width="5"/>
<ellipse cx="175" cy="175" rx="20" ry="5" stroke="red" fill="transparent" stroke-width="5"/>
<line x1="10" x2="50" y1="110" y2="150" stroke="orange" stroke-width="5"/>
<polyline points="60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145"
stroke="orange" fill="transparent" stroke-width="5"/>
<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"/>
<path d="M20,230 Q40,205 50,230 T90,230" fill="none" stroke="blue" stroke-width="5"/>
</svg>

Просмотреть файл

@ -1,175 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom.Svg;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgLayer : SvgElement
{
private List<SvgElement> children = new List<SvgElement>();
public IReadOnlyList<SvgElement> Children => children;
public void Add(SvgElement elm)
{
children.Add(elm);
elm.SetParent(this);
}
internal override void RenderTo(RasterImage img)
{
// note to self each lay can be draw on the previous layer with an image brush for masking and compositing
foreach (var c in Children)
{
c.RenderTo(img);
}
}
public static async Task<SvgElement> LoadLayerAsync(AngleSharp.Dom.Svg.ISvgElement element)
{
var layer = new SvgLayer();
var children = element.Children.OfType<ISvgElement>();
foreach (var c in children)
{
var elm = await SvgElement.LoadElementAsync(c);
if (elm != null)
{
layer.Add(elm);
}
}
return layer;
}
}
internal abstract class SvgElement
{
public SvgLayer Parent { get; private set; }
public static async Task<SvgElement> LoadElementAsync(AngleSharp.Dom.Svg.ISvgElement element)
{
switch (element.TagName)
{
case "svg":
case "g":
return await SvgLayer.LoadLayerAsync(element);
case "rect":
return await SvgRect.LoadAsync(element);
default:
return null;
}
}
internal void SetParent(SvgLayer layer)
{
Parent = layer;
}
internal abstract void RenderTo(RasterImage writer);
}
internal sealed class SvgRect : SvgElement
{
internal override void RenderTo(RasterImage writer)
{
throw new NotImplementedException();
}
public SvgUnitValue X { get; private set; }
public SvgUnitValue Y { get; private set; }
public SvgUnitValue Width { get; private set; }
public SvgUnitValue Height { get; private set; }
public static Task<SvgElement> LoadAsync(AngleSharp.Dom.Svg.ISvgElement element)
{
return Task.FromResult<SvgElement>(new SvgRect()
{
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
});
}
}
internal struct SvgUnitValue
{
public static readonly SvgUnitValue Unset = default(SvgUnitValue);
public bool IsSet { get; }
public float Value { get; }
public Units Unit { get; }
public SvgUnitValue(float value, Units unit)
{
IsSet = true;
Value = value;
Unit = unit;
}
public static SvgUnitValue Parse(string val)
{
val = val?.Trim() ?? "";
if (!string.IsNullOrWhiteSpace(val))
{
var valNum = val;
Units unitType = Units.undefined;
if (val.EndsWith("%"))
{
unitType = Units.percent;
valNum = val.TrimEnd('%');
}
else
{
if (val.Length > 2)
{
var unit = val.Substring(val.Length - 2);
if(unit == "in")
{
unitType = Units.inches;
}
else
if (!Enum.TryParse(unit, true, out unitType))
{
unitType = Units.undefined;
}
if(unitType != Units.undefined)
{
valNum = valNum.Substring(0, val.Length - 2);
}
}
}
float finalVal = 0;
if (float.TryParse(valNum, out finalVal))
{
return new SvgUnitValue(finalVal, unitType);
}
}
return Unset;
}
public enum Units
{
undefined,
percent,
px,
cm,
mm,
em,
ex,
pt,
pc,
inches, // in
}
}
}

Просмотреть файл

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using ImageSharp;
namespace SixLabors.Svg
{
internal class RasterImage : IDisposable
{
private readonly Image image;
public RasterImage(int width, int height)
{
this.image = new ImageSharp.Image(width, height);
}
public void Dispose()
{
}
internal RasterImage CreateChild()
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace SixLabors.Svg.Dom
{
public struct RenderOptions
{
private const float defaultDpi = 96;
private float? dpi;
public float Dpi { get => dpi ?? defaultDpi; set => dpi = value; }
}
}

Просмотреть файл

@ -0,0 +1,64 @@
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgDocument
{
private readonly SvgLayer root;
public SvgUnitValue X { get; private set; }
public SvgUnitValue Y { get; private set; }
public SvgUnitValue Width { get; private set; }
public SvgUnitValue Height { get; private set; }
public SvgDocument()
{
root = new SvgLayer();
}
public void Add(SvgElement elm)
{
root.Add(elm);
elm.SetParent(root);
}
public static async Task<SvgDocument> LoadAsync(ISvgElement element)
{
var document = new SvgDocument()
{
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
};
var children = element.Children.OfType<ISvgElement>();
foreach (var c in children)
{
var elm = await SvgElement.LoadElementAsync(c);
if (elm != null)
{
document.Add(elm);
}
}
return document;
}
internal Image<TPixel> Generate<TPixel>(RenderOptions options) where TPixel : struct, IPixel<TPixel>
{
// todo pass along the ImageSharp configuration
var img = new Image<TPixel>((int)this.Width.AsPixel(options.Dpi), (int)this.Height.AsPixel(options.Dpi));
this.root.RenderTo(img);
return img;
}
}
}

Просмотреть файл

@ -0,0 +1,38 @@
using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.Svg.Dom
{
internal abstract class SvgElement
{
public SvgLayer Parent { get; private set; }
public static async Task<SvgElement> LoadElementAsync(ISvgElement element)
{
switch (element.TagName)
{
case "svg":
case "g":
return await SvgLayer.LoadLayerAsync(element);
case "rect":
return await SvgRect.LoadAsync(element);
case "circle":
case "ellipse":
return await SvgEllipse.LoadAsync(element);
default:
return null;
}
}
internal void SetParent(SvgLayer layer)
{
Parent = layer;
}
internal abstract void RenderTo<TPixel>(Image<TPixel> image) where TPixel : struct, IPixel<TPixel>;
}
}

Просмотреть файл

@ -0,0 +1,69 @@
using System;
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 SvgEllipse : SvgElement
{
public SvgUnitValue X { get; private set; }
public SvgUnitValue Y { get; private set; }
public SvgUnitValue RadiusX { get; private set; }
public SvgUnitValue RadiusY { get; private set; }
public SvgPaint Fill { get; private set; }
public SvgPaint Stroke { get; private set; }
public SvgUnitValue StrokeWidth { get; private set; }
public static Task<SvgElement> LoadAsync(ISvgElement element)
{
var ellipse = new SvgEllipse()
{
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
X = SvgUnitValue.Parse(element.Attributes["cx"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["cy"]?.Value)
};
if (element.TagName == "circle")
{
ellipse.RadiusY = ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["r"]?.Value ?? "0");
}
else
{
ellipse.RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? "0");
ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? "0");
}
return Task.FromResult<SvgElement>(ellipse);
}
internal override void RenderTo<TPixel>(Image<TPixel> image)
{
var rect = new SixLabors.Shapes.EllipsePolygon(X.AsPixelXAxis(image), Y.AsPixelYAxis(image), RadiusX.AsPixelXAxis(image) * 2, RadiusY.AsPixelXAxis(image) * 2);
var fillBrush = Fill.AsBrush<TPixel>();
var strokeBrush = Stroke.AsBrush<TPixel>();
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
image.Mutate(x =>
{
if (fillBrush != null)
{
x = x.Fill(fillBrush, rect);
}
if (strokeBrush != null && strokeWidth > 0)
{
var outline = Outliner.GenerateOutline(rect, strokeWidth);
x = x.Fill(strokeBrush, outline);
}
});
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgLayer : SvgElement
{
private List<SvgElement> children = new List<SvgElement>();
public IReadOnlyList<SvgElement> Children => children;
public void Add(SvgElement elm)
{
children.Add(elm);
elm.SetParent(this);
}
internal override void RenderTo<TPixel>(Image<TPixel> img)
{
// note to self each lay can be draw on the previous layer with an image brush for masking and compositing
foreach (var c in Children)
{
c.RenderTo(img);
}
}
public static async Task<SvgElement> LoadLayerAsync(ISvgElement element)
{
var layer = new SvgLayer();
var children = element.Children.OfType<ISvgElement>();
foreach (var c in children)
{
var elm = await SvgElement.LoadElementAsync(c);
if (elm != null)
{
layer.Add(elm);
}
}
return layer;
}
}
}

Просмотреть файл

@ -0,0 +1,101 @@
using System;
using System.Numerics;
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 SvgRect : SvgElement
{
public SvgUnitValue X { get; private set; }
public SvgUnitValue Y { get; private set; }
public SvgUnitValue Width { get; private set; }
public SvgUnitValue Height { get; private set; }
public SvgPaint Fill { get; private set; }
public SvgPaint Stroke { get; private set; }
public SvgUnitValue StrokeWidth { get; private set; }
public SvgUnitValue RadiusX { get; private set; }
public SvgUnitValue RadiusY { get; private set; }
public static Task<SvgElement> LoadAsync(ISvgElement element)
{
return Task.FromResult<SvgElement>(new SvgRect()
{
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? element.Attributes["ry"]?.Value ?? "0"),
RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? element.Attributes["rx"]?.Value ?? "0"),
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
});
}
internal override void RenderTo<TPixel>(Image<TPixel> image)
{
IPath rect = new SixLabors.Shapes.RectangularPolygon(X.AsPixelXAxis(image), Y.AsPixelYAxis(image), Width.AsPixelXAxis(image), Height.AsPixelXAxis(image));
var rx = RadiusX.AsPixelXAxis(image);
var ry = RadiusY.AsPixelXAxis(image);
if (rx > 0 && ry > 0)
{
rect = MakeRounded(rect, rx, ry);
}
var fillBrush = Fill.AsBrush<TPixel>();
var strokeBrush = Stroke.AsBrush<TPixel>();
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
image.Mutate(x =>
{
if (fillBrush != null)
{
x = x.Fill(fillBrush, rect);
}
if (strokeBrush != null && strokeWidth > 0)
{
var outline = Outliner.GenerateOutline(rect, strokeWidth);
x = x.Fill(strokeBrush, outline);
}
});
}
private IPath MakeRounded(IPath path, float rx, float ry)
{
return path.Clip(BuildCorners(path.Bounds.Width, path.Bounds.Height, rx, ry).Translate(path.Bounds.Location));
}
public static IPathCollection BuildCorners(float imageWidth, float imageHeight, float cornerRadiusX, float cornerRadiusY)
{
// first create a square
var rect = new RectangularPolygon(0, 0, cornerRadiusX, cornerRadiusY);
// then cut out of the square a circle so we are left with a corner
IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadiusX, cornerRadiusY, cornerRadiusX * 2, cornerRadiusY * 2));
// corner is now a corner shape positions top left
//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);
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1;
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
// move it across the widthof the image - the width of the shape
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos - 0.5f, 0);
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos - 0.5f);
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos - 0.5f, bottomPos - 0.5f);
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
}
}
}

Просмотреть файл

@ -0,0 +1,336 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
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
{
public static readonly SvgUnitValue Unset = default(SvgUnitValue);
public bool IsSet { get; }
public float Value { get; }
public Units Unit { get; }
public SvgUnitValue(float value, Units unit)
{
IsSet = true;
Value = value;
Unit = unit;
}
public float AsPixelXAxis<TPixel>(Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
{
return AsPixel((float)img.MetaData.HorizontalResolution, img.Width);
}
public float AsPixelYAxis<TPixel>(Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
{
return AsPixel((float)img.MetaData.VerticalResolution, img.Height);
}
public float AsPixel(float dpi, float maxAxis = 0)
{
switch (Unit)
{
case Units.undefined:
return Value;
break;
case Units.px:
return Value;
case Units.cm:
return (float)(dpi / 2.54) * Value;
case Units.mm:
return (float)(dpi / 2.54) * Value / 10;
case Units.pt:
return dpi / 72 * Value;
case Units.pc:
return dpi / 6 * Value;
break;
case Units.inches:
return Value * dpi;
case Units.percent:
return (Value / 100) * maxAxis;
case Units.em:
case Units.ex:
default:
return Value;
}
}
public static SvgUnitValue Parse(string val)
{
val = val?.Trim() ?? "";
if (!string.IsNullOrWhiteSpace(val))
{
var valNum = val;
Units unitType = Units.undefined;
if (val.EndsWith("%"))
{
unitType = Units.percent;
valNum = val.TrimEnd('%');
}
else
{
if (val.Length > 2)
{
var unit = val.Substring(val.Length - 2);
if (unit == "in")
{
unitType = Units.inches;
}
else
if (!Enum.TryParse(unit, true, out unitType) || !Enum.IsDefined(typeof(Units), unitType))
{
unitType = Units.undefined;
}
if (unitType != Units.undefined)
{
valNum = valNum.Substring(0, val.Length - 2);
}
}
}
float finalVal = 0;
if (float.TryParse(valNum, out finalVal))
{
return new SvgUnitValue(finalVal, unitType);
}
}
return Unset;
}
public enum Units
{
undefined,
percent,
px,
cm,
mm,
em,
ex,
pt,
pc,
inches, // in
}
}
}

Просмотреть файл

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.9.9" />
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha5-00040" />
<PackageReference Include="AngleSharp" Version="0.10.1" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-dev002362" />
</ItemGroup>
</Project>

Просмотреть файл

@ -5,34 +5,40 @@ using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Css;
using AngleSharp.Dom.Svg;
using AngleSharp.Network;
using SixLabors.Svg.Dom;
using AngleSharp.Io;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Configuration = AngleSharp.Configuration;
namespace SixLabors.Svg
{
public partial class SvgImage
public static class SvgImage
{
public static Task<SvgImage> LoadFromFileAsync(string path)
public static Task<Image<TPixel>> LoadFromFileAsync<TPixel>(string path)
where TPixel : struct, IPixel<TPixel>
{
var content = File.ReadAllText(path);
return LoadFromAsync(content, false);
return LoadFromAsync<TPixel>(content, false);
}
public static Task<SvgImage> LoadFromStringAsync(string content)
public static Task<Image<TPixel>> LoadFromStringAsync<TPixel>(string content)
where TPixel : struct, IPixel<TPixel>
{
return LoadFromAsync(content, false);
return LoadFromAsync<TPixel>(content, false);
}
public static Task<SvgImage> LoadFromUrlAsync(string url)
public static Task<Image<TPixel>> LoadFromUrlAsync<TPixel>(Uri url)
where TPixel : struct, IPixel<TPixel>
{
return LoadFromAsync(url, true);
return LoadFromAsync<TPixel>(url.ToString(), true);
}
public static async Task<SvgImage> LoadFromAsync(string content, bool isUrl)
public static async Task<Image<TPixel>> LoadFromAsync<TPixel>(string content, bool isUrl)
where TPixel : struct, IPixel<TPixel>
{
var config = Configuration.Default.WithDefaultLoader().WithCss();
var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
IDocument doc;
@ -50,16 +56,19 @@ namespace SixLabors.Svg
});
}
var svgElement = doc as AngleSharp.Dom.Svg.ISvgDocument;
var svgElement = doc as ISvgDocument;
if (svgElement == null)
{
throw new Exception("Failed to load document");
}
var dom = await SvgElement.LoadElementAsync(svgElement.DocumentElement as ISvgElement);
var dom = await SvgDocument.LoadAsync(svgElement.DocumentElement as ISvgElement);
return new SvgImage(dom as SvgLayer);
return dom.Generate<TPixel>(new RenderOptions
{
Dpi = 96
});
}
}
}

Просмотреть файл

@ -1,16 +0,0 @@
using System;
using AngleSharp.Dom.Svg;
using SixLabors.Svg.Dom;
namespace SixLabors.Svg
{
public sealed partial class SvgImage
{
internal SvgLayer root;
private SvgImage(SvgLayer root)
{
this.root = root;
}
}
}