Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ARGB

0.00/5 (No votes)
15 Jun 2010 1  
How ARGB works

Introduction

This article is about how the structure System.Drawing.Color works.

New Color

Take this example:

Dim color1 As New Color 'VB.NET 
Color color1= new Color(); //C#  

When a variable is declared as a color, what happens is:

Shared Sub New()
    Color.Empty = New Color
    Color.StateKnownColorValid = 1
    Color.StateARGBValueValid = 2
    Color.StateValueMask = Color.StateARGBValueValid
    Color.StateNameValid = 8
    Color.NotDefinedValue = 0
End Sub 
static Color()
{
    Empty = new Color();
    StateKnownColorValid = 1;
    StateARGBValueValid = 2;
    StateValueMask = StateARGBValueValid;
    StateNameValid = 8;
    NotDefinedValue = 0L;
} 
 static Color()
{
    Color::Empty = ();
    Color::StateKnownColorValid = 1;
    Color::StateARGBValueValid = 2;
    Color::StateValueMask = Color::StateARGBValueValid;
    Color::StateNameValid = 8;
    Color::NotDefinedValue = 0;
} 

Empty is a private color declaration which is of not much interest.

StateKnownColorValid is a private Short declaration. This is used in the Color.IsKnownColor (determines if the given color is present in the pre-defined list) property as the AND mask.

StateARGBValueValid is a private Short declaration. This is used in IsKnownColor property, IsNamed property, etc. There are more inaccessible New sub-routines in which this variable is assigned to the variable state used in color-validity checking routines.

StateValueMask has the same uses of StateARGBValueValid.

StateNameValid is used in the Color.ToString function. When we call this function, if that color has a name (like black, etc.), Black is included in the return value. This variable is used as the AND mask in that function to determine if there is a valid name for the color.

NotDefinedValue is used in the Color.FromName function. In that function, if the argument(Object type) is not a valid color, then, the function returns a color. The returned color will have the specification that the color is not defined.

There is an Enumeration called KnownColor. This is the source of all colors. All the color listings in Color are properties.

They look like:

 Public Shared ReadOnly Property AliceBlue As Color
    Get
        Return New Color(KnownColor.AliceBlue)
    End Get
End Property
public static Color AliceBlue
{
    get
    {
        return new Color(KnownColor.AliceBlue);
    }
} 
public: __property static Color __gc* get_AliceBlue()
{
    return (KnownColor::AliceBlue);
} 

But, in our programming environment, we cannot declare like this. This is because there is another Friend (this keyword specifies that the routine or function or variable can be used only by the assembly in which it is present) declaration of New. The declaration is:

Friend Sub New(ByVal knownColor As KnownColor)
    Me.value = 0
    Me.state = Color.StateKnownColorValid
    Me.name = Nothing
    Me.knownColor = CShort(knownColor)
End Sub
internal Color(KnownColor knownColor)
{
    this.value = 0L;
    this.state = StateKnownColorValid;
    this.name = null;
    this.knownColor = (short) knownColor;
} 
public private: Color(KnownColor __gc* knownColor)
{
    this->value = 0;
    this->state = Color::StateKnownColorValid;
    this->name = 0;
    this->knownColor = *static_cast<__box Int16*>(knownColor);
} 

The primary use of this is in returning a color like Color.AliceBlue. This is merely to shorten the code. Writing 4 lines for each and every color is time consuming and increases the size of the assembly. So, this practice is implemented.

There is one last Private declaration of New. The source code for it is:

Private Sub New(ByVal value As Long, ByVal state As Short, _
	ByVal name As String, ByVal knownColor As KnownColor)
    Me.value = value
    Me.state = state
    Me.name = name
    Me.knownColor = CShort(knownColor)
End Sub
private Color(long value, short state, string name, KnownColor knownColor)
{
    this.value = value;
    this.state = state;
    this.name = name;
    this.knownColor = (short) knownColor;
} 
private: Color(Int64 __gc* value, Int16 __gc* state, String __gc* name, 
	KnownColor __gc* knownColor)
{
    this->value = value;
    this->state = state;
    this->name = name;
    this->knownColor = *static_cast<__box Int16*>(knownColor);
} 

This is a Private declaration first of all because the values are directly assigned without any error checking. The main source of error could be the first long argument value. This argument is the ARGB value that is stored in the internal variable value in Color. Since retrieving the Alpha, Red, Green and Blue components of a color from value involves bitwise operations (I will be telling about this soon) - machine-level operations which are intimidating to beginners.

This declaration of New is used in the FromARGB function and the FromName function.

