|
// 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;
using System.Drawing.Drawing2D;
using System.Runtime.CompilerServices;
using Microsoft.Win32;
namespace System.Windows.Forms;
internal static class SystemDrawingExtensions
{
internal static HBITMAP GetHBITMAP(this Bitmap bitmap) => (HBITMAP)bitmap.GetHbitmap();
internal static HFONT ToHFONT(this Font font) => (HFONT)font.ToHfont();
/// <summary>
/// Similar to <see cref="Graphics.GetNearestColor(Color)"/>, but this retains the original color if the color
/// didn't actually change. This retains the state of the color.
/// </summary>
/// <remarks>
/// <para>
/// This is important as the color only changes if <paramref name="graphics"/> has a very low color depth. This
/// is extremely rare for the normal case of HDC backed Graphics objects. Keeping the original color keeps the
/// state that would otherwise be stripped, notably things like <see cref="Color.IsKnownColor"/> which allows
/// us to later pull from a the various caches that <see cref="Drawing"/> maintains (saving allocations).
/// </para>
/// <para>
/// Ideally we'd drop checking at all and just support full color drawing to improve performance for the
/// expected normal case (more than 8 BITSPIXEL for the HDC).
/// </para>
/// </remarks>
internal static Color FindNearestColor(this Graphics graphics, Color color)
{
Color newColor = graphics.GetNearestColor(color);
return newColor.ToArgb() == color.ToArgb() ? color : newColor;
}
/// <summary>
/// Returns true if the color has any transparency. If false, the color is fully opaque.
/// </summary>
internal static bool HasTransparency(this Color color) => color.A != byte.MaxValue;
/// <summary>
/// Returns true if the color is fully transparent.
/// </summary>
internal static bool IsFullyTransparent(this Color color) => color.A == 0;
/// <summary>
/// Draws lines with the <paramref name="pen"/> using points defined in <paramref name="lines"/>.
/// </summary>
/// <param name="lines">
/// MUST be a multiple of 4. Each group of 4 represents x1, y1, x2, y2.
/// </param>
internal static void DrawLines(this Graphics graphics, Pen pen, ReadOnlySpan<int> lines)
{
Debug.Assert((lines.Length % 4) == 0);
for (int i = 0; i < lines.Length; i += 4)
{
graphics.DrawLine(pen, lines[i], lines[i + 1], lines[i + 2], lines[i + 3]);
}
}
/// <summary>
/// Mixes two colors.
/// </summary>
[SkipLocalsInit]
internal static Color MixColor(this Color color1, Color color2)
{
// Some colors look up their values on every property access so we'll get the value out
ARGB argb1 = color1;
ARGB argb2 = color2;
return Color.FromArgb(
(argb1.A + argb2.A) / 2,
(argb1.R + argb2.R) / 2,
(argb1.G + argb2.G) / 2,
(argb1.B + argb2.B) / 2);
}
/// <summary>
/// Inverts the color.
/// </summary>
internal static Color InvertColor(this Color color)
{
ARGB argb = color;
return Color.FromArgb(argb.A, (byte)~argb.R, (byte)~argb.G, (byte)~argb.B);
}
/// <summary>
/// Creates a <see cref="Pen"/>. If <paramref name="color"/> is a system color, makes a static copy of the
/// current color value to avoid having the pen hook itself against <see cref="SystemEvents"/>.
/// </summary>
internal static Pen CreateStaticPen(this Color color, DashStyle dashStyle)
{
if (dashStyle == DashStyle.Solid)
{
// Solid is the default and faster than setting the property
return color.IsSystemColor
? new Pen((ARGB)color)
: new Pen(color);
}
return color.IsSystemColor
? new Pen(Color.FromArgb(color.ToArgb())) { DashStyle = dashStyle }
: new Pen(color) { DashStyle = dashStyle };
}
/// <summary>
/// Not strictly needed (yet), but allows using the same pattern for all pens.
/// </summary>
internal static Pen CreateStaticPen(this Brush brush, float width = 1.0f)
=> new(brush, width);
/// <summary>
/// Creates a <see cref="SolidBrush"/>. If <paramref name="color"/> is a system color, makes a static copy of
/// the current color value to avoid having the pen hook itself against <see cref="SystemEvents"/>.
/// </summary>
internal static SolidBrush CreateStaticBrush(this Color color)
=> color.IsSystemColor
? new SolidBrush(Color.FromArgb(color.ToArgb()))
: new SolidBrush(color);
}
|