Browse Source

Merge branch 'visualbrush'

pull/111/head
Steven Kirk 11 years ago
parent
commit
0a0e25935b
  1. 2
      Perspex.sln
  2. 35
      src/Perspex.Controls/Image.cs
  3. 29
      src/Perspex.SceneGraph/Media/AlignmentX.cs
  4. 29
      src/Perspex.SceneGraph/Media/AlignmentY.cs
  5. 47
      src/Perspex.SceneGraph/Media/MediaExtensions.cs
  6. 136
      src/Perspex.SceneGraph/Media/TileBrush.cs
  7. 45
      src/Perspex.SceneGraph/Media/VisualBrush.cs
  8. 6
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  9. 10
      src/Perspex.SceneGraph/Point.cs
  10. 105
      src/Perspex.SceneGraph/RelativeRect.cs
  11. 15
      src/Perspex.SceneGraph/Rendering/RendererBase.cs
  12. 39
      src/Windows/Perspex.Direct2D1/Disposable.cs
  13. 67
      src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
  14. 2
      src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs
  15. 112
      src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs
  16. 6
      src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
  17. 21
      src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs
  18. 596
      tests/Perspex.RenderTests/Media/VisualBrushTests.cs
  19. 1
      tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj
  20. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_BottomRight.expected.png
  21. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_Center.expected.png
  22. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_TopLeft.expected.png
  23. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_DestinationRect_Absolute.expected.png
  24. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipX.expected.png
  25. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipXY.expected.png
  26. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipY.expected.png
  27. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_Absolute.expected.png
  28. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Absolute.expected.png
  29. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Percent.expected.png
  30. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Fill_Large.expected.png
  31. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Uniform.expected.png
  32. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_UniformToFill.expected.png
  33. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png
  34. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_Alignment_BottomRight.expected.png

2
Perspex.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Win32", "src\Windows\Perspex.Win32\Perspex.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}"
EndProject

35
src/Perspex.Controls/Image.cs

@ -57,7 +57,7 @@ namespace Perspex.Controls
{
Rect viewPort = new Rect(this.Bounds.Size);
Size sourceSize = new Size(source.PixelWidth, source.PixelHeight);
Vector scale = CalculateScaling(this.Bounds.Size, sourceSize, this.Stretch);
Vector scale = this.Stretch.CalculateScaling(this.Bounds.Size, sourceSize);
Size scaledSize = sourceSize * scale;
Rect destRect = viewPort
.CenterIn(new Rect(scaledSize))
@ -95,41 +95,10 @@ namespace Perspex.Controls
availableSize = new Size(availableSize.Width, this.Height);
}
scale = CalculateScaling(availableSize, new Size(width, height), this.Stretch);
scale = this.Stretch.CalculateScaling(availableSize, new Size(width, height));
}
return new Size(width * scale.X, height * scale.Y);
}
/// <summary>
/// Calculates the scaling for the image.
/// </summary>
/// <param name="availableSize">The size available to display the image.</param>
/// <param name="imageSize">The pxiel size of the image.</param>
/// <param name="stretch">The stretch mode of the control.</param>
/// <returns>A vector with the X and Y scaling factors.</returns>
private static Vector CalculateScaling(Size availableSize, Size imageSize, Stretch stretch)
{
double scaleX = 1;
double scaleY = 1;
if (stretch != Stretch.None)
{
scaleX = availableSize.Width / imageSize.Width;
scaleY = availableSize.Height / imageSize.Height;
switch (stretch)
{
case Stretch.Uniform:
scaleX = scaleY = Math.Min(scaleX, scaleY);
break;
case Stretch.UniformToFill:
scaleX = scaleY = Math.Max(scaleX, scaleY);
break;
}
}
return new Vector(scaleX, scaleY);
}
}
}

29
src/Perspex.SceneGraph/Media/AlignmentX.cs

