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