Search code examples
c++colorshexdword

How to extract a DWORD color variable into its parameters? (decimal type)


I have the example :

unsigned int dwColor = 0xAABBCCFF; //Light blue color
  • And its parameters from left to right are : "alpha, red, green, blue"; each parameter requires two hexadecimal values.

  • The maximum value of each parameter is 255; lowest : 0

And, how to extract then convert all parameters of a DWORD color to decimals?

I like the value range "0.00 -> 1.00". For example :

float alpha = convert_to_decimal(0xAA); //It gives 0.666f
float red = convert_to_decimal(0xBB); //It gives 0.733f
float green = convert_to_decimal(0xCC); //It gives 0.800f
float blue = convert_to_decimal(0xFF); //It gives 1.000f

EDIT : I've just seen union, but the answerer says it's UB (Undefined Behaviour). Does anyone know the better solution? :)


Solution

  • I usually use an union:

    union color
    {
        unsigned int value;
        unsigned char component[4];
    };
    
    color c;
    c.value = 0xAABBCCFF;
    unsigned char r = c.component[0];
    unsigned char g = c.component[1];
    unsigned char b = c.component[2];
    unsigned char a = c.component[3];
    

    If you need to treat it as a float value:

    float fr = c.component[0] / 255.0f;
    float fg = c.component[1] / 255.0f;
    float fb = c.component[2] / 255.0f;
    float fa = c.component[3] / 255.0f;
    

    EDIT:

    As mentioned in the comments below, this use of union is Undefined Behaviour (UB), see this question from Luchian Grigore.


    EDIT 2:

    So, another way to break a DWORD into components avoiding the union is using some bitwise magic:

    #define GET_COMPONENT(color, index) (((0xFF << (index * 8)) & color) >> (index * 8))
    

    But I do not advise the macro solution, I think is better to use a function:

    unsigned int get_component(unsigned int color, unsigned int index)
    {
        const unsigned int shift = index * 8;
        const unsigned int mask = 0xFF << shift;
        return (color & mask) >> shift;
    }
    

    How it works? Lets supose we call get_component(0xAABBCCFF, 0):

    shift = 0 * 8
    shift = 0
    
    mask = 0xFF << 0
    mask = 0x000000FF
    
    0x000000FF &
    0xAABBCCFF
    ----------
    0x000000FF
    
    0x000000FF >> 0 = 0xFF
    

    Lets supose we call get_component(0xAABBCCFF, 2):

    shift = 2 * 8
    shift = 16
    
    mask = 0xFF << 16
    mask = 0x00FF0000
    
    0x00FF0000 &
    0xAABBCCFF
    ----------
    0x00BB0000
    
    0x00BB0000 >> 16 = 0xBB
    

    Warning! not all color formats will match that pattern!

    But IMHO, the neater solution is to combine the function with an enum, since we're working with a limited pack of values for the index:

    enum color_component
    {
        A,B,G,R
    };
    
    unsigned int get_component(unsigned int color, color_component component)
    {
        switch (component)
        {
            case R:
            case G:
            case B:
            case A:
            {
                const unsigned int shift = component * 8;
                const unsigned int mask = 0xFF << shift;
                return (color & mask) >> shift;            
            }
    
            default:
                throw std::invalid_argument("invalid color component");
        }
    
        return 0;
    }
    

    The last approach ensures that the bitwise operations will only be performed if the input parameters are valid, this would be an example of usage:

    std::cout
        << "R: " << get_component(the_color, R) / 255.0f << '\n'
        << "G: " << get_component(the_color, G) / 255.0f << '\n'
        << "B: " << get_component(the_color, B) / 255.0f << '\n'
        << "A: " << get_component(the_color, A) / 255.0f << '\n';
    

    And here is a live demo.