I created this to stack two images horizontally together, to make before and after images into one image. Designed for WordPress, but could be tweaked for other systems.

/**
 * use two images to create one image
 */
function msdlab_combine_images($params){
	extract(
		array_merge(
			array(
				'images' => array(),
				'orientation' => 'horizontal',
				'output_format' => 'jpg'
			)
		,$params)
	);
	//get info on each image
	foreach($images AS $key => $url){
		$imgs[$key]['url'] = $url;
		$imgs[$key]['path'] = $path = realpath(preg_replace('@'.site_url().'@i','.',$url));
		$imgs[$key]['file'] = $file = basename($path);
		$imgs[$key]['filename'] = $filename = preg_replace('/\.(?:jpg|jpeg|bmp|png|gif)/i','',$file);
		$imgs[$key]['info'] = $info = getimagesize($path);
		$comboname .= $filename;
		switch($orientation){
			case 'horizontal':
				$combowidth = $combowidth + $info[0];
				$comboheight = $info[1]>$comboheight?$info[1]:$comboheight;
				break;
			case 'vertical':
				$combowidth = $info[0]>$combowidth?$info[0]:$combowidth;
				$comboheight = $comboheight + $info[1];
				break;
		}
	}
	$comboname .= '.'.$output_format;
	$upload_dir = wp_upload_dir();
	//check if it exists
	if(file_exists($upload_dir['basedir'].'/'.$comboname)){
		return $upload_dir['baseurl'].'/'.$comboname;
	}
	//create a holding image
	$newimg = @imagecreatetruecolor($combowidth, $comboheight);
	foreach($imgs AS $key => $img){
		switch($img['info']['mime']){
			case 'image/jpeg':
				$src = @imagecreatefromjpeg($img['path']);
				break;
			case 'image/png':
				$src = @imagecreatefrompng($img['path']);
				break;
			case 'image/gif':
				$src = @imagecreatefromgif($img['path']);
				break;
			default:
				return 'unsupported file type';
		}
		//copy things
		switch($orientation){
			case 'horizontal':
				imagecopy($newimg, $src, $imgs[$key-1]['info'][0], 0, 0, 0, $img['info'][0], $img['info'][1]);
				break;
			case 'vertical':
				imagecopy($newimg, $src, 0, $imgs[$key-1]['info'][1], 0, 0, $img['info'][0], $img['info'][1]);
				break;
		}
		// free up memory
		imagedestroy($src);
	}
	// Save the image
	imagejpeg($newimg, $upload_dir['basedir'].'/'.$comboname);

	// Free up memory
	imagedestroy($newimg);

	//return the image
	return $upload_dir['baseurl'].'/'.$comboname;
}