|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
namespace System.Windows.Forms;
/// <summary>
/// Cache of GDI+ objects to reuse commonly created items.
/// </summary>
internal static class GdiPlusCache
{
// Keeping these as ThreadStatic for now. With testing, and if we can make them immutable ourselves (needs
// additional System.Drawing functionality), we can look at making this shared. Adding locking, however, does
// add cost to retrieving entries so it might not be worth it.
[ThreadStatic]
private static PenCache? s_penCache;
[ThreadStatic]
private static SolidBrushCache? s_brushCache;
// Picking soft and hard limits expecting that 30 of each is a reasonable upper limit of permanently reserved
// space. These items are roughly .5KB each, with _most_ of the memory being native (in the GDI+ object).
private static PenCache PenCache => s_penCache ??= new(softLimit: 30, hardLimit: 40);
private static SolidBrushCache BrushCache => s_brushCache ??= new(softLimit: 30, hardLimit: 40);
private static PenCache.Scope GetPenScope(Color color)
{
if (color.IsKnownColor)
{
Pen? pen = color.IsSystemColor
? SystemPens.FromSystemColor(color)
: PenFromKnownColor(color.ToKnownColor());
if (pen is not null)
{
return new PenCache.Scope(pen);
}
}
return PenCache.GetEntry(color).CreateScope();
}
private static SolidBrushCache.Scope GetSolidBrushScope(Color color)
{
if (color.IsKnownColor)
{
SolidBrush? solidBrush = color.IsSystemColor
? (SolidBrush?)SystemBrushes.FromSystemColor(color)
: (SolidBrush?)BrushFromKnownColor(color.ToKnownColor());
if (solidBrush is not null)
{
return new SolidBrushCache.Scope(solidBrush);
}
}
return BrushCache.GetEntry(color).CreateScope();
}
/// <summary>
/// Returns a cached <see cref="Pen"/>. Use in a using and assign to var.
/// </summary>
/// <remarks>
/// <para>
/// Correct: using var pen = GdiPlusCache.GetCachedPen(Color.Blue);
/// Incorrect (LEAKS): using Pen pen = GdiPlusCache.GetCachedPen(Color.Blue);
/// </para>
/// <para>
/// Debug builds track proper disposal.
/// </para>
/// </remarks>
internal static PenCache.Scope GetCachedPenScope(this Color color) => GetPenScope(color);
/// <inheritdoc cref="GetCachedPenScope(Color)"/>
internal static PenCache.Scope GetCachedPenScope(this Color color, int width)
=> width == 1 ? GetPenScope(color) : new PenCache.Scope(new Pen(color, width));
/// <inheritdoc cref="GetCachedPenScope(Color)"/>
internal static SolidBrushCache.Scope GetCachedSolidBrushScope(this Color color) => GetSolidBrushScope(color);
private static Brush? BrushFromKnownColor(KnownColor color) => color switch
{
// Starting with the expected most common
KnownColor.Black => Brushes.Black,
KnownColor.White => Brushes.White,
KnownColor.Gray => Brushes.Gray,
KnownColor.Red => Brushes.Red,
KnownColor.Green => Brushes.Green,
KnownColor.Blue => Brushes.Blue,
KnownColor.Yellow => Brushes.Yellow,
KnownColor.Brown => Brushes.Brown,
KnownColor.LightGray => Brushes.LightGray,
KnownColor.LightGreen => Brushes.LightGreen,
KnownColor.LightBlue => Brushes.LightBlue,
KnownColor.LightYellow => Brushes.LightYellow,
KnownColor.DarkGray => Brushes.DarkGray,
KnownColor.DarkRed => Brushes.DarkRed,
KnownColor.DarkGreen => Brushes.DarkGreen,
KnownColor.DarkBlue => Brushes.DarkBlue,
KnownColor.Transparent => Brushes.Transparent,
_ => null
};
private static Pen? PenFromKnownColor(KnownColor color) => color switch
{
// Starting with the expected most common
KnownColor.Black => Pens.Black,
KnownColor.White => Pens.White,
KnownColor.Gray => Pens.Gray,
KnownColor.Red => Pens.Red,
KnownColor.Green => Pens.Green,
KnownColor.Blue => Pens.Blue,
KnownColor.Yellow => Pens.Yellow,
KnownColor.Brown => Pens.Brown,
KnownColor.LightGray => Pens.LightGray,
KnownColor.LightGreen => Pens.LightGreen,
KnownColor.LightBlue => Pens.LightBlue,
KnownColor.LightYellow => Pens.LightYellow,
KnownColor.DarkGray => Pens.DarkGray,
KnownColor.DarkRed => Pens.DarkRed,
KnownColor.DarkGreen => Pens.DarkGreen,
KnownColor.DarkBlue => Pens.DarkBlue,
KnownColor.Transparent => Pens.Transparent,
_ => null
};
}
|