Skip to content

Commit 0148516

Browse files
authored
Merge pull request oxyplot#1294 from VisualMelon/brushdisposal
Brushdisposal
2 parents 8139434 + 457e8ab commit 0148516

File tree

5 files changed

+241
-44
lines changed

5 files changed

+241
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ All notable changes to this project will be documented in this file.
5555
- Candle overlap each candle (#623)
5656
- CandleStick is overlapped when item.open == item.close in the CandleStickAndVolumeSeries (#1245)
5757
- Out of memory exception and performance issue with Catmull-Rom Spline (#1237)
58+
- Cache and Dispose Brush and Pen objects used by GraphicsRenderContext (#1230)
5859

5960
## [1.0.0] - 2016-09-11
6061
### Added
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace OxyPlot.WindowsForms
8+
{
9+
/// <summary>
10+
/// Describes a GDI+ Pen.
11+
/// </summary>
12+
public class GraphicsPenDescription
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="GraphicsPenDescription" /> class.
16+
/// </summary>
17+
/// <param name="color">The color.</param>
18+
/// <param name="thickness">The thickness.</param>
19+
/// <param name="dashArray">The dash array.</param>
20+
/// <param name="lineJoin">The line join.</param>
21+
public GraphicsPenDescription(OxyColor color, double thickness, double[] dashArray = null, OxyPlot.LineJoin lineJoin = OxyPlot.LineJoin.Miter)
22+
{
23+
Color = color;
24+
Thickness = thickness;
25+
DashArray = dashArray;
26+
LineJoin = lineJoin;
27+
28+
cachedHashCode = ComputeHashCode();
29+
}
30+
31+
/// <summary>
32+
/// Gets the color of the pen.
33+
/// </summary>
34+
/// <value>The color.</value>
35+
public OxyColor Color { get; }
36+
37+
/// <summary>
38+
/// Gets the line thickness.
39+
/// </summary>
40+
/// <value>The line thickness.</value>
41+
public double Thickness { get; }
42+
43+
/// <summary>
44+
/// Gets the dash array.
45+
/// </summary>
46+
/// <value>The dash array.</value>
47+
public double[] DashArray { get; }
48+
49+
/// <summary>
50+
/// Gets the line join type.
51+
/// </summary>
52+
/// <value>The line join type.</value>
53+
public LineJoin LineJoin { get; }
54+
55+
/// <summary>
56+
/// The HashCode of the <see cref="GraphicsPenDescription" /> instance, as computed in the constructor.
57+
/// </summary>
58+
private readonly int cachedHashCode;
59+
60+
/// <summary>
61+
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
62+
/// </summary>
63+
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
64+
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c> .</returns>
65+
public override bool Equals(object obj)
66+
{
67+
var description = obj as GraphicsPenDescription;
68+
69+
return description != null &&
70+
Color.Equals(description.Color) &&
71+
Thickness == description.Thickness &&
72+
DashArraysEquals(DashArray, description.DashArray) &&
73+
LineJoin == description.LineJoin;
74+
}
75+
76+
/// <summary>
77+
/// Returns a hash code for this instance.
78+
/// </summary>
79+
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
80+
public override int GetHashCode()
81+
{
82+
return cachedHashCode;
83+
}
84+
85+
/// <summary>
86+
/// Computes the HashCode for the instance.
87+
/// </summary>
88+
/// <returns>The HashCode for the instance.</returns>
89+
private int ComputeHashCode()
90+
{
91+
var hashCode = 754997215;
92+
93+
unchecked
94+
{
95+
hashCode = hashCode * -1521134295 + Color.GetHashCode();
96+
hashCode = hashCode * -1521134295 + Thickness.GetHashCode();
97+
hashCode = hashCode * -1521134295 + ComputeDashArrayHashCode(DashArray);
98+
hashCode = hashCode * -1521134295 + LineJoin.GetHashCode();
99+
}
100+
101+
return hashCode;
102+
}
103+
104+
/// <summary>
105+
/// Computes a HashCode for the given array based on every element in the array.
106+
/// </summary>
107+
/// <param name="array">The array</param>
108+
/// <returns>A HashCode</returns>
109+
private static int ComputeDashArrayHashCode(double[] array)
110+
{
111+
if (array == null)
112+
{
113+
return -1;
114+
}
115+
116+
int hashCode = array.Length;
117+
118+
for (int i = 0; i < array.Length; i++)
119+
{
120+
unchecked
121+
{
122+
hashCode = hashCode * 31 + array[i].GetHashCode();
123+
}
124+
}
125+
126+
return hashCode;
127+
}
128+
129+
/// <summary>
130+
/// Determines whether two arrays are equivalent.
131+
/// </summary>
132+
/// <param name="l">The left array.</param>
133+
/// <param name="r">The right array.</param>
134+
/// <returns><c>true</c> if the arrays are the same array, are both null, or have the same elements; otherwise <c>false</c></returns>
135+
private static bool DashArraysEquals(double[] l, double[] r)
136+
{
137+
if (l == r)
138+
{
139+
return true;
140+
}
141+
142+
if (l == null || r == null)
143+
{
144+
return false;
145+
}
146+
147+
if (l.Length != r.Length)
148+
{
149+
return false;
150+
}
151+
152+
for (int i = 0; i < l.Length; i++)
153+
{
154+
if (l[i] != r[i])
155+
{
156+
return false;
157+
}
158+
}
159+
160+
return true;
161+
}
162+
}
163+
}

Source/OxyPlot.WindowsForms/GraphicsRenderContext.cs

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public class GraphicsRenderContext : RenderContextBase, IDisposable
4545
/// </summary>
4646
private readonly Dictionary<OxyColor, Brush> brushes = new Dictionary<OxyColor, Brush>();
4747

48+
/// <summary>
49+
/// The pen cache.
50+
/// </summary>
51+
private readonly Dictionary<GraphicsPenDescription, Pen> pens = new Dictionary<GraphicsPenDescription, Pen>();
52+
4853
/// <summary>
4954
/// The string format.
5055
/// </summary>
@@ -105,12 +110,10 @@ public override void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, d
105110
{
106111
return;
107112
}
108-
109-
using (var pen = this.CreatePen(stroke, thickness))
110-
{
111-
this.g.SmoothingMode = SmoothingMode.HighQuality;
112-
this.g.DrawEllipse(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
113-
}
113+
114+
var pen = this.GetCachedPen(stroke, thickness);
115+
this.g.SmoothingMode = SmoothingMode.HighQuality;
116+
this.g.DrawEllipse(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
114117
}
115118

116119
/// <summary>
@@ -136,10 +139,8 @@ public override void DrawLine(
136139
}
137140

138141
this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality;
139-
using (var pen = this.CreatePen(stroke, thickness, dashArray, lineJoin))
140-
{
141-
this.g.DrawLines(pen, this.ToPoints(points));
142-
}
142+
var pen = this.GetCachedPen(stroke, thickness, dashArray, lineJoin);
143+
this.g.DrawLines(pen, this.ToPoints(points));
143144
}
144145

145146
/// <summary>
@@ -171,35 +172,16 @@ public override void DrawPolygon(
171172
var pts = this.ToPoints(points);
172173
if (fill.IsVisible())
173174
{
174-
this.g.FillPolygon(fill.ToBrush(), pts);
175+
this.g.FillPolygon(this.GetCachedBrush(fill), pts);
175176
}
176177

177178
if (stroke.IsInvisible() || thickness <= 0)
178179
{
179180
return;
180181
}
181182

182-
using (var pen = this.CreatePen(stroke, thickness))
183-
{
184-
if (dashArray != null)
185-
{
186-
pen.DashPattern = this.ToFloatArray(dashArray);
187-
}
188-
189-
switch (lineJoin)
190-
{
191-
case OxyPlot.LineJoin.Round:
192-
pen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
193-
break;
194-
case OxyPlot.LineJoin.Bevel:
195-
pen.LineJoin = System.Drawing.Drawing2D.LineJoin.Bevel;
196-
break;
197-
198-
// The default LineJoin is Miter
199-
}
200-
201-
this.g.DrawPolygon(pen, pts);
202-
}
183+
var pen = this.GetCachedPen(stroke, thickness, dashArray, lineJoin);
184+
this.g.DrawPolygon(pen, pts);
203185
}
204186

205187
/// <summary>
@@ -214,18 +196,16 @@ public override void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke,
214196
if (fill.IsVisible())
215197
{
216198
this.g.FillRectangle(
217-
fill.ToBrush(), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
199+
this.GetCachedBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
218200
}
219201

220202
if (stroke.IsInvisible() || thickness <= 0)
221203
{
222204
return;
223205
}
224206

225-
using (var pen = this.CreatePen(stroke, thickness))
226-
{
227-
this.g.DrawRectangle(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
228-
}
207+
var pen = this.GetCachedPen(stroke, thickness);
208+
this.g.DrawRectangle(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
229209
}
230210

231211
/// <summary>
@@ -304,18 +284,18 @@ public override void DrawText(
304284
var graphicsState = this.g.Save();
305285

306286
this.g.TranslateTransform((float)p.X, (float)p.Y);
307-
287+
308288
var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height);
309289
if (Math.Abs(rotate) > double.Epsilon)
310290
{
311291
this.g.RotateTransform((float)rotate);
312-
292+
313293
layoutRectangle.Height += (float)(fontSize / 18.0);
314294
}
315295

316296
this.g.TranslateTransform(dx, dy);
317297

318-
this.g.DrawString(text, font, fill.ToBrush(), layoutRectangle, this.stringFormat);
298+
this.g.DrawString(text, font, this.GetCachedBrush(fill), layoutRectangle, this.stringFormat);
319299

320300
this.g.Restore(graphicsState);
321301
}
@@ -437,6 +417,7 @@ public void Dispose()
437417
{
438418
i.Value.Dispose();
439419
}
420+
this.imageCache.Clear();
440421

441422
// dispose pens, brushes etc.
442423
this.stringFormat.Dispose();
@@ -445,6 +426,13 @@ public void Dispose()
445426
{
446427
brush.Dispose();
447428
}
429+
this.brushes.Clear();
430+
431+
foreach (var pen in this.pens.Values)
432+
{
433+
pen.Dispose();
434+
}
435+
this.pens.Clear();
448436
}
449437

450438
/// <summary>
@@ -509,7 +497,28 @@ private Brush GetCachedBrush(OxyColor fill)
509497
}
510498

511499
/// <summary>
512-
/// Gets a cached pen.
500+
/// Gets the cached pen.
501+
/// </summary>
502+
/// <param name="stroke">The stroke color.</param>
503+
/// <param name="thickness">The thickness.</param>
504+
/// <param name="dashArray">The dash array.</param>
505+
/// <param name="lineJoin">The line join.</param>
506+
/// <returns>A <see cref="Pen" />.</returns>
507+
private Pen GetCachedPen(OxyColor stroke, double thickness, double[] dashArray = null, OxyPlot.LineJoin lineJoin = OxyPlot.LineJoin.Miter)
508+
{
509+
GraphicsPenDescription description = new GraphicsPenDescription(stroke, thickness, dashArray, lineJoin);
510+
511+
Pen pen;
512+
if (this.pens.TryGetValue(description, out pen))
513+
{
514+
return pen;
515+
}
516+
517+
return this.pens[description] = CreatePen(stroke, thickness, dashArray, lineJoin);
518+
}
519+
520+
/// <summary>
521+
/// Creates a pen.
513522
/// </summary>
514523
/// <param name="stroke">The stroke.</param>
515524
/// <param name="thickness">The thickness.</param>
@@ -519,6 +528,7 @@ private Brush GetCachedBrush(OxyColor fill)
519528
private Pen CreatePen(OxyColor stroke, double thickness, double[] dashArray = null, OxyPlot.LineJoin lineJoin = OxyPlot.LineJoin.Miter)
520529
{
521530
var pen = new Pen(stroke.ToColor(), (float)thickness);
531+
522532
if (dashArray != null)
523533
{
524534
pen.DashPattern = this.ToFloatArray(dashArray);
@@ -532,7 +542,7 @@ private Pen CreatePen(OxyColor stroke, double thickness, double[] dashArray = nu
532542
case OxyPlot.LineJoin.Bevel:
533543
pen.LineJoin = System.Drawing.Drawing2D.LineJoin.Bevel;
534544
break;
535-
//// The default LineJoin is Miter
545+
// The default LineJoin is Miter
536546
}
537547

538548
return pen;
@@ -581,4 +591,4 @@ private PointF[] ToPoints(IList<ScreenPoint> points)
581591
return r;
582592
}
583593
}
584-
}
594+
}

Source/OxyPlot.WindowsForms/OxyPlot.WindowsForms.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<Link>Properties\AssemblyInfo.cs</Link>
5353
</Compile>
5454
<Compile Include="ExporterExtensions.cs" />
55+
<Compile Include="GraphicsPenDescription.cs" />
5556
<Compile Include="PlotView.cs">
5657
<SubType>Component</SubType>
5758
</Compile>

0 commit comments

Comments
 (0)