This struct
is used a lot throughout my programme. This struct is responsible for only things regarding colour, not anything else.
I'm largely concerned of the bitwise operations, and the fact that I store four byte
values in a uint
but access them with bitwise flags. This is a concept I use throughout the programme. (I have one struct that has a 14-bit field, and two 7-bit fields.)
/// <summary>
/// A structure to represent a generic set of Red, Green, Blue and Alpha to represent the shade to paint an object.
/// </summary>
public struct Color
{
private uint _PackedValue;
/// <summary>
/// Gets or sets the Alpha component of the <see cref="Color"/>.
/// </summary>
public byte A { get { return (byte)((_PackedValue & 0xFF000000u) >> 0x18); } set { _PackedValue = (_PackedValue & ~0xFF000000u) | ((uint)value << 0x18); } }
/// <summary>
/// Gets or sets the Red component of the <see cref="Color"/>.
/// </summary>
public byte R { get { return (byte)((_PackedValue & 0x00FF0000u) >> 0x10); } set { _PackedValue = (_PackedValue & ~0x00FF0000u) | ((uint)value << 0x10); } }
/// <summary>
/// Gets or sets the Green component of the <see cref="Color"/>.
/// </summary>
public byte G { get { return (byte)((_PackedValue & 0x0000FF00u) >> 0x08); } set { _PackedValue = (_PackedValue & ~0x0000FF00u) | ((uint)value << 0x08); } }
/// <summary>
/// Gets or sets the Blue component of the <see cref="Color"/>.
/// </summary>
public byte B { get { return (byte)((_PackedValue & 0x000000FFu) >> 0x00); } set { _PackedValue = (_PackedValue & ~0x000000FFu) | ((uint)value << 0x00); } }
/// <summary>
/// Creates an instance of a <see cref="Color"/>.
/// </summary>
/// <param name="a">The alpha <c>byte</c> component.</param>
/// <param name="r">The red <c>byte</c> component.</param>
/// <param name="g">The green <c>byte</c> component.</param>
/// <param name="b">The blue <c>byte</c> component.</param>
public Color(byte r, byte g, byte b, byte a)
{
_PackedValue = ((uint)a << 0x18) | ((uint)r << 0x10) | ((uint)g << 0x08) | ((uint)b << 0x00);
}
/// <summary>
/// Creates an instance of a <see cref="Color"/> with a default alpha of 255.
/// </summary>
/// <param name="r">The red <c>byte</c> component.</param>
/// <param name="g">The green <c>byte</c> component.</param>
/// <param name="b">The blue <c>byte</c> component.</param>
public Color(byte r, byte g, byte b)
{
_PackedValue = ((uint)0xFF << 0x18) | ((uint)r << 0x10) | ((uint)g << 0x08) | ((uint)b << 0x00);
}
/// <summary>
/// Creates an instance of a <see cref="Color"/> from a packed value.
/// </summary>
/// <param name="packedValue">The original packed value of the <see cref="Color"/>.</param>
public Color(uint packedValue)
{
_PackedValue = packedValue;
}
/// <summary>
/// Returns the packed <c>uint</c> for storage.
/// </summary>
/// <returns>A <c>uint</c> representing this <see cref="Color"/> instance.</returns>
public uint GetPackedValue()
{
return _PackedValue;
}
/// <summary>
/// Determines whether two <see cref="Color"/> objects have the same value.
/// </summary>
/// <param name="a">The first <see cref="Color"/> object.</param>
/// <param name="b">The second <see cref="Color"/> object.</param>
/// <returns>True if the objects have the same values, false otherwise.</returns>
public static bool operator ==(Color a, Color b)
{
return a._PackedValue == b._PackedValue;
}
/// <summary>
/// Determines whether two <see cref="Color"/> objects do not have the same value.
/// </summary>
/// <param name="a">The first <see cref="Color"/> object.</param>
/// <param name="b">The second <see cref="Color"/> object.</param>
/// <returns>True if the objects do not have the same values, false otherwise.</returns>
public static bool operator !=(Color a, Color b)
{
return a._PackedValue != b._PackedValue;
}
/// <summary>
/// Determines whether an object is a <see cref="Color"/> and is equal to the current <see cref="Color"/> object.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is a <see cref="Color"/> object and has the same value as the current <see cref="Color"/> object.</returns>
public override bool Equals(object obj)
{
return obj is Color && this == (Color)obj;
}
/// <summary>
/// Gets the hash code for the current <see cref="Color"/> object.
/// </summary>
/// <returns>The hash code that represents the current <see cref="Color"/> object.</returns>
public override int GetHashCode()
{
return _PackedValue.GetHashCode();
}
/// <summary>
/// Implicitly materializes a <see cref="Color.Preset"/> to a <see cref="Color"/>.
/// </summary>
/// <param name="preset">The <see cref="Color.Preset"/> to convert.</param>
/// <returns>The <see cref="Color.Presets"/> value that corrosponds to the <see cref="Color.Preset"/>.</returns>
/// <remarks>
/// This method is equivalent to calling <code>Color.Presets[(int)preset];</code>.
/// </remarks>
public static implicit operator Color(Color.Preset preset)
{
return Presets[(int)preset];
}
/// <summary>
/// A list of <see cref="Color"/> objects that correspond to the <see cref="Color.Preset"/> enumeration values.
/// </summary>
public static readonly List<Color> Presets = new List<Color>() {
new Color(0, 0, 0, 255), // Preset.Black
new Color(0, 0, 128, 255), // Preset.DarkBlue
new Color(0, 128, 0, 255), // Preset.DarkGreen
new Color(0, 128, 128, 255), // Preset.DarkCyan
new Color(128, 0, 0, 255), // Preset.DarkRed
new Color(128, 0, 128, 255), // Preset.DarkMagenta
new Color(128, 128, 0, 255), // Preset.DarkYellow
new Color(192, 192, 192, 255), // Preset.Gray
new Color(128, 128, 128, 255), // Preset.DarkGray
new Color(0, 0, 255, 255), // Preset.Blue
new Color(0, 255, 0, 255), // Preset.Green
new Color(0, 255, 255, 255), // Preset.Cyan
new Color(255, 0, 0, 255), // Preset.Red
new Color(255, 0, 255, 255), // Preset.Magenta
new Color(255, 255, 0, 255), // Preset.Yellow
new Color(255, 255, 255, 255), // Preset.White
};
/// <summary>
/// Provide a list of values to be used to create certain preset colour values.
/// </summary>
public enum Preset : int
{
Black = 0,
DarkBlue = 1,
DarkGreen = 2,
DarkCyan = 3,
DarkRed = 4,
DarkMagenta = 5,
DarkYellow = 6,
Gray = 7,
DarkGray = 8,
Blue = 9,
Green = 10,
Cyan = 11,
Red = 12,
Magenta = 13,
Yellow = 14,
White = 15,
}
}
The Color.Preset
enumeration exists to provide compatibility directly with the ConsoleColor
enumeration. Otherwise, the presets would be in a sub class and be readonly
fields. I.e.:
public static readonly Color White = new Color(255, 255, 255, 255);
public static readonly Color Black = new Color(0, 0, 0, 255);
...
I have a lot of classes and structures designed around the idea of "one packed value that has many values inside it." Hopefully, I can get a broad enough answer here on that concept as well to apply to the other structures and classes (dozens of them).
An additional note:
I use the uint _PackedValue
field to serialize for network transport, as that field is the most commonly accessed field. (Believe it or not, I rarely actually refer to the A
, R
, G
or B
properties, when compared to the _PackedValue
field.)
System.Drawing
namespace for this project. If I could, I would have used it. \$\endgroup\$ – Der Kommissar Jul 30 '15 at 18:10