Browse Source

Merge pull request #96 from ncarrillo/gradient-brush

Implemented LinearGradientBrush
Steven Kirk 10 years ago
parent
commit
9d94c9de5f

+ 9 - 1
src/Perspex.SceneGraph/Media/Brush.cs

@@ -9,7 +9,15 @@ namespace Perspex.Media
     /// <summary>
     /// Describes how an area is painted.
     /// </summary>
-    public abstract class Brush
+    public abstract class Brush : PerspexObject
     {
+        public static readonly PerspexProperty<double> OpacityProperty =
+    PerspexProperty.Register<Brush, double>(nameof(Opacity), 1.0);
+
+        public double Opacity
+        {
+            get { return this.GetValue(OpacityProperty); }
+            set { this.SetValue(OpacityProperty, value); }
+        }
     }
 }

+ 14 - 0
src/Perspex.SceneGraph/Media/BrushMappingMode.cs

@@ -0,0 +1,14 @@
+namespace Perspex.Media
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using System.Threading.Tasks;
+
+    public enum BrushMappingMode
+    {
+        Absolute,
+        RelativeToBoundingBox
+    }
+}

+ 38 - 0
src/Perspex.SceneGraph/Media/GradientBrush.cs

@@ -0,0 +1,38 @@
+namespace Perspex.Media
+{
+    using System.Collections.Generic;
+
+    public abstract class GradientBrush : Brush
+    {
+        public static readonly PerspexProperty<BrushMappingMode> MappingModeProperty =
+PerspexProperty.Register<GradientBrush, BrushMappingMode>(nameof(MappingMode), BrushMappingMode.RelativeToBoundingBox);
+
+        public static readonly PerspexProperty<GradientSpreadMethod> SpreadMethodProperty =
+PerspexProperty.Register<GradientBrush, GradientSpreadMethod>(nameof(SpreadMethod), GradientSpreadMethod.Pad);
+
+        public static readonly PerspexProperty<List<GradientStop>> GradientStopsProperty =
+PerspexProperty.Register<GradientBrush, List<GradientStop>>(nameof(Opacity), new List<GradientStop>());
+
+        public GradientBrush()
+        {
+        }
+
+        public BrushMappingMode MappingMode
+        {
+            get { return this.GetValue(MappingModeProperty); }
+            set { this.SetValue(MappingModeProperty, value); }
+        }
+
+        public GradientSpreadMethod SpreadMethod
+        {
+            get { return this.GetValue(SpreadMethodProperty); }
+            set { this.SetValue(SpreadMethodProperty, value); }
+        }
+
+        public List<GradientStop> GradientStops
+        {
+            get { return this.GetValue(GradientStopsProperty); }
+            set { this.SetValue(GradientStopsProperty, value); }
+        }
+    }
+}

+ 15 - 0
src/Perspex.SceneGraph/Media/GradientSpreadMethod.cs

@@ -0,0 +1,15 @@
+namespace Perspex.Media
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using System.Threading.Tasks;
+
+    public enum GradientSpreadMethod
+    {
+        Pad,
+        Reflect,
+        Repeat
+    }
+}

+ 36 - 0
src/Perspex.SceneGraph/Media/GradientStop.cs

@@ -0,0 +1,36 @@
+namespace Perspex.Media
+{
+    /// <summary>
+    /// GradientStop
+    /// </summary>
+    public sealed class GradientStop
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="GradientStop"/> class.
+        /// </summary>
+        public GradientStop() { }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="GradientStop"/> class.
+        /// </summary>
+        /// <param name="color">The color</param>
+        /// <param name="offset">The offset</param>
+        public GradientStop(Color color, double offset)
+        {
+            this.Color = color;
+            this.Offset = offset;
+        }
+
+        // TODO: Make these dependency properties.
+
+        /// <summary>
+        /// The offset
+        /// </summary>
+        public double Offset { get; set; }
+
+        /// <summary>
+        /// The color
+        /// </summary>
+        public Color Color { get; set; }
+    }
+}

