03.02.2016, 23:27
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:
This would return just the green channel of the above colour:
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:
... 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:
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:
... 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):
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!
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)
PHP Code:
0b00000000100000000000000000000000 // or (0x00800000)
The bitwise OR operator (|) however can let you add to it. Observe the following piece of code:
PHP Code:
new r = 0xFF000000;
new g = 0x00800000;
new b = 0x00000000;
new a = 0x000000FF;
new color = r | g | b | a;
// color is 0xFF8000FF, or orange.
Observe the following:
PHP Code:
new Float:red = 0.5;
new red_as_hex = floatround(red * 255);
new color = red_as_hex << 24;
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:r, Float:g, Float:b, Float:a)
{
// Turn our percentages into denominations of 255.
new red = floatround(r * 255);
new green = floatround(g * 255);
new blue = floatround(b * 255);
new alpha = floatround(a * 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;
}
PHP Code:
stock lighten(color, float: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!