DotSpatialの図形の描画は、Windows Formsアプリケーションで標準で利用されているGDI+が利用されています。ただし、GDI+は、基本的な2D処理のAPIのみで構成されているため、ハードウェアを抽象化するためゲームのような高速な画像処理には適していません。
そこで、画像描画性能を向上させるために登場したのが、DirectXです。DirectX は Windows や Xbox 360 などで使える、ゲームやマルチメディアなどの高速な描画が要求されるアプリケーション開発に用いられる API で、描画の計算を CPU ではなく、GPU によって処理されます。GPU は CPU とは異なり、並列処理に特化したアーキテクチャになっており、画像処理が高速化されます。
ただし、DirectX は C++ で書かれているネイティブの API であり .NET Framework ではありません。そのままの状態では C# から利用することはできませんでしたが、最近は便利な世の中になっており、DirectXをC#でも利用できるよう、ラッパーライブラリが出ています。
今回は、DirectXでもメジャーなライブラリであるSharpDxを利用して、DotSpatialの図形の描画を行いました。
1.描画の比較
GDI+で描画した場合
以下の画面イメージは、従来のDotSpatialを利用して、三豊市の市町村界をラインで表示したものですが、最小ライン幅が1pxまでしか表現できませんした。なんか不細工です。
SharpDXで描画した場合
一方、SharpDxを使用して描画した場合は、最小ライン幅が1px以下でも設定可能になる(以下は0.2px)ため、繊細な表現が可能になります。
さらに、SharpDxでは、svgファイルの表示や3Dの表示も可能になるので、今後のGIS開発では必須のライブラリともいえます。
2.事前準備
まず最初に、SharpDxを入手します。SharpDxのサイトからDLして、.Net4.5.1でビルドしました。
今回は3D等は利用しないので、以下のライブラリを参照に追加しました。
・SharpDX.Desktop.dll
・SharpDX.Direct2D1.dll
・SharpDX.dll
・SharpDX.DXGI.dll
・SharpDX.Mathematics.dll
3.修正箇所
今回のSharpDxを利用した、DotSpatialの修正個所配下の通りです。修正個所が多いため、抜粋しています。
Map.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using SD = System.Drawing; using SDX = SharpDX; using SDX2D = SharpDX.Direct2D1; using SDXDW = SharpDX.DirectWrite; using SDXWin = SharpDX.Windows; using SDXMath = SharpDX.Mathematics.Interop; namespace DotSpatial.Controls { public partial class Map : UserControl, IMap, IMessageFilter { public SDX2D.Factory Factory2D { get; private set; } public SDXDW.Factory FactoryDWrite { get; private set; } public SDX2D.WindowRenderTarget WinRenderTarget { get; private set; } public SDX2D.BitmapRenderTarget BmpRenderTarget { get; private set; } public Map() { InitializeComponent(); InitDirect2DAndDirectWrite(); Configure(); Application.AddMessageFilter(this); } private void InitDirect2DAndDirectWrite() { this.Factory2D = new SDX2D.Factory(); this.FactoryDWrite = new SDXDW.Factory(); var hrtProps = new SDX2D.HwndRenderTargetProperties(); hrtProps.Hwnd = this.Handle; hrtProps.PixelSize = new SDX.Size2(this.ClientSize.Width, this.ClientSize.Height); hrtProps.PresentOptions = SDX2D.PresentOptions.None; var rtProps = new SDX2D.RenderTargetProperties(new SDX2D.PixelFormat(SDX.DXGI.Format.Unknown, SDX2D.AlphaMode.Premultiplied)); this.WinRenderTarget = new SDX2D.WindowRenderTarget(Factory2D, rtProps, hrtProps); this.WinRenderTarget.AntialiasMode = SDX2D.AntialiasMode.PerPrimitive; this.WinRenderTarget.TextAntialiasMode = SDX2D.TextAntialiasMode.Cleartype; } protected virtual void Draw(Graphics g, PaintEventArgs e) { var rawRect = new SDXMath.RawRectangleF(e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Right, e.ClipRectangle.Bottom); var evtDx = new PaintEventArgsDx(this.WinRenderTarget, this.BmpRenderTarget, this.Factory2D, rawRect); _geoMapFrame.DrawDx(evtDx); } protected override void OnPaint(PaintEventArgs e) { if (_geoMapFrame.IsPanning) { return; } var clip = e.ClipRectangle; var rawClip = new SDXMath.RawRectangleF(e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Right, e.ClipRectangle.Bottom); var rawClipWidth = Math.Abs(rawClip.Right - rawClip.Left); var rawClipHeight = Math.Abs(rawClip.Top - rawClip.Bottom); if (rawClipWidth < 1 || rawClipHeight < 1) { return; } //Direct2D用のビットマップ this.BmpRenderTarget = new SDX2D.BitmapRenderTarget( this.WinRenderTarget,SDX2D.CompatibleRenderTargetOptions.None, new SDX.Size2F(rawClipWidth, rawClipHeight), null, null); var dxBrush = new SDX2D.SolidColorBrush(this.WinRenderTarget, SDX.Color.FromAbgr(BackColor.ToArgb())); this.BmpRenderTarget.FillRectangle(new SDXMath.RawRectangleF(0f, 0f, clip.Width, clip.Height), dxBrush); using (var m = new Matrix()) { m.Translate(-clip.X, -clip.Y); g.Transform = m; Draw(g, e); var args = new MapDrawArgs(g, clip, _geoMapFrame); args.WinRenderTarget = this.WinRenderTarget; args.BmpRenderTarget = this.BmpRenderTarget; args.Factory2D = this.Factory2D; foreach (var tool in MapFunctions.Where(_ => _.Enabled)) { tool.Draw(args); } var pe = new PaintEventArgs(g, e.ClipRectangle); base.OnPaint(pe); } // e.Graphics.DrawImageUnscaled(stencil, clip.X, clip.Y); //画像をウィンドウに描画 // this.WinRenderTarget.BeginDraw(); // this.WinRenderTarget.DrawBitmap(this.BmpRenderTarget.Bitmap, 1.0f, SDX2D.BitmapInterpolationMode.NearestNeighbor); // this.WinRenderTarget.EndDraw(); } } } |
MapFrame.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
namespace DotSpatial.Controls { public partial class MapFrame : UserControl, IMap, IMessageFilter { public SDX2D.WindowRenderTarget WinRenderTarget { get; set; } public SDX2D.BitmapRenderTarget BmpRenderTarget { get; set; } public SDX2D.Factory Factory2D { get; set; } public virtual void Initialize(List regions) { bool setView = false; if (_backBuffer == null) { _backBuffer = CreateBuffer(); var mapDx = (MapDx)_parent; this.WinRenderTarget = mapDx.WinRenderTarget; this.Factory2D = mapDx.Factory2D; this.BmpRenderTarget = new SDX2D.BitmapRenderTarget(this.WinRenderTarget, SDX2D.CompatibleRenderTargetOptions.None, new SDX.Size2F(_backBuffer.Width, _backBuffer.Height), null, null); } MapArgs args = new MapArgs(ClientRectangle, ViewExtents, bufferDevice); args.WinRenderTarget = this.WinRenderTarget; args.BmpRenderTarget = this.BmpRenderTarget; this.BmpRenderTarget.BeginDraw(); this.BmpRenderTarget.Clear(SDX.Color.White); this.BmpRenderTarget.EndDraw(); } public SDXMath.RawRectangleF ParentToViewDX(SDXMath.RawRectangleF clip) { var height = Math.Abs(clip.Bottom - clip.Top); var width = Math.Abs(clip.Left - clip.Right); var result = new SDXMath.RawRectangleF { Top = View.X + (clip.Top * View.Width) / _parent.ClientRectangle.Width, Left = View.Y + (clip.Left * View.Height) / _parent.ClientRectangle.Height, Right = width * View.Width / _parent.ClientRectangle.Width, Bottom = height * View.Height / _parent.ClientRectangle.Height }; return result; } public void DrawDx(PaintEventArgsDx pedx) { if (this.BmpRenderTarget == null) { return; } var rawClip = pedx.ClipRawRectangle; var height = Math.Abs(rawClip.Bottom - rawClip.Top); var width = Math.Abs(rawClip.Left - rawClip.Right); var clipView = ParentToViewDX(rawClip); if (width == 0 || height == 0) { return; } var vheight = Math.Abs(clipView.Bottom - clipView.Top); var vwidth = Math.Abs(clipView.Left - clipView.Right); if (vwidth == 0 || vheight == 0) { return; } try { this.WinRenderTarget.BeginDraw(); this.WinRenderTarget.DrawBitmap(this.BmpRenderTarget.Bitmap, rawClip, 1, SDX2D.BitmapInterpolationMode.NearestNeighbor, clipView); this.WinRenderTarget.EndDraw(); } catch { // There was an exception (probably because of sizing issues) so don't bother with the chunk timer. } //base.OnPaint(pe); } … } } |
MapLineLayer.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
namespace DotSpatial.Controls { public class MapLineLayer : LineLayer, IMapLineLayer { private void DrawFeatures(MapArgs e, IEnumerable indices) { if (DrawnStatesNeeded) { for (int selectState = 0; selectState < 2-1; selectState++) { foreach (ILineCategory category in Symbology.Categories) { foreach (IStroke stroke in ls.Strokes) { //stroke.DrawPath(g, graphPath, scale); stroke.DrawPathDx(e.Factory2D, e.BmpRenderTarget, lines, scale); } } } } } } } |
Stroke.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
namespace DotSpatial.Symbology { public class Stroke : Descriptor, IStroke { public virtual void DrawPathDx(SDX2D.Factory factory2D, SDX2D.BitmapRenderTarget bitmapRenderTarget, List<List<double[]>> lines, double scaleWidth) { if (_innerStroke != null) { _innerStroke.DrawPathDx(factory2D, bitmapRenderTarget, lines, scaleWidth); return; } try { bitmapRenderTarget.BeginDraw(); var brush = new SDX2D.SolidColorBrush(bitmapRenderTarget, SDX.Color.Black); // renderTarget2D.DrawGeometry(pathGeom, brush); for (int i = 0; i < lines.Count; i++) { for (int j = 0; j < lines[i].Count - 1; j++) { var vec1 = new SDXMath.RawVector2((float)lines[i][j][0], (float)lines[i][j][1]); var vec2 = new SDXMath.RawVector2((float)lines[i][j + 1][0], (float)lines[i][j + 1][1]); bitmapRenderTarget.DrawLine(vec1, vec2, brush, 0.5f); } } // renderTarget2D.DrawLine(new RawVector2(100f, 100f), new RawVector2(200f, 200f), brush); // renderTarget2D.DrawLine(new RawVector2(100f, 100f), new RawVector2(100f, 200f), brush); bitmapRenderTarget.EndDraw(); } catch (Exception ex) { Debug.WriteLine("Writeline Stroke.DrawPath2:" + ex); } } } } |