+ 23 - 0
src/Perspex.SceneGraph/Media/LinearGradientBrush.cs

@@ -0,0 +1,23 @@
+namespace Perspex.Media
+{
+    public class LinearGradientBrush : GradientBrush
+    {
+        public static readonly PerspexProperty<Point> StartPointProperty =
+PerspexProperty.Register<LinearGradientBrush, Point>(nameof(StartPoint), new Point(0,0));
+
+        public static readonly PerspexProperty<Point> EndPointProperty =
+PerspexProperty.Register<LinearGradientBrush, Point>(nameof(EndPoint), new Point(0, 0));
+
+        public Point StartPoint
+        {
+            get { return this.GetValue(StartPointProperty); }
+            set { this.SetValue(StartPointProperty, value); }
+        }
+
+        public Point EndPoint
+        {
+            get { return this.GetValue(EndPointProperty); }
+            set { this.SetValue(EndPointProperty, value); }
+        }
+    }
+}

+ 5 - 0
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@@ -59,8 +59,13 @@
     <Compile Include="Matrix.cs" />
     <Compile Include="Media\Brush.cs" />
     <Compile Include="Media\Brushes.cs" />
+    <Compile Include="Media\BrushMappingMode.cs" />
     <Compile Include="Media\Color.cs" />
     <Compile Include="Media\Colors.cs" />
+    <Compile Include="Media\GradientBrush.cs" />
+    <Compile Include="Media\GradientSpreadMethod.cs" />
+    <Compile Include="Media\GradientStop.cs" />
+    <Compile Include="Media\LinearGradientBrush.cs" />
     <Compile Include="Media\TextAlignment.cs" />
     <Compile Include="Media\FontWeight.cs" />
     <Compile Include="Media\FontStyle.cs" />

+ 19 - 0
src/Windows/Perspex.Direct2D1/Media/BrushImpl.cs

@@ -0,0 +1,19 @@
+using System;
+
+namespace Perspex.Direct2D1.Media
+{
+    public abstract class BrushImpl : IDisposable
+    {
+        public SharpDX.Direct2D1.Brush PlatformBrush { get; set; }
+
+        public BrushImpl(Perspex.Media.Brush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+        {
+        }
+
+        public virtual void Dispose()
+        {
+            if (this.PlatformBrush != null)
+                this.PlatformBrush.Dispose();
+        }
+    }
+}

+ 33 - 11
src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs

@@ -89,13 +89,15 @@ namespace Perspex.Direct2D1.Media
         {
             if (pen != null)
             {
-                using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
+                var size = new Rect(p1, p2).Size;
+
+                using (var d2dBrush = this.CreateBrush(pen.Brush, size))
                 using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
                 {
                     this.renderTarget.DrawLine(
                         p1.ToSharpDX(),
                         p2.ToSharpDX(),
-                        d2dBrush,
+                        d2dBrush.PlatformBrush,
                         (float)pen.Thickness,
                         d2dStroke);
                 }
@@ -112,20 +114,20 @@ namespace Perspex.Direct2D1.Media
         {
             if (brush != null)
             {
-                using (var d2dBrush = brush.ToDirect2D(this.renderTarget))
+                using (var d2dBrush = this.CreateBrush(brush, geometry.Bounds.Size))
                 {
                     GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
-                    this.renderTarget.FillGeometry(impl.Geometry, d2dBrush);
+                    this.renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
                 }
             }
 
             if (pen != null)
             {
-                using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
+                using (var d2dBrush = this.CreateBrush(pen.Brush, geometry.GetRenderBounds(pen.Thickness).Size))
                 using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
                 {
                     GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
-                    this.renderTarget.DrawGeometry(impl.Geometry, d2dBrush, (float)pen.Thickness, d2dStroke);
+                    this.renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
                 }
             }
         }
@@ -138,12 +140,12 @@ namespace Perspex.Direct2D1.Media
         /// <param name="cornerRadius">The corner radius.</param>
         public void DrawRectange(Pen pen, Rect rect, float cornerRadius)
         {
-            using (var brush = pen.Brush.ToDirect2D(this.renderTarget))
+            using (var brush = this.CreateBrush(pen.Brush, rect.Size))
             using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
             {
                 this.renderTarget.DrawRoundedRectangle(
                     new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
-                    brush,
+                    brush.PlatformBrush,
                     (float)pen.Thickness,
                     d2dStroke);
             }
@@ -161,7 +163,8 @@ namespace Perspex.Direct2D1.Media
             {
                 var impl = (FormattedTextImpl)text.PlatformImpl;
 
-                using (var renderer = new PerspexTextRenderer(this.renderTarget, foreground.ToDirect2D(this.renderTarget)))
+                using (var brush = this.CreateBrush(foreground, impl.Measure()))
+                using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush.PlatformBrush))
                 {
                     impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y);
                 }
@@ -176,7 +179,7 @@ namespace Perspex.Direct2D1.Media
         /// <param name="cornerRadius">The corner radius.</param>
         public void FillRectange(Perspex.Media.Brush brush, Rect rect, float cornerRadius)
         {
-            using (var b = brush.ToDirect2D(this.renderTarget))
+            using (var b = this.CreateBrush(brush, rect.Size))
             {
                 this.renderTarget.FillRoundedRectangle(
                     new RoundedRectangle
@@ -189,7 +192,7 @@ namespace Perspex.Direct2D1.Media
                         RadiusX = cornerRadius,
                         RadiusY = cornerRadius
                     },
-                    b);
+                    b.PlatformBrush);
             }
         }
 
