[Tutorial] 32-bit colours (or hexadecimal colours like 0xFF0000FF)
#1

Hi all,

Tutorial number two as my last one was well quite received!

This one will be a bit more technical and will focus on the concept of 32 bit colours (or "hexadecimal colors" as many of you know them) - specifically how they work, the reasoning behind representing them as hex and how to manipulate them.

I won't bother describing base-16/hexadecimal itself - there's a fairly good description of it here: https://simple.wikipedia.org/wiki/He...numeral_system


The basics: understanding and forming a hard-coded, static 32-bit colour using hexadecimal

So a few things you need to know here:

1. An unsigned, 8-bit integer can store a value between 0 and 255. 0xFF is 255 in hexadecimal.

2. A 32-bit colour is effectively made out of 4, unsigned 8-bit integers slammed together.

3. Therefore, each two hexadecimal figures (00-FF) represents a fraction of 255, where 255 or FF is 100%, 1 is roughly 0.4%. This is why hexadecimal is useful here: every value of a byte can be expressed as two hex characters, meaning that a colour can follow a fixed width pattern, and you can always say "the first two characters are red!"

Yes - it would probably be simpler if we did just genuinely do everything in terms of 100% red, 0% green, 0% blue - but because we use the full 0-255 range of the byte, this means we have access to 2.5 many shades of each colour, so it's well worth the division!


So let's take a look at the colour orange represented in hex:

0xFFAA00FF

Once again: the important thing to do here is to read it two characters at a time, so don't see it as 0xFFBB00 -- instead, see it as:

0x: This tells the Pawn compiler that this is a base-16, hexadecimal number. It's not actually part of the number - treat it like how you prefix float variables with Float:!

FF: The very first two digits (in this case FF) represent how bright red the colour is going to be. FF (or 255) means it will be 100% red. 80 (in hex, or 128 in decimal) means it will be 50% red. For example, 0xFF0000FF is the reddest red you can get whereas 0x330000 is a very dark red.

AA: The second pair of hex digits (BB) represent how bright green the color is going to be. Same as above - FF means 100% green, 80 (in hex) means 50%. 0x00FF00 for example is the greenest green you can get.

00: The third pair of hex digits (00) represent how blue the color is going to be. Same as above - FF. 0x0000FF for example is the richest blue you can get. I say "richest" rather than the brightest because many would argue that cyan is the brightest blue - interestingly, cyan is a composite colour - we'll discuss these a little later.

FF: The fourth and final pair of hex digits (FF) are unique in that they don't denote colour - they denote transparency or alpha. As we know from above, FF translates into 255 which translates into 100% - so if you specify FF as your alpha transparency, it won't be transparent at all (it will be completely opaque). If you specified 80 (hex), it would be 50% transparent. If you set it to 00, it would be invisible.

Now that you know this, it's a matter of remembering back to primary or elementary school and remembering the colour wheel:
- An absence of colour makes black (0x000000FF).
- Red and green in equal quantities make yellow (0xFFFF00FF)
- Red and blue make purple (0xFF00FF)
- Green and blue make our good ol' friend cyan (0x00FFFFFF), and all of the colours together make white (0xFFFFFF)!

You can work out where a colour falls by looking at the colour wheel:




(And while we're at it - here's a cool optics fact: the colour purple (but not violet) does not exist as a wavelength of light. Our vision also works off the basis of three separate light channels and purple is our way of interpreting equal "packets" of red and blue light. The colour spectrum is one dimensional - meaning if you were simply to pick the colour in the middle of red and blue, you'd just get green!)





The more complex: modifying colours dynamically

So now that we know a 32 bit colour is simply four unsigned 8-bit integers mashed together - how do we separate out the colour channels? What if I want to make my red a bit more red programmatically?

This is the part where it starts to get a little more technical - and we get to use a couple of operators that we otherwise would avoid like the plague: bitwise AND (&), bitwise OR (|), left shifts (<<) and right shifts (>>)!


How do I get extract a channel (red, green, blue or alpha) out of a colour?

So here's a representation of the color orange (0xFF8000FF) stored in memory:

11111111 10000000 00000000 11111111 in binary, or 0xFF8000FF in hex

A bitwise AND allows you to effectively apply a "mask" of what digits you want from the original and to extract them. So for example, if you wanted to extract the green byte from the above string (which is 2 bytes or 16 bits to the left), you would take the above and apply the following mask:

PHP Code:
above_color 0b00000000111111110000000000000000 // (or 0x00FF0000) 
This would return just the green channel of the above colour:
PHP Code:
0b00000000100000000000000000000000 // or (0x00800000) 
How can I build up a colour from scratch?

The bitwise OR operator (|) however can let you add to it. Observe the following piece of code:

PHP Code:
new 0xFF000000;
new 
0x00800000;
new 
0x00000000;
new 
0x000000FF;
new 
color a;
// color is 0xFF8000FF, or orange. 
... but what if you want to make your very own torgb function? How do you take a red value of 1.0 and turn it into 0xFF0000FF? This is where bit shifting comes in handy: in a nutshell, bit shifting lets you take values at bit offset 0 and move them however many digits to the left or right - a bit like multiplying/dividing by 10 does in our traditional decimal system.

Observe the following:

PHP Code:
new Float:red 0.5;
new 
red_as_hex floatround(red 255);
new 
color red_as_hex << 24
What we do here is:
1. We take a percentage [named red] (represented as a float between 0.0 and 1.0)
2. We multiply that percentage by our denominator - or 255 - to get our numerator. In this case, we end up with 0.5 * 255, which equals 128, which is 0x80.
3. Finally, to form the colour, we shift it 24 bits to the left, which is how many columns you need to left shift to go from being in the AA column to the RR column.

You can use this together in order to form a fully functioning torgb(Float:r, Float:g, Float:b, Float:a) function:

PHP Code:
stock torgb(Float:rFloat:gFloat:bFloat:a)
{
    
// Turn our percentages into denominations of 255.
    
new red floatround(255);
    new 
green floatround(255);
    new 
blue floatround(255);
    new 
alpha floatround(255);
    
// Now shift them the appropriate amount of bits...
    
red red << 24;
    
green green << 16;
    
blue blue << 8// 
    
alpha alpha << 0// alpha doesn't need to be bit shifted, this is purely for demonstration 
    // Now form our colour!
    
return red green blue alpha;

... or alternatively, we could use it to create a "Lighten" function that brightens up or darkens the colour based on the value inputted (>1.0 = brighter, <1.0 = darker):

PHP Code:
stock lighten(colorfloat:value)
{
    
// Extract each channel from the colour.
    
new red color & (0xFF000000); 
    new 
green color & (0x00FF000);
    new 
blue color & (0x0000FF00);
    new 
alpha color 0x000000FF);
    
// Now let's lighten them...
    
red floatround(red value);
    
green floatround(green value);
    
blue floatround(blue value);
    
alpha alpha// PRESUMABLY you don't want to change the alpha if you are just lightening it!
    // Now reform our new skewed colour!
    
return red green blue alpha;



Disclaimer

So this is my from my own self taught experiences over the years while playing with colours - so I may not be 100% technically accurate in areas. If that is the case, I apologize and thank anyone who can remedy my errors here!

Another thing worth noting is that I don't have a Pawn IDE installed at the moment - in fact I've been doing all my work in SampSharp for the past couple of months. This means I've quite literally just written all the pieces of code here from memory in the awful little VB textboxes and haven't actually had a chance to test them - if you notice any errors, please flag them and I will remedy them! Also, I'm writing this at half 12 on a work day - so I'm not exactly at my best right now! :P

Hopefully anyone who has had the stomach to read through the this has come off with at least a little better understanding of how colours work: if you do have any questions, please feel free to raise them!
Reply
#2

Niceeee thanks! +rep
Reply
#3

clamp
This is a very good function to reset colors in range.

Also, you can checkout this method for changing a color's transparency (in context to your 'lighten' function):
pawn Code:
stock lighten(color, alpha)
{
    return (color & ~0xFF) | alpha;
}
Reference: https://sampwiki.blast.hk/wiki/Colors_List
Reply
#4

Removed
Reply
#5

Thanks!
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)