├── README
├── WpfTextBenchmark.sln
└── WpfTextBenchmark
├── App.xaml
├── App.xaml.cs
├── D2DWrapper.dll
├── Direct2DControl.cs
├── Direct2DTextMatrixView.cs
├── ExtTextOutMatrixView.cs
├── FormattedTextMatrixView.cs
├── GDIMatrixView.cs
├── GlyphMatrixView.cs
├── MatrixView.cs
├── Properties
├── AssemblyInfo.cs
└── WPFAssemblyInfo.cs
├── TextBlockMatrixView.cs
├── TextLineMatrixView.cs
├── WinFormsMatrixView.cs
├── Window1.xaml
├── Window1.xaml.cs
└── WpfTextBenchmark.csproj
/README:
--------------------------------------------------------------------------------
1 | This benchmark tests several approaches to text rendering in WPF against each other:
2 |
3 | TextBlock: a WPF Canvas with TextBlock elements
4 | FormattedText: using the DrawingContext.DrawText method
5 | TextLine: using the Media.TextFormatting namespace
6 | GlyphRun: using the WPF GlyphRun directly
7 | disadvantage: no automatic font fallback -> no unicode support
8 |
9 | Direct2D: using DirectWrite
10 | WinForms: using the WinForms TextRenderer class
11 | GDI: using GDI via P/Invoke
12 | GDI+: using the System.Drawing.Graphics.DrawString method
13 | disadvantage: accurate measurement of font width seems to be impossible (MeasureString is not good enough)
14 |
15 | The speed of the WPF methods is pretty consistent on most machines, ordered like this:
16 | GlyphRun (fastest)
17 | TextLine
18 | FormattedText
19 | TextBlock (slowest)
20 |
21 | However, even on fast machines like my Core i7, none of the WPF methods provides a fluent animation.
22 | It seems as if most frames (counted by number of OnRender calls) are not drawn to the screen.
23 |
24 | "WinForms" is similarly slow to the WPF approaches (maybe a bit faster on older machines).
25 | Direct2D, GDI and GDI+ all beat WPF hands-down, delivering a fluent animation even on my old notebook with
26 | Intel onboard graphics. The exact order of these seems to depend on the hardware.
27 |
28 | Update: I have since discovered that the WPF DrawingVisual class can be used to
29 | significantly speed up WPF text rendering.
30 | I did not update this benchmark to take advantage of DrawingVisual.
31 | Take a look at AvalonEdit's VisualLineDrawingVisual class for details.
32 |
--------------------------------------------------------------------------------
/WpfTextBenchmark.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | # SharpDevelop 4.0.0.6362
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfTextBenchmark", "WpfTextBenchmark\WpfTextBenchmark.csproj", "{121B32DB-5BB9-439F-BF67-1BC697B98EF3}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|x86 = Debug|x86
10 | Release|x86 = Release|x86
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {121B32DB-5BB9-439F-BF67-1BC697B98EF3}.Debug|x86.Build.0 = Debug|x86
16 | {121B32DB-5BB9-439F-BF67-1BC697B98EF3}.Debug|x86.ActiveCfg = Debug|x86
17 | {121B32DB-5BB9-439F-BF67-1BC697B98EF3}.Release|x86.Build.0 = Release|x86
18 | {121B32DB-5BB9-439F-BF67-1BC697B98EF3}.Release|x86.ActiveCfg = Release|x86
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Data;
4 | using System.Xml;
5 | using System.Configuration;
6 |
7 | namespace WpfTextBenchmark
8 | {
9 | ///
10 | /// Interaction logic for App.xaml
11 | ///
12 | public partial class App : Application
13 | {
14 | }
15 | }
--------------------------------------------------------------------------------
/WpfTextBenchmark/D2DWrapper.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dgrunwald/WPF-Text-Rendering-Benchmark/def2a7c36a011836370a2c2743b17fb541793205/WpfTextBenchmark/D2DWrapper.dll
--------------------------------------------------------------------------------
/WpfTextBenchmark/Direct2DControl.cs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Authors:
4 | * Dmitry Kolchev
5 | *
6 | * Copyright (C) 2010 Dmitry Kolchev
7 | *
8 | * This sourcecode is licenced under The GNU Lesser General Public License
9 | *
10 | * The above copyright notice and this permission notice shall be included in
11 | * all copies or substantial portions of the Software.
12 | *
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
16 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | */
21 | using System;
22 | using System.Collections.Generic;
23 | using System.ComponentModel;
24 | using System.Data;
25 | using System.Diagnostics;
26 | using System.Linq;
27 | using System.Text;
28 | using System.Windows.Forms;
29 | using Managed.Graphics.Direct2D;
30 | using Managed.Graphics.DirectWrite;
31 | using Managed.Graphics.Imaging;
32 |
33 | namespace WpfTextBenchmark
34 | {
35 | public class Direct2DControl : Control
36 | {
37 | private Direct2DFactory _factory;
38 | private DirectWriteFactory _directWriteFactory;
39 | private WicImagingFactory _imagingFactory;
40 | private WindowRenderTarget _renderTarget;
41 | private bool _deviceIndependedResourcesCreated;
42 |
43 | public Direct2DControl()
44 | {
45 | SetStyle(
46 | ControlStyles.AllPaintingInWmPaint |
47 | ControlStyles.Opaque |
48 | ControlStyles.UserPaint, true);
49 | }
50 |
51 | protected override void Dispose(bool disposing)
52 | {
53 | CleanUpDeviceIndependentResources();
54 | base.Dispose(disposing);
55 | }
56 |
57 | public Direct2DFactory Direct2DFactory
58 | {
59 | get
60 | {
61 | return this._factory;
62 | }
63 | }
64 |
65 | public DirectWriteFactory DirectWriteFactory
66 | {
67 | get
68 | {
69 | if (this._directWriteFactory == null)
70 | {
71 | this._directWriteFactory = DirectWriteFactory.Create(DirectWriteFactoryType.Shared);
72 | }
73 | return this._directWriteFactory;
74 | }
75 | }
76 |
77 | protected WicImagingFactory ImagingFactory
78 | {
79 | get
80 | {
81 | if (this._imagingFactory == null)
82 | {
83 | this._imagingFactory = WicImagingFactory.Create();
84 | }
85 | return this._imagingFactory;
86 | }
87 | }
88 |
89 | protected WindowRenderTarget RenderTarget
90 | {
91 | get
92 | {
93 | return this._renderTarget;
94 | }
95 | }
96 |
97 | protected override void OnHandleCreated(EventArgs e)
98 | {
99 | CreateDeviceIndependentResources();
100 | base.OnHandleCreated(e);
101 | }
102 |
103 | private void CreateDeviceIndependentResources()
104 | {
105 | try {
106 | this._factory = Direct2DFactory.CreateFactory(FactoryType.SingleThreaded, DebugLevel.None);
107 | this._renderTarget = this._factory.CreateWindowRenderTarget(this);
108 | OnCreateDeviceIndependentResources(this._factory);
109 | this._deviceIndependedResourcesCreated = true;
110 | } catch (Exception ex) {
111 | Debug.WriteLine(ex.ToString());
112 | throw;
113 | }
114 | }
115 |
116 | private void CleanUpDeviceIndependentResources()
117 | {
118 | if(this._deviceIndependedResourcesCreated)
119 | OnCleanUpDeviceIndependentResources();
120 | if (this._imagingFactory != null)
121 | {
122 | this._imagingFactory.Dispose();
123 | this._imagingFactory = null;
124 | }
125 | if (this._directWriteFactory != null)
126 | {
127 | this._directWriteFactory.Dispose();
128 | this._directWriteFactory = null;
129 | }
130 | if (this._renderTarget != null)
131 | {
132 | this._renderTarget.Dispose();
133 | this._renderTarget = null;
134 | }
135 | if (this._factory != null)
136 | {
137 | this._factory.Dispose();
138 | this._factory = null;
139 | }
140 | }
141 |
142 | protected virtual void OnCleanUpDeviceIndependentResources()
143 | {
144 | }
145 |
146 | private void CleanUpDeviceResources()
147 | {
148 | OnCleanUpDeviceResources();
149 | }
150 |
151 | protected virtual void OnCleanUpDeviceResources()
152 | {
153 | }
154 |
155 | private void CreateDeviceResources()
156 | {
157 | OnCreateDeviceResources(this._renderTarget);
158 | }
159 |
160 | protected virtual void OnCreateDeviceResources(WindowRenderTarget renderTarget)
161 | {
162 | }
163 |
164 | protected virtual void OnCreateDeviceIndependentResources(Direct2DFactory factory)
165 | {
166 | }
167 |
168 | protected override void OnPaint(PaintEventArgs e)
169 | {
170 | CreateDeviceResources();
171 | try
172 | {
173 | this._renderTarget.BeginDraw();
174 | try
175 | {
176 | this._renderTarget.Transform = Matrix3x2.Identity;
177 | this._renderTarget.Clear(Color.FromRGB(this.BackColor.R, this.BackColor.G, this.BackColor.B));
178 | OnRender(RenderTarget);
179 | }
180 | finally
181 | {
182 | this._renderTarget.EndDraw();
183 | }
184 | }
185 | finally
186 | {
187 | CleanUpDeviceResources();
188 | }
189 | }
190 |
191 | public void Render()
192 | {
193 | OnPaint(null);
194 | }
195 |
196 | protected virtual void OnRender(WindowRenderTarget renderTarget)
197 | {
198 | }
199 |
200 | protected override void OnResize(EventArgs e)
201 | {
202 | base.OnResize(e);
203 | if (this.RenderTarget != null)
204 | {
205 | this.RenderTarget.Resize(new SizeU { Width = (uint)ClientSize.Width, Height = (uint)ClientSize.Height });
206 | Invalidate();
207 | }
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/Direct2DTextMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Windows.Controls;
21 |
22 | using Managed.Graphics.Direct2D;
23 | using Managed.Graphics.DirectWrite;
24 |
25 | namespace WpfTextBenchmark
26 | {
27 | public class Direct2DTextMatrixView : WinFormsMatrixView
28 | {
29 | D2DView d2d;
30 |
31 | public Direct2DTextMatrixView()
32 | {
33 | host.Child = d2d = new D2DView() { parent = this };
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return "Direct2D";
39 | }
40 |
41 | System.Windows.Media.Typeface cachedTypeface;
42 | List texts = new List();
43 |
44 | public override IText AddText(string text, double fontSize, System.Windows.Media.SolidColorBrush brush)
45 | {
46 | if (cachedTypeface == null) {
47 | cachedTypeface = CreateTypeface();
48 | }
49 | var textFormat = d2d.DirectWriteFactory.CreateTextFormat(
50 | cachedTypeface.FontFamily.Source,
51 | FontWeight.Normal,
52 | FontStyle.Normal,
53 | FontStretch.Normal,
54 | (float)fontSize);
55 | textFormat.TextAlignment = TextAlignment.Leading;
56 | textFormat.ParagraphAlignment = ParagraphAlignment.Near;
57 |
58 | var textLayout = d2d.DirectWriteFactory.CreateTextLayout(
59 | text,
60 | textFormat,
61 | 300, 100);
62 |
63 | MyText myText = new MyText {
64 | parent = this,
65 | textFormat = textFormat,
66 | textLayout = textLayout,
67 | color = Color.FromRGB(brush.Color.R / 255f, brush.Color.G / 255f, brush.Color.B / 255f)
68 | };
69 | texts.Add(myText);
70 | return myText;
71 | }
72 |
73 | class MyText : IText
74 | {
75 | public Direct2DTextMatrixView parent;
76 | public TextFormat textFormat;
77 | public TextLayout textLayout;
78 | public Color color;
79 | public Brush brush;
80 | public System.Windows.Point Position { get; set; }
81 |
82 | public void Remove()
83 | {
84 | textFormat.Dispose();
85 | textLayout.Dispose();
86 | parent.texts.Remove(this);
87 | }
88 | }
89 |
90 | sealed class D2DView : Direct2DControl
91 | {
92 | public Direct2DTextMatrixView parent;
93 |
94 | protected override void OnCreateDeviceIndependentResources(Direct2DFactory factory)
95 | {
96 | base.OnCreateDeviceIndependentResources(factory);
97 | }
98 |
99 | protected override void OnCleanUpDeviceIndependentResources()
100 | {
101 | base.OnCleanUpDeviceIndependentResources();
102 | }
103 |
104 | protected override void OnCreateDeviceResources(WindowRenderTarget renderTarget)
105 | {
106 | base.OnCreateDeviceResources(renderTarget);
107 | foreach (MyText text in parent.texts) {
108 | text.brush = renderTarget.CreateSolidColorBrush(text.color);
109 | }
110 | }
111 |
112 | protected override void OnCleanUpDeviceResources()
113 | {
114 | base.OnCleanUpDeviceResources();
115 | foreach (MyText text in parent.texts) {
116 | text.brush.Dispose();
117 | text.brush = null;
118 | }
119 | parent.OnFrameRendered(EventArgs.Empty);
120 | }
121 |
122 | protected override void OnRender(WindowRenderTarget renderTarget)
123 | {
124 | foreach (MyText text in parent.texts) {
125 | renderTarget.DrawTextLayout(new PointF((float)text.Position.X, (float)text.Position.Y),
126 | text.textLayout,
127 | text.brush,
128 | DrawTextOptions.None);
129 | }
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/ExtTextOutMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Drawing;
21 | using System.Runtime.InteropServices;
22 | using System.Windows.Forms;
23 |
24 | namespace WpfTextBenchmark
25 | {
26 | public unsafe class ExtTextOutMatrixView : WinFormsMatrixView
27 | {
28 | TextView view;
29 |
30 | public ExtTextOutMatrixView()
31 | {
32 | host.Child = view = new TextView() { parent = this };
33 | }
34 |
35 | public override string ToString()
36 | {
37 | return "GDI (ExtTextOut)";
38 | }
39 |
40 | System.Windows.Media.Typeface cachedTypeface;
41 | List texts = new List();
42 |
43 | public override IText AddText(string text, double fontSize, System.Windows.Media.SolidColorBrush brush)
44 | {
45 | if (cachedTypeface == null) {
46 | cachedTypeface = CreateTypeface();
47 | }
48 |
49 | LOGFONT logfont = new LOGFONT();
50 | logfont.lfFaceName = cachedTypeface.FontFamily.Source;
51 | logfont.lfHeight = -(int)fontSize;
52 | //logfont.lfOutPrecision = 4;
53 | //logfont.lfQuality = 5;
54 |
55 | MyText myText = new MyText {
56 | parent = this,
57 | text = text,
58 | color = brush.Color.R | (brush.Color.G << 8) | (brush.Color.B << 16),
59 | font = CreateFontIndirect(ref logfont)
60 | };
61 | //myText.brush = new SolidBrush(myText.color);
62 |
63 | texts.Add(myText);
64 | return myText;
65 | }
66 |
67 | [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
68 | struct GCP_RESULTS
69 | {
70 | public int lStructSize;
71 | public char* lpOutString;
72 | public uint* lpOrder;
73 | public int* lpDx;
74 | public int* lpCaretPos;
75 | public byte* lpClass;
76 | public char* lpGlyphs;
77 | public uint nGlyphs;
78 | public int nMaxFit;
79 | }
80 |
81 | [DllImportAttribute("gdi32.dll", CharSet=CharSet.Unicode)]
82 | static extern int GetCharacterPlacement(
83 | IntPtr hdc,
84 | string lpString,
85 | int nCount,
86 | int nMaxExtent,
87 | ref GCP_RESULTS lpResults,
88 | uint dwFlags
89 | );
90 |
91 | [DllImportAttribute("gdi32.dll")]
92 | static extern IntPtr CreateFontIndirect(ref LOGFONT lplf);
93 |
94 | [DllImportAttribute("gdi32.dll")]
95 | [return: MarshalAs(UnmanagedType.Bool)]
96 | static extern bool DeleteObject(IntPtr hObject);
97 |
98 | [DllImportAttribute("gdi32.dll")]
99 | static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
100 |
101 | [DllImportAttribute("gdi32.dll", CharSet=CharSet.Unicode)]
102 | [return: MarshalAs(UnmanagedType.Bool)]
103 | static extern bool ExtTextOut(
104 | IntPtr hdc,
105 | int X,
106 | int Y,
107 | uint fuOptions,
108 | void* lprc,
109 | string lpString,
110 | int cbCount,
111 | int[] lpDx
112 | );
113 |
114 | const int GCP_REORDER = 2;
115 |
116 | [DllImport("gdi32.dll")]
117 | static extern int SetBkMode(IntPtr hdc, int iBkMode);
118 |
119 | [DllImport("gdi32.dll")]
120 | static extern int SetTextColor(IntPtr hdc, int crColor);
121 |
122 | const int LF_FACESIZE = 32;
123 |
124 | [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
125 | struct LOGFONT
126 | {
127 | public int lfHeight;
128 | public int lfWidth;
129 | public int lfEscapement;
130 | public int lfOrientation;
131 | public int lfWeight;
132 | public byte lfItalic;
133 | public byte lfUnderline;
134 | public byte lfStrikeOut;
135 | public byte lfCharSet;
136 | public byte lfOutPrecision;
137 | public byte lfClipPrecision;
138 | public byte lfQuality;
139 | public byte lfPitchAndFamily;
140 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
141 | public string lfFaceName;
142 | }
143 |
144 | class MyText : IText
145 | {
146 | public string text;
147 | public ExtTextOutMatrixView parent;
148 | public int color;
149 | public IntPtr font;
150 | public System.Windows.Point Position { get; set; }
151 |
152 | public void Remove()
153 | {
154 | DeleteObject(font);
155 | parent.texts.Remove(this);
156 | }
157 | }
158 |
159 | sealed class TextView : Control
160 | {
161 | public ExtTextOutMatrixView parent;
162 |
163 | public TextView()
164 | {
165 | this.BackColor = Color.Black;
166 | SetStyle(
167 | ControlStyles.AllPaintingInWmPaint |
168 | ControlStyles.DoubleBuffer |
169 | ControlStyles.Opaque |
170 | ControlStyles.UserPaint, true);
171 | }
172 |
173 | protected override void OnPaint(PaintEventArgs e)
174 | {
175 | e.Graphics.Clear(Color.Black);
176 | IntPtr hdc = e.Graphics.GetHdc();
177 | try {
178 | SetBkMode(hdc, 1); // TRANSPARENT
179 | foreach (MyText text in parent.texts) {
180 | SetTextColor(hdc, text.color);
181 | SelectObject(hdc, text.font);
182 | ExtTextOut(hdc, (int)text.Position.X, (int)text.Position.Y,
183 | 0, null, text.text, text.text.Length, null);
184 | }
185 | } finally {
186 | e.Graphics.ReleaseHdc();
187 | }
188 | parent.OnFrameRendered(EventArgs.Empty);
189 | }
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/FormattedTextMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Authors:
4 | * Dmitry Kolchev
5 | *
6 | * Copyright (C) 2010 Daniel Grunwald
7 | *
8 | * This sourcecode is licenced under The GNU Lesser General Public License
9 | *
10 | * The above copyright notice and this permission notice shall be included in
11 | * all copies or substantial portions of the Software.
12 | *
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
16 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | */
21 |
22 | using System;
23 | using System.Collections.Generic;
24 | using System.Globalization;
25 | using System.Windows;
26 | using System.Windows.Controls;
27 | using System.Windows.Media;
28 | using System.Windows.Threading;
29 |
30 | namespace WpfTextBenchmark
31 | {
32 | ///
33 | /// Description of FormattedTextMatrixView.
34 | ///
35 | public class FormattedTextMatrixView: MatrixView
36 | {
37 | Typeface cachedTypeface;
38 |
39 | public override string ToString()
40 | {
41 | return "WPF FormattedText";
42 | }
43 |
44 | public override IText AddText(string text, double fontSize, SolidColorBrush brush)
45 | {
46 | if (cachedTypeface == null) {
47 | cachedTypeface = CreateTypeface();
48 | }
49 | FormattedText f = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, cachedTypeface,
50 | fontSize, brush, null, TextFormattingMode.Display);
51 | MyText myText = new MyText { line = f, parent = this };
52 | texts.Add(myText);
53 | return myText;
54 | }
55 |
56 | List texts = new List();
57 |
58 | protected override void OnRender(DrawingContext drawingContext)
59 | {
60 | foreach (MyText text in texts) {
61 | drawingContext.DrawText(text.line, text.Position);
62 | }
63 | Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate { OnFrameRendered(EventArgs.Empty); }));
64 | }
65 |
66 | class MyText : IText
67 | {
68 | public FormattedTextMatrixView parent;
69 | public FormattedText line;
70 | public Point Position { get; set; }
71 |
72 | public void Remove()
73 | {
74 | parent.texts.Remove(this);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/GDIMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Drawing;
21 | using System.Windows.Forms;
22 |
23 | namespace WpfTextBenchmark
24 | {
25 | public class GDIMatrixView : WinFormsMatrixView
26 | {
27 | GDITextView view;
28 |
29 | public GDIMatrixView(bool textRenderer)
30 | {
31 | host.Child = view = new GDITextView() { parent = this, textRenderer = textRenderer };
32 | }
33 |
34 | public override string ToString()
35 | {
36 | return view.textRenderer ? "GDI (TextRenderer)" : "GDI+ (Graphics.DrawString)";
37 | }
38 |
39 | System.Windows.Media.Typeface cachedTypeface;
40 | List texts = new List();
41 |
42 | public override IText AddText(string text, double fontSize, System.Windows.Media.SolidColorBrush brush)
43 | {
44 | if (cachedTypeface == null) {
45 | cachedTypeface = CreateTypeface();
46 | }
47 |
48 | MyText myText = new MyText {
49 | parent = this,
50 | text = text,
51 | color = Color.FromArgb(brush.Color.R, brush.Color.G, brush.Color.B),
52 | font = new Font(cachedTypeface.FontFamily.Source, (float)(fontSize / 96 * 72), FontStyle.Regular),
53 | };
54 | myText.brush = new SolidBrush(myText.color);
55 | texts.Add(myText);
56 | return myText;
57 | }
58 |
59 | class MyText : IText
60 | {
61 | public string text;
62 | public GDIMatrixView parent;
63 | public Font font;
64 | public Color color;
65 | public SolidBrush brush;
66 | public System.Windows.Point Position { get; set; }
67 |
68 | public void Remove()
69 | {
70 | font.Dispose();
71 | brush.Dispose();
72 | parent.texts.Remove(this);
73 | }
74 | }
75 |
76 | sealed class GDITextView : Control
77 | {
78 | public GDIMatrixView parent;
79 | public bool textRenderer;
80 |
81 | public GDITextView()
82 | {
83 | this.BackColor = Color.Black;
84 | SetStyle(
85 | ControlStyles.AllPaintingInWmPaint |
86 | ControlStyles.DoubleBuffer |
87 | ControlStyles.Opaque |
88 | ControlStyles.UserPaint, true);
89 | }
90 |
91 | protected override void OnPaint(PaintEventArgs e)
92 | {
93 | e.Graphics.Clear(Color.Black);
94 | if (textRenderer) {
95 | foreach (MyText text in parent.texts) {
96 | TextRenderer.DrawText(e.Graphics, text.text, text.font, new Point((int)text.Position.X, (int)text.Position.Y), text.color, TextFormatFlags.NoClipping | TextFormatFlags.NoPrefix);
97 | }
98 | } else {
99 | StringFormat f = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox);
100 | foreach (MyText text in parent.texts) {
101 | e.Graphics.DrawString(text.text, text.font, text.brush, (float)text.Position.X, (float)text.Position.Y, f);
102 | }
103 | f.Dispose();
104 | }
105 | parent.OnFrameRendered(EventArgs.Empty);
106 | }
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/GlyphMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Globalization;
21 | using System.Windows;
22 | using System.Windows.Controls;
23 | using System.Windows.Markup;
24 | using System.Windows.Media;
25 | using System.Windows.Threading;
26 |
27 | namespace WpfTextBenchmark
28 | {
29 | ///
30 | /// Description of GlyphMatrixView.
31 | ///
32 | public class GlyphMatrixView : MatrixView
33 | {
34 | GlyphTypeface cachedTypeface;
35 |
36 | public override string ToString()
37 | {
38 | return "WPF GlyphRun";
39 | }
40 |
41 | public override IText AddText(string text, double fontSize, SolidColorBrush brush)
42 | {
43 | if (cachedTypeface == null) {
44 | var t = CreateTypeface();
45 | if (!t.TryGetGlyphTypeface(out cachedTypeface))
46 | throw new NotSupportedException();
47 | }
48 |
49 | ushort[] glyphIndexes = new ushort[text.Length];
50 | double[] advanceWidths = new double[text.Length];
51 |
52 | double totalWidth = 0;
53 | for (int n = 0; n < text.Length; n++) {
54 | ushort glyphIndex;
55 | cachedTypeface.CharacterToGlyphMap.TryGetValue(text[n], out glyphIndex);
56 | glyphIndexes[n] = glyphIndex;
57 | double width = cachedTypeface.AdvanceWidths[glyphIndex] * fontSize;
58 | advanceWidths[n] = width;
59 | totalWidth += width;
60 | }
61 |
62 | GlyphRun run = new GlyphRun(cachedTypeface,
63 | bidiLevel: 0,
64 | isSideways: false,
65 | renderingEmSize: fontSize,
66 | glyphIndices: glyphIndexes,
67 | baselineOrigin: new Point(0, Math.Round(cachedTypeface.Baseline * fontSize)),
68 | advanceWidths: advanceWidths,
69 | glyphOffsets: null,
70 | characters: null,
71 | deviceFontName: null,
72 | clusterMap: null,
73 | caretStops: null,
74 | language: null);
75 | MyText myText = new MyText { run = run, parent = this, brush = brush };
76 | texts.Add(myText);
77 | return myText;
78 | }
79 |
80 | List texts = new List();
81 |
82 | protected override void OnRender(DrawingContext drawingContext)
83 | {
84 | foreach (MyText text in texts) {
85 | drawingContext.PushTransform(new TranslateTransform(text.Position.X, text.Position.Y));
86 | drawingContext.DrawGlyphRun(text.brush, text.run);
87 | drawingContext.Pop();
88 | }
89 | Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate { OnFrameRendered(EventArgs.Empty); }));
90 | }
91 |
92 | class MyText : IText
93 | {
94 | public GlyphMatrixView parent;
95 | public GlyphRun run;
96 | public Brush brush;
97 | public Point Position { get; set; }
98 |
99 | public void Remove()
100 | {
101 | parent.texts.Remove(this);
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/MatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Windows;
20 | using System.Windows.Controls;
21 | using System.Windows.Media;
22 |
23 | namespace WpfTextBenchmark
24 | {
25 | public interface IText
26 | {
27 | Point Position { get; set; }
28 | void Remove();
29 | }
30 |
31 | public abstract class MatrixView : FrameworkElement
32 | {
33 | public MatrixView()
34 | {
35 | this.ClipToBounds = true;
36 | }
37 |
38 | public abstract IText AddText(string text, double fontSize, SolidColorBrush brush);
39 |
40 | public event EventHandler FrameRendered;
41 |
42 | protected virtual void OnFrameRendered(EventArgs e)
43 | {
44 | if (FrameRendered != null) {
45 | FrameRendered(this, e);
46 | }
47 | }
48 |
49 | protected Typeface CreateTypeface()
50 | {
51 | return new Typeface((FontFamily)GetValue(TextBlock.FontFamilyProperty),
52 | (FontStyle)GetValue(TextBlock.FontStyleProperty),
53 | (FontWeight)GetValue(TextBlock.FontWeightProperty),
54 | (FontStretch)GetValue(TextBlock.FontStretchProperty));
55 | }
56 |
57 | public virtual void Refresh()
58 | {
59 | InvalidateVisual();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Using directives
2 |
3 | using System;
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 |
7 | #endregion
8 |
9 | // General Information about an assembly is controlled through the following
10 | // set of attributes. Change these attribute values to modify the information
11 | // associated with an assembly.
12 | [assembly: AssemblyTitle("WpfTextBenchmark")]
13 | [assembly: AssemblyDescription("")]
14 | [assembly: AssemblyConfiguration("")]
15 | [assembly: AssemblyCompany("")]
16 | [assembly: AssemblyProduct("WpfTextBenchmark")]
17 | [assembly: AssemblyCopyright("Copyright 2010")]
18 | [assembly: AssemblyTrademark("")]
19 | [assembly: AssemblyCulture("")]
20 |
21 | // This sets the default COM visibility of types in the assembly to invisible.
22 | // If you need to expose a type to COM, use [ComVisible(true)] on that type.
23 | [assembly: ComVisible(false)]
24 |
25 | // The assembly version has following format :
26 | //
27 | // Major.Minor.Build.Revision
28 | //
29 | // You can specify all the values or you can use the default the Revision and
30 | // Build Numbers by using the '*' as shown below:
31 | [assembly: AssemblyVersion("1.0.*")]
32 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/Properties/WPFAssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Using directives
2 |
3 | using System.Resources;
4 | using System.Windows;
5 |
6 | #endregion
7 |
8 |
9 |
10 | //In order to begin building localizable applications, set
11 | //CultureYouAreCodingWith in your .csproj file
12 | //inside a . For example, if you are using US english
13 | //in your source files, set the to en-US. Then uncomment
14 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
15 | //the line below to match the UICulture setting in the project file.
16 |
17 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
18 |
19 |
20 | [assembly: ThemeInfo(
21 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
22 | //(used if a resource is not found in the page,
23 | // or application resource dictionaries)
24 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
25 | //(used if a resource is not found in the page,
26 | // app, or any theme specific resource dictionaries)
27 | )]
28 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/TextBlockMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Windows;
20 | using System.Windows.Controls;
21 | using System.Windows.Media;
22 | using System.Windows.Threading;
23 |
24 | namespace WpfTextBenchmark
25 | {
26 | public class TextBlockMatrixView : MatrixView
27 | {
28 | protected readonly Canvas host = new Canvas();
29 |
30 | public TextBlockMatrixView()
31 | {
32 | this.AddVisualChild(host);
33 | this.AddLogicalChild(host);
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return "Canvas + TextBlocks";
39 | }
40 |
41 | protected override int VisualChildrenCount {
42 | get { return 1; }
43 | }
44 |
45 | protected override Visual GetVisualChild(int index)
46 | {
47 | return host;
48 | }
49 |
50 | protected override Size MeasureOverride(Size availableSize)
51 | {
52 | host.Measure(availableSize);
53 | return host.DesiredSize;
54 | }
55 |
56 | protected override Size ArrangeOverride(Size finalSize)
57 | {
58 | host.Arrange(new Rect(new Point(0, 0), finalSize));
59 | return finalSize;
60 | }
61 |
62 | public override void Refresh()
63 | {
64 | Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate { OnFrameRendered(EventArgs.Empty); }));
65 | }
66 |
67 | public override IText AddText(string text, double fontSize, SolidColorBrush brush)
68 | {
69 | MyText myText = new MyText();
70 | myText.Text = text;
71 | myText.FontSize = fontSize;
72 | myText.Foreground = brush;
73 | myText.parentCanvas = host;
74 | host.Children.Add(myText);
75 | return myText;
76 | }
77 |
78 | sealed class MyText : TextBlock, IText
79 | {
80 | public Canvas parentCanvas;
81 |
82 | public Point Position {
83 | get {
84 | return new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
85 | }
86 | set {
87 | Canvas.SetLeft(this, value.X);
88 | Canvas.SetTop(this, value.Y);
89 | }
90 | }
91 |
92 | public void Remove()
93 | {
94 | parentCanvas.Children.Remove(this);
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/TextLineMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Globalization;
21 | using System.Windows;
22 | using System.Windows.Controls;
23 | using System.Windows.Media;
24 | using System.Windows.Media.TextFormatting;
25 | using System.Windows.Threading;
26 |
27 | namespace WpfTextBenchmark
28 | {
29 | public class TextLineMatrixView : MatrixView
30 | {
31 | TextFormatter formatter = TextFormatter.Create(TextFormattingMode.Display);
32 | Typeface cachedTypeface;
33 |
34 | public override string ToString()
35 | {
36 | return "WPF TextLine";
37 | }
38 |
39 | public override IText AddText(string text, double fontSize, SolidColorBrush brush)
40 | {
41 | if (cachedTypeface == null) {
42 | cachedTypeface = CreateTypeface();
43 | }
44 | GlobalTextRunProperties p = new GlobalTextRunProperties {
45 | typeface = cachedTypeface,
46 | fontRenderingEmSize = fontSize,
47 | foregroundBrush = brush,
48 | cultureInfo = CultureInfo.CurrentCulture
49 | };
50 | MyTextSource myTextSource = new MyTextSource { text = text, textRunProperties = p };
51 | TextLine line = formatter.FormatLine(myTextSource, 0, 500, new MyTextParagraphProperties {defaultTextRunProperties = p}, null);
52 | MyText myText = new MyText { line = line, parent = this };
53 | texts.Add(myText);
54 | return myText;
55 | }
56 |
57 | List texts = new List();
58 |
59 | protected override void OnRender(DrawingContext drawingContext)
60 | {
61 | foreach (MyText text in texts) {
62 | text.line.Draw(drawingContext, text.Position, InvertAxes.None);
63 | }
64 | Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate { OnFrameRendered(EventArgs.Empty); }));
65 | }
66 |
67 | class MyText : IText
68 | {
69 | public TextLineMatrixView parent;
70 | public TextLine line;
71 | public Point Position { get; set; }
72 |
73 | public void Remove()
74 | {
75 | line.Dispose();
76 | parent.texts.Remove(this);
77 | }
78 | }
79 |
80 | class MyTextSource : TextSource
81 | {
82 | public string text;
83 | public TextRunProperties textRunProperties;
84 |
85 | public override TextRun GetTextRun(int textSourceCharacterIndex)
86 | {
87 | if (textSourceCharacterIndex == 0)
88 | return new TextCharacters(text, 0, text.Length, textRunProperties);
89 | return new TextEndOfParagraph(1);
90 | }
91 |
92 | public override TextSpan GetPrecedingText(int textSourceCharacterIndexLimit)
93 | {
94 | throw new NotImplementedException();
95 | }
96 |
97 | public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
98 | {
99 | throw new NotImplementedException();
100 | }
101 | }
102 |
103 | sealed class GlobalTextRunProperties : TextRunProperties
104 | {
105 | internal Typeface typeface;
106 | internal double fontRenderingEmSize;
107 | internal Brush foregroundBrush;
108 | internal Brush backgroundBrush = null;
109 | internal System.Globalization.CultureInfo cultureInfo;
110 |
111 | public override Typeface Typeface { get { return typeface; } }
112 | public override double FontRenderingEmSize { get { return fontRenderingEmSize; } }
113 | public override double FontHintingEmSize { get { return fontRenderingEmSize; } }
114 | public override TextDecorationCollection TextDecorations { get { return null; } }
115 | public override Brush ForegroundBrush { get { return foregroundBrush; } }
116 | public override Brush BackgroundBrush { get { return backgroundBrush; } }
117 | public override System.Globalization.CultureInfo CultureInfo { get { return cultureInfo; } }
118 | public override TextEffectCollection TextEffects { get { return null; } }
119 | }
120 |
121 | class MyTextParagraphProperties : TextParagraphProperties
122 | {
123 | internal TextRunProperties defaultTextRunProperties;
124 | internal TextWrapping textWrapping = TextWrapping.Wrap;
125 | internal double tabSize = 40;
126 |
127 | public override double DefaultIncrementalTab {
128 | get { return tabSize; }
129 | }
130 |
131 | public override FlowDirection FlowDirection { get { return FlowDirection.LeftToRight; } }
132 | public override TextAlignment TextAlignment { get { return TextAlignment.Left; } }
133 | public override double LineHeight { get { return double.NaN; } }
134 | public override bool FirstLineInParagraph { get { return false; } }
135 | public override TextRunProperties DefaultTextRunProperties { get { return defaultTextRunProperties; } }
136 | public override TextWrapping TextWrapping { get { return textWrapping; } }
137 | public override TextMarkerProperties TextMarkerProperties { get { return null; } }
138 | public override double Indent { get { return 0; } }
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/WinFormsMatrixView.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Windows;
20 | using System.Windows.Forms.Integration;
21 | using System.Windows.Media;
22 |
23 | namespace WpfTextBenchmark
24 | {
25 | public abstract class WinFormsMatrixView : MatrixView
26 | {
27 | protected readonly WindowsFormsHost host = new WindowsFormsHost();
28 |
29 | public WinFormsMatrixView()
30 | {
31 | this.AddVisualChild(host);
32 | this.AddLogicalChild(host);
33 | }
34 |
35 | protected override int VisualChildrenCount {
36 | get { return 1; }
37 | }
38 |
39 | protected override Visual GetVisualChild(int index)
40 | {
41 | return host;
42 | }
43 |
44 | protected override Size MeasureOverride(Size availableSize)
45 | {
46 | host.Measure(availableSize);
47 | return host.DesiredSize;
48 | }
49 |
50 | protected override Size ArrangeOverride(Size finalSize)
51 | {
52 | host.Arrange(new Rect(new Point(0, 0), finalSize));
53 | return finalSize;
54 | }
55 |
56 | public override void Refresh()
57 | {
58 | host.Child.Invalidate();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/Window1.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Pause
19 | Unicode
20 | Keep
21 | Words
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/WpfTextBenchmark/Window1.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 Daniel Grunwald
3 | *
4 | * This sourcecode is licenced under The GNU Lesser General Public License
5 | *
6 | * The above copyright notice and this permission notice shall be included in
7 | * all copies or substantial portions of the Software.
8 | *
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
12 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15 | * USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Diagnostics;
21 | using System.Linq;
22 | using System.Text;
23 | using System.Windows;
24 | using System.Windows.Controls;
25 | using System.Windows.Data;
26 | using System.Windows.Documents;
27 | using System.Windows.Input;
28 | using System.Windows.Media;
29 | using System.Windows.Threading;
30 |
31 | namespace WpfTextBenchmark
32 | {
33 | ///
34 | /// Interaction logic for Window1.xaml
35 | ///
36 | public partial class Window1 : Window
37 | {
38 | MatrixView v ;
39 | Random rnd = new Random(1337);
40 | const int width = 950;
41 | const int height = 350;
42 | List texts = new List();
43 | IText framerateText;
44 | string[] possibleTexts;
45 |
46 | public Window1()
47 | {
48 | possibleTexts = Enumerable.Range(0, char.MaxValue).Select(c => (char)c).Where(char.IsLetterOrDigit).Select(c => c.ToString()).ToArray();
49 | InitializeComponent();
50 | Direct2DButton.IsEnabled = Environment.OSVersion.Version.Major >= 6;
51 | }
52 |
53 | void InitView()
54 | {
55 | this.Title = "Text Benchmark";
56 | Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(
57 | delegate {
58 | if (v != null)
59 | this.Title = "Text Benchmark - " + v.ToString();
60 | }));
61 | framerateText = null;
62 | stopwatch = null;
63 | texts.Clear();
64 | rnd = new Random(1337);
65 | for (int i = 0; i < (int)textCount.Value; i++) {
66 | texts.Add(NewRandomText());
67 | texts[i].Position = new Point(rnd.Next(0, width), rnd.Next(-50, height));
68 | }
69 | v.Width = width;
70 | v.Height = height;
71 | scroll.Content = v;
72 | frameCounter = 0;
73 | lastFrame.Restart();
74 | v.FrameRendered += new EventHandler(v_FrameRendered);
75 | Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(NextFrame));
76 | }
77 |
78 | int frameCounter;
79 |
80 | void v_FrameRendered(object sender, EventArgs e)
81 | {
82 | frameCounter++;
83 | NextFrame();
84 | }
85 |
86 | string RandomCharacter()
87 | {
88 | if (unicode.IsChecked == true)
89 | return possibleTexts[rnd.NextDouble() < 0.2 ? rnd.Next(possibleTexts.Length) : rnd.Next(100)];
90 | else
91 | return possibleTexts[rnd.Next(26*2+10)];
92 | }
93 |
94 | IText NewRandomText()
95 | {
96 | string s;
97 | if (words.IsChecked == true) {
98 | int length = rnd.Next(1, 10);
99 | s = string.Empty;
100 | for (int i = 0; i < length; i++)
101 | s += RandomCharacter();
102 | } else {
103 | s = RandomCharacter();
104 | }
105 | var brush = new SolidColorBrush(Color.FromRgb((byte)rnd.Next(0,200),(byte)rnd.Next(100,255),(byte)rnd.Next(0,200)));
106 | brush.Freeze();
107 | IText text = v.AddText(s,
108 | rnd.Next(10, 16),
109 | brush
110 | );
111 | return text;
112 | }
113 |
114 | Stopwatch stopwatch;
115 | Stopwatch lastFrame = Stopwatch.StartNew();
116 |
117 | void NextFrame()
118 | {
119 | if (stopped.IsChecked == false && v != null) {
120 | if (stopwatch == null)
121 | stopwatch = Stopwatch.StartNew();
122 | if (stopwatch.ElapsedMilliseconds > 1000) {
123 | if (framerateText != null) framerateText.Remove();
124 | double framerate = frameCounter / stopwatch.Elapsed.TotalSeconds;
125 | framerateText = v.AddText(framerate.ToString("f1"), 16, Brushes.White);
126 | stopwatch.Restart();
127 | frameCounter = 0;
128 | }
129 | long elapsed = lastFrame.ElapsedMilliseconds;
130 | lastFrame.Restart();
131 | for (int i = 0; i < texts.Count; i++) {
132 | texts[i].Position += new Vector(0, elapsed / 2f);
133 | if (texts[i].Position.Y > height) {
134 | double oldHeight = texts[i].Position.Y;
135 | if (keepEverything.IsChecked == false) {
136 | texts[i].Remove();
137 | texts[i] = NewRandomText();
138 | }
139 | texts[i].Position = new Point(rnd.Next(0,width), oldHeight - height - 50);
140 | }
141 | }
142 | v.Refresh();
143 | }
144 | }
145 |
146 | void GlyphRunButton_Click(object sender, RoutedEventArgs e)
147 | {
148 | v = new GlyphMatrixView();
149 | InitView();
150 | }
151 |
152 | void TextLineButton_Click(object sender, RoutedEventArgs e)
153 | {
154 | v = new TextLineMatrixView();
155 | InitView();
156 | }
157 |
158 | void FormattedTextButton_Click(object sender, RoutedEventArgs e)
159 | {
160 | v = new FormattedTextMatrixView();
161 | InitView();
162 | }
163 |
164 | void TextBlockButton_Click(object sender, RoutedEventArgs e)
165 | {
166 | v = new TextBlockMatrixView();
167 | InitView();
168 | }
169 |
170 | void Direct2DButton_Click(object sender, RoutedEventArgs e)
171 | {
172 | v = new Direct2DTextMatrixView();
173 | InitView();
174 | }
175 |
176 | void GDIButton_Click(object sender, RoutedEventArgs e)
177 | {
178 | v = new GDIMatrixView(true);
179 | InitView();
180 | }
181 |
182 | void GDIPlusButton_Click(object sender, RoutedEventArgs e)
183 | {
184 | v = new GDIMatrixView(false);
185 | InitView();
186 | }
187 |
188 | void ExtTextOutButton_Click(object sender, RoutedEventArgs e)
189 | {
190 | v = new ExtTextOutMatrixView();
191 | InitView();
192 | }
193 |
194 | void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
195 | {
196 | if (v == null)
197 | return;
198 | while (texts.Count < (int)e.NewValue) {
199 | texts.Add(NewRandomText());
200 | texts[texts.Count-1].Position = new Point(rnd.Next(0, width), rnd.Next(-50, height));
201 | }
202 | while (texts.Count > (int)e.NewValue) {
203 | texts[texts.Count-1].Remove();
204 | texts.RemoveAt(texts.Count-1);
205 | }
206 | v.Refresh();
207 | }
208 |
209 | void Stopped_Unchecked(object sender, RoutedEventArgs e)
210 | {
211 | NextFrame();
212 | }
213 | }
214 | }
--------------------------------------------------------------------------------
/WpfTextBenchmark/WpfTextBenchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {121B32DB-5BB9-439F-BF67-1BC697B98EF3}
5 | Debug
6 | x86
7 | WinExe
8 | WpfTextBenchmark
9 | WpfTextBenchmark
10 | v4.0
11 | Properties
12 | True
13 | False
14 | 4
15 | false
16 |
17 |
18 | bin\Debug\
19 | true
20 | Full
21 | False
22 | True
23 | DEBUG;TRACE
24 |
25 |
26 | bin\Release\
27 | False
28 | None
29 | True
30 | False
31 | TRACE
32 |
33 |
34 | False
35 | Auto
36 | 4194304
37 | AnyCPU
38 | 4096
39 |
40 |
41 | x86
42 |
43 |
44 |
45 | D2DWrapper.dll
46 |
47 |
48 | 3.0
49 |
50 |
51 | 3.0
52 |
53 |
54 |
55 | 3.5
56 |
57 |
58 |
59 | 3.5
60 |
61 |
62 |
63 |
64 | 4.0
65 |
66 |
67 |
68 | 3.5
69 |
70 |
71 | 3.0
72 |
73 |
74 | 3.0
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Code
83 | App.xaml
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | Code
98 | Window1.xaml
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------