@ -0,0 +1,29 @@
// -----------------------------------------------------------------------
// <copyright file="AlignmentX.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
/// <summary>
/// Describes how content is positioned horizontally in a container.
/// </summary>
public enum AlignmentX
{
/// <summary>
/// The contents align themselves with the left of the container
/// </summary>
Left,
/// <summary>
/// The contents align themselves with the center of the container
/// </summary>
Center,
/// <summary>
/// The contents align themselves with the right of the container
/// </summary>
Right,
}
}

29
src/Perspex.SceneGraph/Media/AlignmentY.cs

@ -0,0 +1,29 @@
// -----------------------------------------------------------------------
// <copyright file="AlignmentY.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
/// <summary>
/// Describes how content is positioned vertically in a container.
/// </summary>
public enum AlignmentY
{
/// <summary>
/// The contents align themselves with the top of the container
/// </summary>
Top,
/// <summary>
/// The contents align themselves with the center of the container
/// </summary>
Center,
/// <summary>
/// The contents align themselves with the bottom of the container
/// </summary>
Bottom,
}
}

47
src/Perspex.SceneGraph/Media/MediaExtensions.cs

@ -0,0 +1,47 @@
// -----------------------------------------------------------------------
// <copyright file="MediaExtensions.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
using System;
/// <summary>
/// Provides extension methods for Perspex media.
/// </summary>
public static class MediaExtensions
{
/// <summary>
/// Calculates scaling based on a <see cref="Stretch"/> value.
/// </summary>
/// <param name="stretch">The stretch mode.</param>
/// <param name="destinationSize">The size of the destination viewport.</param>
/// <param name="sourceSize">The size of the source.</param>
/// <returns>A vector with the X and Y scaling factors.</returns>
public static Vector CalculateScaling(this Stretch stretch, Size destinationSize, Size sourceSize)
{
double scaleX = 1;
double scaleY = 1;
if (stretch != Stretch.None)
{
scaleX = destinationSize.Width / sourceSize.Width;
scaleY = destinationSize.Height / sourceSize.Height;
switch (stretch)
{
case Stretch.Uniform:
scaleX = scaleY = Math.Min(scaleX, scaleY);
break;
case Stretch.UniformToFill:
scaleX = scaleY = Math.Max(scaleX, scaleY);
break;
}
}
return new Vector(scaleX, scaleY);
}
}
}

136
src/Perspex.SceneGraph/Media/TileBrush.cs

