wpf使用Direct2D绘制曲线
···
Install-Package SharpDX
Install-Package SharpDX.Direct2D1
Install-Package SharpDX.DXGI
Install-Package SharpDX.Direct3D11
···
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Image Name="DxImage"/>
</Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Interop;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using Device = SharpDX.Direct3D11.Device;
using Factory = SharpDX.Direct2D1.Factory;
using AlphaModeD2D = SharpDX.Direct2D1.AlphaMode;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using SolidColorBrush = SharpDX.Direct2D1.SolidColorBrush;
using SharpDX.DirectWrite;
using System.Windows.Media;
using TextAlignment = SharpDX.DirectWrite.TextAlignment;
using FactoryType = SharpDX.Direct2D1.FactoryType;
using TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode;
using System.Numerics;
using DashStyle = SharpDX.Direct2D1.DashStyle;
using PathGeometry = SharpDX.Direct2D1.PathGeometry;
using static System.Net.Mime.MediaTypeNames;
using SharpDX.Mathematics.Interop;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using System.Security.Cryptography.Xml;
using System.Windows.Media.Media3D;
namespace WpfApp2
{
public partial class MainWindow : Window
{
private Device device;
private SwapChain swapChain;
private RenderTarget renderTarget;
private Factory factory;
private SharpDX.DirectWrite.Factory writeFactory;
private TextFormat textFormat;
private bool isResizing = false;
private StrokeStyle strokeStyle;
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
SizeChanged += OnSizeChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
InitializeSharpDX();
CompositionTarget.Rendering += OnRendering;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
CompositionTarget.Rendering -= OnRendering;
DisposeResources();
}
private void InitializeSharpDX()
{
factory = new Factory(FactoryType.SingleThreaded);
writeFactory = new SharpDX.DirectWrite.Factory();
var swapChainDescription = new SwapChainDescription
{
BufferCount = 1,
ModeDescription = new ModeDescription((int)ActualWidth, (int)ActualHeight, new Rational(60, 1), Format.B8G8R8A8_UNorm),
IsWindowed = true,
OutputHandle = new WindowInteropHelper(this).Handle,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
};
Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDescription, out device, out swapChain);
UpdateTextFormat();
CreateRenderTarget();
CreateStrokeStyle();
}
private void UpdateTextFormat()
{
textFormat?.Dispose();
float fontSize = (float)(Math.Min(ActualWidth, ActualHeight) * 0.02);
textFormat = new TextFormat(writeFactory, "Arial", fontSize)
{
TextAlignment = TextAlignment.Center,
ParagraphAlignment = ParagraphAlignment.Center
};
}
private void CreateRenderTarget()
{
using (var surface = swapChain.GetBackBuffer<Surface>(0))
{
var properties = new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaModeD2D.Premultiplied));
renderTarget = new RenderTarget(factory, surface, properties)
{
AntialiasMode = AntialiasMode.PerPrimitive,
TextAntialiasMode = TextAntialiasMode.Cleartype
};
}
}
private void CreateStrokeStyle()
{
strokeStyle?.Dispose();
strokeStyle = new StrokeStyle(factory, new StrokeStyleProperties
{
DashStyle = DashStyle.Solid,
LineJoin = LineJoin.Round,
StartCap = CapStyle.Round,
EndCap = CapStyle.Round
});
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (swapChain != null && !isResizing)
{
isResizing = true;
try
{
renderTarget?.Dispose();
swapChain.ResizeBuffers(1, (int)ActualWidth, (int)ActualHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.None);
CreateRenderTarget();
UpdateTextFormat();
}
finally
{
isResizing = false;
}
}
}
private void OnRendering(object sender, EventArgs e)
{
if (renderTarget == null || isResizing)
return;
renderTarget.BeginDraw();
DrawScene();
renderTarget.EndDraw();
swapChain.Present(1, PresentFlags.None);
}
private void DrawScene()
{
renderTarget.Clear(new RawColor4(1, 1, 1, 1));
using (var axisBrush = new SolidColorBrush(renderTarget, new RawColor4(0, 0, 0, 1)))
using (var lineBrush = new SolidColorBrush(renderTarget, new RawColor4(1, 0, 0, 1)))
using (var textBrush = new SolidColorBrush(renderTarget, new RawColor4(0, 0, 0, 1)))
{
float width = (float)renderTarget.Size.Width;
float height = (float)renderTarget.Size.Height;
float margin = Math.Min(width, height) * 0.1f;
float axisLength = Math.Min(width, height) * 0.8f;
float lineWidth = 1.5f;
// Draw X-axis
renderTarget.DrawLine(
AlignToPixel(new RawVector2(margin, height / 2)),
AlignToPixel(new RawVector2(width - margin, height / 2)),
axisBrush, lineWidth, strokeStyle);
// Draw Y-axis
renderTarget.DrawLine(
AlignToPixel(new RawVector2(width / 2, margin)),
AlignToPixel(new RawVector2(width / 2, height - margin)),
axisBrush, lineWidth, strokeStyle);
// Draw X-axis ticks and labels
for (int i = -4; i <= 4; i++)
{
float x = width / 2 + i * axisLength / 8;
renderTarget.DrawLine(
AlignToPixel(new RawVector2(x, height / 2 - 5)),
AlignToPixel(new RawVector2(x, height / 2 + 5)),
axisBrush, 1.0f, strokeStyle);
using (var textLayout = new TextLayout(writeFactory, i.ToString(), textFormat, 50, 20))
{
renderTarget.DrawTextLayout(new RawVector2(x - 25, height / 2 + 10), textLayout, textBrush);
}
}
// Draw Y-axis ticks and labels
for (int i = -4; i <= 4; i++)
{
float y = height / 2 - i * axisLength / 8;
renderTarget.DrawLine(
AlignToPixel(new RawVector2(width / 2 - 5, y)),
AlignToPixel(new RawVector2(width / 2 + 5, y)),
axisBrush, 1.0f, strokeStyle);
using (var textLayout = new TextLayout(writeFactory, i.ToString(), textFormat, 50, 20))
{
renderTarget.DrawTextLayout(new RawVector2(width / 2 + 10, y - 10), textLayout, textBrush);
}
}
// Draw a line using geometry for better quality
var startPoint = AlignToPixel(new RawVector2(margin, margin));
var endPoint = AlignToPixel(new RawVector2(width - margin, height - margin));
using (var lineGeometry = new PathGeometry(factory))
{
var sink = lineGeometry.Open();
sink.BeginFigure(startPoint, FigureBegin.Hollow);
sink.AddLine(endPoint);
sink.EndFigure(FigureEnd.Open);
sink.Close();
// 使用 SharpDX.Matrix3x2.Identity 而不是 System.Numerics.Matrix3x2.Identity
//var transform = SharpDX.Matrix3x2.Identity;
var transform = new RawMatrix3x2() { M11 = 1, M22 = 1 };
using (var transformedGeometry = new TransformedGeometry(factory, lineGeometry, transform))
{
renderTarget.DrawGeometry(transformedGeometry, lineBrush, lineWidth, strokeStyle);
}
}
}
}
private RawVector2 AlignToPixel(RawVector2 point)
{
return new RawVector2(
(float)Math.Round(point.X) + 0.5f,
(float)Math.Round(point.Y) + 0.5f
);
}
private void DisposeResources()
{
renderTarget?.Dispose();
swapChain?.Dispose();
device?.Dispose();
textFormat?.Dispose();
writeFactory?.Dispose();
factory?.Dispose();
strokeStyle?.Dispose();
}
}
}