Can I create color struct that works on different endians?

100 views Asked by At

I am trying to create a union to store my color values channel by channel and 4-byte values at the same time. But I am having problems with the order of channels. Different endian machines give different values. Is there any way to create endian proof version of this struct or use different structs when different endians are better for this job? I want to use structs and unions for educational purposes. I am experimenting and want to learn what can and can't do with C structs.

typedef union u_color
{
    struct
    {
        unsigned char   blue;
        unsigned char   green;
        unsigned char   red;
        unsigned char   alpha;
    };
    unsigned int    value;
}   t_color;
3

There are 3 answers

10
gulpr On BEST ANSWER

If you want to use it only for getting and setting the value (or getting colours from the value) then (if you use recent gcc family compilers)

typedef union u_color
{
  #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    struct
    {
        uint8_t   blue;
        uint8_t   green;
        uint8_t   red;
        uint8_t   alpha;
    };
  #else
    struct
    {
        uint8_t   alpha;
        uint8_t   red;
        uint8_t   green;
        uint8_t   blue;
    };
  #endif
    uint32_t    value;
}   t_color;

But for the defining arrays which will represent real screen data structures you need to use one predefined struct layout.

int main(void)
{
    t_color x = {.blue = 0x11, .green = 0x22, .red = 0x33, .alpha = 0x44};
    printf("%"PRIx32"\n", x.value);
}

Will always print 0x11223344 despite the endianness.

2
chux - Reinstate Monica On

Although one could re-order the color/alpha members based on endian, when reading and writing to a graphics file, the sequence of red, green, blue is the norm. I recommend to stay with that.

Code can store 4 bytes at a time with a struct of r,g,b,a instead of an unsigned.

A pixel color is not a good example for union/struct.

Using (u)intN_t types is a better first step than using unsigned when experimenting with unions.

2
Lundin On

The KISS solution is simply to drop the union and the value member, then always obtain the value through a function instead. 100% portable:

#include <stdint.h>

typedef struct
{
  uint8_t blue;
  uint8_t green;
  uint8_t red;
  uint8_t alpha;
} t_color;

uint32_t t_color_value (const t_color* col)
{
  return ( ((uint32_t)col->blue  << 24) |
           ((uint32_t)col->green << 16) |
           ((uint32_t)col->red   <<  8) |
           ((uint32_t)col->alpha <<  0) );
}

int main (void)
{
  t_color col = { .blue=0x11, .green=0x22, .red=0x33, .alpha=0x44};
}

Now blue will always end up in the uint32_t most significant byte, regardless of endianess. This is because the shift operators are endianess-independent (unlike a union or memcpy etc).