@ -0,0 +1,136 @@
// -----------------------------------------------------------------------
// <copyright file="TileBrush.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
/// <summary>
/// Describes how a <see cref="TileBrush"/> is tiled.
/// </summary>
public enum TileMode
{
/// <summary>
/// A single repeat of the content will be displayed.
/// </summary>
None,
/// <summary>
/// The content will be repeated horizontally, with alternate tiles mirrored.
/// </summary>
FlipX,
/// <summary>
/// The content will be repeated vertically, with alternate tiles mirrored.
/// </summary>
FlipY,
/// <summary>
/// The content will be repeated horizontally and vertically, with alternate tiles mirrored.
/// </summary>
FlipXY,
/// <summary>
/// The content will be repeated.
/// </summary>
Tile
}
/// <summary>
/// Base class for brushes which display repeating images.
/// </summary>
public abstract class TileBrush : Brush
{
/// <summary>
/// Defines the <see cref="AlignmentX"/> property.
/// </summary>
public static readonly PerspexProperty<AlignmentX> AlignmentXProperty =
PerspexProperty.Register<TileBrush, AlignmentX>(nameof(AlignmentX), AlignmentX.Center);
/// <summary>
/// Defines the <see cref="AlignmentY"/> property.
/// </summary>
public static readonly PerspexProperty<AlignmentY> AlignmentYProperty =
PerspexProperty.Register<TileBrush, AlignmentY>(nameof(AlignmentY), AlignmentY.Center);
/// <summary>
/// Defines the <see cref="DestinationRect"/> property.
/// </summary>
public static readonly PerspexProperty<RelativeRect> DestinationRectProperty =
PerspexProperty.Register<TileBrush, RelativeRect>(nameof(DestinationRect), RelativeRect.Fill);
/// <summary>
/// Defines the <see cref="SourceRect"/> property.
/// </summary>
public static readonly PerspexProperty<RelativeRect> SourceRectProperty =
PerspexProperty.Register<TileBrush, RelativeRect>(nameof(SourceRect), RelativeRect.Fill);
/// <summary>
/// Defines the <see cref="Stretch"/> property.
/// </summary>
public static readonly PerspexProperty<Stretch> StretchProperty =
PerspexProperty.Register<TileBrush, Stretch>(nameof(Stretch), Stretch.Uniform);
/// <summary>
/// Defines the <see cref="TileMode"/> property.
/// </summary>
public static readonly PerspexProperty<TileMode> TileModeProperty =
PerspexProperty.Register<TileBrush, TileMode>(nameof(TileMode));
/// <summary>
/// Gets or sets the horizontal alignment of a tile in the destination.
/// </summary>
public AlignmentX AlignmentX
{
get { return this.GetValue(AlignmentXProperty); }
set { this.SetValue(AlignmentXProperty, value); }
}
/// <summary>
/// Gets or sets the horizontal alignment of a tile in the destination.
/// </summary>
public AlignmentY AlignmentY
{
get { return this.GetValue(AlignmentYProperty); }
set { this.SetValue(AlignmentYProperty, value); }
}
/// <summary>
/// Gets or sets the rectangle on the destination in which to paint a tile.
/// </summary>
public RelativeRect DestinationRect
{
get { return this.GetValue(DestinationRectProperty); }
set { this.SetValue(DestinationRectProperty, value); }
}
/// <summary>
/// Gets or sets the rectangle of the source image that will be displayed.
/// </summary>
public RelativeRect SourceRect
{
get { return this.GetValue(SourceRectProperty); }
set { this.SetValue(SourceRectProperty, value); }
}
/// <summary>
/// Gets or sets a value controlling how the source rectangle will be stretched to fill
/// the destination rect.
/// </summary>
public Stretch Stretch
{
get { return (Stretch)this.GetValue(StretchProperty); }
set { this.SetValue(StretchProperty, value); }
}
/// <summary>
/// Gets or sets the brush's tile mode.
/// </summary>
public TileMode TileMode
{
get { return (TileMode)this.GetValue(TileModeProperty); }
set { this.SetValue(TileModeProperty, value); }
}
}
}

45
src/Perspex.SceneGraph/Media/VisualBrush.cs

@ -0,0 +1,45 @@
// -----------------------------------------------------------------------
// <copyright file="VisualBrush.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// </summary>
public class VisualBrush : TileBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.
/// </summary>
public static readonly PerspexProperty<IVisual> VisualProperty =
PerspexProperty.Register<VisualBrush, IVisual>("Visual");
/// <summary>
/// Initializes a new instance of the <see cref="VisualBrush"/> class.
/// </summary>
public VisualBrush()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="VisualBrush"/> class.
/// </summary>
/// <param name="visual">The visual to draw.</param>
public VisualBrush(IVisual visual)
{
this.Visual = visual;
}
/// <summary>
/// Gets or sets the visual to draw.
/// </summary>
public IVisual Visual
{
get { return this.GetValue(VisualProperty); }
set { this.SetValue(VisualProperty, value); }
}
}
}

6
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -57,11 +57,14 @@
<Compile Include="Animation\IPageTransition.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Matrix.cs" />
<Compile Include="Media\AlignmentY.cs" />
<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" />
@ -91,6 +94,8 @@
<Compile Include="Media\SweepDirection.cs" />
<Compile Include="Media\TextHitTestResult.cs" />
<Compile Include="Media\Transform.cs" />
<Compile Include="Media\TileBrush.cs" />
<Compile Include="Media\VisualBrush.cs" />
<Compile Include="Origin.cs" />
<Compile Include="Platform\IFormattedTextImpl.cs" />
<Compile Include="Platform\IBitmapImpl.cs" />
@ -102,6 +107,7 @@
<Compile Include="Platform\IStreamGeometryImpl.cs" />
<Compile Include="Point.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RelativeRect.cs" />
<Compile Include="Rect.cs" />
<Compile Include="Rendering\IRenderRoot.cs" />
<Compile Include="Rendering\IRenderManager.cs" />