@@ -256,5 +259,24 @@ namespace Perspex.Direct2D1.Media
                 this.renderTarget.Transform = transform * m3x2;
             });
         }
+
+        public BrushImpl CreateBrush(Perspex.Media.Brush brush, Size destinationSize)
+        {
+            Perspex.Media.SolidColorBrush solidColorBrush = brush as Perspex.Media.SolidColorBrush;
+            Perspex.Media.LinearGradientBrush linearGradientBrush = brush as Perspex.Media.LinearGradientBrush;
+
+            if (solidColorBrush != null)
+            {
+                return new SolidColorBrushImpl(solidColorBrush, this.renderTarget, destinationSize);
+            }
+            else if (linearGradientBrush != null)
+            {
+                return new LinearGradientBrushImpl(linearGradientBrush, this.renderTarget, destinationSize);
+            }
+            else
+            {
+                return new SolidColorBrushImpl(null, this.renderTarget, destinationSize);
+            }
+        }
     }
 }

+ 43 - 0
src/Windows/Perspex.Direct2D1/Media/LinearGradientBrushImpl.cs

@@ -0,0 +1,43 @@
+namespace Perspex.Direct2D1.Media
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using System.Threading.Tasks;
+
+    public class LinearGradientBrushImpl : BrushImpl
+    {
+        public LinearGradientBrushImpl(Perspex.Media.LinearGradientBrush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+            : base(brush, target, destinationSize)
+        {
+            if (brush != null)
+            {
+                var gradientStops = brush.GradientStops.Select(s => new SharpDX.Direct2D1.GradientStop { Color = s.Color.ToDirect2D(), Position = (float)s.Offset }).ToArray();
+
+                Point startPoint = new Point(0, 0);
+                Point endPoint = new Point(0, 0);
+
+                switch (brush.MappingMode)
+                {
+                    case Perspex.Media.BrushMappingMode.Absolute:
+                        // TODO:
+
+                        break;
+                    case Perspex.Media.BrushMappingMode.RelativeToBoundingBox:
+                        startPoint = new Point(brush.StartPoint.X * destinationSize.Width, brush.StartPoint.Y * destinationSize.Height);
+                        endPoint = new Point(brush.EndPoint.X * destinationSize.Width, brush.EndPoint.Y * destinationSize.Height);
+
+                        break;
+                }
+
+                this.PlatformBrush = new SharpDX.Direct2D1.LinearGradientBrush(
+                    target,
+                    new SharpDX.Direct2D1.LinearGradientBrushProperties { StartPoint = startPoint.ToSharpDX(), EndPoint = endPoint.ToSharpDX() },
+                    new SharpDX.Direct2D1.BrushProperties { Opacity = (float)brush.Opacity, Transform = target.Transform },
+                    new SharpDX.Direct2D1.GradientStopCollection(target, gradientStops, brush.SpreadMethod.ToDirect2D())
+                );
+            }
+        }
+    }
+}

