OSDN Git Service

選択領域の表示時の処理を最適化した
[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         new D2D.RenderTarget render;
42         double fontSize;
43         FontFamily fontFamily;
44         FontWeight fontWeigth;
45         FontStyle fontStyle;
46         D3DImage imageSource;
47
48         public D2DRender(FooTextBox textbox, double width, double height,Image image)
49         {
50             this.fontFamily = textbox.FontFamily;
51             this.fontSize = textbox.FontSize;
52             this.fontWeigth = textbox.FontWeight;
53             this.fontStyle = textbox.FontStyle;
54             this.Foreground = ToColor4(textbox.Foreground);
55             this.Background = ToColor4(textbox.Background);
56             this.ControlChar = ToColor4(textbox.ControlChar);
57             this.Hilight = ToColor4(textbox.Hilight);
58             this.Comment = ToColor4(textbox.Comment);
59             this.Url = ToColor4(textbox.URL);
60             this.Keyword1 = ToColor4(textbox.Keyword1);
61             this.Keyword2 = ToColor4(textbox.Keyword2);
62             this.Literal = ToColor4(textbox.Literal);
63             this.InsertCaret = ToColor4(textbox.InsertCaret);
64             this.OverwriteCaret = ToColor4(textbox.OverwriteCaret);
65             this.LineMarker = ToColor4(textbox.LineMarker);
66             this.UpdateArea = ToColor4(textbox.UpdateArea);
67             this.LineNumber = ToColor4(textbox.LineNumber);
68             this.HilightForeground = ToColor4(textbox.HilightForeground);
69             this.store = textbox.TextStore;
70
71             this.CreateDevice();
72
73             this.ConstructDeviceResource(width, height);
74             this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
75
76             this.imageSource = new D3DImage();
77             this.imageSource.Lock();
78             this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);  //設定しないとロード時に例外が発生する
79             this.imageSource.Unlock();
80
81             image.Source = this.imageSource;
82         }
83
84         public FontFamily FontFamily
85         {
86             get { return this.fontFamily; }
87             set
88             {
89                 this.fontFamily = value;
90                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
91                 this.TabWidthChar = this.TabWidthChar;
92             }
93         }
94
95         public double FontSize
96         {
97             get { return this.fontSize; }
98             set
99             {
100                 this.fontSize = value;
101                 this.InitTextFormat(this.fontFamily.Source, (float)value, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
102                 this.TabWidthChar = this.TabWidthChar;
103             }
104         }
105
106         public FontWeight FontWeigth
107         {
108             get
109             {
110                 return this.fontWeigth;
111             }
112             set
113             {
114                 this.fontWeigth = value;
115                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(value),this.GetDWFontStyle(this.fontStyle));
116             }
117         }
118
119         public FontStyle FontStyle
120         {
121             get
122             {
123                 return this.fontStyle;
124             }
125             set
126             {
127                 this.fontStyle = value;
128                 this.InitTextFormat(this.fontFamily.Source, (float)this.fontSize, this.GetDWFontWeigth(this.fontWeigth), this.GetDWFontStyle(this.fontStyle));
129             }
130         }
131
132         public static Color4 ToColor4(System.Windows.Media.Color color)
133         {
134             return new Color4(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f);
135         }
136
137         public bool Resize(double width, double height)
138         {
139             if (Math.Floor(width) != Math.Floor(this.imageSource.Width) || Math.Floor(height) != Math.Floor(this.imageSource.Height))
140             {
141                 this.ReConstructDeviceResource(width, height);
142                 this.imageSource.Lock();
143                 this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);
144                 this.imageSource.Unlock();
145                 return true;
146             }
147             return false;
148         }
149
150         public void DrawContent(EditView view,bool IsEnabled,Rectangle updateRect)
151         {
152             if (this.imageSource.IsFrontBufferAvailable)
153             {
154                 this.imageSource.Lock();
155                 this.imageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, this.surface9.NativePointer);
156
157                 base.BegineDraw();
158                 if (IsEnabled)
159                     view.Draw(updateRect);
160                 else
161                     this.FillBackground(updateRect);
162                 base.EndDraw();
163                 this.device.ImmediateContext.Flush();
164
165                 this.imageSource.AddDirtyRect(new Int32Rect(0, 0, (int)this.imageSource.Width, (int)this.imageSource.Height));
166                 this.imageSource.Unlock();
167             }
168         }
169
170         public void DrawOneLine(Document doc,LineToIndexTable lti, int row, double x, double y)
171         {
172             PreDrawOneLineHandler PreDrawOneLine = null;
173
174             if (InputMethod.Current.ImeState == InputMethodState.On)
175                 PreDrawOneLine = this.DrawImeConversionLine;
176
177             base.DrawOneLine(doc,
178                 lti,
179                 row,
180                 x,
181                 y,
182                 PreDrawOneLine
183                 );
184         }
185
186         private void DrawImeConversionLine(MyTextLayout layout,LineToIndexTable lti,int row,double x,double y)
187         {
188             using (Unlocker locker = this.store.LockDocument(false))
189             {
190                 int lineIndex = lti.GetIndexFromLineNumber(row);
191                 int lineLength = lti.GetLengthFromLineNumber(row);
192                 foreach (TextDisplayAttribute attr in this.store.EnumAttributes(lineIndex, lineIndex + lineLength))
193                 {
194                     if (attr.startIndex == attr.endIndex)
195                         continue;
196                     int length = attr.endIndex - attr.startIndex;
197                     int start = attr.startIndex - lineIndex;
198
199                     HilightType type = HilightType.None;
200                     Color4? color = null;
201                     switch (attr.attribute.lsStyle)
202                     {
203                         case TF_DA_LINESTYLE.TF_LS_DOT:
204                             type = HilightType.Dot;
205                             color = this.GetColor4(attr.attribute.crLine);
206                             break;
207                         case TF_DA_LINESTYLE.TF_LS_SOLID:
208                             type = HilightType.Sold;
209                             color = this.GetColor4(attr.attribute.crLine);
210                             break;
211                         case TF_DA_LINESTYLE.TF_LS_DASH:
212                             type = HilightType.Dash;
213                             color = this.GetColor4(attr.attribute.crLine);
214                             break;
215                         case TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
216                             type = HilightType.Squiggle;
217                             color = this.GetColor4(attr.attribute.crLine);
218                             break;
219                     }
220
221                     if (attr.attribute.crBk.type != TF_DA_COLORTYPE.TF_CT_NONE)
222                     {
223                         type = HilightType.Select;
224                         color = this.GetColor4(attr.attribute.crBk);
225                     }
226
227                     this.DrawMarkerEffect(layout, type, start, length, x, y, attr.attribute.fBoldLine, color);
228
229                     color = this.GetColor4(attr.attribute.crText);
230                     if (color != null)
231                     {
232                         this.SetTextColor(layout, start, length, color);
233                         layout.Invaild = true;
234                     }
235                 }
236             }
237
238         }
239
240         private Color4? GetColor4(TF_DA_COLOR cr)
241         {
242             COLORREF colorref;
243             switch(cr.type)
244             {
245                 case TF_DA_COLORTYPE.TF_CT_SYSCOLOR:
246                     colorref = new COLORREF(NativeMethods.GetSysColor((int)cr.indexOrColorRef));
247                     break;
248                 case TF_DA_COLORTYPE.TF_CT_COLORREF:
249                     colorref = new COLORREF(cr.indexOrColorRef);
250                     break;
251                 default:
252                     return null;
253             }
254             return new Color4(colorref.R / 255.0f, colorref.G / 255.0f, colorref.B / 255.0f, 1);
255         }
256
257         public override void Dispose()
258         {
259             base.Dispose();
260             this.DestructDevice();
261         }
262
263         DW.FontStyle GetDWFontStyle(FontStyle style)
264         {
265             return (DW.FontStyle)Enum.Parse(typeof(DW.FontStyle), style.ToString());
266         }
267
268         DW.FontWeight GetDWFontWeigth(FontWeight weigth)
269         {
270             return (DW.FontWeight)Enum.Parse(typeof(DW.FontWeight), weigth.ToString());
271         }
272
273         public override void DrawCachedBitmap(Rectangle rect)
274         {
275             if (this.render == null || this.render.IsDisposed)
276                 return;
277             render.DrawBitmap(this.cachedBitMap, rect, 1.0f, D2D.BitmapInterpolationMode.Linear, rect);
278         }
279
280         public override void CacheContent()
281         {
282             if (this.render == null || this.cachedBitMap == null || this.cachedBitMap.IsDisposed || this.render.IsDisposed)
283                 return;
284             render.Flush();
285             this.cachedBitMap.CopyFromRenderTarget(this.render, new SharpDX.Point(), new SharpDX.Rectangle(0, 0, (int)this.renderSize.Width, (int)this.renderSize.Height));
286             this.hasCache = true;
287         }
288
289         void CreateDevice()
290         {
291             SharpDX.Direct3D.FeatureLevel[] levels = new SharpDX.Direct3D.FeatureLevel[]{
292                 SharpDX.Direct3D.FeatureLevel.Level_11_0,
293                 SharpDX.Direct3D.FeatureLevel.Level_10_1,
294                 SharpDX.Direct3D.FeatureLevel.Level_10_0,
295                 SharpDX.Direct3D.FeatureLevel.Level_9_3,
296                 SharpDX.Direct3D.FeatureLevel.Level_9_2,
297                 SharpDX.Direct3D.FeatureLevel.Level_9_1};
298             foreach (var level in levels)
299             {
300                 try
301                 {
302                     this.device = new D3D11.Device(SharpDX.Direct3D.DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport, level);
303                     break;
304                 }
305                 catch
306                 {
307                     continue;
308                 }
309             }
310             if (this.device == null)
311                 throw new PlatformNotSupportedException("DirectX10デバイスの作成に失敗しました");
312
313             IntPtr DesktopWnd = NativeMethods.GetDesktopWindow();
314             D3D9.Direct3DEx d3dex = new D3D9.Direct3DEx();
315
316             D3D9.PresentParameters param = new D3D9.PresentParameters();
317             param.Windowed = true;
318             param.SwapEffect = D3D9.SwapEffect.Discard;
319             param.DeviceWindowHandle = DesktopWnd;
320             param.PresentationInterval = D3D9.PresentInterval.Default;
321
322             try
323             {
324                 this.device9 = new D3D9.DeviceEx(
325                     d3dex,
326                     0,
327                     D3D9.DeviceType.Hardware,
328                     DesktopWnd,
329                     D3D9.CreateFlags.HardwareVertexProcessing | D3D9.CreateFlags.Multithreaded | D3D9.CreateFlags.FpuPreserve,
330                     param);
331             }
332             catch
333             {
334                 try
335                 {
336                     this.device9 = new D3D9.DeviceEx(
337                         d3dex,
338                         0,
339                         D3D9.DeviceType.Hardware,
340                         DesktopWnd,
341                         D3D9.CreateFlags.SoftwareVertexProcessing | D3D9.CreateFlags.Multithreaded | D3D9.CreateFlags.FpuPreserve,
342                         param);
343                 }
344                 catch
345                 {
346                     throw new PlatformNotSupportedException("DirectX9デバイスの作成に失敗しました");
347                 }
348             }
349             finally
350             {
351                 d3dex.Dispose();
352             }
353         }
354
355         void DestructDevice()
356         {
357             if (this.device != null)
358             {
359                 this.device.Dispose();
360                 this.device = null;
361             }
362             if (this.device9 != null)
363             {
364                 this.device9.Dispose();
365                 this.device9 = null;
366             }
367         }
368
369         protected override void ReCreateTarget()
370         {
371             System.Diagnostics.Debug.WriteLine("ReCreatedDevice");
372             this.DestructDevice();
373             this.CreateDevice();
374             this.ReConstructDeviceResource();
375         }
376
377         protected override D2D.RenderTarget ConstructRender(D2D.Factory factory, D2D.RenderTargetProperties prop, double width, double height)
378         {
379             D3D11.Texture2DDescription desc = new D3D11.Texture2DDescription();
380             desc.Width = (int)width;
381             desc.Height = (int)height;
382             desc.MipLevels = 1;
383             desc.ArraySize = 1;
384             desc.Format = DXGI.Format.B8G8R8A8_UNorm;
385             desc.SampleDescription = new DXGI.SampleDescription(1, 0);
386             desc.Usage = D3D11.ResourceUsage.Default;
387             desc.BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource;
388             desc.CpuAccessFlags = D3D11.CpuAccessFlags.None;
389             desc.OptionFlags = D3D11.ResourceOptionFlags.Shared;
390             this.texture = new D3D11.Texture2D(this.device, desc);
391
392             this.surface = this.texture.QueryInterface<DXGI.Surface>();
393
394             DXGI.Resource resource = this.texture.QueryInterface<DXGI.Resource>();
395             IntPtr handel = resource.SharedHandle;
396             D3D9.Texture texture = new D3D9.Texture(
397                 this.device9,
398                 this.texture.Description.Width,
399                 this.texture.Description.Height,
400                 1,
401                 D3D9.Usage.RenderTarget,
402                 D3D9.Format.A8R8G8B8,
403                 D3D9.Pool.Default,
404                 ref handel);
405             this.surface9 = texture.GetSurfaceLevel(0);
406             resource.Dispose();
407             texture.Dispose();
408
409             this.render = new D2D.RenderTarget(factory, this.surface, prop);
410             return this.render;
411         }
412
413         protected override void ConstrctedResource()
414         {
415         }
416
417         public double GetScale()
418         {
419             float dpi;
420             this.GetDpi(out dpi, out dpi);
421             return dpi / 96.0;
422         }
423
424         public override void GetDpi(out float dpix, out float dpiy)
425         {
426             var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
427             var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
428
429             dpix = (int)dpiXProperty.GetValue(null, null);
430             dpiy = (int)dpiYProperty.GetValue(null, null);
431         }
432
433         protected override void DestructRender()
434         {
435             if (this.texture != null)
436                 this.texture.Dispose();
437             if (this.surface != null)
438                 this.surface.Dispose();
439             if (this.surface9 != null)
440                 this.surface9.Dispose();
441             if (this.render != null)
442                 this.render.Dispose();
443         }
444     }
445 }