10
src/Perspex.SceneGraph/Point.cs

@ -59,6 +59,16 @@ namespace Perspex
return new Vector(p.x, p.y);
}
/// <summary>
/// Negates a point.
/// </summary>
/// <param name="a">The point.</param>
/// <returns>The negated point.</returns>
public static Point operator -(Point a)
{
return new Point(-a.x, -a.y);
}
/// <summary>
/// Checks for equality between two <see cref="Point"/>s.
/// </summary>

105
src/Perspex.SceneGraph/RelativeRect.cs

@ -0,0 +1,105 @@
// -----------------------------------------------------------------------
// <copyright file="RelativeRect.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
/// <summary>
/// Defines a rectangle that may be defined relative to another rectangle.
/// </summary>
public struct RelativeRect
{
/// <summary>
/// A rectangle that represents 100% of an area.
/// </summary>
public static readonly RelativeRect Fill = new RelativeRect(0, 0, 1, 1, OriginUnit.Percent);
/// <summary>
/// Initializes a new instance of the <see cref="RelativeRect"/> structure.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="unit">The unit of the rect.</param>
public RelativeRect(double x, double y, double width, double height, OriginUnit unit)
{
this.Rect = new Rect(x, y, width, height);
this.Unit = unit;
}
/// <summary>
/// Initializes a new instance of the <see cref="RelativeRect"/> structure.
/// </summary>
/// <param name="rect">The rectangle.</param>
/// <param name="unit">The unit of the rect.</param>
public RelativeRect(Rect rect, OriginUnit unit)
{
this.Rect = rect;
this.Unit = unit;
}
/// <summary>
/// Initializes a new instance of the <see cref="RelativeRect"/> structure.
/// </summary>
/// <param name="size">The size of the rectangle.</param>
/// <param name="unit">The unit of the rect.</param>
public RelativeRect(Size size, OriginUnit unit)
{
this.Rect = new Rect(size);
this.Unit = unit;
}
/// <summary>
/// Initializes a new instance of the <see cref="RelativeRect"/> structure.
/// </summary>
/// <param name="position">The position of the rectangle.</param>
/// <param name="size">The size of the rectangle.</param>
/// <param name="unit">The unit of the rect.</param>
public RelativeRect(Point position, Size size, OriginUnit unit)
{
this.Rect = new Rect(position, size);
this.Unit = unit;
}
/// <summary>
/// Initializes a new instance of the <see cref="RelativeRect"/> structure.
/// </summary>
/// <param name="topLeft">The top left position of the rectangle.</param>
/// <param name="bottomRight">The bottom right position of the rectangle.</param>
/// <param name="unit">The unit of the rect.</param>
public RelativeRect(Point topLeft, Point bottomRight, OriginUnit unit)
{
this.Rect = new Rect(topLeft, bottomRight);
this.Unit = unit;
}
/// <summary>
/// Gets the unit of the rectangle.
/// </summary>
public OriginUnit Unit { get; }
/// <summary>
/// Gets the rectangle.
/// </summary>
public Rect Rect { get; }
/// <summary>
/// Converts a <see cref="RelativeRect"/> into pixels.
/// </summary>
/// <param name="size">The size of the visual.</param>
/// <returns>The origin point in pixels.</returns>
public Rect ToPixels(Size size)
{
return this.Unit == OriginUnit.Pixels ?
this.Rect :
new Rect(
this.Rect.X * size.Width,
this.Rect.Y * size.Height,
this.Rect.Width * size.Width,
this.Rect.Height * size.Height);
}
}
}

15
src/Perspex.SceneGraph/Rendering/RendererBase.cs

