<?php
/*
PHP "Sparkline" Builder
created by Mark Pursey
free to modify or distribute
*/
if (empty($series)) // create a dummy series?
{
$val = rand(0, 100);
for ($i = 0; $i < 32; $i++)
{
$series[$i] = $val;
$val += rand(-10, 10);
}
}
else
{
$series = explode(",", $series, 1024);
}
if (!$h)
$h = 16;
if ($h > 128) // don't want to tax server/bandwidth too much
$h = 128;
if ($sw) // base width on number of samples?
$w = (count($series)-$order) * $sw;
if (!$w) // or just guess one if not already here
$w = $h * 4;
if ($w > 1024) // limit again
$w = 1024;
// we'll draw to a 2x res bitmap then downsample... easiest way to antialias for now
if ($aa)
{
$w *= 2;
$h *= 2;
}
$im = color($w, $h)
or die("Couldn't initialize new GD image stream");
// we allow multiple ways to define a color, all use hex bumbers
// B = 0xBBBBBB (greyscale)
// B9 = 0xB9B9B9 (greyscale)
// B94 = 0xBB9944
// B94CD1 = 0xB94CD1
function ParseColorValue($col)
{
sscanf($col, "%6x", $val); // read the raw number
if (strlen($col) == 1)
$val = $val * 0x111111;
else if (strlen($col) == 2)
$val = $val * 0x10101;
else if (strlen($col) == 3)
$val = (($val & 0xF00) * 0x1100) + (($val & 0xF0) * 0x110) + (($val & 0xF) * 0x11);
return $val;
}
// allocate inks
if (empty($bg))
$bg = 0xffffff;
else
$bg = ParseColorValue($bg);
if (empty($fill))
$fill = 0xcccccc;
else
$fill = ParseColorValue($fill);
if (empty($tint)) // used for optional range bars
$tint = 0xf0f0f0;
else
$tint = ParseColorValue($tint);
if (empty($line))
$line = 0x444444;
else
$line = ParseColorValue($line);
// clear to background color
imagefilledrectangle($im, 0, 0, $w, $h, $bg);
// get data range
$lower = $upper = $series[0];
for ($i = 1; $i < count($series); $i++)
{
if ($lower > $series[$i])
$lower = $series[$i];
else if ($upper < $series[$i])
$upper = $series[$i];
}
// if user has supplied additional min and max values [to expand to, not collapse]
if (!empty($min) && $lower > $min)
$lower = $min;
if (!empty($max) && $upper < $max)
$upper = $max;
if ($lower == $upper) // avoid Divide by zero error, impose a 1.0 range
{
$upper += 0.5;
$lower -= 0.5;
}
function ScaleForRange($v)
{
global $lower, $upper;
return ($v - $lower) / ($upper - $lower);
}
$fudge = 0;
function ScaleForBitmap($v)
{
global $h, $fudge;
return $h - ScaleForRange($v) * ($h-2) - 1 + $fudge; // pixel fudging ;)
}
$zero = ScaleForBitmap($zero);
if (!($zero & 1) && $aa)
{
$fudge = 1;
$zero ++;
}
// we can provide color bands to give some visual indications of scale
if (!empty($zone))
{
$zone = explode(",", $zone);
for ($i = 0; $i < count($zone) >> 1; $i++)
imagefilledrectangle($im, 0, ScaleForBitmap($zone[$i*2+1]), $w, ScaleForBitmap($zone[$i*2]), $tint);
}
if (!$gap)
$gap = 0;
$gap *= 0.5; // shave half off either end (see below)
for ($i = 0; $i < $w; $i++)
{
if ($order == 2) // quadratic?
{
$x = $i * (count($series)-2) / $w;
$f = $x - (int)$x;
$y = ($series[$x] * (1-($f*0.5+0.5)) + $series[$x+1] * ($f*0.5+0.5)) * (1-$f) +
($series[$x+1] * (1-$f*0.5) + $series[$x+2] * $f*0.5) * $f;
}
else if ($order == 1) // linear?
{
$x = $i * (count($series)-1) / $w;
$f = $x - (int)$x;
$y = $series[$x] * (1-$f) + $series[$x+1] * $f;
}
else // bar
{
$x = $i * count($series) / $w;
$f = $x - (int)$x;
$y = $series[$x];
}
if ($gap && ($f < $gap || $f > 1-$gap)) // per sample gap
continue;
$v = ScaleForBitmap($y);
if ($style & 4) // intensity plot
{
$color = ScaleForRange($y);
// mix the colors
$color =
((int)(($line & 0xff) * $color + ($bg & 0xff) * (1-$color)) & 0xff) +
((int)(($line & 0xff00) * $color + ($bg & 0xff00) * (1-$color)) & 0xff00) +
((int)(($line & 0xff0000) * $color + ($bg & 0xff0000) * (1-$color)) & 0xff0000)
;
imagefilledrectangle($im, $i, 0, $i, $h, $color);
}
if ($style & 2) // fill
{
if ($v <= $zero) {
$y1 = $v;
$y2 = $zero;
} else {
$y2 = $v+1;
$y1 = $zero+1;
}
imagefilledrectangle($im, $i, $y1, $i, $y2, $fill);
}
if (($style & 1) || !$style) // line?
{
if (!empty($last)) // only if we have a last point to draw from
{
if ($order) // continuous plot
{
imageline($im, $i-1, $last, $i, $v, $line);
//imageline($im, $i-1, $last+1, $i, $y+1, $line);
imageline($im, $i, $last, $i+1, $v, $line);
}
else // square trace
{
imageline($im, $i-1, $last, $i-1, $v, $line);
imageline($im, $i-1, $v, $i, $v, $line);
}
}
$last = $v;
}
}
if ($aa)
{
$im2 = color(intval($w*0.5), intval($h*0.5))
or die("Couldn't initialize new GD image stream");
imagecopyresampled($im2, $im, 0, 0, 0, 0, imagesx($im2), imagesy($im2), imagesx($im), imagesy($im));
imagedestroy($im);
$im = $im2;
}
// doesn't really need to change at all, but added this just in case the algorithm changes
("Last-Modified: " . gmdate("D, d M Y H:i:s", intval(time() / 86400) * 86400) . " GMT");
("Content-type: image/png");
imagepng($im);
imagedestroy($im);
exit;
?>