From 414d475085d4389829231cf743443e4df60e24a5 Mon Sep 17 00:00:00 2001 From: morpheusxx Date: Mon, 2 Jan 2012 13:02:25 +0100 Subject: [PATCH 1/2] Added support for different PenLineJoins for Shapes and Borders (Miter and Bevel (default) supported, Round not supported) --- .../UI/SkinEngine/Controls/Visuals/Border.cs | 24 +++++- .../SkinEngine/Controls/Visuals/Shapes/Ellipse.cs | 2 +- .../UI/SkinEngine/Controls/Visuals/Shapes/Path.cs | 2 +- .../SkinEngine/Controls/Visuals/Shapes/Polygon.cs | 2 +- .../Controls/Visuals/Shapes/Rectangle.cs | 2 +- .../UI/SkinEngine/Controls/Visuals/Shapes/Shape.cs | 42 ++++++++ .../DirectX/Triangulate/TriangulateHelper.cs | 103 +++++++++++-------- 7 files changed, 129 insertions(+), 48 deletions(-) diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Border.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Border.cs index cbabffe..7dc1b47 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Border.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Border.cs @@ -27,6 +27,7 @@ using System.Collections.Generic; using System.Drawing.Drawing2D; using MediaPortal.Common.General; using MediaPortal.UI.SkinEngine.Controls.Brushes; +using MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes; using MediaPortal.UI.SkinEngine.DirectX.Triangulate; using MediaPortal.UI.SkinEngine.MpfElements; using SlimDX.Direct3D9; @@ -47,6 +48,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals protected AbstractProperty _backgroundProperty; protected AbstractProperty _borderBrushProperty; protected AbstractProperty _borderThicknessProperty; + protected AbstractProperty _borderLineJoinProperty; protected AbstractProperty _cornerRadiusProperty; protected AbstractProperty _contentProperty; protected int _verticesCountBorder; @@ -71,6 +73,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals _borderBrushProperty = new SProperty(typeof(Brush), null); _backgroundProperty = new SProperty(typeof(Brush), null); _borderThicknessProperty = new SProperty(typeof(double), 1.0); + _borderLineJoinProperty = new SProperty(typeof(PenLineJoin), PenLineJoin.Bevel); _cornerRadiusProperty = new SProperty(typeof(double), 0.0); _contentProperty = new SProperty(typeof(FrameworkElement), null); } @@ -80,6 +83,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals _borderBrushProperty.Attach(OnBorderBrushPropertyChanged); _backgroundProperty.Attach(OnBackgroundBrushPropertyChanged); _borderThicknessProperty.Attach(OnLayoutPropertyChanged); + _borderLineJoinProperty.Attach(OnBorderLineJoinChanged); _cornerRadiusProperty.Attach(OnLayoutPropertyChanged); _contentProperty.Attach(OnContentChanged); } @@ -89,6 +93,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals _borderBrushProperty.Detach(OnBorderBrushPropertyChanged); _backgroundProperty.Detach(OnBackgroundBrushPropertyChanged); _borderThicknessProperty.Detach(OnLayoutPropertyChanged); + _borderLineJoinProperty.Detach(OnBorderLineJoinChanged); _cornerRadiusProperty.Detach(OnLayoutPropertyChanged); _contentProperty.Detach(OnContentChanged); } @@ -101,6 +106,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals BorderBrush = copyManager.GetCopy(b.BorderBrush); Background = copyManager.GetCopy(b.Background); BorderThickness = b.BorderThickness; + BorderLineJoin = b.BorderLineJoin; CornerRadius = b.CornerRadius; Content = copyManager.GetCopy(b.Content); _initializedContent = copyManager.GetCopy(b._initializedContent); @@ -141,6 +147,11 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals OnBorderBrushChanged(brush); } + void OnBorderLineJoinChanged(AbstractProperty property, object oldValue) + { + _performLayout = true; + } + void OnBackgroundBrushChanged(IObservable observable) { _performLayout = true; @@ -224,6 +235,17 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals set { _borderThicknessProperty.SetValue(value); } } + public AbstractProperty BorderLineJoinProperty + { + get { return _borderLineJoinProperty; } + } + + public PenLineJoin BorderLineJoin + { + get { return (PenLineJoin) _borderLineJoinProperty.GetValue(); } + set { _borderLineJoinProperty.SetValue(value); } + } + public AbstractProperty CornerRadiusProperty { get { return _cornerRadiusProperty; } @@ -364,7 +386,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals bool isClosed; gpi.NextSubpath(subPath, out isClosed); PointF[] pathPoints = subPath.PathPoints; - TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) BorderThickness, isClosed, 1, + TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) BorderThickness, isClosed, 1, BorderLineJoin, out subPathVerts[i]); } } diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Ellipse.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Ellipse.cs index 948dc35..9c03b74 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Ellipse.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Ellipse.cs @@ -59,7 +59,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes if (Stroke != null && StrokeThickness > 0) { - TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, out verts); + TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, StrokeLineJoin, out verts); Stroke.SetupBrush(this, ref verts, context.ZOrder, true); PrimitiveBuffer.SetPrimitiveBuffer(ref _strokeContext, ref verts, PrimitiveType.TriangleList); } diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Path.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Path.cs index 9a16975..9470d56 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Path.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Path.cs @@ -126,7 +126,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes bool isClosed; gpi.NextSubpath(subPath, out isClosed); PointF[] pathPoints = subPath.PathPoints; - TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, isClosed, 1, + TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, isClosed, 1, StrokeLineJoin, out subPathVerts[i]); } } diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Polygon.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Polygon.cs index cb7cd20..9a4aeee 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Polygon.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Polygon.cs @@ -105,7 +105,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes if (Stroke != null && StrokeThickness > 0) { - TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, out verts); + TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, StrokeLineJoin, out verts); Stroke.SetupBrush(this, ref verts, context.ZOrder, true); PrimitiveBuffer.SetPrimitiveBuffer(ref _strokeContext, ref verts, PrimitiveType.TriangleList); } diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Rectangle.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Rectangle.cs index 82b87f2..224832d 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Rectangle.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Rectangle.cs @@ -134,7 +134,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes if (Stroke != null && StrokeThickness > 0) { - TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, out verts); + TriangulateHelper.TriangulateStroke_TriangleList(pathPoints, (float) StrokeThickness, true, 1, StrokeLineJoin, out verts); Stroke.SetupBrush(this, ref verts, context.ZOrder, true); PrimitiveBuffer.SetPrimitiveBuffer(ref _strokeContext, ref verts, PrimitiveType.TriangleList); } diff --git a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Shape.cs b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Shape.cs index 3e01510..49cb801 100644 --- a/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Shape.cs +++ b/MediaPortal/Source/UI/SkinEngine/Controls/Visuals/Shapes/Shape.cs @@ -30,6 +30,25 @@ using MediaPortal.Utilities.DeepCopy; namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes { + /// + /// Describes the shape that joins two lines or segments. + /// + public enum PenLineJoin + { + /// + /// Line joins use regular angular vertices. + /// + Miter, + /// + /// Line joins use beveled vertices. This is the default behaviour in MPF. + /// + Bevel, + /// + /// Line joins use rounded vertices. + /// + Round + } + public class Shape : FrameworkElement { #region Protected fields @@ -38,6 +57,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes protected AbstractProperty _fillProperty; protected AbstractProperty _strokeProperty; protected AbstractProperty _strokeThicknessProperty; + protected AbstractProperty _strokeLineJoinProperty; protected volatile bool _performLayout; protected PrimitiveBuffer _fillContext; @@ -66,6 +86,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes _fillProperty = new SProperty(typeof(Brush), null); _strokeProperty = new SProperty(typeof(Brush), null); _strokeThicknessProperty = new SProperty(typeof(double), 1.0); + _strokeLineJoinProperty = new SProperty(typeof(PenLineJoin), PenLineJoin.Bevel); _stretchProperty = new SProperty(typeof(Stretch), Stretch.None); } @@ -74,6 +95,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes _fillProperty.Attach(OnFillBrushPropertyChanged); _strokeProperty.Attach(OnStrokeBrushPropertyChanged); _strokeThicknessProperty.Attach(OnStrokeThicknessChanged); + _strokeLineJoinProperty.Attach(OnStrokeLineJoinChanged); } void Detach() @@ -81,6 +103,7 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes _fillProperty.Detach(OnFillBrushPropertyChanged); _strokeProperty.Detach(OnStrokeBrushPropertyChanged); _strokeThicknessProperty.Detach(OnStrokeThicknessChanged); + _strokeLineJoinProperty.Detach(OnStrokeLineJoinChanged); } public override void DeepCopy(IDeepCopyable source, ICopyManager copyManager) @@ -114,6 +137,11 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes _performLayout = true; } + void OnStrokeLineJoinChanged(AbstractProperty property, object oldValue) + { + _performLayout = true; + } + void OnFillBrushPropertyChanged(AbstractProperty property, object oldValue) { if (oldValue is Brush) @@ -175,6 +203,20 @@ namespace MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes get { return (double) _strokeThicknessProperty.GetValue(); } set { _strokeThicknessProperty.SetValue(value); } } + + public AbstractProperty StrokeLineJoinProperty + { + get { return _strokeLineJoinProperty; } + } + + /// + /// Gets or sets a PenLineJoin enumeration value that specifies the type of join that is used at the vertices of a Shape. + /// + public PenLineJoin StrokeLineJoin + { + get { return (PenLineJoin) _strokeLineJoinProperty.GetValue(); } + set { _strokeLineJoinProperty.SetValue(value); } + } protected void PerformLayout(RenderContext context) { diff --git a/MediaPortal/Source/UI/SkinEngine/DirectX/Triangulate/TriangulateHelper.cs b/MediaPortal/Source/UI/SkinEngine/DirectX/Triangulate/TriangulateHelper.cs index 4eb2408..5a0c563 100644 --- a/MediaPortal/Source/UI/SkinEngine/DirectX/Triangulate/TriangulateHelper.cs +++ b/MediaPortal/Source/UI/SkinEngine/DirectX/Triangulate/TriangulateHelper.cs @@ -25,9 +25,10 @@ using System; using System.Collections.Generic; using System.Drawing; +using MediaPortal.UI.SkinEngine.Controls.Visuals.Shapes; using SlimDX; using SlimDX.Direct3D9; -using Matrix=SlimDX.Matrix; +using Matrix = SlimDX.Matrix; namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate { @@ -158,7 +159,7 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate /// /// Converts the graphics path to an array of vertices using TriangleList. /// - public static void TriangulateStroke_TriangleList(PointF[] points, float thickness, bool close, float zCoord, + public static void TriangulateStroke_TriangleList(PointF[] points, float thickness, bool close, float zCoord, PenLineJoin lineJoin, out PositionColoredTextured[] verts) { PointF[] pathPoints = AdjustPoints(points); @@ -169,7 +170,7 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate cpoints[i] = new CPoint2D(pt.X, pt.Y); } PolygonDirection direction = CPolygon.GetPointsDirection(cpoints); - TriangulateStroke_TriangleList(pathPoints, thickness, close, direction, zCoord, out verts); + TriangulateStroke_TriangleList(pathPoints, thickness, close, direction, zCoord, lineJoin, out verts); } /// @@ -180,9 +181,10 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate /// True if we should connect the first and last point. /// The polygon direction. /// Z coordinate of the returned vertices. + /// The PenLineJoin to use. /// The generated verts. public static void TriangulateStroke_TriangleList(PointF[] points, float thickness, bool close, - PolygonDirection direction, float zCoord, out PositionColoredTextured[] verts) + PolygonDirection direction, float zCoord, PenLineJoin lineJoin, out PositionColoredTextured[] verts) { verts = null; PointF[] pathPoints = AdjustPoints(points); @@ -197,7 +199,8 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate pointCount = pathPoints.Length - 1; int pointsLength = pathPoints.Length; - verts = new PositionColoredTextured[pointCount * 3 * 3 - (close ? 0 : 3)]; + int trianglesPerSegment = lineJoin == PenLineJoin.Miter ? 4 : 3; + verts = new PositionColoredTextured[pointCount * trianglesPerSegment * 3 - (close ? 0 : 3)]; float insetX; float insetY; @@ -217,22 +220,36 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate if (lastInset.HasValue) { // If we wanted to have different StrokeLineJoin implementations, this should be done here. At the moment, the join is quite trivial. - verts[offset].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); - verts[offset + 1].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); - verts[offset + 2].Position = new Vector3(pathPoints[i].X + lastInset.Value.X, pathPoints[i].Y + lastInset.Value.Y, zCoord); - offset += 3; + switch (lineJoin) + { + case PenLineJoin.Miter: + verts[offset++].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + lastInset.Value.X, pathPoints[i].Y + lastInset.Value.Y, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX + lastInset.Value.X, pathPoints[i].Y + insetY + lastInset.Value.Y, zCoord); + + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX + lastInset.Value.X, pathPoints[i].Y + insetY + lastInset.Value.Y, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); + break; + // Currently Round is not supported and will be rendered as Bevel. + case PenLineJoin.Round: + case PenLineJoin.Bevel: + verts[offset++].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + lastInset.Value.X, pathPoints[i].Y + lastInset.Value.Y, zCoord); + break; + } } - verts[offset].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); - verts[offset + 1].Position = new Vector3(nextPoint.X, nextPoint.Y, zCoord); - verts[offset + 2].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, 1); + verts[offset++].Position = new Vector3(pathPoints[i].X, pathPoints[i].Y, zCoord); + verts[offset++].Position = new Vector3(nextPoint.X, nextPoint.Y, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); - verts[offset + 3].Position = new Vector3(nextPoint.X, nextPoint.Y, zCoord); - verts[offset + 4].Position = new Vector3(nextPoint.X + insetX, nextPoint.Y + insetY, zCoord); - verts[offset + 5].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); + verts[offset++].Position = new Vector3(nextPoint.X, nextPoint.Y, zCoord); + verts[offset++].Position = new Vector3(nextPoint.X + insetX, nextPoint.Y + insetY, zCoord); + verts[offset++].Position = new Vector3(pathPoints[i].X + insetX, pathPoints[i].Y + insetY, zCoord); lastInset = new PointF(insetX, insetY); - offset += 6; } } @@ -272,31 +289,31 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate } } - /// - /// Describes how points should be painted in a LineStrip relative to its center. - /// - /// - /// The behavior of the and modes depends on the order the points are - /// listed in. will draw the line on the outside of a clockwise curve and on the - /// inside of a counterclockwise curve; is the opposite. - /// - public enum WidthMode - { - /// - /// Centers the width on the line. - /// - Centered, - - /// - /// Places the width on the left-hand side of the line. - /// - LeftHanded, - /// - /// Places the width on the right-hand side of the line. + /// Describes how points should be painted in a LineStrip relative to its center. /// - RightHanded - } + /// + /// The behavior of the and modes depends on the order the points are + /// listed in. will draw the line on the outside of a clockwise curve and on the + /// inside of a counterclockwise curve; is the opposite. + /// + public enum WidthMode + { + /// + /// Centers the width on the line. + /// + Centered, + + /// + /// Places the width on the left-hand side of the line. + /// + LeftHanded, + + /// + /// Places the width on the right-hand side of the line. + /// + RightHanded + } /// /// Generates the vertices of a thickened line strip. @@ -420,12 +437,12 @@ namespace MediaPortal.UI.SkinEngine.DirectX.Triangulate v2 = pathPoints[index]; ZCross(ref v1, ref v2, out temp); area += temp; - centroid.X += (float)((v1.X + v2.X) * temp); - centroid.Y += (float)((v1.Y + v2.Y) * temp); + centroid.X += (float) ((v1.X + v2.X) * temp); + centroid.Y += (float) ((v1.Y + v2.Y) * temp); } temp = 1 / (Math.Abs(area) * 3); - centroid.X *= (float)temp; - centroid.Y *= (float)temp; + centroid.X *= (float) temp; + centroid.Y *= (float) temp; cx = Math.Abs(centroid.X); cy = Math.Abs(centroid.Y); -- 1.7.3.1.msysgit.0