ARGB - Alpha Red Green Blue

What is ARGB?

ARGB is nothing but transparency added to the standard RGB color composition. Alpha can range from 0 to 255 like the Red, Green and Blue components.

How Is It Stored

Instead of wasting 4 byte data types (sums up to 32 bits), ARGB values are stored using one single long data type or a 32 bit integer for efficiency purposes. Operating on 1 variable is way easier than performing operations on 4 different variables. This is just like the way DOS and few versions of Windows stored the date on which a file was created/modified in a single variable (2-byte entry).

Every component is allowed 8 bits because the highest value they can have is 255 and 255 in binary is 11111111.

In earlier versions like VB6, we could create a color using a function called RGB and it is still supported in the .NET versions.

But, the .NET programming languages use ARGB for all colors.

This is how we can make our own color:

Color.FromArgb(alpha, red, green, blue) 'VB.NET. All variables can range from 0-255
Color.FromArgb(alpha, red, green, blue); //C#. All variables can range from 0-255
Color::FromArgb(alpha, red, green, blue); //C++/CLI. All variables can range from 0-255

The code for this FromArgb function is as follows:

 Public Shared Function FromArgb(ByVal alpha As Integer, _
	ByVal red As Integer, ByVal green As Integer, ByVal blue As Integer) As Color
    Color.CheckByte(alpha, "alpha")
    Color.CheckByte(red, "red")
    Color.CheckByte(green, "green")
    Color.CheckByte(blue, "blue")
    Return New Color(Color.MakeArgb(CByte(alpha), _
	CByte(red), CByte(green), CByte(blue)), Color.StateARGBValueValid, _
	Nothing, DirectCast(0, KnownColor))
End Function
public static Color FromArgb(int alpha, int red, int green, int blue)
{
    CheckByte(alpha, "alpha");
    CheckByte(red, "red");
    CheckByte(green, "green");
    CheckByte(blue, "blue");
    return new Color(MakeArgb((byte) alpha, (byte) red, (byte) green, 
	(byte) blue), StateARGBValueValid, null, (KnownColor) 0);
}
public: static Color __gc* FromArgb(Int32 __gc* alpha, 
	Int32 __gc* red, Int32 __gc* green, Int32 __gc* blue)
{
    Color::CheckByte(alpha, S"alpha");
    Color::CheckByte(red, S"red");
    Color::CheckByte(green, S"green");
    Color::CheckByte(blue, S"blue");
    return (Color::MakeArgb(*static_cast<__box Byte*>(alpha), 
	*static_cast<__box Byte*>(red), *static_cast<__box Byte*>(green), 
	*static_cast<__box Byte*>(blue)), Color::StateARGBValueValid, 0, 
	*static_cast<__box KnownColor*>(0));
}

First, the function checks if all the arguments alpha, red, blue and green have valid values(0-255). This is what the CheckByte function does.

Color.MakeArgb function is the function that converts the separate components into one single 32-bit Integer or Long. Its source code is:

Private Shared Function MakeArgb(ByVal alpha As Byte, _
	ByVal red As Byte, ByVal green As Byte, ByVal blue As Byte) As Long
    Return CLng((CULng(((((red << &H10) Or (green << 8)) Or blue) _
	Or (alpha << &H18))) And &HFFFFFFFF))
End Function
private static long MakeArgb(byte alpha, byte red, byte green, byte blue)
{
    return (long) (((ulong) ((((red << 0x10) | (green << 8)) 
		| blue) | (alpha << 0x18))) & 0xffffffffL);
}
private: static Int64 __gc* MakeArgb(Byte __gc* alpha, 
Byte __gc* red, Byte __gc* green, Byte __gc* blue)
{
    return *static_cast<__box Int64*>((*static_cast<__box UInt64*>
	(((((red << 0x10) | (green << 8)) | blue) | (alpha << 0x18))) & 0xffffffff));
} 

If you don't know what Bit-wise operators are or if you have some problems regarding them, read this article on CodeProject - an excellent place to start with.

First, we need to convert the hexadecimal numbers &H10 etc. and octal numbers like 0x10, etc. to human-readable decimal form.

Then, the statements will be:

 CLng((CULng(((((red << 16) Or (green << 8)) Or blue) _
	Or (alpha << 24))) And 4294967295)) 
(long) (((ulong) ((((red << 16) | (green << 8)) | blue) | 
	(alpha << 24))) & 4294967295L)) 
