Sunday, September 14, 2008

Dynamic PHP Image Consolidator

In short, this script dynamically pulls all images from a specified directory and merges them into a single image.Why is this useful? I've been trying to improve performance on my site by minimizing the number of round-trips to the server to retrieve files. I've found scripts out there to dynamically merge css files and javascript files into a single download, and I wondered if something similar could be done for images...

Interestingly, I accidentally discovered that youtube does exactly this by merging a bunch of their images into a single file, and then dynamically plucking them from this 'image menu' using the css background scroll. Cool, you say? That's what I thought. I threw this script together by merging a variety of other (free) scripts I found on the net. Enjoy and good luck.

How to use:
Step 1: Just drop it on your server, and pass the relative path of the directory you want it to draw files from --- example syntax: http://www.yourserver.com/combine.php?dir=/images/

Step 2: Right click and save the image to your desktop, then upload to your server someplace.

Step 3: Reference each image within the consolidated image file your css file:
background: transparent url(/images/yourconsolidatedimagefile.gif) no-repeat scroll -132px -37px; height:10px; width:20px;

A few notes:
1. You need to make sure that the height and width you specify for the element in the css matches the image size, otherwise you'll see the other icons as well in the element's background
2. Obviously, change the filename and path above to whatever/wherever you save the consolidated image to).
3. You can easily locate the upper left position of an image using Microsoft Photo Editor's selection tool, or equivalent.
4. The script filters the files -- i.e., it will only read .png .gif and .jpg (or .jpeg) files from the target directory, so you don't have to worry if you have other files types in that directory as well.
I'm hoping to next figure out how to get it to spit out a .png instead of a gif, and to also find a way to have it write out a prototye css file with all of the image locations. Currently, the script spits out the image data only ...



/* example invocation: http://www.yourserver.com/combine.php?dir=/images/ */

set_time_limit(5*60);

function sanitize($input) {
$input=strip_tags($input);
$input=str_replace("<","<",$input); $input=str_replace(">",">",$input);
$input=str_replace("#","%23",$input);
$input=str_replace("'","`",$input);
$input=str_replace(";","%3B",$input);
$input=str_replace("script","",$input);
$input=str_replace("%3c","",$input);
$input=str_replace("%3e","",$input);
$input=trim($input);
return $input;
}


//accept path to images via http param dir (
// e.g. '/templates/corporate/images/okb/' -- include trailing slash)
$rel = '';
if (array_key_exists("dir", $_REQUEST)) $rel = sanitize($_REQUEST["dir"]);
if ($rel=='') die();
$rt = $_SERVER['DOCUMENT_ROOT'] . $rel;

$i = 0;
$imgBuf = array ();
$maxW=0; $maxH=0;
$imagesperline = 5;
$curlineW = 0;
$curlineMaxH = 0;
$imagespacing=5;

$dir = opendir ($rt);

while (false !== ($link = readdir($dir)))
{

$len = strlen($link);
$off = $len - 3;
$ext = substr($link, $off, 3);

$file = $rt . $link;

switch($ext)
{
case 'png':
$iTmp = imagecreatefrompng($file);
break;
case 'gif':
$iTmp = imagecreatefromgif($file);
break;
case 'jpeg':
case 'jpg':
$iTmp = imagecreatefromjpeg($file);
break;
default:
continue;
}

array_push ($imgBuf,$iTmp);

$i++;
if ($i == $imagesperline + 1)
{
$i = 0;
$maxW=($maxW>$curlineW)?$maxW:$curlineW;
$curlineW = 0;
$maxH+=$curlineMaxH+$imagespacing;
$curlineMaxH=0;
}
else
{
$ix = imagesx($iTmp);
$iy = imagesy($iTmp);
$curlineW+=$ix+$imagespacing;
$curlineMaxH=($iy>$curlineMaxH)?$iy:$curlineMaxH;
}
}

$iOut = imagecreate ($maxW,$maxH) ;
$i=1;
$curxpos = 0;
$curypos = 0;
$curlineMaxH=0;

foreach ($imgBuf as $img)
{
if ($i <= $imagesperline) { $ix = imagesx($img); $iy = imagesy($img); imagecopy ($iOut,$img,$curxpos,$curypos,0,0,$ix,$iy); $curxpos+=$ix + $imagespacing; $curlineMaxH=($iy>$curlineMaxH)?$iy:$curlineMaxH;
}
else
{
$i = 0;
$curxpos = 0;
$curypos += $curlineMaxH + $imagespacing;
$curlineMaxH = 0;
imagecopy ($iOut,$img,$curxpos,$curypos,0,0,$ix,$iy);
}
imagedestroy ($img);
$i++;
}

imagegif($iOut);

closedir ($dir);

3 comments:

Raman said...
This comment has been removed by the author.
ashtakshara said...

Great idea! Will reduce server load tremendously. Please update when the CSS template is ready, and/or the script is modified. Thanks.

ashtakshara said...

I tried to make this work, but failed. I copied the code and pasted it into combine.php, uploaded this to my server and invoked the file as instructed via . A blank image icon does appear and when I try the Firefox View Image, all I see are a GIF87a followed by hieroglyphics associated with image data. But there is no image to right click and save. How else is this to be done? This is a terrific idea, so I would really like to implement it. Thanks.