+ 8 - 3
src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs

@@ -13,14 +13,18 @@ namespace Perspex.Direct2D1.Media
 
     internal class PerspexTextRenderer : TextRenderer
     {
+        private DrawingContext context;
+
         private RenderTarget renderTarget;
 
         private Brush foreground;
 
         public PerspexTextRenderer(
+            DrawingContext context,
             RenderTarget target,
             Brush foreground)
         {
+            this.context = context;
             this.renderTarget = target;
             this.foreground = foreground;
         }
@@ -33,7 +37,6 @@ namespace Perspex.Direct2D1.Media
 
         public void Dispose()
         {
-            this.foreground.Dispose();
         }
 
         public Result DrawGlyphRun(
@@ -46,9 +49,11 @@ namespace Perspex.Direct2D1.Media
             ComObject clientDrawingEffect)
         {
             var wrapper = clientDrawingEffect as BrushWrapper;
+
+            // TODO: Work out how to get the size below rather than passing new Size().
             var brush = (wrapper == null) ?
                 this.foreground :
-                wrapper.Brush.ToDirect2D(this.renderTarget);
+                this.context.CreateBrush(wrapper.Brush, new Size()).PlatformBrush;
 
             this.renderTarget.DrawGlyphRun(
                 new Vector2(baselineOriginX, baselineOriginY),
@@ -94,4 +99,4 @@ namespace Perspex.Direct2D1.Media
             return false;
         }
     }
-}
+}

+ 17 - 0
src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs

@@ -0,0 +1,17 @@
+namespace Perspex.Direct2D1.Media
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using System.Threading.Tasks;
+
+    public class SolidColorBrushImpl : BrushImpl
+    {
+        public SolidColorBrushImpl(Perspex.Media.SolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+            : base(brush, target, destinationSize)
+        {
+            this.PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(target, brush?.Color.ToDirect2D() ?? new SharpDX.Color4());
+        }
+    }
+}

+ 3 - 0
src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

@@ -75,11 +75,14 @@
     </Compile>
     <Compile Include="Direct2D1Platform.cs" />
     <Compile Include="GlobalSuppressions.cs" />
+    <Compile Include="Media\BrushImpl.cs" />
     <Compile Include="Media\BrushWrapper.cs" />
     <Compile Include="Media\DrawingContext.cs" />
     <Compile Include="Media\Imaging\RenderTargetBitmapImpl.cs" />
     <Compile Include="Media\Imaging\BitmapImpl.cs" />
+    <Compile Include="Media\LinearGradientBrushImpl.cs" />
     <Compile Include="Media\PerspexTextRenderer.cs" />
+    <Compile Include="Media\SolidColorBrushImpl.cs" />
     <Compile Include="Media\StreamGeometryContextImpl.cs" />
     <Compile Include="Media\GeometryImpl.cs" />
     <Compile Include="Media\StreamGeometryImpl.cs" />

+ 9 - 0
src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs

@@ -34,6 +34,15 @@ namespace Perspex.Direct2D1
             return new Size2F((float)p.Width, (float)p.Height);
         }
 
+        public static SharpDX.Direct2D1.ExtendMode ToDirect2D(this Perspex.Media.GradientSpreadMethod spreadMethod)
+        {
+            if (spreadMethod == Perspex.Media.GradientSpreadMethod.Pad)
+                return ExtendMode.Clamp;
+            else if (spreadMethod == Perspex.Media.GradientSpreadMethod.Reflect)
+                return ExtendMode.Mirror;
+            else
+                return ExtendMode.Wrap;
+        }
         /// <summary>
         /// Converts a brush to Direct2D.
         /// </summary>