@ -33,10 +33,23 @@ namespace Perspex.Rendering
/// <param name="visual">The visual to render.</param>
/// <param name="handle">An optional platform-specific handle.</param>
public virtual void Render(IVisual visual, IPlatformHandle handle)
{
this.Render(visual, handle, Matrix.Identity);
}
/// <summary>
/// Renders the specified visual with the specified transform.
/// </summary>
/// <param name="visual">The visual to render.</param>
/// <param name="handle">An optional platform-specific handle.</param>
/// <param name="transform">The transform.</param>
/// <param name="clip">An optional clip rectangle.</param>
public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix transform, Rect? clip = null)
{
using (var context = this.CreateDrawingContext(handle))
using (clip.HasValue ? context.PushClip(clip.Value) : null)
{
this.Render(visual, context, Matrix.Identity, Matrix.Identity);
this.Render(visual, context, Matrix.Identity, transform);
}
++this.RenderCount;

39
src/Windows/Perspex.Direct2D1/Disposable.cs

@ -0,0 +1,39 @@
// -----------------------------------------------------------------------
// <copyright file="Disposable.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Direct2D1
{
using System;
public class Disposable<T> : IDisposable where T : IDisposable
{
private IDisposable extra;
public Disposable(T inner)
{
this.Inner = inner;
}
public Disposable(T inner, IDisposable extra)
{
this.Inner = inner;
this.extra = extra;
}
public T Inner { get; }
public static implicit operator T(Disposable<T> i)
{
return i.Inner;
}
public void Dispose()
{
this.Inner.Dispose();
this.extra?.Dispose();
}
}
}

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

@ -143,11 +143,22 @@ namespace Perspex.Direct2D1.Media
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.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
if (cornerRadius == 0)
{
this.renderTarget.DrawRectangle(
rect.ToDirect2D(),
brush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
else
{
this.renderTarget.DrawRoundedRectangle(
new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
brush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
}
}
@ -181,18 +192,25 @@ namespace Perspex.Direct2D1.Media
{
using (var b = this.CreateBrush(brush, rect.Size))
{
this.renderTarget.FillRoundedRectangle(
new RoundedRectangle
{
Rect = new RectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Width,
(float)rect.Height),
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
b.PlatformBrush);
if (cornerRadius == 0)
{
this.renderTarget.FillRectangle(rect.ToDirect2D(), b.PlatformBrush);
}
else
{
this.renderTarget.FillRoundedRectangle(
new RoundedRectangle
{
Rect = new RectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Width,
(float)rect.Height),
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
b.PlatformBrush);
}
}
}
@ -260,10 +278,17 @@ namespace Perspex.Direct2D1.Media
});
}
/// <summary>
/// Creates a Direct2D brush wrapper for 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 wrapper.</returns>
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;
var solidColorBrush = brush as Perspex.Media.SolidColorBrush;
var linearGradientBrush = brush as Perspex.Media.LinearGradientBrush;
var visualBrush = brush as Perspex.Media.VisualBrush;
if (solidColorBrush != null)
{
@ -273,6 +298,10 @@ namespace Perspex.Direct2D1.Media
{
return new LinearGradientBrushImpl(linearGradientBrush, this.renderTarget, destinationSize);
}
else if (visualBrush != null)
{
return new VisualBrushImpl(visualBrush, this.renderTarget, destinationSize);
}
else
{
return new SolidColorBrushImpl(null, this.renderTarget);

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

@ -99,4 +99,4 @@ namespace Perspex.Direct2D1.Media
return false;
}
}
}
}

112
src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs

@ -0,0 +1,112 @@
// -----------------------------------------------------------------------
// <copyright file="VisualBrushImpl.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Direct2D1.Media
{
using System;
using Perspex.Layout;
using Perspex.Media;
using SharpDX.Direct2D1;
public class VisualBrushImpl : BrushImpl
{
public VisualBrushImpl(
Perspex.Media.VisualBrush brush,
SharpDX.Direct2D1.RenderTarget target,
Size targetSize)
{
var visual = brush.Visual;
var layoutable = visual as ILayoutable;
if (layoutable?.IsArrangeValid == false)
{
layoutable.Measure(Size.Infinity);
layoutable.Arrange(new Rect(layoutable.DesiredSize));
}
var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size);
var destinationRect = brush.DestinationRect.ToPixels(targetSize);
var bitmapSize = brush.TileMode == TileMode.None ? targetSize : destinationRect.Size;
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size);
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale);
var options = CompatibleRenderTargetOptions.None;
using (var brt = new BitmapRenderTarget(target, options, bitmapSize.ToSharpDX()))
{
var renderer = new Renderer(brt);
var transform = Matrix.CreateTranslation(-sourceRect.Position) *
Matrix.CreateScale(scale) *
Matrix.CreateTranslation(translate);
Rect drawRect;
if (brush.TileMode == TileMode.None)
{
drawRect = destinationRect;
transform *= Matrix.CreateTranslation(destinationRect.Position);
}
else
{
drawRect = new Rect(0, 0, destinationRect.Width, destinationRect.Height);
}
renderer.Render(visual, null, transform, drawRect);
var result = new BitmapBrush(brt, brt.Bitmap);
result.ExtendModeX = (brush.TileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
result.ExtendModeY = (brush.TileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
if (brush.TileMode != TileMode.None)
{
result.Transform = SharpDX.Matrix3x2.Translation(
(float)destinationRect.X,
(float)destinationRect.Y);
}
this.PlatformBrush = result;
}
}
private static Vector CalculateTranslate(
VisualBrush brush,
Rect sourceRect,
Rect destinationRect,
Vector scale)
{
var x = 0.0;
var y = 0.0;
var size = sourceRect.Size * scale;
switch (brush.AlignmentX)
{
case AlignmentX.Center:
x += (destinationRect.Width - size.Width) / 2;
break;
case AlignmentX.Right:
x += destinationRect.Width - size.Width;
break;
}
switch (brush.AlignmentY)
{
case AlignmentY.Center:
y += (destinationRect.Height - size.Height) / 2;
break;
case AlignmentY.Bottom:
y += destinationRect.Height - size.Height;
break;
}
return new Vector(x, y);
}
public override void Dispose()
{
((BitmapBrush)this.PlatformBrush).Bitmap.Dispose();
base.Dispose();
}
}
}

6
src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

@ -74,6 +74,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Direct2D1Platform.cs" />
<Compile Include="Disposable.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Media\BrushImpl.cs" />
<Compile Include="Media\BrushWrapper.cs" />
@ -82,6 +83,7 @@
<Compile Include="Media\Imaging\BitmapImpl.cs" />
<Compile Include="Media\LinearGradientBrushImpl.cs" />
<Compile Include="Media\PerspexTextRenderer.cs" />
<Compile Include="Media\VisualBrushImpl.cs" />
<Compile Include="Media\SolidColorBrushImpl.cs" />
<Compile Include="Media\StreamGeometryContextImpl.cs" />
<Compile Include="Media\GeometryImpl.cs" />
@ -104,6 +106,10 @@
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Perspex.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Layout\Perspex.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Perspex.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project>
<Name>Perspex.SceneGraph</Name>

21
src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs

@ -43,27 +43,6 @@ namespace Perspex.Direct2D1
else
return ExtendMode.Wrap;
}
/// <summary>
/// Converts a brush to Direct2D.
/// </summary>
/// <param name="brush">The brush to convert.</param>
/// <param name="target">The render target.</param>
/// <returns>The Direct2D brush.</returns>
public static SharpDX.Direct2D1.Brush ToDirect2D(this Perspex.Media.Brush brush, RenderTarget target)
{
Perspex.Media.SolidColorBrush solidColorBrush = brush as Perspex.Media.SolidColorBrush;
if (solidColorBrush != null)
{
return new SharpDX.Direct2D1.SolidColorBrush(target, solidColorBrush.Color.ToDirect2D());
}
else
{
// TODO: Implement other brushes.
return new SharpDX.Direct2D1.SolidColorBrush(target, new Color4());
}
}
/// <summary>
/// Converts a pen to a Direct2D stroke style.
/// </summary>

596
tests/Perspex.RenderTests/Media/VisualBrushTests.cs

