perkiset

Here is the image class that I use as a base for generating images on the fly. Note that it requires both GD and FreeType to be on the

mac

 hine before this will work. In the next post I will show usage:


<?

php

 

class image
{
private $myHandle;
private $myOutputMode;
private $fontHandles;
private $colorHandles;

public $fadeHandles;
private $currentColorHandle;
private $currentFontSize;
private $currentFontHandle;
private $currentFontColor;

public $handle;

function __construct($width=-1, $height=-1, $backgroundColor='ffffff')
{
$this->fontHandles = array();
$this->colorHandles = array();
$this->fadeHandles = array();
$this->outputMode = 0;
$this->myHandle = null;

if (is_string($width) && ($height == -1))
{
$fileName = $width;
if (!file_exists($fileName)) { "Filename passed to image->construct does not exist or is inaccessible"; }
$ptr = strrpos($fileName, '.');
$suffix = strtolower(substr($fileName, $ptr + 1, 4));
switch ($suffix)
{
case 'gif':
$this->myHandle = imagecreatefromgif($fileName);
break;
case 'jpeg':
case 'jpg':
$this->myHandle = imagecreatefromjpeg($fileName);
break;
case 'png':
$this->myHandle = imagecreatefrompng($fileName);
break;
default:
die("Image class cannot be created from file with suffix '$suffix'");
}
} else {
if (($width==-1) || ($height==-1)) { die('You must pass both a height width to create an image object.'); }
$this->myHandle = imagecreate($width, $height);
}

$red = hexdec(substr($backgroundColor, 0, 2));
$green = hexdec(substr($backgroundColor, 2, 2));
$blue = hexdec(substr($backgroundColor, 4, 2));
imagecolorallocate($this->myHandle, $red, $green, $blue);

$this->handle = $this->myHandle;
}
function __destruct()
{
if (!$this->myHandle) { imagedestroy($this->myHandle); }
}

private function buildImage($fileName='')
{
switch ($this->myOutputMode)
{
case 0:
if (strlen($fileName) > 0) { imagegif($this->myHandle, $filename); }
else { imagegif($this->myHandle); }
break;
case 1:
if (strlen($fileName) > 0) { imagejpeg($this->myHandle, $filename); }
else { imagejpeg($this->myHandle); }
break;
case 2:
if (strlen($fileName) > 0) { imagepng($this->myHandle, $filename); }
else { imagepng($this->myHandle); }
break;
default:
die('Unknown output mode "' . $this->myOutputMode . '"');
}
}
public function colorHandle($theHandle) { return $this->colorHandles[$theHandle]; }
public function fadeValue($fadeName, $ptr) { return $this->fadeHandles[$fadeName][$ptr]; }
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;
}
}
public function getHeight() { return imagesy($this->myHandle); }
public function getWidth() { return imagesx($this->myHandle); }
public function importImage($theImage, $x=0, $y=0)
{
if (!is_a($theImage, 'image')) { die("image->importImage must be passed a reference to an image object"); }
imagecopy($this->myHandle, $theImage->handle, $x, $y, 0, 0, imagesx($theImage->handle), imagesy($theImage->handle));
}
public function outputMode($mode=-1)
{
if ($mode == -1) { die("You must pass a mode (0/1/2) to image->outputMode"); }
switch ($mode)
{
case 0:
case 1:
case 2:
$this->myOutputMode = $mode;
break;
default:
die("You must pass a mode that is either 0, 1 or 2 to image->outputMode");
break;
}
}
public function output()
{
list($usec, $sec) = explode(' ', microtime());
$theRand = rand(1000000000);
$fileName = ((float)$usec + (float)$sec) . $theRand;
$this->buildOutput("/tmp/$fileName");
$buffer = file_get_contents($fileName);
unlink($fileName);
return $buffer;
}
public function outputDirect()
{
switch($this->myOutputMode)
{
case 0:
header('Content-type: image/gif');
break;
case 1:
header('Content-type: image/jpeg');
break;
case 2:
header('Content-type: image/png');
break;
}
$this->buildImage();
exit;
}
public function outputFile($fileName='')
{
if ($fileName == '') { die('You must pass a filename to image->fileOutput'); }
$this->buildImage($fileName);
}
function rectangle($x0, $y0, $x1, $y1, $cHandle=-1)
{
if ($cHandle == -1) { $cHandle = $this->currentColorHandle; }
imagerectangle($this->myHandle, $x0, $y0, $x1, $y1, $this->colorHandles[$cHandle]);
}
function rectangleFade($startColor, $endColor, $direction, $width, $height, $destX, $destY)
{
$tempImage = new image($width, $height);
if ($direction)
{
// Horitzontal Fade
$tempImage->generateFade('temp', $startColor, $endColor, $width);
$fadeArr = &$tempImage->fadeHandles['temp'];
for ($i=0; $i<$width; $i++) { imageline($tempImage->handle, $i, 0, $i, $height, $fadeArr[$i]); }
imagecopy($this->myHandle, $tempImage->handle, $destX, $destY, 0, 0, $width, $height);
} else {
// Vertical Fade
$tempImage->generateFade('temp', $startColor, $endColor, $height);
$fadeArr = &$tempImage->fadeHandles['temp'];
for ($i=0; $i<$height; $i++) { imageline($tempImage->handle, 0, $i, $width, $i, $fadeArr[$i]); }
imagecopy($this->myHandle, $tempImage->handle, $destX, $destY, 0, 0, $width, $height);
}

}
function registerColor($colorStr)
{
$red = hexdec(substr($colorStr, 0, 2));
$green = hexdec(substr($colorStr, 2, 2));
$blue = hexdec(substr($colorStr, 4, 2));
$this->colorHandles[] = imagecolorallocate($this->myHandle, $red, $green, $blue);
$retVal = count($this->colorHandles) - 1;
$this->currentColorHandle = $retVal;
return $retVal;

}
function registerFont($fontPath)
{
if (!file_exists($fontPath)) { die("Font file '$fontPath' does not exist, or cannot be accessed (image->registerFont)"); }
$this->fontHandles[] = $fontPath;
return count($this->fontHandles) - 1;
}
public function setCurrentColor($theHandle = -1)
{
if (($theHandle < 0) || ($theHandle >= count($this->colorHandles))) { die('You must pass a valid handle to image->setColorHandle'); }
$this->currentColorHandle = $theHandle;
}
public function setCurrentFont($theHandle)
{
if (($theHandle < 0) || ($theHandle >= count($this->fontHandles))) { die('You must pass a valid handle to image->setCurrentFont'); }
$this->currentFontHandle = $theHandle;
}
public function setFontHandle($theHandle=-1)
{
if (($theHandle < 0) || ($theHandle >= count($this->fontHandles))) { die('You must pass a valid handle to image->setFontHandle'); }
$this->currentFontHandle = $theHandle;
}
public function setFontSize($theSize=-1)
{
if ($theSize == -1) { die('You must pass a point size to image->setFontSize'); }
$this->currentFontSize = $theSize;
}
public function simpleText($x, $y, $message, $cHandle=-1)
{
if ($cHandle == -1) { $cHandle = $this->currentColorHandle; }
imagettftext($this->myHandle, $this->currentFontSize, 0, $x, $y, $this->colorHandles[$cHandle], $this->fontHandles[$this->currentFontHandle], $message);
}
public function vLine($hPos, $cHandle=-1, $vStart=-1, $vEnd = -1)
{
if ($vStart == -1) { $vStart = 0; }
if ($vEnd == -1) { $vEnd = imagesy($this->myHandle); }
if ($cHandle == -1) { $cHandle = $this->currentColorHandle; }
imageline($this->myHandle, $hPos, $vStart, $hPos, $vEnd, $this->colorHandles[$cHandle]);
}
public function hLine($vPos, $cHandle=-1, $hStart=-1, $hEnd = -1)
{
if ($hStart == -1) { $hStart = 0; }
if ($hEnd == -1) { $hEnd = imagesx($this->myHandle); }
if ($cHandle == -1) { $cHandle = $this->currentColorHandle; }
imageline($this->myHandle, $hStart, $vPos, $hEnd, $vPos, $this->colorHandles[$cHandle]);
}
}

