13

How do you convert from color HEX code to RGB in pure C using C library only (without C++ or templates)? The RGB struct may be like this:

typedef struct RGB {
    double r;
    double g;
     double b;
} RGB1;

The function I'm looking for should return RGB1.

k-a-v
  • 326
  • 5
  • 22
Shavkat
  • 139
  • 1
  • 1
  • 3
  • 1
    I'm not sure I understand the question. I believe that flagging things as code and / or inserting newlines into the question in strategic places may be helpful in making the question easier for people to read (and therefore answer). – cubic1271 Sep 16 '10 at 05:24

5 Answers5

26

Assuming that your hex value is a 32-bit 'int' type, and that we use the RGB struct described above, then maybe do something like:

struct RGB colorConverter(int hexValue)
{
  struct RGB rgbColor;
  rgbColor.r = ((hexValue >> 16) & 0xFF) / 255.0;  // Extract the RR byte
  rgbColor.g = ((hexValue >> 8) & 0xFF) / 255.0;   // Extract the GG byte
  rgbColor.b = ((hexValue) & 0xFF) / 255.0;        // Extract the BB byte

  return rgbColor; 
}
cubic1271
  • 1,118
  • 8
  • 14
  • Kind of ripping off the answer @konforce gave aren't we? – ubiquibacon Sep 16 '10 at 09:43
  • Nah. There was code in there for string processing too, but the point konforce made about this probably being a homework question was a good one. Thus, I edited this answer to be more like the one kon posted above. – cubic1271 Sep 16 '10 at 13:30
  • Should hexValue be of type unsigned int? I think the &0xFF cancels out the arithmetic shift, but wouldn't a value like 0xFFFFFF cause an overflow when it's passed in? – Aaron Cowie Aug 11 '15 at 04:44
  • 1
    'unsigned int' would probably be a little nicer. That said, in this instance it wouldn't really matter since RRGGBB fits in 24 bits, and an 'int' type is (pretty universally) 32 bits in length. This answer does assume that the RRGGBB bits are stored a certain way in the int, though ... so caveat emptor. – cubic1271 Nov 24 '16 at 02:34
22

If the hex code is a string, you can parse it like this

char *str = "0000FF";
int r, g, b;
sscanf(str, "%02x%02x%02x", &r, &g, &b);

That is to ints, not doubles. Also do check that sscanf returns 3, the number of items read.

Tor Klingberg
  • 4,790
  • 6
  • 41
  • 51
  • 1
    use the format string `"%2hx%2hx%2hx"` if you want to parse it into `unsigned short` values – bb1950328 Oct 13 '20 at 16:58
  • 1
    I used `sscanf(str.c_str(), "%02hhx%02hhx%02hhx", &r, &g, &b);` with an `std::string` to parse it into *unsigned char* values, – SPA Jan 28 '21 at 14:20
  • I can't upvote Tor's answer enough! Elegant solution. – JWDN Aug 05 '21 at 08:48
14

An RGB value can be stored as in integer via 0xRRGGBB. Examples:

  • Red: 0xff0000
  • Green: 0x00ff00
  • Blue: 0x0000ff

00 is hex for decimal 0, while ff is 255. 0 corresponds to 0.0 and 255 to 1.0. (Actually you didn't specify what the range is. I'm assuming 0.0 to 1.0.)

So with the above assumptions, you need to extract each component and divide by 255. Since it sounds a lot like a homework question, I'll just show you how you can do the red component.

int hex = 0x123456;
c.r = ((hex >> 16) & 0xff) / 255.0;

Each hex digit takes up 4 bits. So shift to the right by 16 bits (to move everything 4 digits to the right) to make 0xRRGGBB become 0xRR. Now you have the red component. (Just in case there is some data higher up in the integer, you can get rid of it by masking the data via & 0xff.)

If you are dealing with a string "#FFFFFF", then you'd first have to convert it to an integer for the above to work.

Matthew
  • 47,584
  • 11
  • 86
  • 98
5

I know this is quite old, but I wanted to give also a solution with unions and without any bitwise operations.

union Color
{
    unsigned int hex;
    struct { unsigned char b, g, r; };
};

