Browse Source

Merge branch 'master' into visualbrush

Conflicts:
	src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
	src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs
pull/111/head
Steven Kirk 11 years ago
parent
commit
82fd79578f
  1. 1
      src/Perspex.Application/Application.cs
  2. 1
      src/Perspex.Base/Perspex.Base.csproj
  3. 18
      src/Perspex.Base/Threading/Dispatcher.cs
  4. 2
      src/Perspex.Base/Threading/DispatcherTimer.cs
  5. 70
      src/Perspex.Base/Threading/MainLoop.cs
  6. 47
      src/Perspex.Base/Threading/PerspexSynchronizationContext.cs
  7. 4
      src/Perspex.ReactiveUI/Registrations.cs
  8. 8
      src/Perspex.SceneGraph/Media/Brush.cs
  9. 14
      src/Perspex.SceneGraph/Media/BrushMappingMode.cs
  10. 38
      src/Perspex.SceneGraph/Media/GradientBrush.cs
  11. 15
      src/Perspex.SceneGraph/Media/GradientSpreadMethod.cs
  12. 36
      src/Perspex.SceneGraph/Media/GradientStop.cs
  13. 23
      src/Perspex.SceneGraph/Media/LinearGradientBrush.cs
  14. 5
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  15. 19
      src/Windows/Perspex.Direct2D1/Media/BrushImpl.cs
  16. 71
      src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
  17. 43
      src/Windows/Perspex.Direct2D1/Media/LinearGradientBrushImpl.cs
  18. 2
      src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs
  19. 17
      src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
  20. 3
      src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
  21. 9
      src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs

1
src/Perspex.Application/Application.cs

@ -150,6 +150,7 @@ namespace Perspex
/// </summary>
protected virtual void RegisterServices()
{
PerspexSynchronizationContext.InstallIfNeeded();
this.FocusManager = new FocusManager();
this.InputManager = new InputManager();

1
src/Perspex.Base/Perspex.Base.csproj

@ -72,6 +72,7 @@
<Compile Include="Threading\DispatcherTimer.cs" />
<Compile Include="Threading\MainLoop.cs" />
<Compile Include="Threading\PerspexScheduler.cs" />
<Compile Include="Threading\PerspexSynchronizationContext.cs" />
<Compile Include="Utilities\MathUtilities.cs" />
<Compile Include="Utilities\TypeUtilities.cs" />
</ItemGroup>

18
src/Perspex.Base/Threading/Dispatcher.cs

@ -50,6 +50,14 @@ namespace Perspex.Threading
this.mainLoop.Run(cancellationToken);
}
/// <summary>
/// Runs continuations pushed on the loop.
/// </summary>
public void RunJobs()
{
this.mainLoop.RunJobs();
}
/// <summary>
/// Invokes a method on the dispatcher thread.
/// </summary>
@ -60,5 +68,15 @@ namespace Perspex.Threading
{
return this.mainLoop.InvokeAsync(action, priority);
}
/// <summary>
/// Post action that will be invoked on main thread
/// </summary>
/// <param name="action">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
internal void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
this.mainLoop.Post(action, priority);
}
}
}

2
src/Perspex.Base/Threading/DispatcherTimer.cs

@ -203,7 +203,7 @@ namespace Perspex.Threading
/// </summary>
private void InternalTick()
{
this.Dispatcher.InvokeAsync(this.RaiseTick, this.priority);
this.Dispatcher.Post(this.RaiseTick, this.priority);
}
/// <summary>

70
src/Perspex.Base/Threading/MainLoop.cs