?>

perkiset

Here is a fragment of an image I created using the class above and the code below. This simulates an area chart with an interesting looking grid, left hand legend and imported images. I use similar stuff to this (although considerably less garrish) for my telemetry pages:

perkiset

This is the code that I used to create the image above. Note that it simply randomizes data points, imports a couple graphics that are on my

mac

 hine and also expects a font, "ApplauseENMARK.TTF" to be available. Again, the class above requires GD installed as well as FreeType, although you can get by without FreeType if you don't use the text functions.
Not also an interesting trick - I show the digits in the left handle legend in white at x-1 & y-1, x+1 & y-1, x-1 & y+1, x+1 & y+1 and then in dark black at x,y - this gives the effect of a white "stroke" around the letters so that it stands out over the blues.


<?

php

 

error_reporting(E_ALL);

require_once('/www/sites/lib/classes/class.image.

php

 ');

$sun = new image('/www/sites/graphics/telem_sun.gif');
$moon = new image('/www/sites/graphics/telem_moon.gif');

$main = new image(960, 300);
$main->outputMode(2);
$ht = $main->getHeight();
$wd = $main->getWidth();
$white = $main->registerColor('ffffff');
$black = $main->registerColor('000000');
$yellow = $main->registerColor('ffff00');
$gray = $main->registerColor('6a6a6a');
$denmark = $main->registerFont('/www/sites/lib/fonts/DENMARK.TTF');

$main->rectangleFade('09293c', '167cb8', true, 240, 300, 0, 0);
$main->rectangleFade('167cb8', '09293c', true, 240, 300, 240, 0);
$main->rectangleFade('09293c', '167cb8', true, 240, 300, 480, 0);
$main->rectangleFade('167cb8', '09293c', true, 240, 300, 720, 0);

// Draw the gray verticals...
$i = -1;
while (($i++ * 20) <= $wd) {$main->vLine($i * 20); }

// Randomize my "data points"
$traffic = array();
for ($i=0; $i<48; $i++) { $traffic[] = rand(75,300) - 50; }

// Create my white polygon
$chart = array(0, 0);
for ($i=0; $i<48; $i++)
{
$chart[] = $i * 20;
$chart[] = $traffic[$i];
}
$chart[] = 960;
$chart[] = 0;
imagefilledpolygon($main->handle, $chart, 50, $main->colorHandle($white));


// Now the horizontal lines & legend...
$i = -1;
while (($i++ * 20) <= $ht) { $main->hLine($i * 20); }

// Sun & moon...
$main->importImage($moon, -40, 200);
$main->importImage($sun, 200, 200);
$main->importImage($moon, 440, 200);
$main->importImage($sun, 680, 200);
$main->importImage($moon, 920, 200);


// Now the horizontal lines & legend...
$i = -1;
$main->setCurrentFont($denmark);
$main->setFontSize(9);
while (($i++ * 20) <= $ht)
{
$msg = (30 - $i) * 10;
if ($msg > '160')
{
$main->simpleText(9, ($i * 20) - 2, $msg, $white);
$main->simpleText(11, ($i * 20) - 2, $msg, $white);
$main->simpleText(10, ($i * 20) - 1, $msg, $white);
$main->simpleText(10, ($i * 20) - 3, $msg, $white);
$main->simpleText(10, ($i * 20) - 2, $msg, $black);
}
}

$main->outputDirect();

?>

thedarkness

Very cool Perk, I have a project which requires the generation of gantt style charts (actually a visual disply of employee's shifts0 this could be very handy.

Thanks,
td

perkiset

Dig it!

A favor - if you make a cool one, please post an example and some script... that'd be ginormously cool.

/p

thedarkness

Done, my "cool" may not be veryone elses "cool" though ;-)

I've got a class that reads mp3 headers and a few other bits and pieces that I wat to post, got to find them though :-P

Cheers,
td


Perkiset's Place Home   Politics @ Perkiset's