<?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 = @imagecreatetruecolor($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 =
@imagecreatetruecolor(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
@header("Last-Modified: " . gmdate("D, d M Y
H:i:s", intval(time() / 86400) * 86400) . " GMT");
@header("Content-type: image/png");
imagepng($im);
imagedestroy($im);
exit;
?>