@ -42,23 +42,40 @@ namespace Perspex.Win32.Threading
{
while (!cancellationToken.IsCancellationRequested)
{
Job job = null;
RunJobs();
while (job != null || this.queue.Count > 0)
platform.ProcessMessage();
}
}
/// <summary>
/// Runs continuations pushed on the loop.
/// </summary>
public void RunJobs()
{
Job job = null;
while (job != null || this.queue.Count > 0)
{
if (job == null)
{
if (job == null)
lock (this.queue)
{
lock (this.queue)
{
job = this.queue.Dequeue();
}
job = this.queue.Dequeue();
}
}
if (job.Priority < DispatcherPriority.Input && platform.HasMessages())
{
break;
}
if (job.Priority < DispatcherPriority.Input && platform.HasMessages())
{
break;
}
if (job.TaskCompletionSource == null)
{
job.Action();
}
else
{
try
{
job.Action();
@ -68,11 +85,9 @@ namespace Perspex.Win32.Threading
{
job.TaskCompletionSource.SetException(e);
}
job = null;
}
platform.ProcessMessage();
job = null;
}
}
@ -84,15 +99,29 @@ namespace Perspex.Win32.Threading
/// <returns>A task that can be used to track the method's execution.</returns>
public Task InvokeAsync(Action action, DispatcherPriority priority)
{
var job = new Job(action, priority);
var job = new Job(action, priority, false);
this.AddJob(job);
return job.TaskCompletionSource.Task;
}
/// <summary>
/// Post action that will be invoked on main thread
/// </summary>
/// <param name="action">The method.</param>
///
/// <param name="priority">The priority with which to invoke the method.</param>
internal void Post(Action action, DispatcherPriority priority)
{
this.AddJob(new Job(action, priority, true));
}
private void AddJob(Job job)
{
lock (this.queue)
{
this.queue.Add(job, priority);
this.queue.Add(job, job.Priority);
}
platform.Wake();
return job.TaskCompletionSource.Task;
}
/// <summary>
@ -105,11 +134,12 @@ namespace Perspex.Win32.Threading
/// </summary>
/// <param name="action">The method to call.</param>
/// <param name="priority">The job priority.</param>
public Job(Action action, DispatcherPriority priority)
/// <param name="throwOnUiThread">Do not wrap excepption in TaskCompletionSource</param>
public Job(Action action, DispatcherPriority priority, bool throwOnUiThread)
{
this.Action = action;
this.Priority = priority;
this.TaskCompletionSource = new TaskCompletionSource<object>();
this.TaskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>();
}
/// <summary>

47
src/Perspex.Base/Threading/PerspexSynchronizationContext.cs

@ -0,0 +1,47 @@
namespace Perspex.Threading
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
/// <summary>
/// SynchronizationContext to be used on main thread
/// </summary>
public class PerspexSynchronizationContext : SynchronizationContext
{
/// <summary>
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
/// </summary>
public static bool AutoInstall { get; set; } = true;
/// <summary>
/// Installs synchronization context in current thread
/// </summary>
public static void InstallIfNeeded()
{
if (!AutoInstall || Current is PerspexSynchronizationContext)
{
return;
}
SetSynchronizationContext(new PerspexSynchronizationContext());
}
/// <inheritdoc/>
public override void Post(SendOrPostCallback d, object state)
{
Dispatcher.UIThread.Post(() => d(state));
}
/// <inheritdoc/>
public override void Send(SendOrPostCallback d, object state)
{
// TODO: Add check for being on the main thread, we should invoke the method immediately in this case
Dispatcher.UIThread.InvokeAsync(() => d(state)).Wait();
}
}
}

4
src/Perspex.ReactiveUI/Registrations.cs

@ -1,4 +1,6 @@
using System;
using System.Reactive.Concurrency;
using System.Threading;
namespace ReactiveUI
@ -12,7 +14,7 @@ namespace ReactiveUI
{
public void Register(Action<Func<object>, Type> registerFunction)
{
RxApp.MainThreadScheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);
}
}
}

8
src/Perspex.SceneGraph/Media/Brush.cs

@ -11,5 +11,13 @@ namespace Perspex.Media
/// </summary>
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
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
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
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
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
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
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -61,9 +61,14 @@
<Compile Include="Media\AlignmentX.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\MediaExtensions.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
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();
}
}
}

71
src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs

@ -98,7 +98,7 @@ namespace Perspex.Direct2D1.Media
this.renderTarget.DrawLine(
p1.ToSharpDX(),
p2.ToSharpDX(),
d2dBrush,
d2dBrush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
@ -118,7 +118,7 @@ namespace Perspex.Direct2D1.Media
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);
}
}
@ -128,7 +128,7 @@ namespace Perspex.Direct2D1.Media
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);
}
}
}
@ -146,7 +146,7 @@ namespace Perspex.Direct2D1.Media
{
this.renderTarget.DrawRoundedRectangle(
new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
brush,
brush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
@ -165,7 +165,7 @@ namespace Perspex.Direct2D1.Media
var impl = (FormattedTextImpl)text.PlatformImpl;
using (var brush = this.CreateBrush(foreground, impl.Measure()))
using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush))
using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush.PlatformBrush))
{
impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y);
}
@ -193,7 +193,7 @@ namespace Perspex.Direct2D1.Media
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
b);
b.PlatformBrush);
}
}
@ -261,67 +261,22 @@ namespace Perspex.Direct2D1.Media
});
}
/// <summary>
/// Creates a Direct2D brush from a Perspex brush.
/// </summary>
/// <param name="brush">The perspex brush.</param>
/// <param name="destinationSize">The size of the brush's target area.</param>
/// <returns>The Direct2D brush.</returns>
public Disposable<SharpDX.Direct2D1.Brush> CreateBrush(Perspex.Media.Brush brush, Size destinationSize)
public BrushImpl CreateBrush(Perspex.Media.Brush brush, Size destinationSize)
{
var solidColorBrush = brush as Perspex.Media.SolidColorBrush;
var visualBrush = brush as Perspex.Media.VisualBrush;
Perspex.Media.SolidColorBrush solidColorBrush = brush as Perspex.Media.SolidColorBrush;
Perspex.Media.LinearGradientBrush linearGradientBrush = brush as Perspex.Media.LinearGradientBrush;
if (solidColorBrush != null)
{
return new Disposable<SharpDX.Direct2D1.Brush>(
new SharpDX.Direct2D1.SolidColorBrush(
this.renderTarget,
solidColorBrush.Color.ToDirect2D()));
return new SolidColorBrushImpl(solidColorBrush, this.renderTarget, destinationSize);
}
else if (visualBrush != null)
else if (linearGradientBrush != null)
{
return this.CreateBrush(visualBrush, destinationSize);
return new LinearGradientBrushImpl(linearGradientBrush, this.renderTarget, destinationSize);
}
else
{
// TODO: Implement other brushes.
return new Disposable<SharpDX.Direct2D1.Brush>(
new SharpDX.Direct2D1.SolidColorBrush(this.renderTarget, new Color4()));
}
}
/// <summary>
/// Creates a Direct2D <see cref="BitmapBrush"/> from a Perspex <see cref="VisualBrush"/>.
/// </summary>
/// <param name="brush">The perspex brush.</param>
/// <param name="destinationSize">The size of the brush's target area.</param>
/// <returns>The Direct2D brush.</returns>
private Disposable<SharpDX.Direct2D1.Brush> CreateBrush(VisualBrush brush, Size destinationSize)
{
var visual = brush.Visual;
var layoutable = visual as ILayoutable;
if (layoutable?.IsArrangeValid == false)
{
layoutable.Measure(Size.Infinity);
layoutable.Arrange(new Rect(layoutable.DesiredSize));
}
var sourceSize = layoutable.Bounds.Size;
var destinationRect = brush.DestinationRect.ToPixels(destinationSize);
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize);
using (var target = new BitmapRenderTarget(
this.renderTarget,
CompatibleRenderTargetOptions.None,
destinationRect.Size.ToSharpDX()))
{
var renderer = new Renderer(target);
renderer.Render(visual, null, Matrix.Identity, Matrix.CreateScale(scale));
var result = new BitmapBrush(this.renderTarget, target.Bitmap);
return new Disposable<SharpDX.Direct2D1.Brush>(result, target);
return new SolidColorBrushImpl(null, this.renderTarget, destinationSize);
}
}
}

43
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())
);
}
}
}
}

2
src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs

@ -53,7 +53,7 @@ namespace Perspex.Direct2D1.Media
// TODO: Work out how to get the size below rather than passing new Size().
var brush = (wrapper == null) ?
this.foreground :
this.context.CreateBrush(wrapper.Brush, new Size());
this.context.CreateBrush(wrapper.Brush, new Size()).PlatformBrush;
this.renderTarget.DrawGlyphRun(
new Vector2(baselineOriginX, baselineOriginY),

17
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
src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

@ -76,11 +76,14 @@
<Compile Include="Direct2D1Platform.cs" />
<Compile Include="Disposable.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
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 pen to a Direct2D stroke style.
/// </summary>

Loading…
Cancel
Save