*static_cast<__box Int64*>((*static_cast<__box UInt64*>(((((red << 16) | 
	(green << 8)) | blue) | (alpha << 24))) & 4294967295)

Let us analyze this expression. I will take the C# expression for analyzing.

((((red << 16) | (green << 8))  | blue)  | (alpha << 24)) & 4294967295
  1. red is left shifted 16 bits.
  2. green is left shifted 8 bits.
  3. Both the results are OR-ed.
  4. The OR-ed result is again OR-ed by blue
  5. alpha is left shifted 24 bits.
  6. Then it is OR-ed with the result in (4).
  7. The result in (6) is AND-ed with 4294967295

Let us take an example to understand it better.

Color : Gold

Alpha : 255 (11111111)

Red   : 255 (11111111)

Green : 215 (11010111)

Blue :  0   (00000000)


Red << 16 : 

16711680

111111110000000000000000

Green << 8 : 

55040

1101011100000000

(Red << 16) | (Green << 8) : 

11111111 00000000 00000000 (Red's value shifted to first 8 bits)

00000000 11010111 00000000 (Green's value shifted to second 8 bits)
_________________________

11111111 11010111 00000000 (OR-ed result)

Notice that the first eight bits are occupied by red and
the second eight bits are occupied by green.

((Red << 16) | (Green << 8))|Blue : 

11111111 11010111 00000000 (The last 8 bits are for Blue)

00000000 000000000 0000000 (The last 8 bits are Blue. In this case, 0)
_________________________

11111111 11010111 00000000 (These 3 values are Red, Blue and Green)

Alpha << 24 : 

4278190080

11111111000000000000000000000000

(((Red << 16) | (Green << 8))|Blue) | Alpha << 24 : 

00000000 11111111 11010111 00000000 (24-bit to 32-bit leaving the first 8 bits for Alpha)

11111111 00000000 00000000 00000000 (First 8 Bits have the value)
__________________________________

11111111 11111111 11010111 00000000 (Alpha, Red, Green, Blue)

((((Red << 16) | (Green << 8))|Blue) | Alpha << 24) & 4294967295 : 

    11111111 11111111 11010111 00000000

    11111111 11111111 11111111 11111111 (256^4)
    ___________________________________

    11111111 11111111 11010111 00000000

       |         |       |         |

      ALPHA     RED    GREEN      BLUE 
BITS  32-24   24-16    16-8       8-0

Finally, we AND by 256^4 because there are 4 values with range 0-255(256 values)

Note: This isn't the value returned by Color.ToArgb() function because it returns an Integer value which is a conversion of a Long value. Since the limit exceeds, a negative value is returned.

Retrieving the Components from an ARGB Value

ALPHA

 Public ReadOnly Property A As Byte
    Get
        Return CByte(((Me.Value >> &H18) And &HFF)) '&H18 is 24, &HFF is 255
    End Get
End Property
public byte A
{
    get
    {
        return (byte) ((this.Value >> 0x18) & 0xffL); // 0x18 is 24, 0xff is 255
    }
}
public: __property Byte __gc* get_A()
{
    return *static_cast<__box Byte*>(((this->Value >> 0x18) & 0xff));
} 

value is right shifted 24 bits and is AND-ed with 255 to get the Alpha.

11111111 11111111 11010111 00000000 >> 24 = 00000000 00000000  00000000 11111111

00000000 00000000  00000000 11111111 & 255 = 11111111 = 255 

The same explanation applies to the following.

RED

Public ReadOnly Property R As Byte
    Get
        Return CByte(((Me.Value >> &H10) And &HFF))
    End Get
End Property
public byte R
{
    get
    {
        return (byte) ((this.Value >> 0x10) & 0xffL);
    }
}
public: __property Byte __gc* get_R()
{
    return *static_cast<__box Byte*>(((this->Value >> 0x10) & 0xff));
}
Red = value >> 16 & 255

BLUE

Public ReadOnly Property B As Byte
    Get
        Return CByte((Me.Value And &HFF))
    End Get
End Property
 public byte B
{
    get
    {
        return (byte) (this.Value & 0xffL);
    }
} 
public: __property Byte __gc* get_B()
{
    return *static_cast<__box Byte*>((this->Value & 0xff));
}

Since blue wasn't shifted, AND-ing it with 255 will give blue.

blue = value & 255

GREEN

Public ReadOnly Property G As Byte
    Get
        Return CByte(((Me.Value >> 8) And &HFF))
    End Get
End Property
public byte G
{
    get
    {
        return (byte) ((this.Value >> 8) & 0xffL);
    }
}
public: __property Byte __gc* get_G()
{
    return *static_cast<__box Byte*>(((this->Value >> 8) & 0xff));
}
green = value >> 8 & 255

That is all!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here