From 0343774d6cf124ba7e55460e94bb3ae2de89de57 Mon Sep 17 00:00:00 2001 From: Emanuele Sabetta Date: Wed, 30 Nov 2016 23:31:46 +0100 Subject: [PATCH 1/3] Various fixes and features (subpath, parsing, path regex, gradientTransform) - Fix to allow reading gradientTransform from inherited href link. Small fix: gradientTransform was ignored when the gradient was using a reference link to another defined gradient. This fix allows the correct reading of the gradientTransform also from inherited href links. - Added support for the transformGradient attribute that was missing (only Android and iOS for now) - Added support for the transformGradient attribute that was missing. Only gradients without transforms were rendered correctly before. Now on Android and iOS the transformGradient matrix is parsed and applied to the LinearGradient and to the RadialGradient. Other platforms are still ToDo. - Added a better way to split path data numbers. Some svg files were not parsed correctly due to some strange formatting. I added a better way to split path data numbers. Now it works with all numbers formatting. I used this regex from the SVGO project: @"([-+]?((\d*\.\d+)|(\d+))([eE][-+]?\d+)?)"; You can test it here: https://regex101.com/r/9Ryu0U/1 - Fix for the SVG SubPath Issue. Now subpaths are rendered correctly. --- NGraphics/Brush.cs | 62 +- NGraphics/SvgReader.cs | 829 ++++++++++-------- .../NGraphics.Android/AndroidPlatform.cs | 245 ++++-- Platforms/NGraphics.Mac/ApplePlatform.cs | 244 ++++-- 4 files changed, 821 insertions(+), 559 deletions(-) diff --git a/NGraphics/Brush.cs b/NGraphics/Brush.cs index 3e642b3c..95591003 100644 --- a/NGraphics/Brush.cs +++ b/NGraphics/Brush.cs @@ -40,7 +40,7 @@ public class GradientStop public double Offset; public Color Color; public GradientStop () - { + { } public GradientStop (double offset, Color color) { @@ -58,7 +58,7 @@ public void AddStop (double offset, Color color) } public void AddStops (IEnumerable stops) { - Stops.AddRange(stops); + Stops.AddRange (stops); } } @@ -66,9 +66,11 @@ public class RadialGradientBrush : GradientBrush { public Point Center; public Point Focus; - public Size Radius; + public Size Radius; public bool Absolute = false; + public Transform GradientTransform { get; set; } + public RadialGradientBrush () { } @@ -99,25 +101,25 @@ public RadialGradientBrush (Point relCenter, Size relRadius, Color startColor, C Stops.Add (new GradientStop (0, startColor)); Stops.Add (new GradientStop (0.5, midColor)); Stops.Add (new GradientStop (1, endColor)); - } + } public RadialGradientBrush (Color startColor, Color midColor, Color endColor) : this (new Point (0.5, 0.5), new Size (0.5), startColor, midColor, endColor) { } - public Point GetAbsoluteCenter (Rect frame) - { - if (Absolute) return Center; - return frame.TopLeft + Center * frame.Size; - } - public Size GetAbsoluteRadius (Rect frame) - { - if (Absolute) return Radius; - return Radius * frame.Size; - } - public Point GetAbsoluteFocus (Rect frame) - { - if (Absolute) return Focus; - return frame.TopLeft + Focus * frame.Size; + public Point GetAbsoluteCenter (Rect frame) + { + if (Absolute) return Center; + return frame.TopLeft + Center * frame.Size; + } + public Size GetAbsoluteRadius (Rect frame) + { + if (Absolute) return Radius; + return Radius * frame.Size; + } + public Point GetAbsoluteFocus (Rect frame) + { + if (Absolute) return Focus; + return frame.TopLeft + Focus * frame.Size; } } @@ -127,6 +129,8 @@ public class LinearGradientBrush : GradientBrush public Point End; public bool Absolute = false; + public Transform GradientTransform { get; set; } + public LinearGradientBrush () { } @@ -150,16 +154,16 @@ public LinearGradientBrush (Point relStart, Point relEnd, Color startColor, Colo Stops.Add (new GradientStop (0, startColor)); Stops.Add (new GradientStop (0.5, midColor)); Stops.Add (new GradientStop (1, endColor)); - } - public Point GetAbsoluteStart (Rect frame) - { - if (Absolute) return Start; - return frame.TopLeft + Start * frame.Size; - } - public Point GetAbsoluteEnd (Rect frame) - { - if (Absolute) return End; - return frame.TopLeft + End * frame.Size; - } + } + public Point GetAbsoluteStart (Rect frame) + { + if (Absolute) return Start; + return frame.TopLeft + Start * frame.Size; + } + public Point GetAbsoluteEnd (Rect frame) + { + if (Absolute) return End; + return frame.TopLeft + End * frame.Size; + } } } diff --git a/NGraphics/SvgReader.cs b/NGraphics/SvgReader.cs index 0811775d..c6f0a063 100644 --- a/NGraphics/SvgReader.cs +++ b/NGraphics/SvgReader.cs @@ -17,7 +17,7 @@ public class SvgReader public Graphic Graphic { get; private set; } readonly Dictionary defs = new Dictionary (); -// readonly XNamespace ns; + // readonly XNamespace ns; public SvgReader (System.IO.TextReader reader, double pixelsPerInch = 160.0) { @@ -33,10 +33,12 @@ void Read (XDocument doc) // // Find the defs (gradients) // - foreach (var d in svg.Descendants ()) { + foreach (var d in svg.Descendants ()) + { var idA = d.Attribute ("id"); - if (idA != null) { - defs [ReadString (idA).Trim ()] = d; + if (idA != null) + { + defs[ReadString (idA).Trim ()] = d; } } @@ -51,14 +53,17 @@ void Read (XDocument doc) var viewBox = new Rect (size); var viewBoxA = svg.Attribute ("viewBox") ?? svg.Attribute ("viewPort"); - if (viewBoxA != null) { + if (viewBoxA != null) + { viewBox = ReadRectangle (viewBoxA.Value); } - if (widthA != null && widthA.Value.Contains ("%")) { + if (widthA != null && widthA.Value.Contains ("%")) + { size.Width *= viewBox.Width; } - if (heightA != null && heightA.Value.Contains ("%")) { + if (heightA != null && heightA.Value.Contains ("%")) + { size.Height *= viewBox.Height; } @@ -87,7 +92,8 @@ void AddElement (IList list, XElement e, Pen inheritPen, Brush inheritB bool hasPen, hasBrush; ApplyStyle (e.Attributes ().ToDictionary (k => k.Name.LocalName, v => v.Value), ref pen, out hasPen, ref brush, out hasBrush); var style = ReadString (e.Attribute ("style")); - if (!string.IsNullOrWhiteSpace (style)) { + if (!string.IsNullOrWhiteSpace (style)) + { ApplyStyle (style, ref pen, out hasPen, ref brush, out hasBrush); } pen = hasPen ? pen : inheritPen; @@ -97,184 +103,193 @@ void AddElement (IList list, XElement e, Pen inheritPen, Brush inheritB // // Elements // - switch (e.Name.LocalName) { - case "text": - { - var x = ReadNumber (e.Attribute ("x")); - var y = ReadNumber (e.Attribute ("y")); - var font = new Font (); - var fontFamily = ReadTextFontFamily(e); - if (!string.IsNullOrEmpty(fontFamily)) - font.Family = fontFamily; - var fontSize = ReadTextFontSize(e); - if (fontSize >= 0) - font.Size = fontSize; - TextAlignment textAlignment = ReadTextAlignment(e); - var txt = new Text (new Rect (new Point (x, y), new Size (double.MaxValue, double.MaxValue)), font, textAlignment, pen, brush); - ReadTextSpans (txt, e); - r = txt; - } + switch (e.Name.LocalName) + { + case "text": + { + var x = ReadNumber (e.Attribute ("x")); + var y = ReadNumber (e.Attribute ("y")); + var font = new Font (); + var fontFamily = ReadTextFontFamily (e); + if (!string.IsNullOrEmpty (fontFamily)) + font.Family = fontFamily; + var fontSize = ReadTextFontSize (e); + if (fontSize >= 0) + font.Size = fontSize; + TextAlignment textAlignment = ReadTextAlignment (e); + var txt = new Text (new Rect (new Point (x, y), new Size (double.MaxValue, double.MaxValue)), font, textAlignment, pen, brush); + ReadTextSpans (txt, e); + r = txt; + } break; - case "rect": - { - var x = ReadNumber (e.Attribute ("x")); - var y = ReadNumber (e.Attribute ("y")); - var width = ReadNumber (e.Attribute ("width")); - var height = ReadNumber (e.Attribute ("height")); - var rx = ReadNumber (e.Attribute ("rx")); - var ry = ReadNumber (e.Attribute ("ry")); - if (ry == 0) { - ry = rx; + case "rect": + { + var x = ReadNumber (e.Attribute ("x")); + var y = ReadNumber (e.Attribute ("y")); + var width = ReadNumber (e.Attribute ("width")); + var height = ReadNumber (e.Attribute ("height")); + var rx = ReadNumber (e.Attribute ("rx")); + var ry = ReadNumber (e.Attribute ("ry")); + if (ry == 0) + { + ry = rx; + } + r = new Rectangle (new Rect (new Point (x, y), new Size (width, height)), new Size (rx, ry), pen, brush); } - r = new Rectangle (new Rect (new Point (x, y), new Size (width, height)), new Size (rx, ry), pen, brush); - } break; - case "ellipse": - { - var cx = ReadNumber (e.Attribute ("cx")); - var cy = ReadNumber (e.Attribute ("cy")); - var rx = ReadNumber (e.Attribute ("rx")); - var ry = ReadNumber (e.Attribute ("ry")); - r = new Ellipse (new Point (cx - rx, cy - ry), new Size (2 * rx, 2 * ry), pen, brush); - } + case "ellipse": + { + var cx = ReadNumber (e.Attribute ("cx")); + var cy = ReadNumber (e.Attribute ("cy")); + var rx = ReadNumber (e.Attribute ("rx")); + var ry = ReadNumber (e.Attribute ("ry")); + r = new Ellipse (new Point (cx - rx, cy - ry), new Size (2 * rx, 2 * ry), pen, brush); + } break; - case "circle": - { - var cx = ReadNumber (e.Attribute ("cx")); - var cy = ReadNumber (e.Attribute ("cy")); - var rr = ReadNumber (e.Attribute ("r")); - r = new Ellipse (new Point (cx - rr, cy - rr), new Size (2 * rr, 2 * rr), pen, brush); - } + case "circle": + { + var cx = ReadNumber (e.Attribute ("cx")); + var cy = ReadNumber (e.Attribute ("cy")); + var rr = ReadNumber (e.Attribute ("r")); + r = new Ellipse (new Point (cx - rr, cy - rr), new Size (2 * rr, 2 * rr), pen, brush); + } break; - case "path": - { - var dA = e.Attribute ("d"); - if (dA != null && !string.IsNullOrWhiteSpace (dA.Value)) { - var p = new Path (pen, brush); - ReadPath (p, dA.Value); - r = p; + case "path": + { + var dA = e.Attribute ("d"); + if (dA != null && !string.IsNullOrWhiteSpace (dA.Value)) + { + var p = new Path (pen, brush); + ReadPath (p, dA.Value); + r = p; + } } - } break; - case "polygon": - { - var pA = e.Attribute ("points"); - if (pA != null && !string.IsNullOrWhiteSpace (pA.Value)) { - var path = new Path (pen, brush); - ReadPoints (path, pA.Value, true); - r = path; + case "polygon": + { + var pA = e.Attribute ("points"); + if (pA != null && !string.IsNullOrWhiteSpace (pA.Value)) + { + var path = new Path (pen, brush); + ReadPoints (path, pA.Value, true); + r = path; + } } - } break; - case "polyline": - { - var pA = e.Attribute ("points"); - if (pA != null && !string.IsNullOrWhiteSpace (pA.Value)) { - var path = new Path (pen, brush); - ReadPoints (path, pA.Value, false); - r = path; + case "polyline": + { + var pA = e.Attribute ("points"); + if (pA != null && !string.IsNullOrWhiteSpace (pA.Value)) + { + var path = new Path (pen, brush); + ReadPoints (path, pA.Value, false); + r = path; + } } - } - break; - case "g": - { - var g = new Group (); - var groupId = e.Attribute("id"); - if (groupId != null && !string.IsNullOrEmpty(groupId.Value)) - g.Id = groupId.Value; - AddElements (g.Children, e.Elements (), pen, brush); - r = g; - } break; - case "use": - { - var href = ReadString (e.Attributes ().FirstOrDefault (x => x.Name.LocalName == "href")); - if (!string.IsNullOrWhiteSpace (href)) { - XElement useE; - if (defs.TryGetValue (href.Trim ().Replace ("#", ""), out useE)) { - var useList = new List (); - AddElement (useList, useE, pen, brush); - r = useList.FirstOrDefault (); + case "g": + { + var g = new Group (); + var groupId = e.Attribute ("id"); + if (groupId != null && !string.IsNullOrEmpty (groupId.Value)) + g.Id = groupId.Value; + AddElements (g.Children, e.Elements (), pen, brush); + r = g; + } + break; + case "use": + { + var href = ReadString (e.Attributes ().FirstOrDefault (x => x.Name.LocalName == "href")); + if (!string.IsNullOrWhiteSpace (href)) + { + XElement useE; + if (defs.TryGetValue (href.Trim ().Replace ("#", ""), out useE)) + { + var useList = new List (); + AddElement (useList, useE, pen, brush); + r = useList.FirstOrDefault (); + } } } - } break; - case "title": - Graphic.Title = ReadString (e); + case "title": + Graphic.Title = ReadString (e); break; - case "desc": - case "description": - Graphic.Description = ReadString (e); + case "desc": + case "description": + Graphic.Description = ReadString (e); break; - case "defs": - // Already read in earlier pass + case "defs": + // Already read in earlier pass break; - case "namedview": - case "metadata": - case "image": - // Ignore + case "namedview": + case "metadata": + case "image": + // Ignore break; case "line": - { - var x1 = ReadNumber ( e.Attribute("x1") ); - var x2 = ReadNumber ( e.Attribute("x2") ); - var y1 = ReadNumber ( e.Attribute("y1") ); - var y2 = ReadNumber ( e.Attribute("y2") ); - var p = new Path (pen, null); - p.MoveTo (x1, y1); - p.LineTo (x2, y2); - r = p; - } + { + var x1 = ReadNumber (e.Attribute ("x1")); + var x2 = ReadNumber (e.Attribute ("x2")); + var y1 = ReadNumber (e.Attribute ("y1")); + var y2 = ReadNumber (e.Attribute ("y2")); + var p = new Path (pen, null); + p.MoveTo (x1, y1); + p.LineTo (x2, y2); + r = p; + } break; case "foreignObject": - { - var x = ReadNumber ( e.Attribute("x") ); - var y = ReadNumber ( e.Attribute("y") ); - var width = ReadNumber ( e.Attribute("width") ); - var height = ReadNumber ( e.Attribute("height") ); - r = new ForeignObject(new Point(x, y), new Size(width, height)); - } + { + var x = ReadNumber (e.Attribute ("x")); + var y = ReadNumber (e.Attribute ("y")); + var width = ReadNumber (e.Attribute ("width")); + var height = ReadNumber (e.Attribute ("height")); + r = new ForeignObject (new Point (x, y), new Size (width, height)); + } break; case "pgf": - { - var id = e.Attribute("id"); - System.Diagnostics.Debug.WriteLine("Ignoring pgf element" + (id != null ? ": '" + id.Value + "'" : "")); - } + { + var id = e.Attribute ("id"); + System.Diagnostics.Debug.WriteLine ("Ignoring pgf element" + (id != null ? ": '" + id.Value + "'" : "")); + } break; case "switch": - { - // Evaluate requiredFeatures, requiredExtensions and systemLanguage - foreach (var ee in e.Elements()) { - var requiredFeatures = ee.Attribute("requiredFeatures"); - var requiredExtensions = ee.Attribute("requiredExtensions"); - var systemLanguage = ee.Attribute("systemLanguage"); - // currently no support for any of these restrictions - if (requiredFeatures == null && requiredExtensions == null && systemLanguage == null) - AddElement (list, ee, pen, brush); + // Evaluate requiredFeatures, requiredExtensions and systemLanguage + foreach (var ee in e.Elements ()) + { + var requiredFeatures = ee.Attribute ("requiredFeatures"); + var requiredExtensions = ee.Attribute ("requiredExtensions"); + var systemLanguage = ee.Attribute ("systemLanguage"); + // currently no support for any of these restrictions + if (requiredFeatures == null && requiredExtensions == null && systemLanguage == null) + AddElement (list, ee, pen, brush); + } } - } break; - // color definition that can be referred to by other elements + // color definition that can be referred to by other elements case "linearGradient": break; - default: - throw new NotSupportedException ("SVG element \"" + e.Name.LocalName + "\" is not supported"); + default: + throw new NotSupportedException ("SVG element \"" + e.Name.LocalName + "\" is not supported"); } - if (r != null) { + if (r != null) + { r.Transform = ReadTransform (ReadString (e.Attribute ("transform"))); - var ida = e.Attribute("id"); - if (ida != null && !string.IsNullOrEmpty (ida.Value)) { + var ida = e.Attribute ("id"); + if (ida != null && !string.IsNullOrEmpty (ida.Value)) + { r.Id = ida.Value.Trim (); } list.Add (r); @@ -285,20 +300,22 @@ void AddElement (IList list, XElement e, Pen inheritPen, Brush inheritB void ApplyStyle (string style, ref Pen pen, out bool hasPen, ref Brush brush, out bool hasBrush) { - var d = ParseStyle(style); + var d = ParseStyle (style); ApplyStyle (d, ref pen, out hasPen, ref brush, out hasBrush); } - Dictionary ParseStyle(string style) + Dictionary ParseStyle (string style) { var d = new Dictionary (); - var kvs = style.Split (new[]{ ';' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var kv in kvs) { + var kvs = style.Split (new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var kv in kvs) + { var m = keyValueRe.Match (kv); - if (m.Success) { - var k = m.Groups [1].Value; - var v = m.Groups [2].Value; - d [k] = v; + if (m.Success) + { + var k = m.Groups[1].Value; + var v = m.Groups[2].Value; + d[k] = v; } } return d; @@ -320,14 +337,16 @@ void ApplyStyle (Dictionary style, ref Pen pen, out bool hasPen, // Pen attributes // var strokeWidth = GetString (style, "stroke-width"); - if (!string.IsNullOrWhiteSpace (strokeWidth)) { + if (!string.IsNullOrWhiteSpace (strokeWidth)) + { if (pen == null) pen = new Pen (); pen.Width = ReadNumber (strokeWidth); } var strokeOpacity = GetString (style, "stroke-opacity"); - if (!string.IsNullOrWhiteSpace (strokeOpacity)) { + if (!string.IsNullOrWhiteSpace (strokeOpacity)) + { if (pen == null) pen = new Pen (); pen.Color = pen.Color.WithAlpha (ReadNumber (strokeOpacity)); @@ -337,18 +356,23 @@ void ApplyStyle (Dictionary style, ref Pen pen, out bool hasPen, // Pen // var stroke = GetString (style, "stroke").Trim (); - if (string.IsNullOrEmpty (stroke)) { + if (string.IsNullOrEmpty (stroke)) + { // No change hasPen = false; - } else if (stroke.Equals("none", StringComparison.OrdinalIgnoreCase)) { + } + else if (stroke.Equals ("none", StringComparison.OrdinalIgnoreCase)) + { hasPen = true; pen = null; - } else { + } + else { hasPen = true; if (pen == null) pen = new Pen (); Color color; - if (Colors.TryParse (stroke, out color)) { + if (Colors.TryParse (stroke, out color)) + { if (pen.Color.Alpha == 1) pen.Color = color; else @@ -360,7 +384,8 @@ void ApplyStyle (Dictionary style, ref Pen pen, out bool hasPen, // Brush attributes // var fillOpacity = GetString (style, "fill-opacity"); - if (!string.IsNullOrWhiteSpace (fillOpacity)) { + if (!string.IsNullOrWhiteSpace (fillOpacity)) + { if (brush == null) brush = new SolidBrush (); var sb = brush as SolidBrush; @@ -372,76 +397,94 @@ void ApplyStyle (Dictionary style, ref Pen pen, out bool hasPen, // Brush // var fill = GetString (style, "fill").Trim (); - if (string.IsNullOrEmpty (fill)) { + if (string.IsNullOrEmpty (fill)) + { // No change hasBrush = false; - } else if (fill.Equals("none", StringComparison.OrdinalIgnoreCase)) { + } + else if (fill.Equals ("none", StringComparison.OrdinalIgnoreCase)) + { hasBrush = true; brush = null; - } else { + } + else { hasBrush = true; Color color; - if (Colors.TryParse (fill, out color)) { + if (Colors.TryParse (fill, out color)) + { var sb = brush as SolidBrush; - if (sb == null) { + if (sb == null) + { brush = new SolidBrush (color); - } else { + } + else { if (sb.Color.Alpha == 1) sb.Color = color; else sb.Color = color.WithAlpha (sb.Color.Alpha); } - } else { + } + else { var urlM = fillUrlRe.Match (fill); - if (urlM.Success) { - var id = urlM.Groups [1].Value.Trim (); - brush = GetGradientBrush(id, null); - } else { + if (urlM.Success) + { + var id = urlM.Groups[1].Value.Trim (); + brush = GetGradientBrush (id, null); + } + else { throw new NotSupportedException ("Fill " + fill); } } } } - protected GradientBrush GetGradientBrush(string fill, GradientBrush child) + protected GradientBrush GetGradientBrush (string fill, GradientBrush child) { XElement defE; - if (defs.TryGetValue (fill, out defE)) { + if (defs.TryGetValue (fill, out defE)) + { GradientBrush brush = null; - switch (defE.Name.LocalName) { - case "linearGradient": - brush = CreateLinearGradientBrush (defE); + switch (defE.Name.LocalName) + { + case "linearGradient": + brush = CreateLinearGradientBrush (defE); break; - case "radialGradient": - brush = CreateRadialGradientBrush (defE); + case "radialGradient": + brush = CreateRadialGradientBrush (defE); break; - default: - throw new NotSupportedException ("Fill " + defE.Name); + default: + throw new NotSupportedException ("Fill " + defE.Name); } if (child != null) - { + { if (child is RadialGradientBrush && brush is RadialGradientBrush) { ((RadialGradientBrush)brush).Center = ((RadialGradientBrush)child).Center; ((RadialGradientBrush)brush).Focus = ((RadialGradientBrush)child).Focus; ((RadialGradientBrush)brush).Radius = ((RadialGradientBrush)child).Radius; - } else if (child is LinearGradientBrush && brush is LinearGradientBrush) + ((RadialGradientBrush)brush).Absolute = ((RadialGradientBrush)child).Absolute; + ((RadialGradientBrush)brush).GradientTransform = ((RadialGradientBrush)child).GradientTransform; + } + else if (child is LinearGradientBrush && brush is LinearGradientBrush) { ((LinearGradientBrush)brush).Start = ((LinearGradientBrush)child).Start; ((LinearGradientBrush)brush).End = ((LinearGradientBrush)child).End; + ((LinearGradientBrush)brush).Absolute = ((LinearGradientBrush)child).Absolute; + ((LinearGradientBrush)brush).GradientTransform = ((LinearGradientBrush)child).GradientTransform; } - brush.AddStops(child.Stops); + brush.AddStops (child.Stops); } XNamespace xlink = "http://www.w3.org/1999/xlink"; - var parent = defE.Attribute(xlink + "href"); - if (parent != null && !string.IsNullOrEmpty(parent.Value)) + var parent = defE.Attribute (xlink + "href"); + if (parent != null && !string.IsNullOrEmpty (parent.Value)) { - brush = GetGradientBrush(parent.Value.Substring(1), brush); + brush = GetGradientBrush (parent.Value.Substring (1), brush); } return brush; - } else { + } + else { throw new Exception ("Invalid fill url reference: " + fill); } } @@ -453,57 +496,69 @@ Transform ReadTransform (string raw) var s = raw.Trim (); - var calls = s.Split (new[]{ ')' }, StringSplitOptions.RemoveEmptyEntries); + var calls = s.Split (new[] { ')' }, StringSplitOptions.RemoveEmptyEntries); var t = Transform.Identity; - foreach (var c in calls) { - var args = c.Split (new[]{ '(', ',', ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var c in calls) + { + var args = c.Split (new[] { '(', ',', ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var nt = Transform.Identity; - switch (args [0]) { - case "matrix": - if (args.Length == 7) { - nt = new Transform ( - ReadNumber(args[1]), - ReadNumber(args[2]), - ReadNumber(args[3]), - ReadNumber(args[4]), - ReadNumber(args[5]), - ReadNumber(args[6])); - } else { - throw new NotSupportedException ("Matrices are expected to have 6 elements, this one has " + (args.Length - 1)); - } + switch (args[0]) + { + case "matrix": + if (args.Length == 7) + { + nt = new Transform ( + ReadNumber (args[1]), + ReadNumber (args[2]), + ReadNumber (args[3]), + ReadNumber (args[4]), + ReadNumber (args[5]), + ReadNumber (args[6])); + } + else { + throw new NotSupportedException ("Matrices are expected to have 6 elements, this one has " + (args.Length - 1)); + } break; - case "translate": - if (args.Length >= 3) { - nt = Transform.Translate (new Size (ReadNumber (args [1]), ReadNumber (args [2]))); - } else if (args.Length >= 2) { - nt = Transform.Translate (new Size (ReadNumber (args[1]), 0)); - } + case "translate": + if (args.Length >= 3) + { + nt = Transform.Translate (new Size (ReadNumber (args[1]), ReadNumber (args[2]))); + } + else if (args.Length >= 2) + { + nt = Transform.Translate (new Size (ReadNumber (args[1]), 0)); + } break; - case "scale": - if (args.Length >= 3) { - nt = Transform.Scale (new Size (ReadNumber (args[1]), ReadNumber (args[2]))); - } else if (args.Length >= 2) { - var sx = ReadNumber (args [1]); - nt = Transform.Scale (new Size (sx, sx)); - } + case "scale": + if (args.Length >= 3) + { + nt = Transform.Scale (new Size (ReadNumber (args[1]), ReadNumber (args[2]))); + } + else if (args.Length >= 2) + { + var sx = ReadNumber (args[1]); + nt = Transform.Scale (new Size (sx, sx)); + } break; - case "rotate": - var a = ReadNumber (args [1]); - if (args.Length >= 4) { - var x = ReadNumber (args [2]); - var y = ReadNumber (args [3]); - var t1 = Transform.Translate (new Size (x, y)); - var t2 = Transform.Rotate (a); - var t3 = Transform.Translate (new Size (-x, -y)); - nt = t1 * t2 * t3; - } else { - nt = Transform.Rotate (a); - } + case "rotate": + var a = ReadNumber (args[1]); + if (args.Length >= 4) + { + var x = ReadNumber (args[2]); + var y = ReadNumber (args[3]); + var t1 = Transform.Translate (new Size (x, y)); + var t2 = Transform.Rotate (a); + var t3 = Transform.Translate (new Size (-x, -y)); + nt = t1 * t2 * t3; + } + else { + nt = Transform.Rotate (a); + } break; - default: - throw new NotSupportedException ("Can't transform " + args[0]); + default: + throw new NotSupportedException ("Can't transform " + args[0]); } t = t * nt; } @@ -513,39 +568,50 @@ Transform ReadTransform (string raw) void ReadTextSpans (Text txt, XElement e) { - foreach (XNode c in e.Nodes ()) { - if (c.NodeType == XmlNodeType.Text) { + foreach (XNode c in e.Nodes ()) + { + if (c.NodeType == XmlNodeType.Text) + { txt.Spans.Add (new TextSpan (((XText)c).Value)); - } else if (c.NodeType == XmlNodeType.Element) { + } + else if (c.NodeType == XmlNodeType.Element) + { var ce = (XElement)c; - if (ce.Name.LocalName == "tspan") { + if (ce.Name.LocalName == "tspan") + { var tspan = new TextSpan (ce.Value); var x = ReadOptionalNumber (ce.Attribute ("x")); var y = ReadOptionalNumber (ce.Attribute ("y")); - if (x.HasValue && y.HasValue) { + if (x.HasValue && y.HasValue) + { tspan.Position = new Point (x.Value, y.Value); } var font = txt.Font; var ffamily = ReadTextFontFamily (ce); - if (!string.IsNullOrWhiteSpace (ffamily)) { + if (!string.IsNullOrWhiteSpace (ffamily)) + { font = font.WithFamily (ffamily); } var fweight = ReadTextFontWeight (ce); - if (!string.IsNullOrWhiteSpace (fweight)) { + if (!string.IsNullOrWhiteSpace (fweight)) + { font = font.WithWeight (fweight); } var fstyle = ReadTextFontStyle (ce); - if (!string.IsNullOrWhiteSpace (fstyle)) { + if (!string.IsNullOrWhiteSpace (fstyle)) + { font = font.WithStyle (fstyle); } var fsize = ReadTextFontSize (ce); - if (fsize > 0) { + if (fsize > 0) + { font = font.WithSize (fsize); } - if (font != txt.Font) { + if (font != txt.Font) + { tspan.Font = font; } @@ -557,48 +623,63 @@ void ReadTextSpans (Text txt, XElement e) } static readonly char[] WSC = new char[] { ',', ' ', '\t', '\n', '\r' }; + static readonly string matchPathNumbers = @"([-+]?((\d*\.\d+)|(\d+))([eE][-+]?\d+)?)"; - static Regex pathRegex = new Regex(@"[MLHVCSQTAZmlhvcsqtaz][^MLHVCSQTAZmlhvcsqtaz]*", RegexOptions.Singleline); - static Regex negativeNumberRe = new Regex("(?<=[0-9])-"); + static Regex pathRegex = new Regex (@"[MLHVCSQTAZmlhvcsqtaz][^MLHVCSQTAZmlhvcsqtaz]*", RegexOptions.Singleline); + static Regex negativeNumberRe = new Regex ("(?<=[0-9])-"); void ReadPath (Path p, string pathDescriptor) { - Match m = pathRegex.Match(pathDescriptor); - while(m.Success) + Match m = pathRegex.Match (pathDescriptor); + while (m.Success) { + var match = m.Value.TrimStart (); var op = match[0]; - if (op == 'z' || op == 'Z') { - p.Close (); - } else { - // make sure negative numbers are split properly - match = negativeNumberRe.Replace(match.Substring(1), " -"); - var args = match.Split(WSC, StringSplitOptions.RemoveEmptyEntries); + if (op != ' ') + { + var args = Regex.Matches (match, matchPathNumbers) + .Cast () + .Select (ml => ml.Value) + .Where ((string arg) => !String.IsNullOrWhiteSpace (arg)) + .ToArray (); Point previousPoint = new Point (); + Point subPathStartPoint = new Point (); //NEEDED FOR SUBPATHS + int index = 0; - while(index < args.Length) + while (index < args.Length) { - if (p.Operations.Count > 0 && !(p.Operations.Last() is ClosePath)) - previousPoint = p.Operations.Last().EndPoint; + if (p.Operations.Count > 0 && !(p.Operations.Last () is ClosePath)) + previousPoint = p.Operations.Last ().EndPoint; - if ((op == 'M' || op == 'm') && args.Length >= index+2) { - var point = new Point (ReadNumber (args [index]), ReadNumber (args [index+1])); + if ((op == 'M' || op == 'm') && args.Length >= index + 2) + { + var point = new Point (ReadNumber (args[index]), ReadNumber (args[index + 1])); + subPathStartPoint = new Point (point.X, point.Y); //NEEDED FOR SUBPATHS if (op == 'm') + { + subPathStartPoint += point; //NEEDED FOR SUBPATHS point += previousPoint; + } p.MoveTo (point); index += 2; - } else if ((op == 'L' || op == 'l') && args.Length >= index+2) { - var point = new Point (ReadNumber (args [index]), ReadNumber (args [index+1])); + op = (char)(((int)op) - 1); // Change op from M to L (and m to l). This is needed because in the SVG 1.1 syntax if you place multiple pairs of coordinates after a moveto, all the pairs after the first are presumed to be preceded by a lineto. + } + else if ((op == 'L' || op == 'l') && args.Length >= index + 2) + { + var point = new Point (ReadNumber (args[index]), ReadNumber (args[index + 1])); if (op == 'l') point += previousPoint; p.LineTo (point); index += 2; - } else if ((op == 'C' || op == 'c') && args.Length >= index+6) { - var c1 = new Point (ReadNumber (args [index]), ReadNumber (args [index+1])); - var c2 = new Point (ReadNumber (args [index+2]), ReadNumber (args [index+3])); - var pt = new Point (ReadNumber (args [index+4]), ReadNumber (args [index+5])); + } + else if ((op == 'C' || op == 'c') && args.Length >= index + 6) + { + var c1 = new Point (ReadNumber (args[index]), ReadNumber (args[index + 1])); + var c2 = new Point (ReadNumber (args[index + 2]), ReadNumber (args[index + 3])); + var pt = new Point (ReadNumber (args[index + 4]), ReadNumber (args[index + 5])); if (op == 'c') { c1 += previousPoint; @@ -607,9 +688,11 @@ void ReadPath (Path p, string pathDescriptor) } p.CurveTo (c1, c2, pt); index += 6; - } else if ((op == 'S' || op == 's') && args.Length >= index+4) { - var c = new Point (ReadNumber (args [index]), ReadNumber (args [index+1])); - var pt = new Point (ReadNumber (args [index+2]), ReadNumber (args [index+3])); + } + else if ((op == 'S' || op == 's') && args.Length >= index + 4) + { + var c = new Point (ReadNumber (args[index]), ReadNumber (args[index + 1])); + var pt = new Point (ReadNumber (args[index + 2]), ReadNumber (args[index + 3])); if (op == 's') { c += previousPoint; @@ -617,57 +700,71 @@ void ReadPath (Path p, string pathDescriptor) } p.ContinueCurveTo (c, pt); index += 4; - } else if ((op == 'A' || op == 'a') && args.Length >= index+7) { - var r = new Size (ReadNumber (args [index]), ReadNumber (args [index+1])); - // var xr = ReadNumber (args [i + 2]); - var laf = ReadNumber (args [index+3]) != 0; - var swf = ReadNumber (args [index+4]) != 0; - var pt = new Point (ReadNumber (args [index+5]), ReadNumber (args [index+6])); + } + else if ((op == 'A' || op == 'a') && args.Length >= index + 7) + { + var r = new Size (ReadNumber (args[index]), ReadNumber (args[index + 1])); + // var xr = ReadNumber (args [i + 2]); + var laf = ReadNumber (args[index + 3]) != 0; + var swf = ReadNumber (args[index + 4]) != 0; + var pt = new Point (ReadNumber (args[index + 5]), ReadNumber (args[index + 6])); if (op == 'a') pt += previousPoint; p.ArcTo (r, laf, swf, pt); index += 7; - } else if ((op == 'V' || op == 'v') && args.Length >= index+1 && p.Operations.Count > 0) { + } + else if ((op == 'V' || op == 'v') && args.Length >= index + 1 && p.Operations.Count > 0) + { var previousX = previousPoint.X; - var y = ReadNumber(args[index]); + var y = ReadNumber (args[index]); if (op == 'v') y += previousPoint.Y; - var point = new Point(previousX, y); - p.LineTo(point); + var point = new Point (previousX, y); + p.LineTo (point); index += 1; - } else if ((op == 'H' || op == 'h') && args.Length >= index+1 && p.Operations.Count > 0) { + } + else if ((op == 'H' || op == 'h') && args.Length >= index + 1 && p.Operations.Count > 0) + { var previousY = previousPoint.Y; - var x = ReadNumber(args[index]); + var x = ReadNumber (args[index]); if (op == 'h') x += previousPoint.X; - var point = new Point(x, previousY); - p.LineTo(point); + var point = new Point (x, previousY); + p.LineTo (point); index += 1; - } else { + } + else if ((op == 'Z' || op == 'z') && args.Length >= index + 1 && p.Operations.Count > 0) + { + p.Close (); + p.MoveTo (subPathStartPoint); //NEEDED FOR SUBPATHS + } + else { throw new NotSupportedException ("Path Operation " + op); } } } - m = m.NextMatch(); + m = m.NextMatch (); } } void ReadPoints (Path p, string pathDescriptor, bool closePath) { - var args = pathDescriptor.Split (new[]{' '}, StringSplitOptions.RemoveEmptyEntries); + var args = pathDescriptor.Split (new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var i = 0; var n = args.Length; if (n == 0) throw new Exception ("No points specified"); - while (i < n) { - var xy = args[i].Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries); + while (i < n) + { + var xy = args[i].Split (new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var x = 0.0; var y = 0.0; var di = 1; - if (xy.Length == 1) { - x = ReadNumber (args [i]); - y = ReadNumber (args [i + 1]); + if (xy.Length == 1) + { + x = ReadNumber (args[i]); + y = ReadNumber (args[i + 1]); di = 2; } else { @@ -675,9 +772,11 @@ void ReadPoints (Path p, string pathDescriptor, bool closePath) y = ReadNumber (xy[1]); } - if (i == 0) { + if (i == 0) + { p.MoveTo (x, y); - } else { + } + else { p.LineTo (x, y); } i += di; @@ -717,13 +816,19 @@ RadialGradientBrush CreateRadialGradientBrush (XElement e) var r = ReadNumber (e.Attribute ("r")); b.Radius = new Size (r); - var gradientUnits = e.Attribute("gradientUnits"); + var gradientUnits = e.Attribute ("gradientUnits"); if (gradientUnits != null) { b.Absolute = gradientUnits.Value == "userSpaceOnUse"; } - // TODO: check gradientTransform attribute + //Read transformation defined in the GradientTransform attribute if present + Transform GradientTransform = Transform.Identity; + if (e.Attribute ("gradientTransform") != null) + { + GradientTransform = ReadTransform (e.Attribute ("gradientTransform").Value); + b.GradientTransform = GradientTransform; + } ReadStops (e, b.Stops); @@ -739,13 +844,19 @@ LinearGradientBrush CreateLinearGradientBrush (XElement e) b.End.X = ReadNumber (e.Attribute ("x2")); b.End.Y = ReadNumber (e.Attribute ("y2")); - var gradientUnits = e.Attribute("gradientUnits"); + var gradientUnits = e.Attribute ("gradientUnits"); if (gradientUnits != null) { b.Absolute = gradientUnits.Value == "userSpaceOnUse"; } - // TODO: check gradientTransform attribute + //Read transformation defined in the gradientTransform attribute if present + Transform GradientTransform = Transform.Identity; + if (e.Attribute ("gradientTransform") != null) + { + GradientTransform = ReadTransform (e.Attribute ("gradientTransform").Value); + b.GradientTransform = GradientTransform; + } ReadStops (e, b.Stops); @@ -755,32 +866,33 @@ LinearGradientBrush CreateLinearGradientBrush (XElement e) void ReadStops (XElement e, List stops) { var ns = e.Name.Namespace; - foreach (var se in e.Elements (ns + "stop")) { + foreach (var se in e.Elements (ns + "stop")) + { var s = new GradientStop (); s.Offset = ReadNumber (se.Attribute ("offset")); double alpha = 1.0; - var styleAttribute = se.Attribute("style"); + var styleAttribute = se.Attribute ("style"); if (styleAttribute != null) { - var styleSettings = styleAttribute.Value.Split(';'); - foreach(var style in styleSettings) + var styleSettings = styleAttribute.Value.Split (';'); + foreach (var style in styleSettings) { - if (style.Contains("stop-color") && style.IndexOf(':') != -1) + if (style.Contains ("stop-color") && style.IndexOf (':') != -1) { - s.Color = ReadColor(style.Substring(style.IndexOf(':')+1)); + s.Color = ReadColor (style.Substring (style.IndexOf (':') + 1)); } - else if (style.Contains("stop-opacity") && style.IndexOf(':') != -1) + else if (style.Contains ("stop-opacity") && style.IndexOf (':') != -1) { - alpha = ReadNumber(style.Substring(style.IndexOf(':')+1)); + alpha = ReadNumber (style.Substring (style.IndexOf (':') + 1)); } } } - var stopColorAttribute = se.Attribute("stop-color"); + var stopColorAttribute = se.Attribute ("stop-color"); if (stopColorAttribute != null) s.Color = ReadColor (stopColorAttribute.Value); - var opacityAttribute = se.Attribute("stop-opacity"); + var opacityAttribute = se.Attribute ("stop-opacity"); if (opacityAttribute != null) - alpha = ReadNumber(opacityAttribute.Value); + alpha = ReadNumber (opacityAttribute.Value); s.Color.Alpha = alpha; stops.Add (s); } @@ -795,16 +907,16 @@ Color ReadColor (XElement e, string attrib) return ReadColor (a.Value); } - Regex rgbRe = new Regex("([0-9]+).*?([0-9]+).*?([0-9]+)"); + Regex rgbRe = new Regex ("([0-9]+).*?([0-9]+).*?([0-9]+)"); Color ReadColor (string raw) { - if (string.IsNullOrWhiteSpace (raw) || raw.Equals("none", StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace (raw) || raw.Equals ("none", StringComparison.OrdinalIgnoreCase)) return Colors.Clear; var s = raw.Trim (); - if (s.Length == 7 && s [0] == '#') + if (s.Length == 7 && s[0] == '#') { var r = int.Parse (s.Substring (1, 2), NumberStyles.HexNumber, icult); @@ -814,12 +926,12 @@ Color ReadColor (string raw) return new Color (r / 255.0, g / 255.0, b / 255.0, 1); } - var match = rgbRe.Match(s); + var match = rgbRe.Match (s); if (match.Success && match.Groups.Count == 4) { - var r = int.Parse( match.Groups[1].Value ); - var g = int.Parse( match.Groups[2].Value ); - var b = int.Parse( match.Groups[3].Value ); + var r = int.Parse (match.Groups[1].Value); + var g = int.Parse (match.Groups[2].Value); + var b = int.Parse (match.Groups[3].Value); return new Color (r / 255.0, g / 255.0, b / 255.0, 1); } @@ -836,15 +948,14 @@ string ReadTextFontAttr (XElement element, string attr) string value = null; if (element != null) { - var attrib = element.Attribute(attr); - if (attrib != null && !string.IsNullOrWhiteSpace(attrib.Value)) - value = attrib.Value.Trim(); - else - { - var style = element.Attribute("style"); - if (style != null && !string.IsNullOrWhiteSpace(style.Value)) + var attrib = element.Attribute (attr); + if (attrib != null && !string.IsNullOrWhiteSpace (attrib.Value)) + value = attrib.Value.Trim (); + else { + var style = element.Attribute ("style"); + if (style != null && !string.IsNullOrWhiteSpace (style.Value)) { - value = GetString(ParseStyle(style.Value), "attr"); + value = GetString (ParseStyle (style.Value), "attr"); } } } @@ -867,20 +978,19 @@ string ReadTextFontStyle (XElement element) return ReadTextFontAttr (element, "font-style"); } - double ReadTextFontSize(XElement element) + double ReadTextFontSize (XElement element) { double value = -1; if (element != null) { - var attrib = element.Attribute("font-size"); - if (attrib != null && !string.IsNullOrWhiteSpace(attrib.Value)) - value = ReadNumber(attrib.Value); - else - { - var style = element.Attribute("style"); - if (style != null && !string.IsNullOrWhiteSpace(style.Value)) + var attrib = element.Attribute ("font-size"); + if (attrib != null && !string.IsNullOrWhiteSpace (attrib.Value)) + value = ReadNumber (attrib.Value); + else { + var style = element.Attribute ("style"); + if (style != null && !string.IsNullOrWhiteSpace (style.Value)) { - value = ReadNumber(GetString(ParseStyle(style.Value), "font-size", "-1")); + value = ReadNumber (GetString (ParseStyle (style.Value), "font-size", "-1")); } } } @@ -888,30 +998,30 @@ double ReadTextFontSize(XElement element) return value; } - TextAlignment ReadTextAlignment(XElement element) + TextAlignment ReadTextAlignment (XElement element) { string value = null; if (element != null) { - var attrib = element.Attribute("text-anchor"); - if (attrib != null && !string.IsNullOrWhiteSpace(attrib.Value)) + var attrib = element.Attribute ("text-anchor"); + if (attrib != null && !string.IsNullOrWhiteSpace (attrib.Value)) value = attrib.Value; - else - { - var style = element.Attribute("style"); - if (style != null && !string.IsNullOrWhiteSpace(style.Value)) + else { + var style = element.Attribute ("style"); + if (style != null && !string.IsNullOrWhiteSpace (style.Value)) { - value = GetString (ParseStyle(style.Value), "text-anchor"); + value = GetString (ParseStyle (style.Value), "text-anchor"); } } } - switch (value) { - case "end": + switch (value) + { + case "end": return TextAlignment.Right; - case "middle": + case "middle": return TextAlignment.Center; - default: + default: return TextAlignment.Left; } } @@ -930,8 +1040,8 @@ double ReadNumber (XAttribute a) return ReadNumber (a.Value); } - Regex unitRe = new Regex("px|pt|em|ex|pc|cm|mm|in"); - Regex percRe = new Regex("%"); + Regex unitRe = new Regex ("px|pt|em|ex|pc|cm|mm|in"); + Regex percRe = new Regex ("%"); double ReadNumber (string raw) { @@ -941,26 +1051,39 @@ double ReadNumber (string raw) var s = raw.Trim (); var m = 1.0; - if (unitRe.IsMatch(s)) { - if (s.EndsWith ("in", StringComparison.Ordinal)) { + if (unitRe.IsMatch (s)) + { + if (s.EndsWith ("in", StringComparison.Ordinal)) + { m = PixelsPerInch; - } else if (s.EndsWith ("cm", StringComparison.Ordinal)) { + } + else if (s.EndsWith ("cm", StringComparison.Ordinal)) + { m = PixelsPerInch / 2.54; - } else if (s.EndsWith ("mm", StringComparison.Ordinal)) { + } + else if (s.EndsWith ("mm", StringComparison.Ordinal)) + { m = PixelsPerInch / 25.4; - } else if (s.EndsWith ("pt", StringComparison.Ordinal)) { + } + else if (s.EndsWith ("pt", StringComparison.Ordinal)) + { m = PixelsPerInch / 72.0; - } else if (s.EndsWith ("pc", StringComparison.Ordinal)) { + } + else if (s.EndsWith ("pc", StringComparison.Ordinal)) + { m = PixelsPerInch / 6.0; } s = s.Substring (0, s.Length - 2); - } else if (percRe.IsMatch(s)) { + } + else if (percRe.IsMatch (s)) + { s = s.Substring (0, s.Length - 1); m = 0.01; } double v; - if (!double.TryParse (s, NumberStyles.Float, icult, out v)) { + if (!double.TryParse (s, NumberStyles.Float, icult, out v)) + { v = 0; } return m * v; @@ -973,14 +1096,16 @@ Rect ReadRectangle (string s) var r = new Rect (); var p = s.Split (WS, StringSplitOptions.RemoveEmptyEntries); if (p.Length > 0) - r.X = ReadNumber (p [0]); + r.X = ReadNumber (p[0]); if (p.Length > 1) - r.Y = ReadNumber (p [1]); + r.Y = ReadNumber (p[1]); if (p.Length > 2) - r.Width = ReadNumber (p [2]); + r.Width = ReadNumber (p[2]); if (p.Length > 3) - r.Height = ReadNumber (p [3]); + r.Height = ReadNumber (p[3]); return r; } } + + } diff --git a/Platforms/NGraphics.Android/AndroidPlatform.cs b/Platforms/NGraphics.Android/AndroidPlatform.cs index 9ec41eff..d7da4741 100644 --- a/Platforms/NGraphics.Android/AndroidPlatform.cs +++ b/Platforms/NGraphics.Android/AndroidPlatform.cs @@ -22,7 +22,8 @@ public IImageCanvas CreateImageCanvas (Size size, double scale = 1.0, bool trans var pixelWidth = (int)Math.Ceiling (size.Width * scale); var pixelHeight = (int)Math.Ceiling (size.Height * scale); var bitmap = Bitmap.CreateBitmap (pixelWidth, pixelHeight, Bitmap.Config.Argb8888); - if (!transparency) { + if (!transparency) + { bitmap.EraseColor (Colors.Black.Argb); } return new BitmapCanvas (bitmap, scale); @@ -45,8 +46,9 @@ public IImage CreateImage (Color[] colors, int width, double scale = 1.0) var pixelWidth = width; var pixelHeight = colors.Length / width; var acolors = new int[pixelWidth * pixelHeight]; - for (var i = 0; i < colors.Length; i++) { - acolors [i] = colors [i].Argb; + for (var i = 0; i < colors.Length; i++) + { + acolors[i] = colors[i].Argb; } var bitmap = Bitmap.CreateBitmap (acolors, pixelWidth, pixelHeight, Bitmap.Config.Argb8888); return new BitmapImage (bitmap, scale); @@ -70,10 +72,11 @@ public static TextPaint GlobalGetFontPaint (Font font, TextAlignment alignment) public static TextMetrics GlobalMeasureText (string text, Font font) { - var paint = GlobalGetFontPaint(font, TextAlignment.Left); + var paint = GlobalGetFontPaint (font, TextAlignment.Left); var w = paint.MeasureText (text); var fm = paint.GetFontMetrics (); - return new TextMetrics { + return new TextMetrics + { Width = w, Ascent = -fm.Ascent, Descent = fm.Descent @@ -89,10 +92,12 @@ public TextMetrics MeasureText (string text, Font font) public class BitmapImage : IImage { readonly Bitmap bitmap; -// readonly double scale; + // readonly double scale; - public Bitmap Bitmap { - get { + public Bitmap Bitmap + { + get + { return bitmap; } } @@ -100,12 +105,13 @@ public Bitmap Bitmap { public BitmapImage (Bitmap bitmap, double scale = 1.0) { this.bitmap = bitmap; -// this.scale = scale; + // this.scale = scale; } public void SaveAsPng (string path) { - using (var f = System.IO.File.OpenWrite (path)) { + using (var f = System.IO.File.OpenWrite (path)) + { bitmap.Compress (Bitmap.CompressFormat.Png, 100, f); } } @@ -119,7 +125,7 @@ public Size Size { get { - return new Size(bitmap.Width, bitmap.Height); + return new Size (bitmap.Width, bitmap.Height); } } @@ -166,23 +172,31 @@ public CanvasCanvas (Canvas graphics) public void SaveState () { - graphics.Save (SaveFlags.Matrix|SaveFlags.Clip); + graphics.Save (SaveFlags.Matrix | SaveFlags.Clip); } public void Transform (Transform transform) - { + { var t = new Matrix (); t.SetValues (new[] { (float)transform.A, (float)transform.C, (float)transform.E, (float)transform.B, (float)transform.D, (float)transform.F, 0, 0, 1, }); - graphics.Concat(t); + graphics.Concat (t); + } + + public Matrix AndroidMatrixFromTransform (Transform t) + { + Matrix m = new Matrix (); + m.SetValues (new float[] { (float)t.A, (float)t.C, (float)t.E, (float)t.B, (float)t.D, (float)t.F, 0f, 0f, 1f }); + return m; } + public void RestoreState () { graphics.Restore (); } - + Paint GetImagePaint (double alpha) { var paint = new Paint (PaintFlags.AntiAlias); @@ -197,10 +211,11 @@ Paint GetPenPaint (Pen pen) paint.SetARGB (pen.Color.A, pen.Color.R, pen.Color.G, pen.Color.B); paint.StrokeWidth = (float)pen.Width; - if (pen.DashPattern != null && pen.DashPattern.Any ()) { - var dashPathEffect = new DashPathEffect(pen.DashPattern.ToArray(), 0); - paint.SetPathEffect(dashPathEffect); - } + if (pen.DashPattern != null && pen.DashPattern.Any ()) + { + var dashPathEffect = new DashPathEffect (pen.DashPattern.ToArray (), 0); + paint.SetPathEffect (dashPathEffect); + } return paint; } @@ -215,54 +230,79 @@ void AddBrushPaint (Paint paint, Brush brush, Rect bb) paint.SetStyle (Paint.Style.Fill); var sb = brush as SolidBrush; - if (sb != null) { + if (sb != null) + { paint.SetARGB (sb.Color.A, sb.Color.R, sb.Color.G, sb.Color.B); return; } var lgb = brush as LinearGradientBrush; - if (lgb != null) { + if (lgb != null) + { var n = lgb.Stops.Count; - if (n >= 2) { - var locs = new float [n]; - var comps = new int [n]; - for (var i = 0; i < n; i++) { - var s = lgb.Stops [i]; - locs [i] = (float)s.Offset; - comps [i] = s.Color.Argb; + if (n >= 2) + { + var locs = new float[n]; + var comps = new int[n]; + for (var i = 0; i < n; i++) + { + var s = lgb.Stops[i]; + locs[i] = (float)s.Offset; + comps[i] = s.Color.Argb; } var p1 = lgb.Absolute ? lgb.Start : bb.Position + lgb.Start * bb.Size; var p2 = lgb.Absolute ? lgb.End : bb.Position + lgb.End * bb.Size; var lg = new LinearGradient ( - (float)p1.X, (float)p1.Y, - (float)p2.X, (float)p2.Y, - comps, - locs, - Shader.TileMode.Clamp); + (float)p1.X, (float)p1.Y, + (float)p2.X, (float)p2.Y, + comps, + locs, + Shader.TileMode.Clamp); + + //Apply GradientTransform if present + if (lgb.GradientTransform != null) + { + Matrix matrix = new Matrix (); + matrix.PreConcat (AndroidMatrixFromTransform (lgb.GradientTransform)); + lg.SetLocalMatrix (matrix); + } + + paint.SetShader (lg); } return; } var rgb = brush as RadialGradientBrush; - if (rgb != null) { + if (rgb != null) + { var n = rgb.Stops.Count; - if (n >= 2) { - var locs = new float [n]; - var comps = new int [n]; - for (var i = 0; i < n; i++) { - var s = rgb.Stops [i]; - locs [i] = (float)s.Offset; - comps [i] = s.Color.Argb; + if (n >= 2) + { + var locs = new float[n]; + var comps = new int[n]; + for (var i = 0; i < n; i++) + { + var s = rgb.Stops[i]; + locs[i] = (float)s.Offset; + comps[i] = s.Color.Argb; } var p1 = rgb.GetAbsoluteCenter (bb); var r = rgb.GetAbsoluteRadius (bb); var rg = new RadialGradient ( - (float)p1.X, (float)p1.Y, - (float)r.Max, - comps, - locs, - Shader.TileMode.Clamp); + (float)p1.X, (float)p1.Y, + (float)r.Max, + comps, + locs, + Shader.TileMode.Clamp); + + //Apply GradientTransform if present + if (rgb.GradientTransform != null) + { + Matrix matrix = new Matrix (); + matrix.PreConcat (AndroidMatrixFromTransform (rgb.GradientTransform)); + rg.SetLocalMatrix (matrix); + } paint.SetShader (rg); } @@ -287,10 +327,10 @@ public void DrawText (string text, Rect frame, Font font, TextAlignment alignmen var fm = paint.GetFontMetrics (); var h = fm.Ascent + fm.Descent; var point = alignment == TextAlignment.Left - ? frame.TopLeft - : alignment == TextAlignment.Center - ? (frame.TopLeft + frame.TopRight) / 2 - : frame.TopRight; + ? frame.TopLeft + : alignment == TextAlignment.Center + ? (frame.TopLeft + frame.TopRight) / 2 + : frame.TopRight; var fr = new Rect (point, new Size (w, h)); AddBrushPaint (paint, brush, fr); graphics.DrawText (text, (float)point.X, (float)point.Y, paint); @@ -300,15 +340,18 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul if (pen == null && brush == null) return; - using (var path = new global::Android.Graphics.Path ()) { + using (var path = new global::Android.Graphics.Path ()) + { var bb = new BoundingBoxBuilder (); Point? prevPoint = null; - foreach (var op in ops) { + foreach (var op in ops) + { var mt = op as MoveTo; - if (mt != null) { + if (mt != null) + { var p = mt.Point; path.MoveTo ((float)p.X, (float)p.Y); bb.Add (p); @@ -316,63 +359,70 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul continue; } var lt = op as LineTo; - if (lt != null) { + if (lt != null) + { var p = lt.Point; path.LineTo ((float)p.X, (float)p.Y); bb.Add (p); prevPoint = p; continue; - } + } var at = op as ArcTo; - if (at != null) { + if (at != null) + { var p = at.Point; - if (!prevPoint.HasValue) { - throw new NotSupportedException("Cannot begin path with Arc."); + if (!prevPoint.HasValue) + { + throw new NotSupportedException ("Cannot begin path with Arc."); } var pp = prevPoint.Value; Point c1, c2; - at.GetCircles(pp, out c1, out c2); + at.GetCircles (pp, out c1, out c2); var circleCenter = at.LargeArc ^ !at.SweepClockwise ? c2 : c1; - var rect = new Rect(circleCenter - at.Radius, at.Radius * 2); + var rect = new Rect (circleCenter - at.Radius, at.Radius * 2); - var startAngle = Conversions.RadToDeg((float)Math.Atan2(pp.Y - circleCenter.Y, pp.X - circleCenter.X)); - var endAngle = Conversions.RadToDeg((float)Math.Atan2(p.Y - circleCenter.Y, p.X - circleCenter.X)); + var startAngle = Conversions.RadToDeg ((float)Math.Atan2 (pp.Y - circleCenter.Y, pp.X - circleCenter.X)); + var endAngle = Conversions.RadToDeg ((float)Math.Atan2 (p.Y - circleCenter.Y, p.X - circleCenter.X)); var sweepAngle = endAngle - startAngle; - if (at.SweepClockwise && sweepAngle < 0) { + if (at.SweepClockwise && sweepAngle < 0) + { // If we want to go CW, sweepAngle needs to be positive sweepAngle += 360.0f; } - else if (!at.SweepClockwise && sweepAngle > 0) { + else if (!at.SweepClockwise && sweepAngle > 0) + { // If we want to go CCW, sweepAngle needs to be negative sweepAngle -= 360.0f; } - path.AddArc(Conversions.GetRectF(rect), startAngle, sweepAngle); + path.AddArc (Conversions.GetRectF (rect), startAngle, sweepAngle); bb.Add (p); prevPoint = p; continue; } - var ct = op as CurveTo; - if (ct != null) { - var p = ct.Point; - var c1 = ct.Control1; - var c2 = ct.Control2; - path.CubicTo ((float)c1.X, (float)c1.Y, (float)c2.X, (float)c2.Y, (float)p.X, (float)p.Y); + var ct = op as CurveTo; + if (ct != null) + { + var p = ct.Point; + var c1 = ct.Control1; + var c2 = ct.Control2; + path.CubicTo ((float)c1.X, (float)c1.Y, (float)c2.X, (float)c2.Y, (float)p.X, (float)p.Y); bb.Add (p); bb.Add (c1); bb.Add (c2); prevPoint = p; - continue; - } - var cp = op as ClosePath; - if (cp != null) { + continue; + } + var cp = op as ClosePath; + if (cp != null) + { path.Close (); continue; } @@ -382,11 +432,13 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul var frame = bb.BoundingBox; - if (brush != null) { + if (brush != null) + { var paint = GetBrushPaint (brush, frame); graphics.DrawPath (path, paint); } - if (pen != null) { + if (pen != null) + { var paint = GetPenPaint (pen); graphics.DrawPath (path, paint); } @@ -400,30 +452,38 @@ public void DrawRectangle (Rect frame, Size corner, Pen pen = null, Brush brush var bottom = (float)(frame.Y + frame.Height); var rx = (float)corner.Width; var ry = (float)corner.Height; - if (brush != null) { + if (brush != null) + { var paint = GetBrushPaint (brush, frame); - if (rx > 0 || ry > 0) { + if (rx > 0 || ry > 0) + { graphics.DrawRoundRect (new RectF (left, top, right, bottom), rx, ry, paint); - } else { + } + else { graphics.DrawRect (left, top, right, bottom, paint); } } - if (pen != null) { + if (pen != null) + { var paint = GetPenPaint (pen); - if (rx > 0 || ry > 0) { + if (rx > 0 || ry > 0) + { graphics.DrawRoundRect (new RectF (left, top, right, bottom), rx, ry, paint); - } else { + } + else { graphics.DrawRect (left, top, right, bottom, paint); } } } public void DrawEllipse (Rect frame, Pen pen = null, Brush brush = null) { - if (brush != null) { + if (brush != null) + { var paint = GetBrushPaint (brush, frame); graphics.DrawOval (Conversions.GetRectF (frame), paint); } - if (pen != null) { + if (pen != null) + { var paint = GetPenPaint (pen); graphics.DrawOval (Conversions.GetRectF (frame), paint); } @@ -431,7 +491,8 @@ public void DrawEllipse (Rect frame, Pen pen = null, Brush brush = null) public void DrawImage (IImage image, Rect frame, double alpha = 1.0) { var ii = image as BitmapImage; - if (ii != null) { + if (ii != null) + { var paint = GetImagePaint (alpha); var isize = new Size (ii.Bitmap.Width, ii.Bitmap.Height); var scale = frame.Size / isize; @@ -445,17 +506,17 @@ public void DrawImage (IImage image, Rect frame, double alpha = 1.0) public static class Conversions { - public static PointF GetPointF (this Point point) - { - return new PointF ((float)point.X, (float)point.Y); - } + public static PointF GetPointF (this Point point) + { + return new PointF ((float)point.X, (float)point.Y); + } public static RectF GetRectF (this Rect frame) { return new RectF ((float)frame.X, (float)frame.Y, (float)(frame.X + frame.Width), (float)(frame.Y + frame.Height)); } - - public static float RadToDeg(float rad) + + public static float RadToDeg (float rad) { return rad / (float)Math.PI * 180.0f; } diff --git a/Platforms/NGraphics.Mac/ApplePlatform.cs b/Platforms/NGraphics.Mac/ApplePlatform.cs index c9d237c7..739c8303 100644 --- a/Platforms/NGraphics.Mac/ApplePlatform.cs +++ b/Platforms/NGraphics.Mac/ApplePlatform.cs @@ -13,14 +13,16 @@ namespace NGraphics { public class ApplePlatform : IPlatform { - public string Name { - get { + public string Name + { + get + { #if __IOS__ - return "iOS"; - #else + return "iOS"; +#else return "Mac"; #endif - } + } } public Task OpenFileStreamForWritingAsync (string path) @@ -50,12 +52,16 @@ public IImage CreateImage (Color[] colors, int width, double scale = 1.0) var colorSpace = CGColorSpace.CreateDeviceRGB (); var bitmap = new CGBitmapContext (IntPtr.Zero, pixelWidth, pixelHeight, bitsPerComp, bytesPerRow, colorSpace, bitmapInfo); var data = bitmap.Data; - unsafe { - fixed (Color *c = colors) { - for (var y = 0; y < pixelHeight; y++) { - var s = (byte*)c + 4*pixelWidth*y; - var d = (byte*)data + bytesPerRow*y; - for (var x = 0; x < pixelWidth; x++) { + unsafe + { + fixed (Color* clors) + { + for (var y = 0; y < pixelHeight; y++) + { + var s = (byte*)c + 4 * pixelWidth * y; + var d = (byte*)data + bytesPerRow * y; + for (var x = 0; x < pixelWidth; x++) + { var b = *s++; var g = *s++; var r = *s++; @@ -68,15 +74,17 @@ public IImage CreateImage (Color[] colors, int width, double scale = 1.0) } } } - var image = bitmap.ToImage (); + var image = bitmap.Tomage (); return new CGImageImage (image, scale); } public IImage LoadImage (Stream stream) { var mem = new MemoryStream ((int)stream.Length); stream.CopyTo (mem); - unsafe { - fixed (byte *x = mem.GetBuffer ()) { + unsafe + { + fixed (byte* x = mem.GetBuffer ()) + { var provider = new CGDataProvider (new IntPtr (x), (int)mem.Length, false); var image = CGImage.FromPNG (provider, null, false, CGColorRenderingIntent.Default); return new CGImageImage (image, 1); @@ -87,9 +95,11 @@ public IImage LoadImage (string path) { var provider = new CGDataProvider (path); CGImage image; - if (System.IO.Path.GetExtension (path).ToLowerInvariant () == ".png") { + if (System.IO.Path.GetExtension (path).ToLowerInvariant () == ".png") + { image = CGImage.FromPNG (provider, null, false, CGColorRenderingIntent.Default); - } else { + } + else { image = CGImage.FromJPEG (provider, null, false, CGColorRenderingIntent.Default); } return new CGImageImage (image, 1); @@ -97,24 +107,28 @@ public IImage LoadImage (string path) public static TextMetrics GlobalMeasureText (string text, Font font) { - if (string.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty (text)) return new TextMetrics (); if (font == null) - throw new ArgumentNullException("font"); + throw new ArgumentNullException ("font"); - using (var atext = new NSMutableAttributedString (text)) { + using (var atext = new NSMutableAttributedString (text)) + { - atext.AddAttributes (new CTStringAttributes { + atext.AddAttributes (new CTStringAttributes + { ForegroundColorFromContext = true, Font = font.GetCTFont (), }, new NSRange (0, text.Length)); - using (var l = new CTLine (atext)) { + using (var l = new CTLine (atext)) + { nfloat asc, desc, lead; var len = l.GetTypographicBounds (out asc, out desc, out lead); - return new TextMetrics { + return new TextMetrics + { Width = len, Ascent = asc, Descent = desc, @@ -179,8 +193,10 @@ public void SaveAsPng (string path) { if (string.IsNullOrEmpty (path)) throw new ArgumentException ("path"); - using (var dest = CGImageDestination.Create (NSUrl.FromFilename (path), "public.png", 1)) { - if (dest == null) { + using (var dest = CGImageDestination.Create (NSUrl.FromFilename (path), "public.png", 1)) + { + if (dest == null) + { throw new InvalidOperationException (string.Format ("Could not create image destination {0}.", path)); } dest.AddImage (image); @@ -192,10 +208,13 @@ public void SaveAsPng (Stream stream) { if (stream == null) throw new ArgumentNullException (); - - using (var data = new NSMutableData ()) { - using (var dest = CGImageDestination.Create (data, "public.png", 1)) { - if (dest == null) { + + using (var data = new NSMutableData ()) + { + using (var dest = CGImageDestination.Create (data, "public.png", 1)) + { + if (dest == null) + { throw new InvalidOperationException (string.Format ("Could not create image destination from {0}.", stream)); } dest.AddImage (image); @@ -215,7 +234,7 @@ public class CGContextCanvas : ICanvas public CGContextCanvas (CGContext context) { this.context = context; -// context.InterpolationQuality = CGInterpolationQuality.High; + // context.InterpolationQuality = CGInterpolationQuality.High; context.TextMatrix = CGAffineTransform.MakeScale (1, -1); } @@ -230,6 +249,13 @@ public void Transform (Transform transform) (nfloat)transform.C, (nfloat)transform.D, (nfloat)transform.E, (nfloat)transform.F)); } + public CGAffineTransform ConvertTransformToAppleCGAffineTransform (Transform transform) + { + return new CGAffineTransform ( + (nfloat)transform.A, (nfloat)transform.B, + (nfloat)transform.C, (nfloat)transform.D, + (nfloat)transform.E, (nfloat)transform.F); + } public void RestoreState () { context.RestoreState (); @@ -238,23 +264,24 @@ public void RestoreState () CGGradient CreateGradient (IList stops) { var n = stops.Count; - var locs = new nfloat [n]; - var comps = new nfloat [4 * n]; - for (var i = 0; i < n; i++) { - var s = stops [i]; - locs [i] = (nfloat)s.Offset; - comps [4 * i + 0] = (nfloat)s.Color.Red; - comps [4 * i + 1] = (nfloat)s.Color.Green; - comps [4 * i + 2] = (nfloat)s.Color.Blue; - comps [4 * i + 3] = (nfloat)s.Color.Alpha; + var locs = new nfloat[n]; + var comps = new nfloat[4 * n]; + for (var i = 0; i < n; i++) + { + var s = stops[i]; + locs[i] = (nfloat)s.Offset; + comps[4 * i + 0] = (nfloat)s.Color.Red; + comps[4 * i + 1] = (nfloat)s.Color.Green; + comps[4 * i + 2] = (nfloat)s.Color.Blue; + comps[4 * i + 3] = (nfloat)s.Color.Alpha; } var cs = CGColorSpace.CreateDeviceRGB (); return new CGGradient (cs, comps, locs); } - private static NSString NSFontAttributeName = new NSString("NSFontAttributeName"); + private static NSString NSFontAttributeName = new NSString ("NSFontAttributeName"); - public TextMetrics MeasureText(string text, Font font) + public TextMetrics MeasureText (string text, Font font) { return ApplePlatform.GlobalMeasureText (text, font); } @@ -268,28 +295,32 @@ public void DrawText (string text, Rect frame, Font font, TextAlignment alignmen SetBrush (brush); - using (var atext = new NSMutableAttributedString (text)) { + using (var atext = new NSMutableAttributedString (text)) + { - atext.AddAttributes (new CTStringAttributes { + atext.AddAttributes (new CTStringAttributes + { ForegroundColorFromContext = true, - StrokeColor = pen != null ? pen.Color.GetCGColor () : null, + StrokeColor = pen != null ? pen.Color.GetCGClor () : null, Font = font.GetCTFont (), }, new NSRange (0, text.Length)); - using (var l = new CTLine (atext)) { + using (var l = new CTLine (atext)) + { nfloat asc, desc, lead; var len = l.GetTypographicBounds (out asc, out desc, out lead); var pt = frame.TopLeft; - switch (alignment) { - case TextAlignment.Left: - pt.X = frame.X; + switch (alignment) + { + case TextAlignment.Left: + pt.X = frame.X; break; - case TextAlignment.Center: - pt.X = frame.X + (frame.Width - len) / 2; + case TextAlignment.Center: + pt.X = frame.X + (frame.Width - len) / 2; break; - case TextAlignment.Right: - pt.X = frame.Right - len; + case TextAlignment.Right: + pt.X = frame.Right - len; break; } @@ -308,7 +339,8 @@ void DrawElement (Func add, Pen pen = null, Brush brush = null) return; var lgb = brush as LinearGradientBrush; - if (lgb != null) { + if (lgb != null) + { var cg = CreateGradient (lgb.Stops); context.SaveState (); var frame = add (); @@ -317,13 +349,24 @@ void DrawElement (Func add, Pen pen = null, Brush brush = null) var size = frame.Size; var start = Conversions.GetCGPoint (lgb.Absolute ? lgb.Start : frame.Position + lgb.Start * size); var end = Conversions.GetCGPoint (lgb.Absolute ? lgb.End : frame.Position + lgb.End * size); + + //Apply GradientTransform if present + if (lgb.GradientTransform != null) + { + CGAffineTransform matrix = CGAffineTransform.MakeIdentity (); + CGAffineTransform m = ConvertTransformToAppleCGAffineTransform (lgb.GradientTransform); + matrix.Multiply (m); + context.ConcatCTM (matrix); + }; + context.DrawLinearGradient (cg, start, end, options); context.RestoreState (); brush = null; } var rgb = brush as RadialGradientBrush; - if (rgb != null) { + if (rgb != null) + { var cg = CreateGradient (rgb.Stops); context.SaveState (); var frame = add (); @@ -333,6 +376,16 @@ void DrawElement (Func add, Pen pen = null, Brush brush = null) var start = Conversions.GetCGPoint (rgb.GetAbsoluteCenter (frame)); var r = (nfloat)rgb.GetAbsoluteRadius (frame).Max; var end = Conversions.GetCGPoint (rgb.GetAbsoluteFocus (frame)); + + //Apply GradientTransform if present + if (rgb.GradientTransform != null) + { + CGAffineTransform matrix = CGAffineTransform.MakeIdentity (); + CGAffineTransform m = ConvertTransformToAppleCGAffineTransform (rgb.GradientTransform); + matrix.Multiply (m); + context.ConcatCTM (matrix); + }; + context.DrawRadialGradient (cg, start, 0, end, r, options); context.RestoreState (); brush = null; @@ -357,13 +410,16 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul if (pen == null && brush == null) return; - DrawElement (() => { + DrawElement (() => + { var bb = new BoundingBoxBuilder (); - foreach (var op in ops) { + foreach (var op in ops) + { var mt = op as MoveTo; - if (mt != null) { + if (mt != null) + { var p = mt.Point; if (!IsValid (p.X) || !IsValid (p.Y)) continue; @@ -372,7 +428,8 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul continue; } var lt = op as LineTo; - if (lt != null) { + if (lt != null) + { var p = lt.Point; if (!IsValid (p.X) || !IsValid (p.Y)) continue; @@ -381,7 +438,8 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul continue; } var at = op as ArcTo; - if (at != null) { + if (at != null) + { var p = at.Point; if (!IsValid (p.X) || !IsValid (p.Y)) continue; @@ -393,27 +451,30 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul var circleCenter = at.LargeArc ^ !at.SweepClockwise ? c2 : c1; - var startAngle = (float)Math.Atan2(pp.Y - circleCenter.Y, pp.X - circleCenter.X); - var endAngle = (float)Math.Atan2(p.Y - circleCenter.Y, p.X - circleCenter.X); + var startAngle = (float)Math.Atan2 (pp.Y - circleCenter.Y, pp.X - circleCenter.X); + var endAngle = (float)Math.Atan2 (p.Y - circleCenter.Y, p.X - circleCenter.X); - if (!IsValid (circleCenter.X) || !IsValid (circleCenter.Y) || !IsValid (startAngle) || !IsValid (endAngle)) { + if (!IsValid (circleCenter.X) || !IsValid (circleCenter.Y) || !IsValid (startAngle) || !IsValid (endAngle)) + { context.MoveTo ((nfloat)p.X, (nfloat)p.Y); continue; } - context.AddArc((nfloat)circleCenter.X, (nfloat)circleCenter.Y, (nfloat)at.Radius.Min, startAngle, endAngle, at.SweepClockwise); + context.AddArc ((nfloat)circleCenter.X, (nfloat)circleCenter.Y, (nfloat)at.Radius.Min, startAngle, endAngle, at.SweepClockwise); bb.Add (p); continue; } var ct = op as CurveTo; - if (ct != null) { + if (ct != null) + { var p = ct.Point; if (!IsValid (p.X) || !IsValid (p.Y)) continue; var c1 = ct.Control1; var c2 = ct.Control2; - if (!IsValid (c1.X) || !IsValid (c1.Y) || !IsValid (c2.X) || !IsValid (c2.Y)) { + if (!IsValid (c1.X) || !IsValid (c1.Y) || !IsValid (c2.X) || !IsValid (c2.Y)) + { context.MoveTo ((nfloat)p.X, (nfloat)p.Y); continue; } @@ -424,7 +485,8 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul continue; } var cp = op as ClosePath; - if (cp != null) { + if (cp != null) + { context.ClosePath (); continue; } @@ -440,11 +502,13 @@ public void DrawPath (IEnumerable ops, Pen pen = null, Brush brush = nul void AddRoundedRect (CGRect rrect, CGSize corner) { var rx = corner.Width; - if (rx * 2 > rrect.Width) { + if (rx * 2 > rrect.Width) + { rx = rrect.Width / 2; } var ry = corner.Height; - if (ry * 2 > rrect.Height) { + if (ry * 2 > rrect.Height) + { ry = rrect.Height / 2; } var path = CGPath.FromRoundedRect (rrect, rx, ry); @@ -455,8 +519,10 @@ public void DrawRectangle (Rect frame, Size corner, Pen pen = null, Brush brush if (pen == null && brush == null) return; - DrawElement (() => { - if (corner.Width > 0 || corner.Height > 0) { + DrawElement (() => + { + if (corner.Width > 0 || corner.Height > 0) + { AddRoundedRect (Conversions.GetCGRect (frame), Conversions.GetCGSize (corner)); } else { @@ -470,7 +536,8 @@ public void DrawEllipse (Rect frame, Pen pen = null, Brush brush = null) if (pen == null && brush == null) return; - DrawElement (() => { + DrawElement (() => + { context.AddEllipseInRect (Conversions.GetCGRect (frame)); return frame; }, pen, brush); @@ -479,7 +546,8 @@ public void DrawImage (IImage image, Rect frame, double alpha = 1.0) { var cgi = image as CGImageImage; - if (cgi != null) { + if (cgi != null) + { var i = cgi.Image; var h = frame.Height; context.SaveState (); @@ -494,12 +562,14 @@ public void DrawImage (IImage image, Rect frame, double alpha = 1.0) CGPathDrawingMode SetPenAndBrush (Pen pen, Brush brush) { var mode = CGPathDrawingMode.Fill; - if (brush != null) { + if (brush != null) + { SetBrush (brush); if (pen != null) mode = CGPathDrawingMode.FillStroke; } - if (pen != null) { + if (pen != null) + { SetPen (pen); if (brush == null) mode = CGPathDrawingMode.Stroke; @@ -512,22 +582,24 @@ void SetPen (Pen pen) context.SetStrokeColor ((nfloat)pen.Color.Red, (nfloat)pen.Color.Green, (nfloat)pen.Color.Blue, (nfloat)pen.Color.Alpha); context.SetLineWidth ((nfloat)pen.Width); - if (pen.DashPattern != null && pen.DashPattern.Any ()) { - var pattern = pen.DashPattern - .Select (dp => (nfloat)dp) - .ToArray (); + if (pen.DashPattern != null && pen.DashPattern.Any ()) + { + var pattern = pen.DashPattern + .Select (dp => (nfloat)dp) + .ToArray (); - context.SetLineDash (0, pattern, pattern.Length); - } - else { - context.SetLineDash(0, null, 0); - } - } + context.SetLineDash (0, pattern, pattern.Length); + } + else { + context.SetLineDash (0, null, 0); + } + } void SetBrush (Brush brush) { var sb = brush as SolidBrush; - if (sb != null) { + if (sb != null) + { context.SetFillColor ((nfloat)sb.Color.Red, (nfloat)sb.Color.Green, (nfloat)sb.Color.Blue, (nfloat)sb.Color.Alpha); } } @@ -577,7 +649,7 @@ public static Color GetColor (this CGColor color) return Color.FromRGB (c[0], c[1], c[2], c[3]); } #if __IOS__ || __TVOS__ - public static UIKit.UIColor GetUIColor (this Color color) + public static UIKit.UIColor GetUIColor (this Color color) { return UIKit.UIColor.FromRGBA (color.R, color.G, color.B, color.A); } @@ -592,7 +664,7 @@ public static UIKit.UIImage GetUIImage (this IImage image) var c = (CGImageImage)image; return new UIKit.UIImage (c.Image, (nfloat)c.Scale, UIKit.UIImageOrientation.Up); } - #else +#else public static AppKit.NSImage GetNSImage (this IImage image) { var c = (CGImageImage)image; From e5c5474bb6e78f215f51d0070f99a63b978e1581 Mon Sep 17 00:00:00 2001 From: Emanuele Sabetta Date: Thu, 1 Dec 2016 00:37:51 +0100 Subject: [PATCH 2/3] added gitattributes --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..eba1110b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto \ No newline at end of file From 9616cd992972224c01bae005bdfe794dd8b52d55 Mon Sep 17 00:00:00 2001 From: Emanuele Sabetta Date: Thu, 1 Dec 2016 00:54:00 +0100 Subject: [PATCH 3/3] added exception for .sln files to .gitattributes --- .gitattributes | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index eba1110b..a7484158 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ # Auto detect text files and perform LF normalization -* text=auto \ No newline at end of file +* text=auto + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf