Browse Source

Added Image control.

pull/4/head
Steven Kirk 12 years ago
parent
commit
d034952c6a
  1. 107
      Perspex.Direct2D1.RenderTests/Controls/ImageTests.cs
  2. 1
      Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj
  3. 2
      Perspex.Direct2D1.RenderTests/TestBase.cs
  4. 5
      Perspex.Direct2D1/Direct2D1Platform.cs
  5. 12
      Perspex.Direct2D1/Media/DrawingContext.cs
  6. 36
      Perspex.Direct2D1/Media/Imaging/BitmapImpl.cs
  7. 5
      Perspex.Direct2D1/PrimitiveExtensions.cs
  8. 2
      Perspex/Controls/Control.cs
  9. 115
      Perspex/Controls/Image.cs
  10. 4
      Perspex/Media/IDrawingContext.cs
  11. 20
      Perspex/Media/Imaging/Bitmap.cs
  12. 17
      Perspex/Media/Imaging/IBitmap.cs
  13. 2
      Perspex/Media/Imaging/RenderTargetBitmap.cs
  14. 3
      Perspex/Perspex.csproj
  15. 4
      Perspex/Platform/IBitmapImpl.cs
  16. 2
      Perspex/Platform/IPlatformRenderInterface.cs
  17. 56
      Perspex/Rect.cs
  18. 22
      Perspex/Size.cs
  19. 14
      Perspex/Thickness.cs
  20. 72
      Perspex/Vector.cs
  21. 6
      TestApplication/Program.cs
  22. 5
      TestApplication/TestApplication.csproj
  23. BIN
      TestApplication/github_icon.png
  24. BIN
      TestFiles/Direct2D1/Controls/Image/Image_Stretch_Fill.expected.png
  25. BIN
      TestFiles/Direct2D1/Controls/Image/Image_Stretch_None.expected.png
  26. BIN
      TestFiles/Direct2D1/Controls/Image/Image_Stretch_Uniform.expected.png
  27. BIN
      TestFiles/Direct2D1/Controls/Image/Image_Stretch_UniformToFill.expected.png
  28. BIN
      TestFiles/Direct2D1/Controls/Image/test.png

107
Perspex.Direct2D1.RenderTests/Controls/ImageTests.cs

@ -0,0 +1,107 @@
// -----------------------------------------------------------------------
// <copyright file="ImageTests.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Direct2D1.RenderTests.Controls
{
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Perspex.Controls;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Media.Imaging;
[TestClass]
public class ImageTests : TestBase
{
private Bitmap bitmap;
public ImageTests()
: base(@"Controls\Image")
{
this.bitmap = new Bitmap(Path.Combine(this.OutputPath, "test.png"));
}
[TestMethod]
public void Image_Stretch_None()
{
Decorator target = new Decorator
{
Padding = new Thickness(20, 8),
Width = 200,
Height = 200,
Content = new Image
{
Background = Brushes.Red,
Source = this.bitmap,
Stretch = Stretch.None,
}
};
this.RenderToFile(target);
this.CompareImages();
}
[TestMethod]
public void Image_Stretch_Fill()
{
Decorator target = new Decorator
{
Padding = new Thickness(20, 8),
Width = 200,
Height = 200,
Content = new Image
{
Background = Brushes.Red,
Source = this.bitmap,
Stretch = Stretch.Fill,
}
};
this.RenderToFile(target);
this.CompareImages();
}
[TestMethod]
public void Image_Stretch_Uniform()
{
Decorator target = new Decorator
{
Padding = new Thickness(20, 8),
Width = 200,
Height = 200,
Content = new Image
{
Background = Brushes.Red,
Source = this.bitmap,
Stretch = Stretch.Uniform,
}
};
this.RenderToFile(target);
this.CompareImages();
}
[TestMethod]
public void Image_Stretch_UniformToFill()
{
Decorator target = new Decorator
{
Padding = new Thickness(20, 8),
Width = 200,
Height = 200,
Content = new Image
{
Background = Brushes.Red,
Source = this.bitmap,
Stretch = Stretch.UniformToFill,
}
};
this.RenderToFile(target);
this.CompareImages();
}
}
}

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

@ -58,6 +58,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Controls\ImageTests.cs" />
<Compile Include="Controls\BorderTests.cs" />
<Compile Include="Shapes\RectangleTests.cs" />
<Compile Include="TestBase.cs" />

2
Perspex.Direct2D1.RenderTests/TestBase.cs

@ -11,7 +11,7 @@ namespace Perspex.Direct2D1.RenderTests
using ImageMagick;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Perspex.Controls;
using Perspex.Media;
using Perspex.Media.Imaging;
public class TestBase
{

5
Perspex.Direct2D1/Direct2D1Platform.cs

@ -57,5 +57,10 @@ namespace Perspex.Direct2D1
{
return new StreamGeometryImpl();
}
public IBitmapImpl LoadBitmap(string fileName)
{
return new BitmapImpl(imagingFactory, fileName);
}
}
}

12
Perspex.Direct2D1/Media/DrawingContext.cs

@ -51,6 +51,18 @@ namespace Perspex.Direct2D1.Media
this.renderTarget.EndDraw();
}
public void DrawImage(Perspex.Media.Imaging.Bitmap bitmap, double opacity, Rect sourceRect, Rect destRect)
{
BitmapImpl impl = (BitmapImpl)bitmap.PlatformImpl;
Bitmap d2d = impl.GetDirect2DBitmap(this.renderTarget);
this.renderTarget.DrawBitmap(
d2d,
destRect.ToSharpDX(),
(float)opacity,
BitmapInterpolationMode.Linear,
sourceRect.ToSharpDX());
}
/// <summary>
/// Draws a line.
/// </summary>

36
Perspex.Direct2D1/Media/Imaging/BitmapImpl.cs

@ -15,23 +15,57 @@ namespace Perspex.Direct2D1.Media
{
private ImagingFactory factory;
private SharpDX.Direct2D1.Bitmap direct2D;
public BitmapImpl(ImagingFactory factory, string fileName)
{
this.factory = factory;
using (BitmapDecoder decoder = new BitmapDecoder(factory, fileName, DecodeOptions.CacheOnDemand))
{
this.WicImpl = new Bitmap(factory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand);
}
}
public BitmapImpl(ImagingFactory factory, int width, int height)
{
this.factory = factory;
this.WicImpl = new Bitmap(
factory,
factory,
width,
height,
PixelFormat.Format32bppPBGRA,
BitmapCreateCacheOption.CacheOnLoad);
}
public int PixelHeight
{
get { return this.WicImpl.Size.Width; }
}
public int PixelWidth
{
get { return this.WicImpl.Size.Height; }
}
public Bitmap WicImpl
{
get;
private set;
}
public SharpDX.Direct2D1.Bitmap GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget renderTarget)
{
if (this.direct2D == null)
{
FormatConverter converter = new FormatConverter(this.factory);
converter.Initialize(this.WicImpl, PixelFormat.Format32bppPBGRA);
this.direct2D = SharpDX.Direct2D1.Bitmap.FromWicBitmap(renderTarget, converter);
}
return this.direct2D;
}
public void Save(string fileName)
{
if (Path.GetExtension(fileName) != ".png")

5
Perspex.Direct2D1/PrimitiveExtensions.cs

@ -15,6 +15,11 @@ namespace Perspex.Direct2D1
return new Rect(r.X, r.Y, r.Width, r.Height);
}
public static RectangleF ToSharpDX(this Rect r)
{
return new RectangleF((float)r.X, (float)r.Y, (float)r.Width, (float)r.Height);
}
public static Vector2 ToSharpDX(this Perspex.Point p)
{
return new Vector2((float)p.X, (float)p.Y);

2
Perspex/Controls/Control.cs

@ -8,11 +8,9 @@ namespace Perspex.Controls
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Styling;
using Splat;

115
Perspex/Controls/Image.cs

@ -0,0 +1,115 @@
// -----------------------------------------------------------------------
// <copyright file="Image.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using Perspex.Media;
using Perspex.Media.Imaging;
public class Image : Control
{
public static readonly PerspexProperty<Bitmap> SourceProperty =
PerspexProperty.Register<Image, Bitmap>("Source");
public static readonly PerspexProperty<Stretch> StretchProperty =
PerspexProperty.Register<Image, Stretch>("Stretch", Stretch.Uniform);
public Bitmap Source
{
get { return this.GetValue(SourceProperty); }
set { this.SetValue(SourceProperty, value); }
}
public Stretch Stretch
{
get { return (Stretch)this.GetValue(StretchProperty); }
set { this.SetValue(StretchProperty, value); }
}
public override void Render(IDrawingContext drawingContext)
{
Brush background = this.Background;
Bitmap source = this.Source;
if (background != null)
{
drawingContext.FillRectange(background, new Rect(this.ActualSize));
}
if (source != null)
{
Rect viewPort = new Rect(this.ActualSize);
Size sourceSize = new Size(source.PixelWidth, source.PixelHeight);
Vector scale = CalculateScaling(this.ActualSize, sourceSize, this.Stretch);
Size scaledSize = sourceSize * scale;
Rect destRect = viewPort
.Center(new Rect(scaledSize))
.Intersect(viewPort);
Rect sourceRect = new Rect(sourceSize)
.Center(new Rect(destRect.Size / scale));
drawingContext.DrawImage(source, 1, sourceRect, destRect);
}
}
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);
}
protected override Size MeasureOverride(Size availableSize)
{
double width = 0;
double height = 0;
Vector scale = new Vector();
if (this.Source != null)
{
width = this.Source.PixelWidth;
height = this.Source.PixelHeight;
if (this.Width > 0)
{
availableSize = new Size(this.Width, availableSize.Height);
}
if (this.Height > 0)
{
availableSize = new Size(availableSize.Width, this.Height);
}
scale = CalculateScaling(availableSize, new Size(width, height), this.Stretch);
}
return new Size(width * scale.X, height * scale.Y);
}
protected override Size ArrangeOverride(Size finalSize)
{
return finalSize;
}
}
}

4
Perspex/Media/IDrawingContext.cs

@ -7,12 +7,16 @@
namespace Perspex.Media
{
using System;
using Perspex.Media.Imaging;
/// <summary>
/// Defines the interface through which drawing occurs.
/// </summary>
public interface IDrawingContext : IDisposable
{
void DrawImage(Bitmap source, double opacity, Rect sourceRect, Rect destRect);
/// <summary>
/// Draws a line.
/// </summary>

20
Perspex/Media/Imaging/Bitmap.cs

@ -4,13 +4,19 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
namespace Perspex.Media.Imaging
{
using Perspex.Platform;
using Splat;
public class Bitmap
public class Bitmap : IBitmap
{
public Bitmap(string fileName)
{
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
this.PlatformImpl = factory.LoadBitmap(fileName);
}
public Bitmap(int width, int height)
{
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
@ -22,6 +28,16 @@ namespace Perspex.Media
this.PlatformImpl = impl;
}
public int PixelWidth
{
get { return this.PlatformImpl.PixelWidth; }
}
public int PixelHeight
{
get { return this.PlatformImpl.PixelHeight; }
}
public IBitmapImpl PlatformImpl
{
get;

17
Perspex/Media/Imaging/IBitmap.cs

@ -0,0 +1,17 @@
// -----------------------------------------------------------------------
// <copyright file="IBitmap.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media.Imaging
{
public interface IBitmap
{
int PixelHeight { get; }
int PixelWidth { get; }
void Save(string fileName);
}
}

2
Perspex/Media/Imaging/RenderTargetBitmap.cs

@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
namespace Perspex.Media.Imaging
{
using Perspex.Platform;
using Splat;

3
Perspex/Perspex.csproj

@ -76,6 +76,7 @@
<Compile Include="Controls\DefinitionBase.cs" />
<Compile Include="Controls\Grid.cs" />
<Compile Include="Controls\GridLength.cs" />
<Compile Include="Controls\Image.cs" />
<Compile Include="Controls\RowDefinition.cs" />
<Compile Include="Controls\RowDefinitions.cs" />
<Compile Include="Controls\TextBox.cs" />
@ -105,6 +106,7 @@
<Compile Include="Layout\LayoutHelper.cs" />
<Compile Include="Media\Brushes.cs" />
<Compile Include="Media\Imaging\Bitmap.cs" />
<Compile Include="Media\Imaging\IBitmap.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmap.cs" />
<Compile Include="Platform\IPlatformThreadingInterface.cs" />
<Compile Include="Platform\IRenderTargetBitmapImpl.cs" />
@ -119,6 +121,7 @@
<Compile Include="Media\StreamGeometry.cs" />
<Compile Include="Media\Geometry.cs" />
<Compile Include="Media\Stretch.cs" />
<Compile Include="Vector.cs" />
<Compile Include="Rendering\RenderManager.cs" />
<Compile Include="Rendering\IRenderManager.cs" />
<Compile Include="Rendering\IRendered.cs" />

4
Perspex/Platform/IBitmapImpl.cs

@ -8,6 +8,10 @@ namespace Perspex.Platform
{
public interface IBitmapImpl
{
int PixelWidth { get; }
int PixelHeight { get; }
void Save(string fileName);
}
}

2
Perspex/Platform/IPlatformRenderInterface.cs

@ -20,5 +20,7 @@ namespace Perspex.Platform
IRenderer CreateRenderer(IntPtr handle, double width, double height);
IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height);
IBitmapImpl LoadBitmap(string fileName);
}
}

56
Perspex/Rect.cs

@ -191,6 +191,32 @@ namespace Perspex
get { return this.width == 0 && this.height == 0; }
}
public static Rect operator *(Rect rect, Vector scale)
{
double centerX = rect.x + rect.width / 2;
double centerY = rect.y + rect.height / 2;
double width = rect.width * scale.X;
double height = rect.height * scale.Y;
return new Rect(
centerX - (width / 2),
centerY - (height / 2),
width,
height);
}
public static Rect operator /(Rect rect, Vector scale)
{
double centerX = rect.x + rect.width / 2;
double centerY = rect.y + rect.height / 2;
double width = rect.width / scale.X;
double height = rect.height / scale.Y;
return new Rect(
centerX - (width / 2),
centerY - (height / 2),
width,
height);
}
/// <summary>
/// Determines whether a points in in the bounds of the rectangle.
/// </summary>
@ -202,6 +228,15 @@ namespace Perspex
p.Y >= this.y && p.Y < this.y + this.height;
}
public Rect Center(Rect rect)
{
return new Rect(
this.x + ((this.width - rect.width) / 2),
this.y + ((this.height - rect.height) / 2),
rect.width,
rect.height);
}
/// <summary>
/// Deflates the rectangle.
/// </summary>
@ -226,6 +261,27 @@ namespace Perspex
this.Size.Deflate(thickness));
}
public Rect Intersect(Rect rect)
{
double x = Math.Max(this.x, rect.x);
double y = Math.Max(this.y, rect.y);
double width = Math.Min(this.Right, rect.Right) - x;
double height = Math.Min(this.Bottom, rect.Bottom) - y;
if (width < 0 || height < 0)
{
return new Rect(
double.PositiveInfinity,
double.PositiveInfinity,
double.NegativeInfinity,
double.NegativeInfinity);
}
else
{
return new Rect(x, y, width, height);
}
}
/// <summary>
/// Returns the string representation of the rectangle.
/// </summary>

22
Perspex/Size.cs

@ -73,6 +73,28 @@ namespace Perspex
return !(left == right);
}
/// <summary>
/// Scales a size.
/// </summary>
/// <param name="rect">The size</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The scaled size.</returns>
public static Size operator *(Size size, Vector scale)
{
return new Size(size.width * scale.X, size.height * scale.Y);
}
/// <summary>
/// Scales a size.
/// </summary>
/// <param name="rect">The size</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The scaled size.</returns>
public static Size operator /(Size size, Vector scale)
{
return new Size(size.width / scale.X, size.height / scale.Y);
}
/// <summary>
/// Constrains the size.
/// </summary>

14
Perspex/Thickness.cs

@ -41,6 +41,20 @@ namespace Perspex
this.left = this.top = this.right = this.bottom = uniformLength;
}
/// <summary>
/// Initializes a new instance of the <see cref="Thickness"/> structure.
/// </summary>
/// <param name="horizontal">The thickness on the left and right.</param>
/// <param name="top">The thickness on the top and bottom.</param>
public Thickness(double horizontal, double vertical)
{
Contract.Requires<ArgumentException>(horizontal >= 0);
Contract.Requires<ArgumentException>(vertical >= 0);
this.left = this.right = horizontal;
this.top = this.bottom = vertical;
}
/// <summary>
/// Initializes a new instance of the <see cref="Thickness"/> structure.
/// </summary>

72
Perspex/Vector.cs

@ -0,0 +1,72 @@
// -----------------------------------------------------------------------
// <copyright file="Vector.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System.Globalization;
/// <summary>
/// Defines a vector.
/// </summary>
public struct Vector
{
/// <summary>
/// The X vector.
/// </summary>
private double x;
/// <summary>
/// The Y vector.
/// </summary>
private double y;
/// <summary>
/// Initializes a new instance of the <see cref="Vector"/> structure.
/// </summary>
/// <param name="x">The X vector.</param>
/// <param name="y">The Y vector.</param>
public Vector(double x, double y)
{
this.x = x;
this.y = y;
}
/// <summary>
/// Gets the X vector.
/// </summary>
public double X
{
get { return this.x; }
}
/// <summary>
/// Gets the Y vector.
/// </summary>
public double Y
{
get { return this.y; }
}
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.x + b.x, a.y + b.y);
}
public static Vector operator -(Vector a, Vector b)
{
return new Vector(a.x - b.x, a.y - b.y);
}
/// <summary>
/// Returns the string representation of the point.
/// </summary>
/// <returns>The string representation of the point.</returns>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", this.x, this.y);
}
}
}

6
TestApplication/Program.cs

@ -10,6 +10,7 @@ using Perspex.Controls;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Media.Imaging;
using Perspex.Shapes;
using Perspex.Styling;
using Perspex.Themes.Default;
@ -69,6 +70,11 @@ namespace TestApplication
new TextBox
{
Text = "Hello World!",
},
new Image
{
Source = new Bitmap("github_icon.png"),
Width = 200,
}
}
}

5
TestApplication/TestApplication.csproj

@ -98,6 +98,11 @@
<Name>Perspex</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="github_icon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

BIN
TestApplication/github_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
TestFiles/Direct2D1/Controls/Image/Image_Stretch_Fill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
TestFiles/Direct2D1/Controls/Image/Image_Stretch_None.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
TestFiles/Direct2D1/Controls/Image/Image_Stretch_Uniform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
TestFiles/Direct2D1/Controls/Image/Image_Stretch_UniformToFill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
TestFiles/Direct2D1/Controls/Image/test.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Loading…
Cancel
Save