OSDN Git Service

ed6d9da55d1f2cecf2502bf5acc07da1ea89028d
[fooeditengine/FooEditEngine.git] / Core / 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,LineToIndexTable lti,int row,double x,double y);
25
26     delegate void GetDpiHandler(out float dpix,out float 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                 this.Clear();
68                 if (this.RenderChanged != null)
69                     this.RenderChanged(this, null);
70             }
71         }
72
73         public D2D.SolidColorBrush Get(Color4 key)
74         {
75             if (this.Render == null || this.Render.IsDisposed)
76                 throw new InvalidOperationException();
77             D2D.SolidColorBrush brush;
78             bool result = cache.TryGetValue(key, out brush);
79             if (!result)
80             {
81                 brush = new D2D.SolidColorBrush(this.Render, key);
82                 cache.Add(key, brush);
83             }
84             
85             return brush;
86         }
87
88         public void Clear()
89         {
90             cache.Clear();
91         }
92     }
93
94     sealed class StrokeCollection
95     {
96         ResourceManager<HilightType, D2D.StrokeStyle> cache = new ResourceManager<HilightType, D2D.StrokeStyle>();
97
98         public D2D.StrokeStyle Get(D2D.RenderTarget render,HilightType type)
99         {
100             return this.Get(render.Factory, type);
101         }
102
103         [Obsolete]
104         public D2D.StrokeStyle Get(HilightType type)
105         {
106             return this.Get(D2DRenderShared.D2DFactory, type);
107         }
108
109         public D2D.StrokeStyle Get(D2D.Factory factory,HilightType type)
110         {
111             D2D.StrokeStyle stroke;
112             if (this.cache.TryGetValue(type, out stroke))
113                 return stroke;
114
115             D2D.StrokeStyleProperties prop = new D2D.StrokeStyleProperties();
116             prop.DashCap = D2D.CapStyle.Flat;
117             prop.DashOffset = 0;
118             prop.DashStyle = D2D.DashStyle.Solid;
119             prop.EndCap = D2D.CapStyle.Flat;
120             prop.LineJoin = D2D.LineJoin.Miter;
121             prop.MiterLimit = 0;
122             prop.StartCap = D2D.CapStyle.Flat;
123             switch (type)
124             {
125                 case HilightType.Sold:
126                 case HilightType.Url:
127                 case HilightType.Squiggle:
128                     prop.DashStyle = D2D.DashStyle.Solid;
129                     break;
130                 case HilightType.Dash:
131                     prop.DashStyle = D2D.DashStyle.Dash;
132                     break;
133                 case HilightType.DashDot:
134                     prop.DashStyle = D2D.DashStyle.DashDot;
135                     break;
136                 case HilightType.DashDotDot:
137                     prop.DashStyle = D2D.DashStyle.DashDotDot;
138                     break;
139                 case HilightType.Dot:
140                     prop.DashStyle = D2D.DashStyle.Dot;
141                     break;
142             }
143             stroke = new D2D.StrokeStyle(D2DRenderShared.D2DFactory, prop);
144             this.cache.Add(type, stroke);
145             return stroke;
146         }
147         public void Clear()
148         {
149             cache.Clear();
150         }
151     }
152
153     class D2DRenderShared
154     {
155         static DW.Factory _DWFactory;
156         static public DW.Factory DWFactory
157         {
158             get
159             {
160                 if(_DWFactory == null)
161                 {
162                     _DWFactory = new DW.Factory(DW.FactoryType.Shared);
163                 }
164                 return _DWFactory;
165             }
166         }
167 #if METRO || WINDOWS_UWP
168         static D2D.Factory1 _D2DFactory;
169
170         static public D2D.Factory1 D2DFactory
171         {
172             get
173             {
174                 if (_D2DFactory == null)
175                 {
176                     _D2DFactory = new D2D.Factory1(D2D.FactoryType.SingleThreaded);
177                 }
178                 return _D2DFactory;
179             }
180         }
181 #else
182         static D2D.Factory _D2DFactory;
183         static public D2D.Factory D2DFactory
184         {
185             get
186             {
187                 if (_D2DFactory == null)
188                 {
189                     _D2DFactory = new D2D.Factory(D2D.FactoryType.SingleThreaded);
190                 }
191                 return _D2DFactory;
192             }
193         }
194 #endif
195
196     }
197
198     class D2DRenderCommon : IDisposable
199     {
200         InlineManager HiddenChars;
201         TextAntialiasMode _TextAntialiasMode;
202         Color4 _ControlChar,_Forground,_URL,_Hilight;
203         DW.TextFormat format;
204         protected CustomTextRenderer textRender;
205         protected D2D.Bitmap cachedBitMap;
206         int tabLength = 8;
207         bool _ShowLineBreak,_RightToLeft;
208         Color4 _Comment, _Literal, _Keyword1, _Keyword2;
209         protected bool hasCache = false;
210         protected Size renderSize;
211
212         protected ColorBrushCollection Brushes
213         {
214             get;
215             private set;
216         }
217
218         protected StrokeCollection Strokes
219         {
220             get;
221             private set;
222         }
223
224         D2D.RenderTarget _render;
225         protected D2D.RenderTarget render
226         {
227             get { return _render; }
228             set
229             {
230                 _render = value;
231                 this.TextAntialiasMode = this._TextAntialiasMode;
232                 this.Brushes.Render = this.render;
233             }
234         }
235
236         public D2DRenderCommon()
237         {
238             this.Brushes = new ColorBrushCollection();
239             this.Strokes = new StrokeCollection();
240             this.ChangedRenderResource += (s, e) => { };
241             this.ChangedRightToLeft += (s, e) => { };
242             this.renderSize = new Size();
243         }
244
245         public event ChangedRenderResourceEventHandler ChangedRenderResource;
246
247         public event EventHandler ChangedRightToLeft;
248
249         public const int MiniumeWidth = 40;    //これ以上ないと誤操作が起こる
250
251         public void InitTextFormat(string fontName, float fontSize, DW.FontWeight fontWeigth = DW.FontWeight.Normal,DW.FontStyle fontStyle = DW.FontStyle.Normal)
252         {
253             if(this.format != null)
254                 this.format.Dispose();
255
256             if (this.HiddenChars != null)
257                 this.HiddenChars = null;
258
259             float dpix, dpiy;
260             this.GetDpi(out dpix, out dpiy);
261
262             this.format = new DW.TextFormat(D2DRenderShared.DWFactory, fontName, fontWeigth, fontStyle, fontSize);
263             this.format.WordWrapping = DW.WordWrapping.NoWrap;
264             this.format.ReadingDirection = GetDWRightDirect(_RightToLeft);
265
266             if (this.HiddenChars == null)
267                 this.HiddenChars = new InlineManager(D2DRenderShared.DWFactory, this.format, this.ControlChar, this.Brushes);
268             else
269                 this.HiddenChars.Format = this.format;
270
271             this.TabWidthChar = this.TabWidthChar;
272
273             this.hasCache = false;
274
275             MyTextLayout layout = new MyTextLayout(D2DRenderShared.DWFactory, "0", this.format, float.MaxValue, float.MaxValue, dpix, false);
276             layout.RightToLeft = false;
277             this.emSize = new Size(layout.Width, layout.Height);
278             layout.Dispose();
279
280             layout = new MyTextLayout(D2DRenderShared.DWFactory, "+", this.format, float.MaxValue, float.MaxValue, dpix, false);
281             layout.RightToLeft = false;
282 #if METRO
283             this.FoldingWidth = Math.Max(D2DRenderCommon.MiniumeWidth, layout.Width);
284 #else
285             this.FoldingWidth = layout.Width;
286 #endif
287             layout.Dispose();
288
289             this.ChangedRenderResource(this,new ChangedRenderRsourceEventArgs(ResourceType.Font));
290         }
291
292         DW.ReadingDirection GetDWRightDirect(bool rtl)
293         {
294             return rtl ? DW.ReadingDirection.RightToLeft : DW.ReadingDirection.LeftToRight;
295         }
296
297         public bool RightToLeft
298         {
299             get
300             {
301                 return _RightToLeft;
302             }
303             set
304             {
305                 _RightToLeft = value;
306                 this.format.ReadingDirection = GetDWRightDirect(value);
307                 this.ChangedRightToLeft(this, null);                
308             }
309         }
310
311         public TextAntialiasMode TextAntialiasMode
312         {
313             get
314             {
315                 return this._TextAntialiasMode;
316             }
317             set
318             {
319                 if (this.render == null)
320                     throw new InvalidOperationException();
321                 this._TextAntialiasMode = value;
322                 this.render.TextAntialiasMode = (D2D.TextAntialiasMode)value;
323                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Antialias));
324             }
325         }
326
327         public bool ShowFullSpace
328         {
329             get
330             {
331                 if (this.HiddenChars == null)
332                     return false;
333                 else
334                     return this.HiddenChars.ContainsSymbol(' ');
335             }
336             set
337             {
338                 if (this.HiddenChars == null)
339                     throw new InvalidOperationException();
340                 if (value)
341                     this.HiddenChars.AddSymbol(' ', '□');
342                 else
343                     this.HiddenChars.RemoveSymbol(' ');
344             }
345         }
346
347         public bool ShowHalfSpace
348         {
349             get
350             {
351                 if (this.HiddenChars == null)
352                     return false;
353                 else
354                     return this.HiddenChars.ContainsSymbol(' ');
355             }
356             set
357             {
358                 if (this.HiddenChars == null)
359                     throw new InvalidOperationException();
360                 if (value)
361                     this.HiddenChars.AddSymbol(' ', 'ロ');
362                 else
363                     this.HiddenChars.RemoveSymbol(' ');
364             }
365         }
366
367         public bool ShowTab
368         {
369             get
370             {
371                 if (this.HiddenChars == null)
372                     return false;
373                 else
374                     return this.HiddenChars.ContainsSymbol('\t');
375             }
376             set
377             {
378                 if (this.HiddenChars == null)
379                     throw new InvalidOperationException();
380                 if (value)
381                     this.HiddenChars.AddSymbol('\t', '>');
382                 else
383                     this.HiddenChars.RemoveSymbol('\t');
384             }
385         }
386
387         public bool ShowLineBreak
388         {
389             get
390             {
391                 return this._ShowLineBreak;
392             }
393             set
394             {
395                 this._ShowLineBreak = value;
396             }
397         }
398
399         public Color4 Foreground
400         {
401             get
402             {
403                 return this._Forground;
404             }
405             set
406             {
407                 if (this.render == null)
408                     return;
409                 this._Forground = value;
410                 if (this.textRender != null)
411                     this.textRender.DefaultFore = value;
412                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
413             }
414         }
415
416         public Color4 HilightForeground
417         {
418             get;
419             set;
420         }
421
422         public Color4 Background
423         {
424             get;
425             set;
426         }
427
428         public Color4 InsertCaret
429         {
430             get;
431             set;
432         }
433
434         public Color4 OverwriteCaret
435         {
436             get;
437             set;
438         }
439
440         public Color4 LineMarker
441         {
442             get;
443             set;
444         }
445
446         public Color4 UpdateArea
447         {
448             get;
449             set;
450         }
451
452         public Color4 LineNumber
453         {
454             get;
455             set;
456         }
457
458         public Color4 ControlChar
459         {
460             get
461             {
462                 return this._ControlChar;
463             }
464             set
465             {
466                 this._ControlChar = value;
467                 if (this.HiddenChars != null)
468                     this.HiddenChars.Fore = value;
469                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
470             }
471         }
472
473         public Color4 Url
474         {
475             get
476             {
477                 return this._URL;
478             }
479             set
480             {
481                 this._URL = value;
482                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
483             }
484         }
485
486         public Color4 Hilight
487         {
488             get
489             {
490                 return this._Hilight;
491             }
492             set
493             {
494                 this._Hilight = value;
495             }
496         }
497
498         public Color4 Comment
499         {
500             get
501             {
502                 return this._Comment;
503             }
504             set
505             {
506                 this._Comment = value;
507                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
508             }
509         }
510
511         public Color4 Literal
512         {
513             get
514             {
515                 return this._Literal;
516             }
517             set
518             {
519                 this._Literal = value;
520                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
521             }
522         }
523
524         public Color4 Keyword1
525         {
526             get
527             {
528                 return this._Keyword1;
529             }
530             set
531             {
532                 this._Keyword1 = value;
533                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
534             }
535         }
536
537         public Color4 Keyword2
538         {
539             get
540             {
541                 return this._Keyword2;
542             }
543             set
544             {
545                 this._Keyword2 = value;
546                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
547             }
548         }
549
550         public Rectangle TextArea
551         {
552             get;
553             set;
554         }
555
556         public double LineNemberWidth
557         {
558             get
559             {
560                 return this.emSize.Width * EditView.LineNumberLength;
561             }
562         }
563
564         public double FoldingWidth
565         {
566             get;
567             private set;
568         }
569
570         public int TabWidthChar
571         {
572             get { return this.tabLength; }
573             set
574             {
575                 if (value == 0)
576                     return;
577                 this.tabLength = value;
578                 DW.TextLayout layout = new DW.TextLayout(D2DRenderShared.DWFactory, "0", this.format, float.MaxValue, float.MaxValue);
579                 float width = (float)(layout.Metrics.Width * value);
580                 this.HiddenChars.TabWidth = width;
581                 this.format.IncrementalTabStop = width;
582                 layout.Dispose();
583             }
584         }
585
586         public Size emSize
587         {
588             get;
589             private set;
590         }
591
592         public void DrawGripper(Point p, double radius)
593         {
594             D2D.Ellipse ellipse = new D2D.Ellipse();
595             ellipse.Point = p;
596             ellipse.RadiusX = (float)radius;
597             ellipse.RadiusY = (float)radius;
598             this.render.FillEllipse(ellipse, this.Brushes.Get(this.Background));
599             this.render.DrawEllipse(ellipse, this.Brushes.Get(this.Foreground));
600         }
601
602
603         public virtual void DrawCachedBitmap(Rectangle rect)
604         {
605         }
606
607         public virtual void CacheContent()
608         {
609         }
610
611         public virtual bool IsVaildCache()
612         {
613             return this.hasCache;
614         }
615
616         protected void BegineDraw()
617         {
618             if (this.render == null || this.render.IsDisposed)
619                 return;
620             this.render.BeginDraw();
621             this.render.AntialiasMode = D2D.AntialiasMode.Aliased;
622         }
623
624         protected void EndDraw()
625         {
626             if (this.render == null || this.render.IsDisposed)
627                 return;
628             this.render.AntialiasMode = D2D.AntialiasMode.PerPrimitive;
629             this.render.EndDraw();
630         }
631
632         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect, StringColorType colorType = StringColorType.Forground)
633         {
634             if (this.render == null || this.render.IsDisposed)
635                 return;
636             float dpix, dpiy;
637             D2D.SolidColorBrush brush;
638             switch (colorType)
639             {
640                 case StringColorType.Forground:
641                     brush = this.Brushes.Get(this.Foreground);
642                     break;
643                 case StringColorType.LineNumber:
644                     brush = this.Brushes.Get(this.LineNumber);
645                     break;
646                 default:
647                     throw new ArgumentOutOfRangeException();
648             }
649             this.GetDpi(out dpix, out dpiy);
650             MyTextLayout layout = new MyTextLayout(D2DRenderShared.DWFactory, str, this.format, (float)layoutRect.Width, (float)layoutRect.Height,dpix,false);
651             layout.StringAlignment = align;
652             layout.Draw(this.render, (float)x, (float)y, brush);
653             layout.Dispose();
654         }
655
656         public void DrawFoldingMark(bool expand, double x, double y)
657         {
658             string mark = expand ? "-" : "+";
659             this.DrawString(mark, x, y,StringAlignment.Left, new Size(this.FoldingWidth, this.emSize.Height));
660         }
661
662         public void FillRectangle(Rectangle rect,FillRectType type)
663         {
664             if (this.render == null || this.render.IsDisposed)
665                 return;
666             D2D.SolidColorBrush brush = null;
667             switch(type)
668             {
669                 case FillRectType.OverwriteCaret:
670                     brush = this.Brushes.Get(this.OverwriteCaret);
671                     this.render.FillRectangle(rect, brush);
672                     break;
673                 case FillRectType.InsertCaret:
674                     brush = this.Brushes.Get(this.InsertCaret);
675                     this.render.FillRectangle(rect, brush);
676                     break;
677                 case FillRectType.InsertPoint:
678                     brush = this.Brushes.Get(this.Hilight);
679                     this.render.FillRectangle(rect, brush);
680                     break;
681                 case FillRectType.LineMarker:
682                     brush = this.Brushes.Get(this.LineMarker);
683                     this.render.DrawRectangle(rect, brush, EditView.LineMarkerThickness);
684                     break;
685                 case FillRectType.UpdateArea:
686                     brush = this.Brushes.Get(this.UpdateArea);
687                     this.render.FillRectangle(rect, brush);
688                     break;
689             }
690         }
691
692         public void FillBackground(Rectangle rect)
693         {
694             if (this.render == null || this.render.IsDisposed)
695                 return;
696             this.render.Clear(this.Background);
697         }
698
699         public void DrawOneLine(Document doc,LineToIndexTable lti, int row, double x, double y, PreDrawOneLineHandler PreDrawOneLine)
700         {
701             int lineLength = lti.GetLengthFromLineNumber(row);
702
703             if (lineLength == 0 || this.render == null || this.render.IsDisposed)
704                 return;
705
706             MyTextLayout layout = (MyTextLayout)lti.GetLayout(row);
707
708             if(PreDrawOneLine != null)
709                 PreDrawOneLine(layout,lti,row,x,y);
710
711             if(doc.Selections.Count > 0)
712             {
713                 int lineIndex = lti.GetIndexFromLineNumber(row);
714                 var SelectRanges = from s in doc.Selections.Get(lineIndex, lineLength)
715                                    let n = Util.ConvertAbsIndexToRelIndex(s, lineIndex, lineLength)
716                                    select n;
717
718                 if (SelectRanges != null)
719                 {
720                     foreach (Selection sel in SelectRanges)
721                     {
722                         if (sel.length == 0 || sel.start == -1)
723                             continue;
724
725                         this.DrawMarkerEffect(layout, HilightType.Select, sel.start, sel.length, x, y, false);
726                     }
727                 }
728             }
729
730             layout.Draw(textRender, (float)x, (float)y);
731
732         }
733
734         public void BeginClipRect(Rectangle rect)
735         {
736             this.render.PushAxisAlignedClip(rect, D2D.AntialiasMode.Aliased);
737         }
738
739         public void EndClipRect()
740         {
741             this.render.PopAxisAlignedClip();
742         }
743
744         public void SetTextColor(MyTextLayout layout,int start, int length, Color4? color)
745         {
746             if (color == null)
747                 return;
748             layout.SetDrawingEffect(this.Brushes.Get((Color4)color), new DW.TextRange(start, length));
749         }
750
751         public void DrawLine(Point from, Point to)
752         {
753             D2D.Brush brush = this.Brushes.Get(this.Foreground);
754             D2D.StrokeStyle stroke = this.Strokes.Get(D2DRenderShared.D2DFactory,HilightType.Sold);
755             this.render.DrawLine(from, to, brush, 1.0f, stroke);
756         }
757
758         public const int BoldThickness = 2;
759         public const int NormalThickness = 1;
760
761         public void DrawMarkerEffect(MyTextLayout layout, HilightType type, int start, int length, double x, double y, bool isBold, Color4? effectColor = null)
762         {
763             if (type == HilightType.None)
764                 return;
765
766             float thickness = isBold ? BoldThickness : NormalThickness;
767
768             Color4 color;
769             if (effectColor != null)
770                 color = (Color4)effectColor;
771             else if (type == HilightType.Select)
772                 color = this.Hilight;
773             else
774                 color = this.Foreground;
775
776             IMarkerEffecter effecter = null;
777             D2D.SolidColorBrush brush = this.Brushes.Get(color);
778
779             if (type == HilightType.Squiggle)
780                 effecter = new D2DSquilleLineMarker(this.render, brush, this.Strokes.Get(D2DRenderShared.D2DFactory, HilightType.Squiggle), thickness);
781             else if (type == HilightType.Select)
782                 effecter = new HilightMarker(this.render, brush);
783             else if (type == HilightType.None)
784                 effecter = null;
785             else
786                 effecter = new LineMarker(this.render, brush, this.Strokes.Get(D2DRenderShared.D2DFactory,type), thickness);
787
788             if (effecter != null)
789             {
790                 bool isUnderline = type != HilightType.Select;
791
792                 DW.HitTestMetrics[] metrics = layout.HitTestTextRange(start, length, (float)x, (float)y);
793                 foreach (DW.HitTestMetrics metric in metrics)
794                 {
795                     float offset = isUnderline ? metric.Height : 0;
796                     effecter.Draw(metric.Left, metric.Top + offset, metric.Width, metric.Height);
797                 }
798             }
799         }
800
801         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges, IEnumerable<Selection> SelectRanges)
802         {
803             float dpix,dpiy;
804             this.GetDpi(out dpix,out dpiy);
805
806             bool hasNewLine = str.Length > 0 && str[str.Length - 1] == Document.NewLine;
807             MyTextLayout newLayout = new MyTextLayout(D2DRenderShared.DWFactory,
808                 str,
809                 this.format,
810                 this.TextArea.Width,
811                 this.TextArea.Height,
812                 dpiy,
813                 hasNewLine && this._ShowLineBreak);
814             ParseLayout(newLayout, str);
815             if (syntaxCollection != null)
816             {
817                 foreach (SyntaxInfo s in syntaxCollection)
818                 {
819                     D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
820                     switch (s.type)
821                     {
822                         case TokenType.Comment:
823                             brush = this.Brushes.Get(this.Comment);
824                             break;
825                         case TokenType.Keyword1:
826                             brush = this.Brushes.Get(this.Keyword1);
827                             break;
828                         case TokenType.Keyword2:
829                             brush = this.Brushes.Get(this.Keyword2);
830                             break;
831                         case TokenType.Literal:
832                             brush = this.Brushes.Get(this.Literal);
833                             break;
834                     }
835                     newLayout.SetDrawingEffect(brush, new DW.TextRange(s.index, s.length));
836                 }
837             }
838
839             if (MarkerRanges != null)
840             {
841                 foreach (Marker m in MarkerRanges)
842                 {
843                     if (m.start == -1 || m.length == 0)
844                         continue;
845                     Color4 color = new Color4(m.color.R / 255.0f, m.color.G / 255.0f, m.color.B / 255.0f, m.color.A / 255.0f);
846                     if (m.hilight == HilightType.Url)
847                         color = this.Url;
848                     if (m.hilight == HilightType.Select)
849                         newLayout.SetDrawingEffect(new SelectedEffect(this.HilightForeground, color, m.isBoldLine), new DW.TextRange(m.start, m.length));
850                     else
851                         newLayout.SetDrawingEffect(new DrawingEffect(m.hilight, color,m.isBoldLine), new DW.TextRange(m.start, m.length));
852                     if (m.hilight != HilightType.None && m.hilight != HilightType.Select)
853                         newLayout.SetUnderline(true, new DW.TextRange(m.start, m.length));
854                 }
855             }
856
857             if (SelectRanges != null && this.HilightForeground.Alpha > 0.0)
858             {
859                 foreach (Selection sel in SelectRanges)
860                 {
861                     if (sel.length == 0 || sel.start == -1)
862                         continue;
863
864                     newLayout.SetDrawingEffect(new SelectedEffect(this.HilightForeground, this.Hilight, false), new DW.TextRange(sel.start, sel.length));
865                 }
866             }
867
868             return newLayout;
869        }
870
871         public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
872         {
873             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
874
875             this.format.WordWrapping = DW.WordWrapping.Wrap;
876
877             foreach (string str in doc.GetLines(startIndex, endIndex))
878             {
879                 DW.TextLayout layout = new DW.TextLayout(D2DRenderShared.DWFactory, str, this.format, (float)wrapwidth, float.MaxValue);
880
881                 int i = startIndex;
882                 foreach (DW.LineMetrics metrics in layout.GetLineMetrics())
883                 {
884                     if (metrics.Length == 0 && metrics.NewlineLength == 0)
885                         continue;
886
887                     bool lineend = false;
888                     if (metrics.NewlineLength > 0)
889                         lineend = true;
890
891                     output.Add(layoutLineCollection.CreateLineToIndexTableData(i, (int)metrics.Length, lineend, null));
892                     i += Util.RoundUp(metrics.Length);
893                 }
894
895                 layout.Dispose();
896
897                 startIndex += str.Length;
898             }
899
900             this.format.WordWrapping = DW.WordWrapping.NoWrap;
901
902             if (output.Count > 0)
903                 output.Last().LineEnd = true;
904
905             return output;
906         }
907
908         bool _Disposed = false;
909         public void Dispose()
910         {
911             if (!_Disposed)
912             {
913                 this.Dispose(true);
914                 this.HiddenChars.Clear();
915                 if (this.format != null)
916                     this.format.Dispose();
917             }
918             this._Disposed = true;
919         }
920
921         protected virtual void Dispose(bool dispose)
922         {
923         }
924
925         public virtual void GetDpi(out float dpix,out float dpiy)
926         {
927             throw new NotImplementedException();
928         }
929
930         public double GetScale()
931         {
932             float dpi;
933             this.GetDpi(out dpi, out dpi);
934             return dpi / 96.0;
935         }
936
937         void ParseLayout(MyTextLayout layout, string str)
938         {
939             for (int i = 0; i < str.Length; i++)
940             {
941                 DW.InlineObject inlineObject = this.HiddenChars.Get(layout,i, str);
942                 if (inlineObject != null)
943                 {
944                     layout.SetInlineObject(inlineObject, new DW.TextRange(i, 1));
945                     layout.SetDrawingEffect(this.Brushes.Get(this.ControlChar), new DW.TextRange(i, 1));
946                 }
947             }
948             layout.SetLineBreakBrush(this.Brushes.Get(this.ControlChar));
949         }
950     }
951 }