This way you can convert from HEX to RGB and from RGB to HEX, easily.

union Color hex;
hex.hex = 0x07C73C;

union Color rgb;
rgb.r = 7;
rgb.g = 199;
rgb.b = 60;

printf("RGB(%d, %d, %d), HEX(%06x)", hex.r, hex.g, hex.b, rgb.hex);

Output:

RGB(7, 199, 60), HEX(07c73c)

and to map the values in the range of 0.0 to 1.0 you simply divide it by 255.0 :)

EDIT: The code above^^ is only supported under Little-Endian CPU's architecture, so a better way is to check what endianness does the system runs on.

Then you can check the endianness to define the order of the struct variables.

union Color
{
    unsigned int hex;

#if IS_BIG_ENDIAN
    struct { unsigned char a, r, g, b; };
#else
    struct { unsigned char b, g, r, a; };
#endif
};

This code also supports alpha ( transparency ) in RGBA.

therealcain
  • 175
  • 1
  • 4
  • 10
  • good answer, but you should check for endianness for answer to be complete. See https://stackoverflow.com/q/18863913/8339821 for a reference. – user14063792468 Oct 16 '20 at 16:57
  • good point, I will do some tests and update the answer. – therealcain Oct 16 '20 at 17:34
  • great, please comment when you do an update. mb you will get an upvote ;) – user14063792468 Oct 16 '20 at 17:37
  • @yvw I updated the answer that supports both endianness, I was very unsure if windows systems are only little-endian, ended up using a CPU check macro. – therealcain Oct 16 '20 at 19:53
  • The `BSD` seem to have `` as ``. Other systems may not have the header at all. – user14063792468 Oct 16 '20 at 20:14
  • @yvw I added BSD and Mac OS. – therealcain Oct 16 '20 at 20:35
  • where does all those defines come from? For example FreeBSD handbook tells us that **`__FreeBSD__` is defined in all versions of FreeBSD**. I do not see `OS_FREEBSD` defined on my FreeBSD machine. Maybe you should leave the OS issue? You could do a remark on your answer that you have two snippets of code. One for little endian and one for other. – user14063792468 Oct 16 '20 at 21:23
  • @yvw hmm... I saw a list of preprocessor macros of some systems on some GitHub gist. I just did a recheck of that, and fixed it. I found that `__BIG_ENDIAN__` is defined by clang and gcc without ``, maybe I should update the code to use it instead? And I also specified that a cross-platform library would better for this, maybe something like boost endian, but I'm pretty sure it's only for C++. – therealcain Oct 16 '20 at 21:37
  • I look at `FreeBSD` blob and do not see any `_BIG_ENDIAN` define there. There are only `_LITTLE_ENDIAN`. I think you should stop copy/paste from random git. – user14063792468 Oct 16 '20 at 21:42
  • I saw it on https://github.com/openbsd/src/blob/master/sys/sys/endian.h I think I'm just going to redirect to a different StackOverflow question when it comes to endianness. And just going to show both examples for little and big endian. – therealcain Oct 16 '20 at 21:52
  • This will be a right call. Check the other answers tho. They do not have to guess the endianess, still they work. Just be careful with `union`'s in `C`. – user14063792468 Oct 17 '20 at 00:33
  • I personally barely use unions, I just wanted to show a different way of doing the same thing. :) I would probably never use something like this, due to also performance decrease ( 14 times slower than bitwise ), and due to compilers/endianness limitations. But I think it's nice to know multiple ways of doing the same things, even if they are not the best. Another way of doing it would be by using `unsigned char rgba[4]` and use getters/setters that check the endianness of the system to behave accordingly. Maybe i should add that too? – therealcain Oct 17 '20 at 13:25
  • Just leave the endianness issue. Those who could use your code shall take care of it. Apart from that, if your code adds something new to the answers, feel free to commit. – user14063792468 Oct 17 '20 at 14:45
1

I guess RGB could be stored as 0xRRGGBB on some systems, but in Windows it is actually stored as 0xBBGGRR (see http://msdn.microsoft.com/en-us/library/windows/desktop/dd183449). As the article mentions, there are macros GetRValue, GetGValue, and GetBValue already available.