Browse Source

Started adding render transforms.

The maths isn't quite right on child controls yet...
pull/4/head
Steven Kirk 12 years ago
parent
commit
3a9c5308e2
  1. 17
      Perspex.Direct2D1/Media/DrawingContext.cs
  2. 25
      Perspex.Direct2D1/Renderer.cs
  3. 4
      Perspex/IVisual.cs
  4. 2
      Perspex/Media/IDrawingContext.cs
  5. 51
      Perspex/Media/Matrix.cs
  6. 34
      Perspex/Media/RotateTransform.cs
  7. 13
      Perspex/Media/Transform.cs
  8. 54
      Perspex/Origin.cs
  9. 3
      Perspex/Perspex.csproj
  10. 5
      Perspex/Point.cs
  11. 8
      Perspex/Themes/Default/TreeViewItemStyle.cs
  12. 5
      Perspex/Vector.cs
  13. 18
      Perspex/Visual.cs

17
Perspex.Direct2D1/Media/DrawingContext.cs

@ -43,6 +43,12 @@ namespace Perspex.Direct2D1.Media
this.renderTarget.BeginDraw();
}
public Matrix CurrentTransform
{
get { return Convert(this.renderTarget.Transform); }
set { this.renderTarget.Transform = Convert(value); }
}
/// <summary>
/// Ends a draw operation.
/// </summary>
@ -235,6 +241,17 @@ namespace Perspex.Direct2D1.Media
(float)matrix.OffsetY);
}
private Matrix Convert(Matrix3x2 matrix)
{
return new Matrix(
matrix.M11,
matrix.M12,
matrix.M21,
matrix.M22,
matrix.M31,
matrix.M32);
}
/// <summary>
/// Converts a <see cref="Rect"/> to a <see cref="RectangleF"/>
/// </summary>

25
Perspex.Direct2D1/Renderer.cs

@ -9,12 +9,14 @@ namespace Perspex.Direct2D1
using System;
using System.Linq;
using Perspex.Direct2D1.Media;
using Perspex.Media;
using Perspex.Platform;
using SharpDX;
using SharpDX.Direct2D1;
using Splat;
using DwFactory = SharpDX.DirectWrite.Factory;
using Matrix = Perspex.Media.Matrix;
using Point = Perspex.Point;
/// <summary>
/// Renders a <see cref="Canvas"/>.
@ -122,29 +124,30 @@ namespace Perspex.Direct2D1
{
if (visual.IsVisible && visual.Opacity > 0)
{
if (visual.Opacity < 1)
Matrix transform = Matrix.Identity;
if (visual.RenderTransform != null)
{
Layer layer = new Layer(this.renderTarget);
LayerParameters p = new LayerParameters();
p.Opacity = (float)visual.Opacity;
this.renderTarget.PushLayer(ref p, layer);
Matrix current = context.CurrentTransform;
Matrix offset = Matrix.Translation(visual.TransformOrigin.ToPixels(visual.Bounds.Size));
transform = -current * -offset * visual.RenderTransform.Value * offset * current;
}
visual.Render(context);
transform *= Matrix.Translation(visual.Bounds.Position);
foreach (IVisual child in visual.VisualChildren)
using (context.PushTransform(transform))
{
Matrix translate = Matrix.Translation(child.Bounds.X, child.Bounds.Y);
visual.Render(context);
using (context.PushTransform(translate))
foreach (var child in visual.VisualChildren)
{
this.Render(child, context);
}
}
if (visual.Opacity < 1)
if (visual.RenderTransform != null)
{
this.renderTarget.PopLayer();
((RotateTransform)visual.RenderTransform).Angle++;
}
}
}

4
Perspex/IVisual.cs

@ -20,6 +20,10 @@ namespace Perspex
double Opacity { get; }
Transform RenderTransform { get; }
Origin TransformOrigin { get; }
IEnumerable<IVisual> VisualChildren { get; }
IVisual VisualParent { get; set; }

2
Perspex/Media/IDrawingContext.cs

@ -15,6 +15,8 @@ namespace Perspex.Media
/// </summary>
public interface IDrawingContext : IDisposable
{
Matrix CurrentTransform { get; }
void DrawImage(Bitmap source, double opacity, Rect sourceRect, Rect destRect);
/// <summary>

51
Perspex/Media/Matrix.cs

@ -83,16 +83,49 @@ namespace Perspex.Media
get { return this.offsetY; }
}
public static Matrix operator *(Matrix left, Matrix right)
{
return new Matrix(
(left.M11 * right.M11) + (left.M12 * right.M21),
(left.M11 * right.M12) + (left.M12 * right.M22),
(left.M21 * right.M11) + (left.M22 * right.M21),
(left.M21 * right.M12) + (left.M22 * right.M22),
(left.offsetX * right.M11) + (left.offsetY * right.M21) + right.offsetX,
(left.offsetX * right.M12) + (left.offsetY * right.M22) + right.offsetY);
}
public static bool Equals(Matrix matrix1, Matrix matrix2)
{
return matrix1.Equals(matrix2);
}
public static Matrix Rotation(double angle)
{
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
return new Matrix(cos, sin, -sin, cos, 0, 0);
}
public static Matrix Translation(Vector v)
{
return Translation(v.X, v.Y);
}
public static Matrix Translation(double x, double y)
{
return new Matrix(1.0, 0.0, 0.0, 1.0, x, y);
}
public static double ToRadians(double angle)
{
return angle * 0.0174532925;
}
public static Matrix operator -(Matrix matrix)
{
return matrix.Invert();
}
public static bool operator ==(Matrix matrix1, Matrix matrix2)
{
return matrix1.Equals(matrix2);
@ -127,5 +160,23 @@ namespace Perspex.Media
{
throw new NotImplementedException();
}
public Matrix Invert()
{
if (!this.HasInverse)
{
throw new InvalidOperationException("Transform is not invertible.");
}
double d = this.Determinant;
return new Matrix(
this.m22 / d,
-this.m12 / d,
-this.m21 / d,
this.m11 / d,
((this.m21 * this.offsetY) - (this.m22 * this.offsetX)) / d,
((this.m12 * this.offsetX) - (this.m11 * this.offsetY)) / d);
}
}
}

34
Perspex/Media/RotateTransform.cs

@ -0,0 +1,34 @@
// -----------------------------------------------------------------------
// <copyright file="RotateTransform.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
public class RotateTransform : Transform
{
public static readonly PerspexProperty<double> AngleProperty =
PerspexProperty.Register<RotateTransform, double>("Angle");
public RotateTransform()
{
}
public RotateTransform(double angle)
{
this.Angle = angle;
}
public double Angle
{
get { return this.GetValue(AngleProperty); }
set { this.SetValue(AngleProperty, value); }
}
public override Matrix Value
{
get { return Matrix.Rotation(Matrix.ToRadians(this.Angle)); }
}
}
}

13
Perspex/Media/Transform.cs

@ -0,0 +1,13 @@
// -----------------------------------------------------------------------
// <copyright file="Transform.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
public abstract class Transform : PerspexObject
{
public abstract Matrix Value { get; }
}
}

54
Perspex/Origin.cs

@ -0,0 +1,54 @@
// -----------------------------------------------------------------------
// <copyright file="Origin.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Globalization;
public enum OriginUnit
{
Percent,
Pixels,
}
public struct Origin
{
public static readonly Origin Default = new Origin(0.5, 0.5, OriginUnit.Percent);
private Point point;
private OriginUnit unit;
public Origin(double x, double y, OriginUnit unit)
: this(new Point(x, y), unit)
{
}
public Origin(Point point, OriginUnit unit)
{
this.point = point;
this.unit = unit;
}
public Point Point
{
get { return this.point; }
}
public OriginUnit Unit
{
get { return this.unit; }
}
public Point ToPixels(Size size)
{
return this.unit == OriginUnit.Pixels ?
point :
new Point(point.X * size.Width, point.Y * size.Height);
}
}
}

3
Perspex/Perspex.csproj

@ -128,6 +128,8 @@
<Compile Include="Media\Imaging\Bitmap.cs" />
<Compile Include="Media\Imaging\IBitmap.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmap.cs" />
<Compile Include="Media\RotateTransform.cs" />
<Compile Include="Media\Transform.cs" />
<Compile Include="Platform\IPlatformThreadingInterface.cs" />
<Compile Include="Platform\IRenderTargetBitmapImpl.cs" />
<Compile Include="Platform\IBitmapImpl.cs" />
@ -141,6 +143,7 @@
<Compile Include="Media\StreamGeometry.cs" />
<Compile Include="Media\Geometry.cs" />
<Compile Include="Media\Stretch.cs" />
<Compile Include="Origin.cs" />
<Compile Include="Themes\Default\ContentControlStyle.cs" />
<Compile Include="Themes\Default\TreeViewItemStyle.cs" />
<Compile Include="Themes\Default\TreeViewStyle.cs" />

5
Perspex/Point.cs

@ -60,6 +60,11 @@ namespace Perspex
return new Point(a.x - b.x, a.y - b.y);
}
public static implicit operator Vector(Point p)
{
return new Vector(p.x, p.y);
}
/// <summary>
/// Returns the string representation of the point.
/// </summary>

8
Perspex/Themes/Default/TreeViewItemStyle.cs

@ -48,6 +48,13 @@ namespace Perspex.Themes.Default
new Setter(ToggleButton.TemplateProperty, ControlTemplate.Create<ToggleButton>(this.ToggleButtonTemplate)),
},
},
new Style(x => x.OfType<TreeViewItem>().Template().OfType<ToggleButton>().Class("expander").Class(":checked"))
{
Setters = new[]
{
new Setter(ToggleButton.RenderTransformProperty, new RotateTransform(1)),
},
},
new Style(x => x.OfType<TreeViewItem>().Class(":empty").Template().OfType<ToggleButton>().Class("expander"))
{
Setters = new[]
@ -105,6 +112,7 @@ namespace Perspex.Themes.Default
{
return new Border
{
Background = Brushes.Chartreuse,
Content = new Path
{
Fill = Brushes.Black,

5
Perspex/Vector.cs

@ -50,6 +50,11 @@ namespace Perspex
get { return this.y; }
}
public static Vector operator -(Vector a)
{
return new Vector(-a.x, -a.y);
}
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.x + b.x, a.y + b.y);

18
Perspex/Visual.cs

@ -22,6 +22,12 @@ namespace Perspex
public static readonly PerspexProperty<double> OpacityProperty =
PerspexProperty.Register<Visual, double>("Opacity", 1);
public static readonly PerspexProperty<Transform> RenderTransformProperty =
PerspexProperty.Register<Visual, Transform>("RenderTransform");
public static readonly PerspexProperty<Origin> TransformOriginProperty =
PerspexProperty.Register<Visual, Origin>("TransformOrigin", defaultValue: Origin.Default);
private IVisual visualParent;
private Rect bounds;
@ -43,6 +49,18 @@ namespace Perspex
set { this.SetValue(OpacityProperty, value); }
}
public Transform RenderTransform
{
get { return this.GetValue(RenderTransformProperty); }
set { this.SetValue(RenderTransformProperty, value); }
}
public Origin TransformOrigin
{
get { return this.GetValue(TransformOriginProperty); }
set { this.SetValue(TransformOriginProperty, value); }
}
Rect IVisual.Bounds
{
get { return this.bounds; }

Loading…
Cancel
Save