OSDN Git Service

初コミット
[fooeditengine/FooEditEngine.git] / Common / Direct2D / D2DRenderCommon.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 FooEditEngine;
16 using SharpDX;
17 using D2D = SharpDX.Direct2D1;
18 using DW = SharpDX.DirectWrite;
19 using DXGI = SharpDX.DXGI;
20 using System.Runtime.InteropServices;
21
22 namespace FooEditEngine
23 {
24     delegate void PreDrawOneLineHandler(MyTextLayout layout);
25
26     delegate void GetDpiHandler(out int dpix,out int dpiy);
27
28     /// <summary>
29     /// 文字列のアンチエイリアシングモードを指定する
30     /// </summary>
31     public enum TextAntialiasMode
32     {
33         /// <summary>
34         /// 最適なものが選択されます
35         /// </summary>
36         Default = D2D.TextAntialiasMode.Default,
37         /// <summary>
38         /// ClearTypeでアンチエイリアシングを行います
39         /// </summary>
40         ClearType = D2D.TextAntialiasMode.Cleartype,
41         /// <summary>
42         /// グレイスケールモードでアンチエイリアシングを行います
43         /// </summary>
44         GrayScale = D2D.TextAntialiasMode.Grayscale,
45         /// <summary>
46         /// アンチエイリアシングを行いません
47         /// </summary>
48         Aliased = D2D.TextAntialiasMode.Aliased,
49     }
50
51     sealed class ColorBrushCollection
52     {
53         ResourceManager<Color4, D2D.SolidColorBrush> cache = new ResourceManager<Color4, D2D.SolidColorBrush>();
54         D2D.RenderTarget _render;
55
56         public event EventHandler RenderChanged;
57
58         public D2D.RenderTarget Render
59         {
60             get
61             {
62                 return this._render;
63             }
64             set
65             {
66                 this._render = value;
67                 if (this.RenderChanged != null)
68                     this.RenderChanged(this, null);
69             }
70         }
71
72         public D2D.SolidColorBrush Get(Color4 key)
73         {
74             if (this.Render == null || this.Render.IsDisposed)
75                 throw new InvalidOperationException();
76             D2D.SolidColorBrush brush;
77             bool result = cache.TryGetValue(key, out brush);
78             if (!result)
79             {
80                 brush = new D2D.SolidColorBrush(this.Render, key);
81                 cache.Add(key, brush);
82             }
83             
84             return brush;
85         }
86
87         public void Clear()
88         {
89             cache.Clear();
90         }
91     }
92
93     sealed class StrokeCollection
94     {
95         ResourceManager<HilightType, D2D.StrokeStyle> cache = new ResourceManager<HilightType, D2D.StrokeStyle>();
96
97         public D2D.Factory Factory;
98
99         public D2D.StrokeStyle Get(HilightType type)
100         {
101             if(this.Factory == null || this.Factory.IsDisposed)
102                 throw new InvalidOperationException();
103             D2D.StrokeStyle stroke;
104             if (this.cache.TryGetValue(type, out stroke))
105                 return stroke;
106
107             D2D.StrokeStyleProperties prop = new D2D.StrokeStyleProperties();
108             prop.DashCap = D2D.CapStyle.Flat;
109             prop.DashOffset = 0;
110             prop.DashStyle = D2D.DashStyle.Solid;
111             prop.EndCap = D2D.CapStyle.Flat;
112             prop.LineJoin = D2D.LineJoin.Miter;
113             prop.MiterLimit = 0;
114             prop.StartCap = D2D.CapStyle.Flat;
115             switch (type)
116             {
117                 case HilightType.Sold:
118                 case HilightType.Url:
119                 case HilightType.Squiggle:
120                     prop.DashStyle = D2D.DashStyle.Solid;
121                     break;
122                 case HilightType.Dash:
123                     prop.DashStyle = D2D.DashStyle.Dash;
124                     break;
125                 case HilightType.DashDot:
126                     prop.DashStyle = D2D.DashStyle.DashDot;
127                     break;
128                 case HilightType.DashDotDot:
129                     prop.DashStyle = D2D.DashStyle.DashDotDot;
130                     break;
131                 case HilightType.Dot:
132                     prop.DashStyle = D2D.DashStyle.Dot;
133                     break;
134             }
135             stroke = new D2D.StrokeStyle(this.Factory, prop);
136             this.cache.Add(type, stroke);
137             return stroke;
138         }
139         public void Clear()
140         {
141             cache.Clear();
142         }
143     }
144
145     sealed class EffectCollection
146     {
147         ResourceManager<Color4, ResourceManager<HilightType, DrawingEffect>> cache = new ResourceManager<Color4, ResourceManager<HilightType, DrawingEffect>>();
148         public DrawingEffect Get(Color4 color, HilightType type)
149         {
150             ResourceManager<HilightType, DrawingEffect> hilights;
151             DrawingEffect effect;
152             if (this.cache.TryGetValue(color, out hilights))
153             {
154                 if (hilights.TryGetValue(type, out effect))
155                     return effect;
156                 effect = new DrawingEffect(type, color);
157                 hilights.Add(type, effect);
158                 return effect;
159             }
160             effect = new DrawingEffect(type, color);
161             hilights = new ResourceManager<HilightType, DrawingEffect>();
162             hilights.Add(type, effect);
163             this.cache.Add(color, hilights);
164             return effect;
165         }
166         public void Clear()
167         {
168             foreach (ResourceManager<HilightType, DrawingEffect> hilights in this.cache.Values)
169                 hilights.Clear();
170             cache.Clear();
171         }
172     }
173
174     sealed class D2DRenderCommon : IDisposable
175     {
176         ColorBrushCollection Brushes = new ColorBrushCollection();
177         StrokeCollection Strokes = new StrokeCollection();
178         EffectCollection Effects = new EffectCollection();
179         InlineManager HiddenChars;
180         TextAntialiasMode _TextAntialiasMode;
181         Color4 _ControlChar,_Forground,_URL,_Hilight;
182         DW.Factory DWFactory;
183 #if METRO
184         D2D.Factory1 D2DFactory;
185 #else
186         D2D.Factory D2DFactory;
187 #endif
188         DW.TextFormat format;
189         D2D.Bitmap bitmap;
190         D2D.RenderTarget render;
191         CustomTextRenderer textRender;
192         int tabLength = 8;
193         bool hasCache;
194         Size renderSize = new Size();
195         Color4 _Comment, _Literal, _Keyword1, _Keyword2;
196
197         public D2DRenderCommon()
198         {
199             this.DWFactory = new DW.Factory(DW.FactoryType.Shared);
200 #if METRO
201             this.D2DFactory = new D2D.Factory1(D2D.FactoryType.MultiThreaded);
202 #else
203             this.D2DFactory = new D2D.Factory(D2D.FactoryType.MultiThreaded);
204 #endif
205             this.Strokes.Factory = this.D2DFactory;
206             this.ChangedRenderResource += (s, e) => { };
207             this.ChangedRightToLeft += (s, e) => { };
208         }
209
210         public event ChangedRenderResourceEventHandler ChangedRenderResource;
211
212         public event EventHandler ChangedRightToLeft;
213
214         public void InitTextFormat(string fontName, float fontSize, DW.FontWeight fontWeigth = DW.FontWeight.Normal,DW.FontStyle fontStyle = DW.FontStyle.Normal)
215         {
216             if(this.format != null)
217                 this.format.Dispose();
218             
219             this.format = new DW.TextFormat(this.DWFactory, fontName,fontWeigth,fontStyle, fontSize / 72.0f * 96.0f);
220             this.format.WordWrapping = DW.WordWrapping.NoWrap;
221
222             if (this.HiddenChars == null)
223             {
224                 this.HiddenChars = new InlineManager(this.DWFactory, this.format, this.ControlChar, this.Brushes);
225                 this.HiddenChars.TabWidth = this.HiddenChars.TabWidth;
226             }
227             else
228                 this.HiddenChars.Format = this.format;
229
230             this.hasCache = false;
231
232             DW.TextLayout layout = new DW.TextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue);
233             layout.ReadingDirection = DW.ReadingDirection.LeftToRight;
234             this.emSize = new Size(layout.Metrics.WidthIncludingTrailingWhitespace, layout.Metrics.Height);
235
236             layout = new DW.TextLayout(this.DWFactory, "+", this.format, float.MaxValue, float.MaxValue);
237             layout.ReadingDirection = DW.ReadingDirection.LeftToRight;
238             this.FoldingWidth = layout.Metrics.Width;
239
240             this.ChangedRenderResource(this,new ChangedRenderRsourceEventArgs(ResourceType.Font));
241         }
242
243         public bool RightToLeft
244         {
245             get
246             {
247                 return this.format.ReadingDirection == DW.ReadingDirection.RightToLeft;
248             }
249             set
250             {
251                 this.format.ReadingDirection = value ? DW.ReadingDirection.RightToLeft : DW.ReadingDirection.LeftToRight;
252                 this.ChangedRightToLeft(this, null);                
253             }
254         }
255
256         public TextAntialiasMode TextAntialiasMode
257         {
258             get
259             {
260                 return this._TextAntialiasMode;
261             }
262             set
263             {
264                 if (this.render == null)
265                     throw new InvalidOperationException();
266                 this._TextAntialiasMode = value;
267                 this.render.TextAntialiasMode = (D2D.TextAntialiasMode)value;
268                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Antialias));
269             }
270         }
271
272         public bool ShowFullSpace
273         {
274             get
275             {
276                 if (this.HiddenChars == null)
277                     return false;
278                 else
279                     return this.HiddenChars.ContainsSymbol(' ');
280             }
281             set
282             {
283                 if (this.HiddenChars == null)
284                     throw new InvalidOperationException();
285                 if (value)
286                     this.HiddenChars.AddSymbol(' ', '□');
287                 else
288                     this.HiddenChars.RemoveSymbol(' ');
289                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
290             }
291         }
292
293         public bool ShowHalfSpace
294         {
295             get
296             {
297                 if (this.HiddenChars == null)
298                     return false;
299                 else
300                     return this.HiddenChars.ContainsSymbol(' ');
301             }
302             set
303             {
304                 if (this.HiddenChars == null)
305                     throw new InvalidOperationException();
306                 if (value)
307                     this.HiddenChars.AddSymbol(' ', 'ロ');
308                 else
309                     this.HiddenChars.RemoveSymbol(' ');
310                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
311             }
312         }
313
314         public bool ShowTab
315         {
316             get
317             {
318                 if (this.HiddenChars == null)
319                     return false;
320                 else
321                     return this.HiddenChars.ContainsSymbol('\t');
322             }
323             set
324             {
325                 if (this.HiddenChars == null)
326                     throw new InvalidOperationException();
327                 if (value)
328                     this.HiddenChars.AddSymbol('\t', '>');
329                 else
330                     this.HiddenChars.RemoveSymbol('\t');
331                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
332             }
333         }
334
335         public Color4 Foreground
336         {
337             get
338             {
339                 return this._Forground;
340             }
341             set
342             {
343                 if (this.render == null)
344                     return;
345                 this._Forground = value;
346                 if (this.textRender != null)
347                     this.textRender.DefaultFore = value;
348                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
349             }
350         }
351
352         public Color4 Background
353         {
354             get;
355             set;
356         }
357
358         public Color4 InsertCaret
359         {
360             get;
361             set;
362         }
363
364         public Color4 OverwriteCaret
365         {
366             get;
367             set;
368         }
369
370         public Color4 LineMarker
371         {
372             get;
373             set;
374         }
375
376         public Color4 ControlChar
377         {
378             get
379             {
380                 return this._ControlChar;
381             }
382             set
383             {
384                 if (this.render == null)
385                     return;
386                 this._ControlChar = value;
387                 if (this.HiddenChars != null)
388                     this.HiddenChars.Fore = value;
389                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
390             }
391         }
392
393         public Color4 Url
394         {
395             get
396             {
397                 return this._URL;
398             }
399             set
400             {
401                 if (this.render == null)
402                     return;
403
404                 this._URL = value;
405                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
406             }
407         }
408
409         public Color4 Hilight
410         {
411             get
412             {
413                 return this._Hilight;
414             }
415             set
416             {
417                 this._Hilight = value;
418             }
419         }
420
421         public Color4 Comment
422         {
423             get
424             {
425                 return this._Comment;
426             }
427             set
428             {
429                 this._Comment = value;
430                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
431             }
432         }
433
434         public Color4 Literal
435         {
436             get
437             {
438                 return this._Literal;
439             }
440             set
441             {
442                 this._Literal = value;
443                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
444             }
445         }
446
447         public Color4 Keyword1
448         {
449             get
450             {
451                 return this._Keyword1;
452             }
453             set
454             {
455                 this._Keyword1 = value;
456                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
457             }
458         }
459
460         public Color4 Keyword2
461         {
462             get
463             {
464                 return this._Keyword2;
465             }
466             set
467             {
468                 this._Keyword2 = value;
469                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
470             }
471         }
472
473         public Rectangle ClipRect
474         {
475             get;
476             set;
477         }
478
479         public double LineNemberWidth
480         {
481             get
482             {
483                 return this.emSize.Width * (EditView.LineNumberLength + 1);
484             }
485         }
486
487         public double FoldingWidth
488         {
489             get;
490             private set;
491         }
492
493         public int TabWidthChar
494         {
495             get { return this.tabLength; }
496             set
497             {
498                 this.tabLength = value;
499                 DW.TextLayout layout = new DW.TextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue);
500                 float width = (float)(layout.Metrics.Width * value);
501                 this.HiddenChars.TabWidth = width;
502                 this.format.IncrementalTabStop = width;
503             }
504         }
505
506         public Size emSize
507         {
508             get;
509             private set;
510         }
511
512         public void DrawGripper(Point p, double radius)
513         {
514             D2D.Ellipse ellipse = new D2D.Ellipse();
515             ellipse.Point = p;
516             ellipse.RadiusX = (float)radius;
517             ellipse.RadiusY = (float)radius;
518             this.render.FillEllipse(ellipse, this.Brushes.Get(this.Background));
519             this.render.DrawEllipse(ellipse, this.Brushes.Get(this.Foreground));
520         }
521
522         public void ReConstructDeviceResource()
523         {
524             this.ReConstructDeviceResource(this.renderSize.Width, this.renderSize.Height);
525         }
526
527         public void ReConstructDeviceResource(double width, double height)
528         {
529             this.DestructDeviceResource();
530             this.ConstructDeviceResource(width, height);
531         }
532
533         public void DrawCachedBitmap(Rectangle rect)
534         {
535             if (this.render == null || this.render.IsDisposed)
536                 return;
537             render.DrawBitmap(this.bitmap, rect, 1.0f, D2D.BitmapInterpolationMode.Linear, rect);
538         }
539
540         public void CacheContent()
541         {
542             if (this.render == null || this.bitmap == null || this.bitmap.IsDisposed || this.render.IsDisposed)
543                 return;
544             render.Flush();
545             this.bitmap.CopyFromRenderTarget(this.render, new SharpDX.Point(), new SharpDX.Rectangle(0, 0, (int)this.renderSize.Width, (int)this.renderSize.Height));
546             this.hasCache = true;
547         }
548
549         public bool IsVaildCache()
550         {
551             return this.hasCache;
552         }
553
554         public void BegineDraw()
555         {
556             if (this.render == null || this.render.IsDisposed)
557                 return;
558             this.render.BeginDraw();
559             this.render.AntialiasMode = D2D.AntialiasMode.Aliased;
560         }
561
562         public void EndDraw()
563         {
564             if (this.render == null || this.render.IsDisposed)
565                 return;
566             this.render.AntialiasMode = D2D.AntialiasMode.PerPrimitive;
567             this.render.EndDraw();
568         }
569
570         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect)
571         {
572             if (this.render == null || this.render.IsDisposed)
573                 return;
574             DW.TextLayout layout = new DW.TextLayout(this.DWFactory, str, this.format, (float)layoutRect.Width, (float)layoutRect.Height);
575             switch (align)
576             {
577                 case StringAlignment.Left:
578                     layout.TextAlignment = DW.TextAlignment.Leading;
579                     break;
580                 case StringAlignment.Center:
581                     layout.TextAlignment = DW.TextAlignment.Center;
582                     break;
583                 case StringAlignment.Right:
584                     layout.TextAlignment = DW.TextAlignment.Trailing;
585                     break;
586             }
587             D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
588             this.render.DrawTextLayout(new Vector2((float)x, (float)y), layout, brush);
589             layout.Dispose();
590         }
591
592         public void DrawFoldingMark(bool expand, double x, double y)
593         {
594             string mark = expand ? "-" : "+";
595             this.DrawString(mark, x, y,StringAlignment.Left, new Size(this.FoldingWidth, this.emSize.Height));
596         }
597
598         public void FillRectangle(Rectangle rect,FillRectType type)
599         {
600             const float lineMarkerThickness = 2;
601             if (this.render == null || this.render.IsDisposed)
602                 return;
603             D2D.SolidColorBrush brush = null;
604             switch(type)
605             {
606                 case FillRectType.OverwriteCaret:
607                     brush = this.Brushes.Get(this.OverwriteCaret);
608                     this.render.FillRectangle(rect, brush);
609                     break;
610                 case FillRectType.InsertCaret:
611                     brush = this.Brushes.Get(this.InsertCaret);
612                     this.render.FillRectangle(rect, brush);
613                     break;
614                 case FillRectType.InsertPoint:
615                     brush = this.Brushes.Get(this.Hilight);
616                     this.render.FillRectangle(rect, brush);
617                     break;
618                 case FillRectType.LineMarker:
619                     brush = this.Brushes.Get(this.LineMarker);
620                     this.render.DrawRectangle(rect, brush, lineMarkerThickness);
621                     break;
622             }
623         }
624
625         public void FillBackground(Rectangle rect)
626         {
627             if (this.render == null || this.render.IsDisposed)
628                 return;
629             this.render.Clear(this.Background);
630         }
631
632         public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges,PreDrawOneLineHandler PreDrawOneLine)
633         {
634             LineToIndexTableData lineData = lti.GetData(row);
635
636             if (lineData.Length == 0 || this.render == null || this.render.IsDisposed)
637                 return;
638
639             MyTextLayout layout = (MyTextLayout)lti.GetData(row).Layout;
640
641             this.render.PushAxisAlignedClip(this.ClipRect, D2D.AntialiasMode.Aliased);
642
643             if(PreDrawOneLine != null)
644                 PreDrawOneLine(layout);
645
646             if (SelectRanges != null)
647             {
648                 foreach (Selection sel in SelectRanges)
649                 {
650                     if (sel.length == 0 || sel.start == -1)
651                         continue;
652
653                     this.DrawMarkerEffect(layout, HilightType.Select, sel.start, sel.length, x, y, false);
654                 }
655             }
656
657             layout.Draw(textRender, (float)x, (float)y);
658
659             this.render.PopAxisAlignedClip();
660         }
661
662         public void SetTextColor(MyTextLayout layout,int start, int length, Color4? color)
663         {
664             if (color == null)
665                 return;
666             layout.SetDrawingEffect(this.Brushes.Get((Color4)color), new DW.TextRange(start, length));
667         }
668
669         public void DrawLine(Point from, Point to)
670         {
671             D2D.Brush brush = this.Brushes.Get(this.Foreground);
672             D2D.StrokeStyle stroke = this.Strokes.Get(HilightType.Sold);
673             this.render.DrawLine(from, to, brush, 1.0f, stroke);
674         }
675
676         public void DrawMarkerEffect(MyTextLayout layout, HilightType type, int start, int length, double x, double y, bool isBold, Color4? effectColor = null)
677         {
678             if (type == HilightType.None)
679                 return;
680
681             float thickness = isBold ? 2 : 1;
682
683             Color4 color;
684             if (effectColor != null)
685                 color = (Color4)effectColor;
686             else if (type == HilightType.Select)
687                 color = this.Hilight;
688             else
689                 color = this.Foreground;
690
691             IMarkerEffecter effecter = null;
692             D2D.SolidColorBrush brush = this.Brushes.Get(color);
693
694             if (type == HilightType.Squiggle)
695                 effecter = new D2DSquilleLineMarker(this.render, brush, this.Strokes.Get(HilightType.Squiggle), thickness);
696             else if (type == HilightType.Select)
697                 effecter = new HilightMarker(this.render, brush);
698             else if (type == HilightType.None)
699                 effecter = null;
700             else
701                 effecter = new LineMarker(this.render, brush, this.Strokes.Get(type), thickness);
702
703             if (effecter != null)
704             {
705                 bool isUnderline = type != HilightType.Select;
706
707                 DW.HitTestMetrics[] metrics = layout.HitTestTextRange(start, length, (float)x, (float)y);
708                 foreach (DW.HitTestMetrics metric in metrics)
709                 {
710                     float offset = isUnderline ? metric.Height : 0;
711                     effecter.Draw(metric.Left, metric.Top + offset, metric.Width, metric.Height);
712                 }
713             }
714         }
715
716         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
717        {
718             MyTextLayout newLayout = new MyTextLayout(new DW.TextLayout(this.DWFactory, 
719                 //this.HiddenChars.ContainsSymbol('\t') ? str.Replace('\t', '0'): str,    //プロポーショナルフォント対策
720                 str,
721                 this.format, 
722                 (float)this.ClipRect.Width, 
723                 (float)this.ClipRect.Height));
724             ParseLayout(newLayout, str);
725             if (syntaxCollection != null)
726             {
727                 foreach (SyntaxInfo s in syntaxCollection)
728                 {
729                     D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
730                     switch (s.type)
731                     {
732                         case TokenType.Comment:
733                             brush = this.Brushes.Get(this.Comment);
734                             break;
735                         case TokenType.Keyword1:
736                             brush = this.Brushes.Get(this.Keyword1);
737                             break;
738                         case TokenType.Keyword2:
739                             brush = this.Brushes.Get(this.Keyword2);
740                             break;
741                         case TokenType.Literal:
742                             brush = this.Brushes.Get(this.Literal);
743                             break;
744                     }
745                     newLayout.SetDrawingEffect(brush, new DW.TextRange(s.index, s.length));
746                 }
747             }
748
749             if (MarkerRanges != null)
750             {
751                 foreach (Marker m in MarkerRanges)
752                 {
753                     if (m.start == -1 || m.length == 0)
754                         continue;
755                     Color4 color = new Color4(m.R / 255.0f, m.G / 255.0f, m.B / 255.0f, m.A / 255.0f);
756                     if (m.hilight == HilightType.Url)
757                         color = this.Url;
758                     newLayout.SetDrawingEffect(this.Effects.Get(color, m.hilight), new DW.TextRange(m.start, m.length));
759                 }
760             }
761
762             return newLayout;
763        }
764
765        public List<LineToIndexTableData> BreakLine(Document doc, int startIndex, int endIndex, double wrapwidth)
766        {
767             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
768
769             this.format.WordWrapping = DW.WordWrapping.Wrap;
770
771             foreach (string str in doc.GetLines(startIndex, endIndex))
772             {
773                 DW.TextLayout layout = new DW.TextLayout(this.DWFactory, str, this.format, (float)wrapwidth, float.MaxValue);
774
775                 int i = startIndex;
776                 foreach (DW.LineMetrics metrics in layout.GetLineMetrics())
777                 {
778                     if (metrics.Length == 0 && metrics.NewlineLength == 0)
779                         continue;
780
781                     bool lineend = false;
782                     if (metrics.NewlineLength > 0)
783                         lineend = true;
784
785                     output.Add(new LineToIndexTableData(i, (int)metrics.Length, lineend, null));
786                     i += Util.RoundUp(metrics.Length);
787                 }
788
789                 layout.Dispose();
790
791                 startIndex += str.Length;
792             }
793
794             this.format.WordWrapping = DW.WordWrapping.NoWrap;
795
796             if (output.Count > 0)
797                 output.Last().LineEnd = true;
798
799             return output;
800         }
801
802         public void Dispose()
803         {
804             this.DestructDeviceResource();
805             this.HiddenChars.Clear();
806             if (this.format != null)
807                 this.format.Dispose();
808             if(this.DWFactory != null)
809                 this.DWFactory.Dispose();
810             if(this.D2DFactory != null)
811                 this.D2DFactory.Dispose();
812         }
813
814         public void ConstructDeviceResource(double width, double height)
815         {
816             int dpiX, dpiY;
817             this.GetDpi(out dpiX, out dpiY);
818             D2D.RenderTargetProperties prop = new D2D.RenderTargetProperties(
819                 D2D.RenderTargetType.Default,
820                 new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore),
821                 dpiX,
822                 dpiY,
823                 D2D.RenderTargetUsage.None,
824                 D2D.FeatureLevel.Level_DEFAULT);
825
826             this.render = this.ConstructRender(this.D2DFactory,prop,width,height);
827
828             this.Brushes.Render = this.render;
829
830             D2D.BitmapProperties bmpProp = new D2D.BitmapProperties();
831             bmpProp.DpiX = dpiX;
832             bmpProp.DpiY = dpiY;
833             bmpProp.PixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore);
834             this.bitmap = new D2D.Bitmap(this.render, new SharpDX.Size2((int)width, (int)height), bmpProp);
835             this.hasCache = false;
836
837             this.ConstrctedResource();
838
839             this.textRender = new CustomTextRenderer(this.render, this.Brushes,this.Strokes, this.Foreground);
840
841             this.renderSize.Width = width;
842             this.renderSize.Height = height;
843
844             this.TextAntialiasMode = this._TextAntialiasMode;
845         }
846
847 #if METRO
848         public System.Func<D2D.Factory1,D2D.RenderTargetProperties,double,double,D2D.RenderTarget> ConstructRender;
849 #else
850         public System.Func<D2D.Factory,D2D.RenderTargetProperties,double,double,D2D.RenderTarget> ConstructRender;
851 #endif
852         public System.Action ConstrctedResource;
853
854         public GetDpiHandler GetDpi;
855
856         public System.Action DestructRender;
857
858         public System.Action ReCreateTarget;
859
860         public void DestructDeviceResource()
861         {
862             this.hasCache = false;
863             if (this.bitmap != null)
864                 this.bitmap.Dispose();
865             this.Brushes.Clear();
866             this.Strokes.Clear();
867             this.Effects.Clear();
868             if (this.textRender != null)
869                 this.textRender.Dispose();
870             this.DestructRender();
871         }
872
873         void ParseLayout(MyTextLayout layout, string str)
874         {
875             for (int i = 0; i < str.Length; i++)
876             {
877                 DW.InlineObject inlineObject = this.HiddenChars.Get(layout,i, str);
878                 if (inlineObject != null)
879                 {
880                     layout.SetInlineObject(inlineObject, new DW.TextRange(i, 1));
881                     layout.SetDrawingEffect(this.Brushes.Get(this.ControlChar), new DW.TextRange(i, 1));
882                 }
883             }
884             return;
885         }
886     }
887 }