I made a rather quick&dirty script to do automatically combine many images into one – saves a great deal of time in showing maps! I used to take screenshots manually until now, crazy I know.
Now, since this is the kind of thing I typically stitch together after googling and then getting the info from blog posts of kind people, I thought I’d give back for a change.
The most common application of combining images with PHP is adding a watermark to a picture. I had to do a little bit more than that though, namely stitching many identically sized images together on one ‘background’. In essence I am combining 144 of these:
… into something like this:
So basically we will be putting many different images all together on one big ‘canvas’ image. If you need to do that for some reason, read on to find out how.
Note: you need to have the GD module for PHP enabled or the functions used will throw ‘undefined’ errors. Run a php script that has phpinfo() to find out if you have GD installed yet. If not, here is some more info on GD.
Anyway, here’s (a slightly simplified version of) my code to combine many images into one.
Step 1: get an array with source image locations
This step is different for anybody, but in its easiest form you would define something like this:
$srcImagePaths = Array('https://diceattack.files.wordpress.com/2011/01/tile_e_o_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_e_g_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_e_b_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_b_q_fh.png');
Step 2: define some measures and initialize a blank ‘background’ image
Here we use the first GD functions: imagecreatetruecolor() creates a generic ‘base’ image, imagecolorallocate() to define an RGB color and imagefill() to fill our generic image with that color.
$tileWidth = $tileHeight = 28;
$numberOfTiles = 12;
$pxBetweenTiles = 1;
$mapWidth = $mapHeight = ($tileWidth + $pxBetweenTiles) * $numberOfTiles;
$mapImage = imagecreatetruecolor($mapWidth, $mapHeight);
$bgColor = imagecolorallocate($mapImage, 50, 40, 0);
imagefill($mapImage, 0, 0, $bgColor);
Step 3: think at which coordinates you want your source images to end up
There are some different ways to specify this, but if you are dealing with many images of the same size it makes sense to write a small function that maps an (array) index to a set of X,Y coordinates. Here is mine which arranges them all in a 12×12 square grid:
function indexToCoords($index)
{
global $tileWidth, $pxBetweenTiles, $leftOffSet, $topOffSet, $numberOfTiles;
$x = ($index % $numberOfTiles) * ($tileWidth + $pxBetweenTiles) + $leftOffSet;
$y = floor($index / $numberOfTiles) * ($tileWidth + $pxBetweenTiles) + $topOffSet;
return Array($x, $y);
}
Step 4: loop over the source images and copy them on the base image
We use function imagecopy() to do this, like this:
/*
* COPY SOURCE IMAGES TO MAP
*/
foreach ($srcImagePaths as $index => $srcImagePath)
{
list ($x, $y) = indexToCoords($index);
$tileImg = imagecreatefrompng($srcImagePath);
imagecopy($mapImage, $tileImg, $x, $y, 0, 0, $tileWidth, $tileHeight);
imagedestroy($tileImg);
}
… Note how we used the indexToCoords() function in there – we do not want all the source images on the same position of course.
Step 5 (intermezzo): resizing an image with PHP
The same imagecopy() function we used to put our source images on the base image can also be used to resize images. Handy if you want to generate thumbnails automatically! Here’s how you can do that:
/*
* RESCALE TO THUMB FORMAT
*/
$thumbSize = 200;
$thumbImage = imagecreatetruecolor($thumbSize, $thumbSize);
imagecopyresampled($thumbImage, $mapImage, 0, 0, 0, 0, $thumbSize, $thumbSize, $mapWidth, $mapWidth);
Final step: set header to tell the browser there’s an image coming, and output the final image
/*
* OUTPUT THUMBNAIL IMAGE
*/
header ("Content-type: image/png");
imagepng($thumbImage); //change argument to $mapImage to output the original size image
And that’s it! Note that you may not want a uniformly filled background but rather a real background image – you can easily do this by using imagecreatefrompng() in step 2.
Here’s all the code once more together for convenience.
<?php
//Source image paths (DISCLAIMER: this is just to demonstrate, to generate a real TT map you need 144 of these)
<pre>$srcImagePaths = Array('https://diceattack.files.wordpress.com/2011/01/tile_e_o_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_e_g_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_e_b_fh.png',
'https://diceattack.files.wordpress.com/2011/01/tile_b_q_fh.png');
</pre>
/*
* INIT BASE IMAGE FILLED WITH BACKGROUND COLOR
*/
$tileWidth = $tileHeight = 28;
$numberOfTiles = 12;
$pxBetweenTiles = 1;
$leftOffSet = $topOffSet = 1;
$mapWidth = $mapHeight = ($tileWidth + $pxBetweenTiles) * $numberOfTiles;
$mapImage = imagecreatetruecolor($mapWidth, $mapHeight);
$bgColor = imagecolorallocate($mapImage, 50, 40, 0);
imagefill($mapImage, 0, 0, $bgColor);
/*
* PUT SRC IMAGES ON BASE IMAGE
*/
function indexToCoords($index)
{
global $tileWidth, $pxBetweenTiles, $leftOffSet, $topOffSet, $numberOfTiles;
$x = ($index % 12) * ($tileWidth + $pxBetweenTiles) + $leftOffSet;
$y = floor($index / 12) * ($tileWidth + $pxBetweenTiles) + $topOffSet;
return Array($x, $y);
}
foreach ($srcImagePaths as $index => $srcImagePath)
{
list ($x, $y) = indexToCoords($index);
$tileImg = imagecreatefrompng($srcImagePath);
imagecopy($mapImage, $tileImg, $x, $y, 0, 0, $tileWidth, $tileHeight);
imagedestroy($tileImg);
}
/*
* RESCALE TO THUMB FORMAT
*/
$thumbSize = 200;
$thumbImage = imagecreatetruecolor($thumbSize, $thumbSize);
imagecopyresampled($thumbImage, $mapImage, 0, 0, 0, 0, $thumbSize, $thumbSize, $mapWidth, $mapWidth);
header ("Content-type: image/png");
imagepng($thumbImage);
?>
If there are any errors here or you have other suggestions, feel free to let me know in the comments!
Also if anybody would be interested, the next thing I do is of course store this resulting image somewhere (generating them dynamically each time would cost ridiculous amounts of time); perhaps that would make for a nice follow-up post?