In Chapter 1 we have discussed that colors are coded in 32-bit integers and that screen pixels are made up of red, green, and blue subpixels. We have also used color constants like clRed to define colors. What we have not discussed are the details of how colors are coded and how to make custom colors (assuming you want something more than the 20 or so color constants defined in FTGraphics). As we have mentioned before, each byte (8-bits) in a 32-bit color integer represents red, green, blue, and alpha (where alpha is the transparency). Here we focus only on red, green, and blue (RGB). The first thing we have to consider is how to write a color in a way that makes clear the RGB values. For example, execute the following code:
program Workspace;
uses FTGraphics;

procedure Initialize;
begin
end;

procedure OnEnterFrame;
begin
  FillEllipse(-2,-2,2,2,65535);
  FillEllipse(2,2,6,6,16777215);
  FillEllipse(-6,2,-2,6,255);
end;

begin
  RunGraphicalApp(@Initialize,@OnEnterFrame);
end.
Once the code is executed you can tell that the colors of the circles are white, yellow, and red, but it is impossible to tell this from the code. Let's say we know that the human brain will perceive the color white when 100% red, green, and blue light is coincident on a point on the retina. The color yellow comes from 100% red and 100% green with 0% blue. The color red of course is 100% red with 0% green and blue. Now, we said that each color is stored in 8-bits. This means that the total number of values that can be stored is . If we start with 0 as the lowest value, then 255 must be the highest. With these facts, it is clear that the third ellipse with the color value of 255 must be a primary color:
FillEllipse(-6,2,-2,6,255);
But which primary color is it? In this case, it happens to be red (as we can see when running the program). This is because in Object Pascal, the low-order byte (the one of smallest value) is red and the high-order byte is blue. This means that red is coded as 255, green is coded as , and blue is coded as . All other colors can be generated from calculating the percentage of each color and summing these three values. For example, consider the color white which was 16777215 (see ellipse code above). If we sum 100% red, green, and blue we get this value exactly:
  Likewise, for yellow we can sum red and green and get the color value we used above:
  Although this technique works, it is somewhat cumbersome and doesn't immediately lend itself to easy interpretation. Fortunately, Object Pascal provides efficient syntax for writing numbers that need to be aligned into byte-sized bins: hexadecimal numbers. So far we have discussed decimal numbers (base 10) and binary numbers (base 2), but hexadecimal numbers (base 16) are also a very common in programming from defining colors to memory addresses. Of course, given that we need 16 digits instead of only 10, we'll need to define 6 more digits. On almost every modern computer system these extra 6 digits are the letters A through F. The digit A in hexadecimal (hex) stands for the decimal number 10. The digit F stands for the decimal 15. To more easily understand hex numbers, think of what each place holder in the decimal system means: 1, 10, 100, 1000... each one is a multiplication by 10. In hex, each is a multiplication by 16. Thus, 1, 10, 100, 1000 in hex stand for 1, 16, 256, 4096 in decimal.
Excercise 3-3.
Convert between the following number systems.
  1. Convert the binary number 111 to decimal.
  2. Convert the binary number 1010 to decimal.
  3. Convert the hexadecimal number 15 to decimal.
  4. Convert the hexadecimal number A9 to decimal.
  5. Convert the hexadecimal number FF to decimal.
We will deal more with conversions between number bases in later units but for now this is all we need to know to deal with colors. You will notice from the exercise that FF corresponds to 255. Remember that 255 is the largest number an unsigned 8-bit integer (a byte) can hold. 100 in hex corresponds to 256. Thus, 1 byte can be represented by exactly 2 hexadecimal digits. This is extremely convenient in the case of colors because we can represent the RGB values as a hexadecimal number. Object Pascal uses the $ symbol to distinguish a hexadecimal number from a decimal number. So, while A0 is an identifier, $A0 is a number.

Full brightness of a color subpixel corresponds to the maximum value of the byte (255) and no brightness corresponds to 0. So for full red the color is $0000FF or 255. Full green is $00FF00 or 65280 and full blue is $FF0000 or 16711680. White, the mixture of all three, is $FFFFFF or 16777215. These colors correspond to the constants clRed, clLime, and clBlue in FTGraphics (lime is used instead of green for historical reasons). As you can see, using hexadecimal numbers instead of decimal numbers makes the definition of the color much easier to immediately understand because the RGB components are divided into pairs of digits.
Excercise 3-4.
Write code for the following.
  1. Write a program that draws lines from the center of the screen outward in a circular pattern. Choose a radius for the circle that is large enough so that the lines extend beyond the boundary of the screen. Create some custom color (for example, $80CC) and use a pen of width 2.
  2. The FTGraphics library provides a function called RGB2Color(R,G,B) which will combine three bytes to make a color. In most cases it is sufficient to use simply a hexadecimal number, but there are situations in which RGB2Color can be very useful. Create a program identical to the one above but choose a random color for each line. Remember that Random(255) will return a random number between 0 and 255.
  3. You will notice that in the previous problem, the colors appear to change randomly. Instead of random changes, make only a single line move in a circle around the screen. Then, after this is working, set the FTGraphics variable ClearEachFrame to False in Initialize.
  4. In the previous problem, the line moves rather slowly around the screen. Often we will want to take multiple steps in an animation in order to speed it up. To the observer, the multiple steps will be perceived as continuous motion because single frames are so short. Change the previous program so that 20 steps are taken per frame instead of just a single step.
  5. Change the code so that only random shades of green are displayed instead of all random colors.
Although the RGB color model is what the hardware uses to actually display colored pixels, there are other color models that are often used. For example, HSI stands for Hue-Saturation-Intensity. A hue is the actual shade of color and it ranges from 0 to 255 starting and ending with red. To understand hues, try running the following (Note: ClearEachFrame will need to be set to True in order for this to work):
procedure OnEnterFrame;
begin
  BkColor := HSI2Color(FrameCount mod 255,255,255);
end;
As you can see, as hue changes, it ranges over colors as if one were working through the colors of the rainbow (which is also the visible spectrum of light). Saturation is simply how pure the hue is. A saturation of 255 is a full color Hue. 128 is approximately half of the Hue mixed with white and 0 is white (no color). Try running the program again with 64 as the saturation:
procedure OnEnterFrame;
begin
  BkColor := HSI2Color(FrameCount mod 255,64,255);
end;
Finally, intensity is essentially the brightness of the light. 255 is the brightest light that a pixel can physically emit. 0 is no emission at all (black). Try running the program with 64 as the intensity and you will see that the colors are 1/4 as bright:
procedure OnEnterFrame;
begin
  BkColor := HSI2Color(FrameCount mod 255,255,64);
end;
Though RGB values are what the hardware needs in order to display a color, the HSI model is often much more intuitive for choosing a color. Thus, if you want to make a custom color and you know about what hue, saturation, and intensity you want then you can use HSI2Color to determine the RGB color. Another helpful function provided by FTGraphics is GetHue. GetHue returns the hue of any color value passed to it. For example, let's say you want a yellowish hue with varying intensity but you don't know which value from 0 to 255 corresponds to yellow. The following code will do the trick:
procedure OnEnterFrame;
begin
  BkColor := HSI2Color(GetHue(clYellow),255,FrameCount mod 255);
end;