OSDN Git Service

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