This post will explain how to do some dynamic manipulation of transparent png images with the GD Library, which can be used within JavaScript and other applications.

The GD library is an image creation and manipulation tool that is part of the PHP package. It is enabled on most web hosting sites, however you may want to check by uploading and running a php document with the following:

phpinfo();

If there is a GD Library section, it will be installed.

The Tiles – Edges

As a working example, I will use the tiles for the Isometric game world that I am working on.

Each zone of the map is made up of a 20×20 grid of x,y coordinates, rendered isometric by layering tile images in the document. Refer to this article for further details on this process.

But what if we could soften edges to reduce some of the box feel of the lands? Have a look at the image below to see the modified tiles (1,2,3).

 isoTileMod1

In my original illustration of the map editor, shown at the end of the article linked above, the same effect was achieved by slicing a new tile skin in photoshop and inputting the details into the database as a new tile entry.

With the GD library this same effect can be achieved with a single tool in the editor, on any tile skins.

The Tiles – Color

What if we could go even further than shape, and adjust the color of the tile dynamically, depending on a value in a database. The GD library is capable of that also. Below is an image of the same scene as above, but with a single value “color” changed in the database for each tile.

 isoTileMod2

I’ll post the script below, and then pick it apart after that.

The Script: tileMod.php

$tileID=$_GET['tileID'];
$tilePath=$tileID.".png";
$edgeID=$_GET['edgeID'];
$color=$_GET['color'];
$imageHeight=22;
$imageWidth=40;
$imageSrc = imageCreateFromPNG ($tilePath); // transparent PNG
if ($tileID==4){
	//the tileID denoting blank tiles
	$edgeID=0;
	$color="";
}
if ($edgeID==0){
	//no edge modification, color only
	$imageDest=imagecreate($imageWidth,$imageHeight);
	$safeCoX=20;
	$safeCoY=10;
	imagecopy($imageDest,$imageSrc,0,0,0,0,$imageWidth,$imageHeight);
	imagesavealpha($imageDest,true);
	imagedestroy($imageSrc);
} else if ($edgeID==1){
	//edge 1 is right side cut off
 
	$imageDest=imagecreate($imageWidth,$imageHeight);
	$safeCoX=15;
	$safeCoY=10;
	$startX=0;
	$startY=0;
	$chunkWidth=$imageWidth/2;
	$chunkHeight=$imageHeight;
	imagecopy($imageDest,$imageSrc,$startX,$startY,$startX,$startY,$chunkWidth,$chunkHeight);
	imagesavealpha($imageDest,true);
	imagedestroy($imageSrc);
} else if ($edgeID==2){
	//edge 2 is left cut off
 
	$imageDest=imagecreate($imageWidth,$imageHeight);
	$safeCoX=25;
	$safeCoY=10;
	$startX=20;
	$startY=0;
	$chunkWidth=$imageWidth/2;
	$chunkHeight=$imageHeight;
	imagecopy($imageDest,$imageSrc,$startX,$startY,$startX,$startY,$chunkWidth,$chunkHeight);
	imagesavealpha($imageDest,true);
	imagedestroy($imageSrc);
} else if ($edgeID==3){
	//edge 3 is top cut off
 
	$imageDest=imagecreate($imageWidth,$imageHeight);
	$safeCoX=20;
	$safeCoY=15;
	$startX=0;
	$startY=($imageHeight-2)/2;
	$chunkWidth=$imageWidth;
	$chunkHeight=(($imageHeight-2)/2)+2;
	imagecopy($imageDest,$imageSrc,$startX,$startY,$startX,$startY,$chunkWidth,$chunkHeight);
	imagesavealpha($imageDest,true);
	imagedestroy($imageSrc);
} else if ($edgeID==4){
	//edge 4 is bomttom cut off
 
	$imageDest=imagecreate($imageWidth,$imageHeight);
	$safeCoX=20;
	$safeCoY=5;
	$startX=0;
	$startY=0;
	$chunkWidth=$imageWidth;
	$chunkHeight=($imageHeight-2)/2;
	imagecopy($imageDest,$imageSrc,0,0,$startX,$startY,$chunkWidth,$chunkHeight);
	imagesavealpha($imageDest,true);
	imagedestroy($imageSrc);
 
} else {
	exit();
}
if ($color!=""){
	$rgb2 = imagecolorallocate ($imageDest, hexdec($color{0}.$color{1}), hexdec($color{2}.$color{3}), hexdec($color{4}.$color{5}));
	imagefill($imageDest, $safeCoX, $safeCoY, $rgb2); //rgb fill
}
Header("Content-type: image/png");
imagePNG ($imageDest);
imagedestroy ($imageDest);

The script takes a parameter “edgeID”, which can be 0 (no modification), 1 (the right side of the diamond tile is cut away), 2 (the left side of the tile is cut away), 3 (The top of the tile is cut away), 4 (the bottom of the tile is cut away)

The second parameter is “color”, which contains a hex color value (eg. FFF000)

The last parameter is “tileID”, which tells the script which tile skin to modify.

The modification process is started by opening a new image stream at $tilePath, like this:

$tilePath=$tileID.".png";
$imageSrc = imageCreateFromPNG ($tilePath);

To start an image stream using other file types, there are functions such as imageCreateFromGIF, imageCreateFromJPG, etc.

Let’s dissect the code within condition edgeID=1 (right side cut off):

Note: While the tiles are 20hx40w, the images themselves are 2pixels higher, due to a footer effect, this is taken into account in the script (edgeID 3,4) and may need to be modified in other applications.

The first line of the edgeID triggered code creates a new image to be the destination of our slice, it is given the same dimensions as the original image:

$imageDest=imagecreate($imageWidth,$imageHeight);

This next section creates the necessary variables for the next steps:

$safeCoX=15;
$safeCoY=10;
$startX=0;
$startY=0;
$chunkWidth=$imageWidth/2;
$chunkHeight=$imageHeight;

$safeCoX and $safeCoY are coordinates of a point within the tile slice that can be filled. This is important to note, as this pixel coordinate should be attached to any area that you wish to be filled using the Auto Fill color value. (it operates similar to a paint bucket fill in photoshop or ms paint)

$startX and $startY are the starting coordinates of the slice area (the part of the image being copied from the original image, and being pasted into the new blank image) – since we wish to remove the right hand side of the image, this should be the left hand side, beginning at 0,0.

$chunkWidth and $chunkHeight are the dimensions of the slice area to be copied. We want half of the tile width, and the full tile height.

This next line is where the magic happens, and the slice area defined by the variables is copied into the new image ($imageDest):

imagecopy($imageDest,$imageSrc,$startX,$startY,$startX,$startY,$chunkWidth,$chunkHeight);

The next line is important, as it saves the alpha (transparent) information, allowing the output to be transparent:

imagesavealpha($imageDest,true);

Finally the original image is destroyed:

imagedestroy($imageSrc);

Note: For more information on any of these GD Library functions, please have a look at The PHP Image Manual.

The color fill:

if ($color!=""){
	$rgb2 = imagecolorallocate ($imageDest, hexdec($color{0}.$color{1}), hexdec($color{2}.$color{3}), hexdec($color{4}.$color{5}));

$rgb2 is set using the function imagecolorallocate, and will be used for the paint bucket fill.

Since the RGB values are contained within the hex in 3 pairs, hexdec is used to convert the hexadecimal value in $color into the 3 values needed by imagecolorallocate.

The color is then filled into $imageDest at the coordinates set earlier $safeCoX, $safeCoY using imagefill():

imagefill($imageDest, $safeCoX, $safeCoY, $rgb2);

The final Steps:

Set the header of the PHP file to mimic that of a png file, allowing it to be regarded by the browser as such, and output the image using imagePNG

Header("Content-type: image/png");
imagePNG ($imageDest);

And lastly delete the image from the server:

imagedestroy ($imageDest);

The end result will be a sliced output image with transparency, like this (this example is left side cut off, edgeID=2):

 isoTileMod3

If you’d like to download the script, get it here

To use the script, upload it to your webserver and use your browser to run it like this:
http://www.yourserver.com/yourpath/tileMod.php?tileID=3&edgeID=2&color=D3d5d3

To play around with it, modify the values of tileID, edgeID and color.

Update: I came across some troubles modifying my original script to include an optional gradient overlay. I couldn’t manage to get a second png (containing the gradient) to paste over the top of $imageDest while preserving transparency to allow the underlying image to show through. The solution was to use a transparent GIF instead of a PNG file.

It seems imagecopy function works better with GIF transparency than the PNG. If anyone can suggest what might have stopped it working with a PNG while a GIF worked just fine, Leave a comment and I’ll be happy to hear it.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting

Switch to our mobile site