OSDN Git Service

メモリーリークが発生するのでsharpdxだけ4.0.1に戻した
[fooeditengine/FooEditEngine.git] / WPF / FooEditEngine / Direct2D / D2DRender.cs
1 /*
2  * Copyright (C) 2013 FooProject
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
5
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
10  */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15 using System.Windows;
16 using System.Windows.Controls;
17 using System.Windows.Media;
18 using System.Windows.Input;
19 using System.Windows.Interop;
20 using FooEditEngine;
21 using DotNetTextStore;
22 using DotNetTextStore.UnmanagedAPI.TSF;
23 using DotNetTextStore.UnmanagedAPI.WinDef;
24 using SharpDX;
25 using D2D = SharpDX.Direct2D1;
26 using DW = SharpDX.DirectWrite;
27 using D3D11 = SharpDX.Direct3D11;
28 using D3D9 = SharpDX.Direct3D9;
29 using DXGI = SharpDX.DXGI;
30
31 namespace FooEditEngine.WPF
32 {
33     sealed class D2DRender : D2DRenderCommon, IEditorRender
34     {
35         DotNetTextStore.TextStore store;
36         D3D11.Texture2D texture;
37         DXGI.Surface surface;
38         D3D11.Device device;
39         D3D9.Device device9;
40         D3D9.Surface surface9;
41         double fontSize;
42         FontFamily fontFamily;
43         FontWeight fontWeigth;
44         FontStyle fontStyle;
45         D3DImage imageSource;
46
47         public D2DRender(FooTextBox textbox, double width, double height, Image image)
48         {
49             this.fontFamily = textbox.FontFamily;
50             this.fontSize = textbox.FontSize;
51             this.fontWeigth = textbox.FontWeight;
52             this.fontStyle = textbox.FontStyle;
53             this.Foreground = ToColor4(textbox.Foreground);
54             this.Background = ToColor4(textbox.Background);
55             this.ControlChar = ToColor4(textbox.ControlChar);
56             this.Hilight = ToColor4(textbox.Hilight);
57             this.Comment = ToColor4(textbox.Comment);
58             this.Url = ToColor4(textbox.URL);
59             this.Keyword1 = ToColor4(textbox.Keyword1);
60             this.Keyword2 = ToColor4(textbox.Keyword2);
61             this.Literal = ToColor4(textbox.Literal);
62             this.InsertCaret = ToColor4(textbox.InsertCaret);
63             this.OverwriteCaret = ToColor4(textbox.OverwriteCaret);
64             this.LineMarker = ToColor4(textbox.LineMarker);
65             this.UpdateArea = ToColor4(textbox.UpdateArea);
66             this.LineNumber = ToColor4(textbox.LineNumber);
67             this.HilightForeground = ToColor4(textbox.HilightForeground);
68             this.store = textbox.TextStore;
69
70             this.CreateDevice();
71
72             this.ConstructRenderAndResource(width, height);
73             this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
74
75             this.imageSource = new D3DImage();
76             this.imageSource.Lock();
77             this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);  //設定しないとロード時に例外が発生する
78             this.imageSource.Unlock();
79
80             image.Source = this.imageSource;
81         }
82
83         public FontFamily FontFamily
84         {
85             get { return this.fontFamily; }
86             set
87             {
88                 this.fontFamily = value;
89                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
90                 this.TabWidthChar = this.TabWidthChar;
91             }
92         }
93
94         public double FontSize
95         {
96             get { return this.fontSize; }
97             set
98             {
99                 this.fontSize = value;
100                 this.InitTextFormat(this.fontFamily.Source, (float)value, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
101                 this.TabWidthChar = this.TabWidthChar;
102             }
103         }
104
105         public FontWeight FontWeigth
106         {
107             get
108             {
109                 return this.fontWeigth;
110             }
111             set
112             {
113                 this.fontWeigth = value;
114                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(value), this.GetDWFontStyle(this.fontStyle));
115             }
116         }
117
118         public FontStyle FontStyle
119         {
120             get
121             {
122                 return this.fontStyle;
123             }
124             set
125             {
126                 this.fontStyle = value;
127                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
128             }
129         }
130
131         public static Color4 ToColor4(System.Windows.Media.Color color)
132         {
133             return new Color4(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f);
134         }
135
136         public bool Resize(double width, double height)
137         {
138             if (Math.Floor(width) != Math.Floor(this.imageSource.Width) || Math.Floor(height) != Math.Floor(this.imageSource.Height))
139             {
140                 this.ReConstructRenderAndResource(width, height);
141                 this.imageSource.Lock();
142                 this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);
143                 this.imageSource.Unlock();
144                 return true;
145             }
146             return false;
147         }
148
149         public void ReConstructRenderAndResource(double width, double height)
150         {
151             this.DestructRenderAndResource();
152             this.ConstructRenderAndResource(width, height);
153         }
154
155         public void DrawContent(EditView view, bool IsEnabled, Rectangle updateRect)
156         {
157             if (this.imageSource.IsFrontBufferAvailable)
158             {
159                 this.imageSource.Lock();
160                 this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);
161
162                 base.BegineDraw();
163                 if (IsEnabled)
164                     view.Draw(updateRect);
165                 else
166                     this.FillBackground(updateRect);
167                 base.EndDraw();
168                 this.device.ImmediateContext.Flush();
169
170                 this.imageSource.AddDirtyRect(new Int32Rect(0, 0, (int)this.imageSource.PixelWidth, (int)this.imageSource.PixelHeight));
171                 this.imageSource.Unlock();
172             }
173         }
174
175         public void DrawOneLine(Document doc, LineToIndexTable lti, int row, double x, double y)
176         {
177             PreDrawOneLineHandler PreDrawOneLine = null;
178
179             if (InputMethod.Current.ImeState == InputMethodState.On)
180                 PreDrawOneLine = this.DrawImeConversionLine;
181
182             base.DrawOneLine(doc,
183                 lti,
184                 row,
185                 x,
186                 y,
187                 PreDrawOneLine
188                 );
189         }
190
191         private void DrawImeConversionLine(MyTextLayout layout, LineToIndexTable lti, int row, double x, double y)
192         {
193             using (Unlocker locker = this.store.LockDocument(false))
194             {
195                 int lineIndex = lti.GetIndexFromLineNumber(row);
196                 int lineLength = lti.GetLengthFromLineNumber(row);
197                 foreach (TextDisplayAttribute attr in this.store.EnumAttributes(lineIndex, lineIndex + lineLength))
198                 {
199                     if (attr.startIndex == attr.endIndex)
200                         continue;
201                     int length = attr.endIndex - attr.startIndex;
202                     int start = attr.startIndex - lineIndex;
203
204                     HilightType type = HilightType.None;
205                     Color4? color = null;
206                     switch (attr.attribute.lsStyle)
207                     {
208                         case TF_DA_LINESTYLE.TF_LS_DOT:
209                             type = HilightType.Dot;
210                             color = this.GetColor4(attr.attribute.crLine);
211                             break;
212                         case TF_DA_LINESTYLE.TF_LS_SOLID:
213                             type = HilightType.Sold;
214                             color = this.GetColor4(attr.attribute.crLine);
215                             break;
216                         case TF_DA_LINESTYLE.TF_LS_DASH:
217                             type = HilightType.Dash;
218                             color = this.GetColor4(attr.attribute.crLine);
219                             break;
220                         case TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
221                             type = HilightType.Squiggle;
222                             color = this.GetColor4(attr.attribute.crLine);
223                             break;
224                     }
225
226                     if (attr.attribute.crBk.type != TF_DA_COLORTYPE.TF_CT_NONE)
227                     {
228                         type = HilightType.Select;
229                         color = this.GetColor4(attr.attribute.crBk);
230                     }
231
232                     this.DrawMarkerEffect(layout, type, start, length, x, y, attr.attribute.fBoldLine, color);
233
234                     color = this.GetColor4(attr.attribute.crText);
235                     if (color != null)
236                     {
237                         this.SetTextColor(layout, start, length, color);
238                         layout.Invaild = true;
239                     }
240                 }
241             }
242
243         }
244
245         private Color4? GetColor4(TF_DA_COLOR cr)
246         {
247             COLORREF colorref;
248             switch (cr.type)
249             {
250                 case TF_DA_COLORTYPE.TF_CT_SYSCOLOR:
251                     colorref = new COLORREF(NativeMethods.GetSysColor((int)cr.indexOrColorRef));
252                     break;
253                 case TF_DA_COLORTYPE.TF_CT_COLORREF:
254                     colorref = new COLORREF(cr.indexOrColorRef);
255                     break;
256                 default:
257                     return null;
258             }
259             return new Color4(colorref.R / 255.0f, colorref.G / 255.0f, colorref.B / 255.0f, 1);
260         }
261
262         DW.FontStyle GetDWFontStyle(FontStyle style)
263         {
264             return (DW.FontStyle)Enum.Parse(typeof(DW.FontStyle), style.ToString());
265         }
266
267         DW.FontWeight GetDWFontWeigth(FontWeight weigth)
268         {
269             return (DW.FontWeight)Enum.Parse(typeof(DW.FontWeight), weigth.ToString());
270         }
271
272         public override void DrawCachedBitmap(Rectangle rect)
273         {
274             if (this.render == null || this.render.IsDisposed || this.cachedBitMap == null || this.cachedBitMap.IsDisposed)
275                 return;
276             render.DrawBitmap(this.cachedBitMap, rect, 1.0f, D2D.BitmapInterpolationMode.Linear, rect);
277         }
278
279         public override void CacheContent()
280         {
281             if (this.render == null || this.cachedBitMap == null || this.cachedBitMap.IsDisposed || this.render.IsDisposed)
282                 return;
283             render.Flush();
284             this.cachedBitMap.CopyFromRenderTarget(this.render, new SharpDX.Point(), new SharpDX.Rectangle(0, 0, (int)this.renderSize.Width, (int)this.renderSize.Height));
285             this.hasCache = true;
286         }
287
288         void CreateDevice()
289         {
290             SharpDX.Direct3D.FeatureLevel[] levels = new SharpDX.Direct3D.FeatureLevel[]{
291                 SharpDX.Direct3D.FeatureLevel.Level_11_0,
292                 SharpDX.Direct3D.FeatureLevel.Level_10_1,
293                 SharpDX.Direct3D.FeatureLevel.Level_10_0,
294                 SharpDX.Direct3D.FeatureLevel.Level_9_3,
295                 SharpDX.Direct3D.FeatureLevel.Level_9_2,
296                 SharpDX.Direct3D.FeatureLevel.Level_9_1};
297             foreach (var level in levels)
298             {
299                 try
300                 {
301                     this.device = new D3D11.Device(SharpDX.Direct3D.DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport, level);
302                     break;
303                 }
304                 catch
305                 {
306                     continue;
307                 }
308             }
309             if (this.device == null)
310                 throw new PlatformNotSupportedException("DirectX10デバイスの作成に失敗しました");
311
312             IntPtr DesktopWnd = NativeMethods.GetDesktopWindow();
313             D3D9.Direct3DEx d3dex = new D3D9.Direct3DEx();
314
315             D3D9.PresentParameters param = new D3D9.PresentParameters();
316             param.Windowed = true;
317             param.SwapEffect = D3D9.SwapEffect.Discard;
318             param.DeviceWindowHandle = DesktopWnd;
319             param.PresentationInterval = D3D9.PresentInterval.Default;
320
321             try
322             {
323                 this.device9 = new D3D9.DeviceEx(
324                     d3dex,
325                     0,
326                     D3D9.DeviceType.Hardware,
327                     DesktopWnd,
328                     D3D9.CreateFlags.HardwareVertexProcessing | D3D9.CreateFlags.Multithreaded | D3D9.CreateFlags.FpuPreserve,
329                     param);
330             }
331             catch
332             {
333                 try
334                 {
335                     this.device9 = new D3D9.DeviceEx(
336                         d3dex,
337                         0,
338                         D3D9.DeviceType.Hardware,
339                         DesktopWnd,
340                         D3D9.CreateFlags.SoftwareVertexProcessing | D3D9.CreateFlags.Multithreaded | D3D9.CreateFlags.FpuPreserve,
341                         param);
342                 }
343                 catch
344                 {
345                     throw new PlatformNotSupportedException("DirectX9デバイスの作成に失敗しました");
346                 }
347             }
348             finally
349             {
350                 d3dex.Dispose();
351             }
352         }
353
354         void DestructDevice()
355         {
356             if (this.device != null)
357             {
358                 this.device.Dispose();
359                 this.device = null;
360             }
361             if (this.device9 != null)
362             {
363                 this.device9.Dispose();
364                 this.device9 = null;
365             }
366         }
367
368         public void ConstructRenderAndResource(double width, double height)
369         {
370             float dpiX, dpiY;
371             this.GetDpi(out dpiX, out dpiY);
372             D2D.RenderTargetProperties prop = new D2D.RenderTargetProperties(
373                 D2D.RenderTargetType.Default,
374                 new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied),
375                 dpiX,
376                 dpiY,
377                 D2D.RenderTargetUsage.None,
378                 D2D.FeatureLevel.Level_DEFAULT);
379
380             D3D11.Texture2DDescription desc = new D3D11.Texture2DDescription();
381             desc.Width = (int)width;
382             desc.Height = (int)height;
383             desc.MipLevels = 1;
384             desc.ArraySize = 1;
385             desc.Format = DXGI.Format.B8G8R8A8_UNorm;
386             desc.SampleDescription = new DXGI.SampleDescription(1, 0);
387             desc.Usage = D3D11.ResourceUsage.Default;
388             desc.BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource;
389             desc.CpuAccessFlags = D3D11.CpuAccessFlags.None;
390             desc.OptionFlags = D3D11.ResourceOptionFlags.Shared;
391             this.texture = new D3D11.Texture2D(this.device, desc);
392
393             this.surface = this.texture.QueryInterface<DXGI.Surface>();
394
395             DXGI.Resource resource = this.texture.QueryInterface<DXGI.Resource>();
396             IntPtr handel = resource.SharedHandle;
397             D3D9.Texture texture = new D3D9.Texture(
398                 this.device9,
399                 this.texture.Description.Width,
400                 this.texture.Description.Height,
401                 1,
402                 D3D9.Usage.RenderTarget,
403                 D3D9.Format.A8R8G8B8,
404                 D3D9.Pool.Default,
405                 ref handel);
406             this.surface9 = texture.GetSurfaceLevel(0);
407             resource.Dispose();
408             texture.Dispose();
409
410             this.render = new D2D.RenderTarget(this.D2DFactory, this.surface, prop);
411
412             D2D.BitmapProperties bmpProp = new D2D.BitmapProperties();
413             bmpProp.DpiX = dpiX;
414             bmpProp.DpiY = dpiY;
415             bmpProp.PixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied);
416             this.cachedBitMap = new D2D.Bitmap(this.render, new SharpDX.Size2((int)width, (int)height), bmpProp);
417             this.hasCache = false;
418
419             this.textRender = new CustomTextRenderer(this.render, this.Brushes, this.Strokes, this.Foreground);
420
421             this.renderSize = new Size(width, height);
422         }
423
424         public void DestructRenderAndResource()
425         {
426             this.hasCache = false;
427             if (this.cachedBitMap != null)
428                 this.cachedBitMap.Dispose();
429             this.Brushes.Clear();
430             this.Strokes.Clear();
431             if (this.textRender != null)
432                 this.textRender.Dispose();
433             if (this.texture != null)
434                 this.texture.Dispose();
435             if (this.surface != null)
436                 this.surface.Dispose();
437             if (this.surface9 != null)
438                 this.surface9.Dispose();
439             if (this.render != null)
440                 this.render.Dispose();
441         }
442
443         public override void GetDpi(out float dpix, out float dpiy)
444         {
445             var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
446             var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
447
448             dpix = (int)dpiXProperty.GetValue(null, null);
449             dpiy = (int)dpiYProperty.GetValue(null, null);
450         }
451
452         protected override void Dispose(bool dispose)
453         {
454             base.Dispose(dispose);
455             this.DestructRenderAndResource();
456             this.DestructDevice();
457         }
458
459     }
460 }