OSDN Git Service

コア部分を共通プロジェクト化した
[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);
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                 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     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, _ShowLineBreak;
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 const int MiniumeWidth = 40;    //これ以上ないと誤操作が起こる
215
216         public void InitTextFormat(string fontName, float fontSize, DW.FontWeight fontWeigth = DW.FontWeight.Normal,DW.FontStyle fontStyle = DW.FontStyle.Normal)
217         {
218             if(this.format != null)
219                 this.format.Dispose();
220
221             float dpix, dpiy;
222             this.GetDpi(out dpix, out dpiy);
223
224             this.format = new DW.TextFormat(this.DWFactory, fontName, fontWeigth, fontStyle, fontSize);
225             this.format.WordWrapping = DW.WordWrapping.NoWrap;
226
227             if (this.HiddenChars == null)
228                 this.HiddenChars = new InlineManager(this.DWFactory, this.format, this.ControlChar, this.Brushes);
229             else
230                 this.HiddenChars.Format = this.format;
231
232             this.TabWidthChar = this.TabWidthChar;
233
234             this.hasCache = false;
235
236             MyTextLayout layout = new MyTextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue, dpix, false);
237             layout.RightToLeft = false;
238             this.emSize = new Size(layout.Width, layout.Height);
239             layout.Dispose();
240
241             layout = new MyTextLayout(this.DWFactory, "+", this.format, float.MaxValue, float.MaxValue, dpix, false);
242             layout.RightToLeft = false;
243 #if METRO
244             this.FoldingWidth = Math.Max(D2DRenderCommon.MiniumeWidth, layout.Width);
245 #else
246             this.FoldingWidth = layout.Width;
247 #endif
248             layout.Dispose();
249
250             this.ChangedRenderResource(this,new ChangedRenderRsourceEventArgs(ResourceType.Font));
251         }
252
253         public bool RightToLeft
254         {
255             get
256             {
257                 return this.format.ReadingDirection == DW.ReadingDirection.RightToLeft;
258             }
259             set
260             {
261                 this.format.ReadingDirection = value ? DW.ReadingDirection.RightToLeft : DW.ReadingDirection.LeftToRight;
262                 this.ChangedRightToLeft(this, null);                
263             }
264         }
265
266         public TextAntialiasMode TextAntialiasMode
267         {
268             get
269             {
270                 return this._TextAntialiasMode;
271             }
272             set
273             {
274                 if (this.render == null)
275                     throw new InvalidOperationException();
276                 this._TextAntialiasMode = value;
277                 this.render.TextAntialiasMode = (D2D.TextAntialiasMode)value;
278                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Antialias));
279             }
280         }
281
282         public bool ShowFullSpace
283         {
284             get
285             {
286                 if (this.HiddenChars == null)
287                     return false;
288                 else
289                     return this.HiddenChars.ContainsSymbol(' ');
290             }
291             set
292             {
293                 if (this.HiddenChars == null)
294                     throw new InvalidOperationException();
295                 if (value)
296                     this.HiddenChars.AddSymbol(' ', '□');
297                 else
298                     this.HiddenChars.RemoveSymbol(' ');
299                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
300             }
301         }
302
303         public bool ShowHalfSpace
304         {
305             get
306             {
307                 if (this.HiddenChars == null)
308                     return false;
309                 else
310                     return this.HiddenChars.ContainsSymbol(' ');
311             }
312             set
313             {
314                 if (this.HiddenChars == null)
315                     throw new InvalidOperationException();
316                 if (value)
317                     this.HiddenChars.AddSymbol(' ', 'ロ');
318                 else
319                     this.HiddenChars.RemoveSymbol(' ');
320                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
321             }
322         }
323
324         public bool ShowTab
325         {
326             get
327             {
328                 if (this.HiddenChars == null)
329                     return false;
330                 else
331                     return this.HiddenChars.ContainsSymbol('\t');
332             }
333             set
334             {
335                 if (this.HiddenChars == null)
336                     throw new InvalidOperationException();
337                 if (value)
338                     this.HiddenChars.AddSymbol('\t', '>');
339                 else
340                     this.HiddenChars.RemoveSymbol('\t');
341                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
342             }
343         }
344
345         public bool ShowLineBreak
346         {
347             get
348             {
349                 return this._ShowLineBreak;
350             }
351             set
352             {
353                 this._ShowLineBreak = value;
354                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
355             }
356         }
357
358         public Color4 Foreground
359         {
360             get
361             {
362                 return this._Forground;
363             }
364             set
365             {
366                 if (this.render == null)
367                     return;
368                 this._Forground = value;
369                 if (this.textRender != null)
370                     this.textRender.DefaultFore = value;
371                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
372             }
373         }
374
375         public Color4 Background
376         {
377             get;
378             set;
379         }
380
381         public Color4 InsertCaret
382         {
383             get;
384             set;
385         }
386
387         public Color4 OverwriteCaret
388         {
389             get;
390             set;
391         }
392
393         public Color4 LineMarker
394         {
395             get;
396             set;
397         }
398
399         public Color4 UpdateArea
400         {
401             get;
402             set;
403         }
404
405         public Color4 LineNumber
406         {
407             get;
408             set;
409         }
410
411         public Color4 ControlChar
412         {
413             get
414             {
415                 return this._ControlChar;
416             }
417             set
418             {
419                 this._ControlChar = value;
420                 if (this.HiddenChars != null)
421                     this.HiddenChars.Fore = value;
422                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
423             }
424         }
425
426         public Color4 Url
427         {
428             get
429             {
430                 return this._URL;
431             }
432             set
433             {
434                 this._URL = value;
435                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
436             }
437         }
438
439         public Color4 Hilight
440         {
441             get
442             {
443                 return this._Hilight;
444             }
445             set
446             {
447                 this._Hilight = value;
448             }
449         }
450
451         public Color4 Comment
452         {
453             get
454             {
455                 return this._Comment;
456             }
457             set
458             {
459                 this._Comment = value;
460                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
461             }
462         }
463
464         public Color4 Literal
465         {
466             get
467             {
468                 return this._Literal;
469             }
470             set
471             {
472                 this._Literal = value;
473                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
474             }
475         }
476
477         public Color4 Keyword1
478         {
479             get
480             {
481                 return this._Keyword1;
482             }
483             set
484             {
485                 this._Keyword1 = value;
486                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
487             }
488         }
489
490         public Color4 Keyword2
491         {
492             get
493             {
494                 return this._Keyword2;
495             }
496             set
497             {
498                 this._Keyword2 = value;
499                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
500             }
501         }
502
503         public Rectangle TextArea
504         {
505             get;
506             set;
507         }
508
509         public double LineNemberWidth
510         {
511             get
512             {
513                 return this.emSize.Width * EditView.LineNumberLength;
514             }
515         }
516
517         public double FoldingWidth
518         {
519             get;
520             private set;
521         }
522
523         public int TabWidthChar
524         {
525             get { return this.tabLength; }
526             set
527             {
528                 if (value == 0)
529                     return;
530                 this.tabLength = value;
531                 DW.TextLayout layout = new DW.TextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue);
532                 float width = (float)(layout.Metrics.Width * value);
533                 this.HiddenChars.TabWidth = width;
534                 this.format.IncrementalTabStop = width;
535             }
536         }
537
538         public Size emSize
539         {
540             get;
541             private set;
542         }
543
544         public void DrawGripper(Point p, double radius)
545         {
546             D2D.Ellipse ellipse = new D2D.Ellipse();
547             ellipse.Point = p;
548             ellipse.RadiusX = (float)radius;
549             ellipse.RadiusY = (float)radius;
550             this.render.FillEllipse(ellipse, this.Brushes.Get(this.Background));
551             this.render.DrawEllipse(ellipse, this.Brushes.Get(this.Foreground));
552         }
553
554         public void ReConstructDeviceResource()
555         {
556             this.ReConstructDeviceResource(this.renderSize.Width, this.renderSize.Height);
557         }
558
559         public void ReConstructDeviceResource(double width, double height)
560         {
561             this.DestructDeviceResource();
562             this.ConstructDeviceResource(width, height);
563         }
564
565         public virtual void DrawCachedBitmap(Rectangle rect)
566         {
567             if (this.render == null || this.render.IsDisposed)
568                 return;
569             render.DrawBitmap(this.bitmap, rect, 1.0f, D2D.BitmapInterpolationMode.Linear, rect);
570         }
571
572         public virtual void CacheContent()
573         {
574             if (this.render == null || this.bitmap == null || this.bitmap.IsDisposed || this.render.IsDisposed)
575                 return;
576             render.Flush();
577             this.bitmap.CopyFromRenderTarget(this.render, new SharpDX.Point(), new SharpDX.Rectangle(0, 0, (int)this.renderSize.Width, (int)this.renderSize.Height));
578             this.hasCache = true;
579         }
580
581         public virtual bool IsVaildCache()
582         {
583             return this.hasCache;
584         }
585
586         public virtual void BegineDraw()
587         {
588             if (this.render == null || this.render.IsDisposed)
589                 return;
590             this.render.BeginDraw();
591             this.render.AntialiasMode = D2D.AntialiasMode.Aliased;
592         }
593
594         public virtual void EndDraw()
595         {
596             if (this.render == null || this.render.IsDisposed)
597                 return;
598             this.render.AntialiasMode = D2D.AntialiasMode.PerPrimitive;
599             this.render.EndDraw();
600         }
601
602         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect, StringColorType colorType = StringColorType.Forground)
603         {
604             if (this.render == null || this.render.IsDisposed)
605                 return;
606             float dpix, dpiy;
607             D2D.SolidColorBrush brush;
608             switch (colorType)
609             {
610                 case StringColorType.Forground:
611                     brush = this.Brushes.Get(this.Foreground);
612                     break;
613                 case StringColorType.LineNumber:
614                     brush = this.Brushes.Get(this.LineNumber);
615                     break;
616                 default:
617                     throw new ArgumentOutOfRangeException();
618             }
619             this.GetDpi(out dpix, out dpiy);
620             MyTextLayout layout = new MyTextLayout(this.DWFactory, str, this.format, (float)layoutRect.Width, (float)layoutRect.Height,dpix,false);
621             layout.StringAlignment = align;
622             layout.Draw(this.render, (float)x, (float)y, brush);
623             layout.Dispose();
624         }
625
626         public void DrawFoldingMark(bool expand, double x, double y)
627         {
628             string mark = expand ? "-" : "+";
629             this.DrawString(mark, x, y,StringAlignment.Left, new Size(this.FoldingWidth, this.emSize.Height));
630         }
631
632         public void FillRectangle(Rectangle rect,FillRectType type)
633         {
634             if (this.render == null || this.render.IsDisposed)
635                 return;
636             D2D.SolidColorBrush brush = null;
637             switch(type)
638             {
639                 case FillRectType.OverwriteCaret:
640                     brush = this.Brushes.Get(this.OverwriteCaret);
641                     this.render.FillRectangle(rect, brush);
642                     break;
643                 case FillRectType.InsertCaret:
644                     brush = this.Brushes.Get(this.InsertCaret);
645                     this.render.FillRectangle(rect, brush);
646                     break;
647                 case FillRectType.InsertPoint:
648                     brush = this.Brushes.Get(this.Hilight);
649                     this.render.FillRectangle(rect, brush);
650                     break;
651                 case FillRectType.LineMarker:
652                     brush = this.Brushes.Get(this.LineMarker);
653                     this.render.DrawRectangle(rect, brush, EditView.LineMarkerThickness);
654                     break;
655                 case FillRectType.UpdateArea:
656                     brush = this.Brushes.Get(this.UpdateArea);
657                     this.render.FillRectangle(rect, brush);
658                     break;
659             }
660         }
661
662         public void FillBackground(Rectangle rect)
663         {
664             if (this.render == null || this.render.IsDisposed)
665                 return;
666             this.render.Clear(this.Background);
667         }
668
669         public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges,PreDrawOneLineHandler PreDrawOneLine)
670         {
671             int lineLength = lti.GetLengthFromLineNumber(row);
672
673             if (lineLength == 0 || this.render == null || this.render.IsDisposed)
674                 return;
675
676             MyTextLayout layout = (MyTextLayout)lti.GetLayout(row);
677
678             this.render.PushAxisAlignedClip(this.TextArea, D2D.AntialiasMode.Aliased);
679
680             if(PreDrawOneLine != null)
681                 PreDrawOneLine(layout);
682
683             if (SelectRanges != null)
684             {
685                 foreach (Selection sel in SelectRanges)
686                 {
687                     if (sel.length == 0 || sel.start == -1)
688                         continue;
689
690                     this.DrawMarkerEffect(layout, HilightType.Select, sel.start, sel.length, x, y, false);
691                 }
692             }
693
694             layout.Draw(textRender, (float)x, (float)y);
695
696             this.render.PopAxisAlignedClip();
697         }
698
699         public void SetTextColor(MyTextLayout layout,int start, int length, Color4? color)
700         {
701             if (color == null)
702                 return;
703             layout.SetDrawingEffect(this.Brushes.Get((Color4)color), new DW.TextRange(start, length));
704         }
705
706         public void DrawLine(Point from, Point to)
707         {
708             D2D.Brush brush = this.Brushes.Get(this.Foreground);
709             D2D.StrokeStyle stroke = this.Strokes.Get(HilightType.Sold);
710             this.render.DrawLine(from, to, brush, 1.0f, stroke);
711         }
712
713         public void DrawMarkerEffect(MyTextLayout layout, HilightType type, int start, int length, double x, double y, bool isBold, Color4? effectColor = null)
714         {
715             if (type == HilightType.None)
716                 return;
717
718             float thickness = isBold ? 2 : 1;
719
720             Color4 color;
721             if (effectColor != null)
722                 color = (Color4)effectColor;
723             else if (type == HilightType.Select)
724                 color = this.Hilight;
725             else
726                 color = this.Foreground;
727
728             IMarkerEffecter effecter = null;
729             D2D.SolidColorBrush brush = this.Brushes.Get(color);
730
731             if (type == HilightType.Squiggle)
732                 effecter = new D2DSquilleLineMarker(this.render, brush, this.Strokes.Get(HilightType.Squiggle), thickness);
733             else if (type == HilightType.Select)
734                 effecter = new HilightMarker(this.render, brush);
735             else if (type == HilightType.None)
736                 effecter = null;
737             else
738                 effecter = new LineMarker(this.render, brush, this.Strokes.Get(type), thickness);
739
740             if (effecter != null)
741             {
742                 bool isUnderline = type != HilightType.Select;
743
744                 DW.HitTestMetrics[] metrics = layout.HitTestTextRange(start, length, (float)x, (float)y);
745                 foreach (DW.HitTestMetrics metric in metrics)
746                 {
747                     float offset = isUnderline ? metric.Height : 0;
748                     effecter.Draw(metric.Left, metric.Top + offset, metric.Width, metric.Height);
749                 }
750             }
751         }
752
753         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
754         {
755             float dpix,dpiy;
756             this.GetDpi(out dpix,out dpiy);
757
758             bool hasNewLine = str.Length > 0 && str[str.Length - 1] == Document.NewLine;
759             MyTextLayout newLayout = new MyTextLayout(this.DWFactory,
760                 str,
761                 this.format,
762                 this.TextArea.Width,
763                 this.TextArea.Height,
764                 dpiy,
765                 hasNewLine && this._ShowLineBreak);
766             ParseLayout(newLayout, str);
767             if (syntaxCollection != null)
768             {
769                 foreach (SyntaxInfo s in syntaxCollection)
770                 {
771                     D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
772                     switch (s.type)
773                     {
774                         case TokenType.Comment:
775                             brush = this.Brushes.Get(this.Comment);
776                             break;
777                         case TokenType.Keyword1:
778                             brush = this.Brushes.Get(this.Keyword1);
779                             break;
780                         case TokenType.Keyword2:
781                             brush = this.Brushes.Get(this.Keyword2);
782                             break;
783                         case TokenType.Literal:
784                             brush = this.Brushes.Get(this.Literal);
785                             break;
786                     }
787                     newLayout.SetDrawingEffect(brush, new DW.TextRange(s.index, s.length));
788                 }
789             }
790
791             if (MarkerRanges != null)
792             {
793                 foreach (Marker m in MarkerRanges)
794                 {
795                     if (m.start == -1 || m.length == 0)
796                         continue;
797                     Color4 color = new Color4(m.color.R / 255.0f, m.color.G / 255.0f, m.color.B / 255.0f, m.color.A / 255.0f);
798                     if (m.hilight == HilightType.Url)
799                         color = this.Url;
800                     newLayout.SetDrawingEffect(this.Effects.Get(color, m.hilight), new DW.TextRange(m.start, m.length));
801                 }
802             }
803
804             return newLayout;
805        }
806
807         public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
808         {
809             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
810
811             this.format.WordWrapping = DW.WordWrapping.Wrap;
812
813             foreach (string str in doc.GetLines(startIndex, endIndex))
814             {
815                 DW.TextLayout layout = new DW.TextLayout(this.DWFactory, str, this.format, (float)wrapwidth, float.MaxValue);
816
817                 int i = startIndex;
818                 foreach (DW.LineMetrics metrics in layout.GetLineMetrics())
819                 {
820                     if (metrics.Length == 0 && metrics.NewlineLength == 0)
821                         continue;
822
823                     bool lineend = false;
824                     if (metrics.NewlineLength > 0)
825                         lineend = true;
826
827                     output.Add(layoutLineCollection.CreateLineToIndexTableData(i, (int)metrics.Length, lineend, null));
828                     i += Util.RoundUp(metrics.Length);
829                 }
830
831                 layout.Dispose();
832
833                 startIndex += str.Length;
834             }
835
836             this.format.WordWrapping = DW.WordWrapping.NoWrap;
837
838             if (output.Count > 0)
839                 output.Last().LineEnd = true;
840
841             return output;
842         }
843
844         public virtual void Dispose()
845         {
846             this.DestructDeviceResource();
847             this.HiddenChars.Clear();
848             if (this.format != null)
849                 this.format.Dispose();
850             if(this.DWFactory != null)
851                 this.DWFactory.Dispose();
852             if(this.D2DFactory != null)
853                 this.D2DFactory.Dispose();
854         }
855
856         public void ConstructDeviceResource(double width, double height)
857         {
858             float dpiX, dpiY;
859             this.GetDpi(out dpiX, out dpiY);
860             D2D.RenderTargetProperties prop = new D2D.RenderTargetProperties(
861                 D2D.RenderTargetType.Default,
862                 new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore),
863                 dpiX,
864                 dpiY,
865                 D2D.RenderTargetUsage.None,
866                 D2D.FeatureLevel.Level_DEFAULT);
867
868             this.render = this.ConstructRender(this.D2DFactory,prop,width,height);
869
870             this.Brushes.Render = this.render;
871
872             D2D.BitmapProperties bmpProp = new D2D.BitmapProperties();
873             bmpProp.DpiX = dpiX;
874             bmpProp.DpiY = dpiY;
875             bmpProp.PixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore);
876             this.bitmap = new D2D.Bitmap(this.render, new SharpDX.Size2((int)width, (int)height), bmpProp);
877             this.hasCache = false;
878
879             this.ConstrctedResource();
880
881             this.textRender = new CustomTextRenderer(this.render, this.Brushes,this.Strokes, this.Foreground);
882
883             this.renderSize.Width = width;
884             this.renderSize.Height = height;
885
886             this.TextAntialiasMode = this._TextAntialiasMode;
887         }
888
889 #if METRO
890         protected virtual D2D.RenderTarget ConstructRender(D2D.Factory1 factory, D2D.RenderTargetProperties prop, double width, double height)
891 #else
892         protected virtual D2D.RenderTarget ConstructRender(D2D.Factory factory, D2D.RenderTargetProperties prop, double width, double height)
893 #endif
894         {
895             throw new NotImplementedException();
896         }
897
898         protected virtual void ConstrctedResource()
899         {
900             throw new NotImplementedException();
901         }
902
903         public virtual void GetDpi(out float dpix,out float dpiy)
904         {
905             throw new NotImplementedException();
906         }
907
908         protected virtual void DestructRender()
909         {
910             throw new NotImplementedException();
911
912         }
913
914         protected virtual void ReCreateTarget()
915         {
916             throw new NotImplementedException();
917         }
918
919         public void DestructDeviceResource()
920         {
921             this.hasCache = false;
922             if (this.bitmap != null)
923                 this.bitmap.Dispose();
924             this.Brushes.Clear();
925             this.Strokes.Clear();
926             this.Effects.Clear();
927             if (this.textRender != null)
928                 this.textRender.Dispose();
929             this.DestructRender();
930         }
931
932         void ParseLayout(MyTextLayout layout, string str)
933         {
934             for (int i = 0; i < str.Length; i++)
935             {
936                 DW.InlineObject inlineObject = this.HiddenChars.Get(layout,i, str);
937                 if (inlineObject != null)
938                 {
939                     layout.SetInlineObject(inlineObject, new DW.TextRange(i, 1));
940                     layout.SetDrawingEffect(this.Brushes.Get(this.ControlChar), new DW.TextRange(i, 1));
941                 }
942             }
943             layout.SetLineBreakBrush(this.Brushes.Get(this.ControlChar));
944         }
945     }
946 }