31 changed files with 1729 additions and 434 deletions
@ -0,0 +1,32 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using OmniXaml.TypeConversion; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.Converters |
||||
|
{ |
||||
|
public class RelativeRectTypeConverter : ITypeConverter |
||||
|
{ |
||||
|
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType) |
||||
|
{ |
||||
|
return sourceType == typeof(string); |
||||
|
} |
||||
|
|
||||
|
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value) |
||||
|
{ |
||||
|
return RelativeRect.Parse((string)value, culture); |
||||
|
} |
||||
|
|
||||
|
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,13 @@ |
|||||
|
using Perspex.Platform; |
||||
|
|
||||
|
namespace Perspex.RenderHelpers |
||||
|
{ |
||||
|
static class QuadBezierHelper |
||||
|
{ |
||||
|
public static void QuadTo(IStreamGeometryContextImpl context, Point current, Point controlPoint, Point endPoint) |
||||
|
{ |
||||
|
//(s, (s + 2c)/ 3, (e + 2c)/ 3, e)
|
||||
|
context.BezierTo((current + 2*controlPoint)/3, (endPoint + 2*controlPoint)/3, endPoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
|
<HasSharedItems>true</HasSharedItems> |
||||
|
<SharedGUID>3c4c0cb4-0c0f-4450-a37b-148c84ff905f</SharedGUID> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Label="Configuration"> |
||||
|
<Import_RootNamespace>Perspex.RenderHelpers</Import_RootNamespace> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="$(MSBuildThisFileDirectory)ArcToHelper.cs" /> |
||||
|
<Compile Include="$(MSBuildThisFileDirectory)QuadBezierHelper.cs" /> |
||||
|
<Compile Include="$(MSBuildThisFileDirectory)TileBrushImplHelper.cs" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>3c4c0cb4-0c0f-4450-a37b-148c84ff905f</ProjectGuid> |
||||
|
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> |
||||
|
<PropertyGroup /> |
||||
|
<Import Project="RenderHelpers.projitems" Label="Shared" /> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> |
||||
|
</Project> |
||||
@ -0,0 +1,174 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using Perspex; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Rendering; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
|
||||
|
namespace Perspex.RenderHelpers |
||||
|
{ |
||||
|
class TileBrushImplHelper |
||||
|
{ |
||||
|
public Size IntermediateSize { get; } |
||||
|
public Rect DestinationRect { get; } |
||||
|
private readonly TileMode _tileMode; |
||||
|
private readonly Rect _sourceRect; |
||||
|
private readonly Vector _scale; |
||||
|
private readonly Vector _translate; |
||||
|
private readonly Size _imageSize; |
||||
|
private readonly VisualBrush _visualBrush; |
||||
|
private readonly ImageBrush _imageBrush; |
||||
|
|
||||
|
public bool IsValid { get; } |
||||
|
|
||||
|
|
||||
|
public TileBrushImplHelper(TileBrush brush, Size targetSize) |
||||
|
{ |
||||
|
_imageBrush = brush as ImageBrush; |
||||
|
_visualBrush = brush as VisualBrush; |
||||
|
if (_imageBrush != null) |
||||
|
{ |
||||
|
if (_imageBrush.Source == null) |
||||
|
return; |
||||
|
_imageSize = new Size(_imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight); |
||||
|
IsValid = true; |
||||
|
} |
||||
|
else if (_visualBrush != null) |
||||
|
{ |
||||
|
var visual = _visualBrush.Visual; |
||||
|
if (visual == null) |
||||
|
return; |
||||
|
var layoutable = visual as ILayoutable; |
||||
|
|
||||
|
if (layoutable?.IsArrangeValid == false) |
||||
|
{ |
||||
|
layoutable.Measure(Size.Infinity); |
||||
|
layoutable.Arrange(new Rect(layoutable.DesiredSize)); |
||||
|
} |
||||
|
//I have no idea why are we using layoutable after `as` cast, but it was in original VisualBrush code by @grokys
|
||||
|
_imageSize = layoutable.Bounds.Size; |
||||
|
IsValid = true; |
||||
|
} |
||||
|
else |
||||
|
return; |
||||
|
|
||||
|
_tileMode = brush.TileMode; |
||||
|
_sourceRect = brush.SourceRect.ToPixels(_imageSize); |
||||
|
DestinationRect = brush.DestinationRect.ToPixels(targetSize); |
||||
|
_scale = brush.Stretch.CalculateScaling(DestinationRect.Size, _sourceRect.Size); |
||||
|
_translate = CalculateTranslate(brush, _sourceRect, DestinationRect, _scale); |
||||
|
IntermediateSize = CalculateIntermediateSize(_tileMode, targetSize, DestinationRect.Size); |
||||
|
} |
||||
|
|
||||
|
public void DrawIntermediate(DrawingContext ctx) |
||||
|
{ |
||||
|
Rect drawRect; |
||||
|
var transform = CalculateIntermediateTransform( |
||||
|
_tileMode, |
||||
|
_sourceRect, |
||||
|
DestinationRect, |
||||
|
_scale, |
||||
|
_translate, |
||||
|
out drawRect); |
||||
|
using (ctx.PushClip(drawRect)) |
||||
|
using (ctx.PushPostTransform(transform)) |
||||
|
{ |
||||
|
if (_imageBrush != null) |
||||
|
{ |
||||
|
var bmpRc = new Rect(0, 0, _imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight); |
||||
|
ctx.DrawImage(_imageBrush.Source, 1, bmpRc, bmpRc); |
||||
|
} |
||||
|
else if (_visualBrush != null) |
||||
|
{ |
||||
|
ctx.FillRectangle(Brushes.Black, new Rect(new Point(0, 0), IntermediateSize)); |
||||
|
ctx.Render(_visualBrush.Visual); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates a _translate based on a <see cref="TileBrush"/>, a source and destination
|
||||
|
/// rectangle and a _scale.
|
||||
|
/// </summary>
|
||||
|
/// <param name="brush">The brush.</param>
|
||||
|
/// <param name="sourceRect">The source rectangle.</param>
|
||||
|
/// <param name="destinationRect">The destination rectangle.</param>
|
||||
|
/// <param name="scale">The _scale factor.</param>
|
||||
|
/// <returns>A vector with the X and Y _translate.</returns>
|
||||
|
|
||||
|
public static Vector CalculateTranslate( |
||||
|
TileBrush 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 static Matrix CalculateIntermediateTransform( |
||||
|
TileMode tileMode, |
||||
|
Rect sourceRect, |
||||
|
Rect destinationRect, |
||||
|
Vector scale, |
||||
|
Vector translate, |
||||
|
out Rect drawRect) |
||||
|
{ |
||||
|
var transform = Matrix.CreateTranslation(-sourceRect.Position)* |
||||
|
Matrix.CreateScale(scale)* |
||||
|
Matrix.CreateTranslation(translate); |
||||
|
Rect dr; |
||||
|
|
||||
|
if (tileMode == TileMode.None) |
||||
|
{ |
||||
|
dr = destinationRect; |
||||
|
transform *= Matrix.CreateTranslation(destinationRect.Position); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
dr = new Rect(destinationRect.Size); |
||||
|
} |
||||
|
|
||||
|
drawRect = dr; |
||||
|
|
||||
|
return transform; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
static Size CalculateIntermediateSize( |
||||
|
TileMode tileMode, |
||||
|
Size targetSize, |
||||
|
Size destinationSize) => tileMode == TileMode.None ? targetSize : destinationSize; |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -1,94 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using Perspex.Media; |
|
||||
using SharpDX.Direct2D1; |
|
||||
|
|
||||
namespace Perspex.Direct2D1.Media |
|
||||
{ |
|
||||
public class ImageBrushImpl : TileBrushImpl |
|
||||
{ |
|
||||
public ImageBrushImpl( |
|
||||
ImageBrush brush, |
|
||||
SharpDX.Direct2D1.RenderTarget target, |
|
||||
Size targetSize) |
|
||||
{ |
|
||||
if (brush.Source == null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var image = ((BitmapImpl)brush.Source.PlatformImpl).GetDirect2DBitmap(target); |
|
||||
var imageSize = new Size(brush.Source.PixelWidth, brush.Source.PixelHeight); |
|
||||
var tileMode = brush.TileMode; |
|
||||
var sourceRect = brush.SourceRect.ToPixels(imageSize); |
|
||||
var destinationRect = brush.DestinationRect.ToPixels(targetSize); |
|
||||
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); |
|
||||
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); |
|
||||
var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); |
|
||||
var brtOpts = CompatibleRenderTargetOptions.None; |
|
||||
|
|
||||
// TODO: There are times where we don't need to draw an intermediate bitmap. Identify
|
|
||||
// them and directly use 'image' in those cases.
|
|
||||
using (var intermediate = new BitmapRenderTarget(target, brtOpts, intermediateSize)) |
|
||||
{ |
|
||||
Rect drawRect; |
|
||||
var transform = CalculateIntermediateTransform( |
|
||||
tileMode, |
|
||||
sourceRect, |
|
||||
destinationRect, |
|
||||
scale, |
|
||||
translate, |
|
||||
out drawRect); |
|
||||
|
|
||||
intermediate.BeginDraw(); |
|
||||
intermediate.PushAxisAlignedClip(drawRect.ToDirect2D(), AntialiasMode.Aliased); |
|
||||
intermediate.Transform = transform.ToDirect2D(); |
|
||||
intermediate.DrawBitmap(image, 1, BitmapInterpolationMode.Linear); |
|
||||
intermediate.PopAxisAlignedClip(); |
|
||||
intermediate.EndDraw(); |
|
||||
|
|
||||
this.PlatformBrush = new BitmapBrush( |
|
||||
target, |
|
||||
intermediate.Bitmap, |
|
||||
GetBitmapBrushProperties(brush), |
|
||||
GetBrushProperties(brush, destinationRect)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private BitmapBrush CreateDirectBrush( |
|
||||
ImageBrush brush, |
|
||||
SharpDX.Direct2D1.RenderTarget target, |
|
||||
Bitmap image, |
|
||||
Rect sourceRect, |
|
||||
Rect destinationRect) |
|
||||
{ |
|
||||
var tileMode = brush.TileMode; |
|
||||
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); |
|
||||
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); |
|
||||
var transform = Matrix.CreateTranslation(-sourceRect.Position) * |
|
||||
Matrix.CreateScale(scale) * |
|
||||
Matrix.CreateTranslation(translate); |
|
||||
|
|
||||
var opts = new BrushProperties |
|
||||
{ |
|
||||
Transform = transform.ToDirect2D(), |
|
||||
Opacity = (float)brush.Opacity, |
|
||||
}; |
|
||||
|
|
||||
var bitmapOpts = new BitmapBrushProperties |
|
||||
{ |
|
||||
ExtendModeX = GetExtendModeX(tileMode), |
|
||||
ExtendModeY = GetExtendModeY(tileMode), |
|
||||
}; |
|
||||
|
|
||||
return new BitmapBrush(target, image, bitmapOpts, opts); |
|
||||
} |
|
||||
|
|
||||
private BitmapBrush CreateIndirectBrush() |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,73 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using Perspex.Layout; |
|
||||
using Perspex.Media; |
|
||||
using Perspex.Rendering; |
|
||||
using SharpDX; |
|
||||
using SharpDX.Direct2D1; |
|
||||
|
|
||||
namespace Perspex.Direct2D1.Media |
|
||||
{ |
|
||||
public class VisualBrushImpl : TileBrushImpl |
|
||||
{ |
|
||||
public VisualBrushImpl( |
|
||||
VisualBrush brush, |
|
||||
SharpDX.Direct2D1.RenderTarget target, |
|
||||
Size targetSize) |
|
||||
{ |
|
||||
var visual = brush.Visual; |
|
||||
|
|
||||
if (visual == null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var layoutable = visual as ILayoutable; |
|
||||
|
|
||||
if (layoutable?.IsArrangeValid == false) |
|
||||
{ |
|
||||
layoutable.Measure(Size.Infinity); |
|
||||
layoutable.Arrange(new Rect(layoutable.DesiredSize)); |
|
||||
} |
|
||||
|
|
||||
var tileMode = brush.TileMode; |
|
||||
var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); |
|
||||
var destinationRect = brush.DestinationRect.ToPixels(targetSize); |
|
||||
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); |
|
||||
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); |
|
||||
var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); |
|
||||
var brtOpts = CompatibleRenderTargetOptions.None; |
|
||||
|
|
||||
// TODO: There are times where we don't need to draw an intermediate bitmap. Identify
|
|
||||
// them and directly use 'image' in those cases.
|
|
||||
using (var intermediate = new BitmapRenderTarget(target, brtOpts, intermediateSize)) |
|
||||
{ |
|
||||
Rect drawRect; |
|
||||
var transform = CalculateIntermediateTransform( |
|
||||
tileMode, |
|
||||
sourceRect, |
|
||||
destinationRect, |
|
||||
scale, |
|
||||
translate, |
|
||||
out drawRect); |
|
||||
var renderer = new RenderTarget(intermediate); |
|
||||
|
|
||||
using (var ctx = renderer.CreateDrawingContext()) |
|
||||
using (ctx.PushClip(drawRect)) |
|
||||
using (ctx.PushPostTransform(transform)) |
|
||||
{ |
|
||||
intermediate.Clear(new Color4(0)); |
|
||||
ctx.Render(visual); |
|
||||
} |
|
||||
|
|
||||
this.PlatformBrush = new BitmapBrush( |
|
||||
target, |
|
||||
intermediate.Bitmap, |
|
||||
GetBitmapBrushProperties(brush), |
|
||||
GetBrushProperties(brush, destinationRect)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.SceneGraph.UnitTests |
||||
|
{ |
||||
|
public class RelativeRectComparer : IEqualityComparer<RelativeRect> |
||||
|
{ |
||||
|
public bool Equals(RelativeRect a, RelativeRect b) |
||||
|
{ |
||||
|
return a.Unit == b.Unit && |
||||
|
Math.Round(a.Rect.X, 3) == Math.Round(b.Rect.X, 3) && |
||||
|
Math.Round(a.Rect.Y, 3) == Math.Round(b.Rect.Y, 3) && |
||||
|
Math.Round(a.Rect.Width, 3) == Math.Round(b.Rect.Width, 3) && |
||||
|
Math.Round(a.Rect.Height, 3) == Math.Round(b.Rect.Height, 3); |
||||
|
} |
||||
|
|
||||
|
public int GetHashCode(RelativeRect obj) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Globalization; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.SceneGraph.UnitTests |
||||
|
{ |
||||
|
public class RelativeRectTests |
||||
|
{ |
||||
|
private static readonly RelativeRectComparer Compare = new RelativeRectComparer(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Parse_Should_Accept_Absolute_Value() |
||||
|
{ |
||||
|
var result = RelativeRect.Parse("4,5,50,60", CultureInfo.InvariantCulture); |
||||
|
|
||||
|
Assert.Equal(new RelativeRect(4, 5, 50, 60, RelativeUnit.Absolute), result, Compare); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Parse_Should_Accept_Relative_Value() |
||||
|
{ |
||||
|
var result = RelativeRect.Parse("10%, 20%, 40%, 70%", CultureInfo.InvariantCulture); |
||||
|
|
||||
|
Assert.Equal(new RelativeRect(0.1, 0.2, 0.4, 0.7, RelativeUnit.Relative), result, Compare); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue