perkiset

I really love the GD library and have several graphics that are built on the fly. I always admired "fades" - where one color fades gradually into another and wanted this to be a dynamic effect. But how to calculate the colors?

Color values in the GD (and for the most part everywhere else...) are 3 hexadecimal values scrunched up to look like a single number. The values are from 0..255 (0x0 - 0xff = red) 0..255 (0x0 - 0xff = green) and 0..255 (0x0 - 0xff = blue). That's why the color #000000 is black - no color intensity at all. Primary red is #ff0000. Gray is an equal blend of everything: #6a6a6a for example.

The trick to building a blend is to figure out the number of steps you want the blend to span across, then figure out how slowly/quickly each value in the color number must move towards that. As a simple example, we will move from pure red (#ff0000) to pure blue (#0000ff) in 4 steps:
#FF0000
#BF0040
#800080
#4000BF
#0000FF

In this case, the red value has proceeded towards its target value (0) in 4 steps, just as the blue has proceeded towards its target value (255) in 4 steps. The two move independently of each other, but when used as a color value we get the intermediate colors we are looking for.

The challenge mathematically is simply to figure out which direction each value needs to move (ie., red moves towards 255 or towards 0) and how far to we change at each increment. Note, in the previous example, that green's target vale was the same as it's starting value (0) - regardless of how much we move, it's increment will always be zero.

Finally, after we have calculated all the values in the spectrum between the initial color and the target color, we draw lines of <that color> on a canvas and move over. Here is a fragment of my image manager class that generates all of the color values into an array. The "fadeHandles" variable is where I store the color values - I use it in another place in my image generation class to actually draw the lines.

(I was noticing when I pasted this that I should have used a

regex

  to parse the values, but hey... you can instantly make this better Applause )


public function generateFade($fadeName='NONAME', $fadeStart='######', $fadeEnd='######', $fadeSteps=0)
{
// Pass colors as "ae126f' - you do not need the #
if ($fadeName == 'NONAME') { die('You must pass a fade name to image->generateFade as parameter 1'); }
if ($fadeStart == '#######') { die('You must pass a fade start color value (hex) to image->generateFade as parameter 2'); }
if ($fadeEnd == '#######') { die('You must pass a fade end color value (hex) to image->generateFade as parameter 3'); }
if ($fadeSteps <= 0) { die('You must pass a non-zero positive value to image->generateFade as parameter 4'); }

unset($this->fadeHandles[$fadeName]);

$startRed = hexdec(substr($fadeStart, 0, 2));
$startGreen = hexdec(substr($fadeStart, 2, 2));
$startBlue = hexdec(substr($fadeStart, 4, 2));

$endRed = hexdec(substr($fadeEnd, 0, 2));
$endGreen = hexdec(substr($fadeEnd, 2, 2));
$endBlue = hexdec(substr($fadeEnd, 4, 2));

$deltaRed = $endRed - $startRed;
$deltaGreen = $endGreen - $startGreen;
$deltaBlue = $endBlue - $startBlue;

$sineRed = ($deltaRed >= 0);
$sineGreen = ($deltaGreen >= 0);
$sineBlue = ($deltaBlue >= 0);

$deltaRed = abs($deltaRed);
$deltaGreen = abs($deltaGreen);
$deltaBlue = abs($deltaBlue);

$stepsRed = $deltaRed / $fadeSteps;
$stepsGreen = $deltaGreen / $fadeSteps;
$stepsBlue = $deltaBlue / $fadeSteps;

$this->fadeHandles[$fadeName][0] = imagecolorallocate($this->myHandle, $startRed, $startGreen, $startBlue);
$lastRed = $startRed;
$lastGreen = $startGreen;
$lastBlue = $startBlue;
for ($i=1; $i<$fadeSteps; $i++)
{
$newRed = ($sineRed) ? ($lastRed + $stepsRed) : ($lastRed - $stepsRed);
$newGreen = ($sineGreen) ? ($lastGreen + $stepsGreen) : ($lastGreen - $stepsGreen);
$newBlue = ($sineBlue) ? ($lastBlue + $stepsBlue) : ($lastBlue - $stepsBlue);

$thisRed = floor($newRed);
$thisGreen = floor($newGreen);
$thisBlue = floor($newBlue);

if (
($thisRed == floor($lastRed)) &&
($thisGreen == floor($lastGreen)) &&
($thisBlue == floor($lastBlue))
)
{
$this->fadeHandles[$fadeName][$i] = $this->fadeHandles[$fadeName][$i - 1];
} else {
$this->fadeHandles[$fadeName][$i] = imagecolorallocate($this->myHandle, $thisRed, $thisGreen, $thisBlue);
}
$lastRed = $newRed;
$lastGreen = $newGreen;
$lastBlue = $newBlue;
}
}


This routine also has an efficiency mechanism if the previous color is the same as the next - it only allocates a new color if there is a change, otherwise the new color is a pointer to the previous one. This happens a lot when the color shift is small and the span size is big.

Good luck!
/perkiset


Perkiset's Place Home   Politics @ Perkiset's