@ -0,0 +1,596 @@
// -----------------------------------------------------------------------
// <copyright file="VisualBrushTests.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Direct2D1.RenderTests.Media
{
using Perspex.Controls;
using Perspex.Controls.Shapes;
using Perspex.Layout;
using Perspex.Media;
using Xunit;
public class VisualBrushTests : TestBase
{
public VisualBrushTests()
: base(@"Media\VisualBrush")
{
}
[Fact]
public void VisualBrush_Align_TopLeft()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
AlignmentX = AlignmentX.Left,
AlignmentY = AlignmentY.Top,
Stretch = Stretch.None,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Align_Center()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
AlignmentX = AlignmentX.Center,
AlignmentY = AlignmentY.Center,
Stretch = Stretch.None,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Align_BottomRight()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
AlignmentX = AlignmentX.Right,
AlignmentY = AlignmentY.Bottom,
Stretch = Stretch.None,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Stretch_Fill_Large()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 920,
Height = 920,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.Fill,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Stretch_Uniform()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 920,
Height = 820,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.Uniform,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Stretch_UniformToFill()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 920,
Height = 820,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.UniformToFill,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_SourceRect_Absolute()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
SourceRect = new RelativeRect(40, 40, 100, 100, OriginUnit.Pixels),
Visual = new Border
{
Width = 180,
Height = 180,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new Ellipse
{
Width = 100,
Height = 100,
Fill = Brushes.Yellow,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_DestinationRect_Absolute()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
DestinationRect = new RelativeRect(92, 92, 92, 92, OriginUnit.Pixels),
Visual = new Border
{
Width = 180,
Height = 180,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new Ellipse
{
Width = 100,
Height = 100,
Fill = Brushes.Yellow,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_SourceRect_DestinationRect_Absolute()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
SourceRect = new RelativeRect(40, 40, 100, 100, OriginUnit.Pixels),
DestinationRect = new RelativeRect(92, 92, 92, 92, OriginUnit.Pixels),
Visual = new Border
{
Width = 180,
Height = 180,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new Ellipse
{
Width = 100,
Height = 100,
Fill = Brushes.Yellow,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_SourceRect_DestinationRect_Percent()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
SourceRect = new RelativeRect(0.22, 0.22, 0.56, 0.56, OriginUnit.Percent),
DestinationRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, OriginUnit.Percent),
Visual = new Border
{
Width = 180,
Height = 180,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new Ellipse
{
Width = 100,
Height = 100,
Fill = Brushes.Yellow,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Tile()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.None,
TileMode = TileMode.Tile,
DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, OriginUnit.Percent),
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_Tile_Alignment_BottomRight()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.None,
TileMode = TileMode.Tile,
AlignmentX = AlignmentX.Right,
AlignmentY = AlignmentY.Bottom,
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_FlipX()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.None,
TileMode = TileMode.FlipX,
DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent),
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_FlipY()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.None,
TileMode = TileMode.FlipY,
DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent),
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
[Fact]
public void VisualBrush_FlipXY()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Rectangle
{
Fill = new VisualBrush
{
Stretch = Stretch.None,
TileMode = TileMode.FlipXY,
DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent),
Visual = new Border
{
Width = 92,
Height = 92,
Background = Brushes.Red,
BorderBrush = Brushes.Black,
BorderThickness = 2,
Child = new TextBlock
{
Text = "Perspex",
FontSize = 12,
FontFamily = "Arial",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
}
}
}
}
};
this.RenderToFile(target);
this.CompareImages();
}
}
}

1
tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj

@ -77,6 +77,7 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="Media\VisualBrushTests.cs" />
<Compile Include="Controls\ImageTests.cs" />
<Compile Include="Controls\BorderTests.cs" />
<Compile Include="GlobalSuppressions.cs" />

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_BottomRight.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_Center.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_TopLeft.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_DestinationRect_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipX.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipXY.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipY.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Absolute.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Percent.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Fill_Large.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Uniform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_UniformToFill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_Alignment_BottomRight.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Loading…
Cancel
Save