| View previous topic :: View next topic |
| Author |
Message |
nation-x Newbie
Joined: 28 Aug 2005 Posts: 6
|
Posted: Sun Aug 28, 2005 1:19 pm Post subject: Improved thumbnailing class |
|
|
These classes are a combination of the canvasCrop(), imgRedim(), UnsharpMask() and parts of phpThumb(). I put them together more for a friend that wanted to provide thumbnails for the images and more that people upload to his bulletin board. The site isn't working as yet so I can't give you a URL but I thought I should share this here.
I added the phpThumb() and UnsharpMask() filters in order to provide very nice looking thumbs. Adding the filters drastically increases processing time.
The class is declared as follows:
| PHP: | <?php require_once(dirname(__FILE__) . '/includes/imagehandler.php');
/* CONFIG SECTION */
$improve_thumbnails = true; // Auto Contrast, Unsharp Mask, Desaturate, White Balance
$temp_path = dirname(__FILE__) . "/temp/"; //required for improved gd_verion() function
$thumb_quality = 90;
$width = 90;
$height = 120;
/* END CONFIG SECTION */
$rd = new imgRedim(false, $improve_thumbnails, $temp_path);
$image_filename = "SOURCE_IMAGE"; // define source image here
$output_filename = "DESTINATION_IMAGE"; // define destination image here
$rd->loadImage($image_filename);
$rd->redimToSize($width, $height, true);
if ($rd->saveImage($output_filename, $thumb_quality)){
echo "Thumbnail created.";
} ?> |
Here is the source of imagehandler.php:
| PHP: | <?php //////////////////////////////////////////////////////////////
/// phpThumb() by James Heinrich <info@silisoftware.com> //
// available at http://phpthumb.sourceforge.net ///
//////////////////////////////////////////////////////////////
/// canvasCrop() by Andrew Collington <php@amnuts.com> //
// available at http://php.amnuts.com //
// ///
//////////////////////////////////////////////////////////////
define("ccTOPLEFT", 0);
define("ccTOP", 1);
define("ccTOPRIGHT", 2);
define("ccLEFT", 3);
define("ccCENTRE", 4);
define("ccCENTER", 4);
define("ccRIGHT", 5);
define("ccBOTTOMLEFT", 6);
define("ccBOTTOM", 7);
define("ccBOTTOMRIGHT", 8);
class canvasCrop{
var $_imgOrig;
var $_imgFinal;
var $_showDebug;
var $_gdVersion;
/*
*
* @return canvasCrop
* @param bool $debug
* @desc Class initializer
*/
function canvasCrop($debug = false)
{
$this->_showDebug = ($debug ? true : false);
$this->_gdVersion = $this->gd_version();
}
/*
*
* @return bool
* @param string $filename
* @desc Load an image from the file system - method based on file extension
*/
function loadImage($filename)
{
if (!@file_exists($filename)){
$this->_debug('loadImage', "The supplied file name '$filename' does not point to a readable file.");
return false;
}
$ext = strtolower($this->_getExtension($filename));
$func = "imagecreatefrom$ext";
if (!@function_exists($func)){
$this->_debug('loadImage', "That file cannot be loaded with the function '$func'.");
return false;
}
$this->_imgOrig = @$func($filename);
if ($this->_imgOrig == null){
$this->_debug('loadImage', 'The image could not be loaded.');
return false;
}
return true;
}
/*
*
* @return bool
* @param string $string
* @desc Load an image from a string (eg. from a database table)
*/
function loadImageFromString($string)
{
$this->_imgOrig = @ImageCreateFromString($string);
if (!$this->_imgOrig){
$this->_debug('loadImageFromString', 'The image could not be loaded.');
return false;
}
return true;
}
/*
*
* @return bool
* @param string $filename
* @param int $quality
* @desc Save the cropped image
*/
function saveImage($filename, $quality = 100)
{
if ($this->_imgFinal == null){
$this->_debug('saveImage', 'There is no processed image to save.');
return false;
}
$ext = strtolower($this->_getExtension($filename));
$func = "image$ext";
if (!@function_exists($func)){
$this->_debug('saveImage', "That file cannot be saved with the function '$func'.");
return false;
}
$saved = false;
if ($ext == 'png') $saved = $func($this->_imgFinal, $filename);
if ($ext == 'jpeg') $saved = $func($this->_imgFinal, $filename, $quality);
if ($saved == false){
$this->_debug('saveImage', "Could not save the output file '$filename' as a $ext.");
return false;
}
return true;
}
/*
*
* @return bool
* @param string $type
* @param int $quality
* @desc Shows the cropped image without any saving
*/
function showImage($type = 'png', $quality = 100)
{
if ($this->_imgFinal == null){
$this->_debug('showImage', 'There is no processed image to show.');
return false;
}
if ($type == 'png'){
echo @ImagePNG($this->_imgFinal);
return true;
}else if ($type == 'jpg' || $type == 'jpeg'){
echo @ImageJPEG($this->_imgFinal, '', $quality);
return true;
}else{
$this->_debug('showImage', "Could not show the output file as a $type.");
return false;
}
}
/*
*
* @return int
* @param int $x
* @param int $y
* @param int $position
* @desc Determines the dimensions to crop to if using the 'crop by size' method
*/
function cropBySize($x, $y, $position = ccCENTRE)
{
if ($x == 0){
$nx = @ImageSX($this->_imgOrig);
}else{
$nx = @ImageSX($this->_imgOrig) - $x;
}
if ($y == 0){
$ny = @ImageSY($this->_imgOrig);
}else{
$ny = @ImageSY($this->_imgOrig) - $y;
}
return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropBySize'));
}
/*
*
* @return int
* @param int $x
* @param int $y
* @param int $position
* @desc Determines the dimensions to crop to if using the 'crop to size' method
*/
function cropToSize($x, $y, $position = ccCENTRE)
{
if ($x == 0) $x = 1;
if ($y == 0) $y = 1;
return ($this->_cropSize(-1, -1, $x, $y, $position, 'cropToSize'));
}
/*
*
* @return int
* @param int $sx
* @param int $sy
* @param int $ex
* @param int $ey
* @desc Determines the dimensions to crop to if using the 'crop to dimensions' method
*/
function cropToDimensions($sx, $sy, $ex, $ey)
{
$nx = abs($ex - $sx);
$ny = abs($ey - $sy);
return ($this->_cropSize($sx, $sy, $nx, $ny, $position, 'cropToDimensions'));
}
/*
*
* @return int
* @param int $percentx
* @param int $percenty
* @param int $position
* @desc Determines the dimensions to crop to if using the 'crop by percentage' method
*/
function cropByPercent($percentx, $percenty, $position = ccCENTRE)
{
if ($percentx == 0){
$nx = @ImageSX($this->_imgOrig);
}else{
$nx = @ImageSX($this->_imgOrig) - (($percentx / 100) * @ImageSX($this->_imgOrig));
}
if ($percenty == 0){
$ny = @ImageSY($this->_imgOrig);
}else{
$ny = @ImageSY($this->_imgOrig) - (($percenty / 100) * @ImageSY($this->_imgOrig));
}
return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropByPercent'));
}
/*
*
* @return int
* @param int $percentx
* @param int $percenty
* @param int $position
* @desc Determines the dimensions to crop to if using the 'crop to percentage' method
*/
function cropToPercent($percentx, $percenty, $position = ccCENTRE)
{
if ($percentx == 0){
$nx = @ImageSX($this->_imgOrig);
}else{
$nx = ($percentx / 100) * @ImageSX($this->_imgOrig);
}
if ($percenty == 0){
$ny = @ImageSY($this->_imgOrig);
}else{
$ny = ($percenty / 100) * @ImageSY($this->_imgOrig);
}
return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropByPercent'));
}
/*
*
* @return bool
* @param int $threshold
* @desc Determines the dimensions to crop to if using the 'automatic crop by threshold' method
*/
function cropByAuto($threshold = 254)
{
if ($threshold < 0) $threshold = 0;
if ($threshold > 255) $threshold = 255;
$sizex = @ImageSX($this->_imgOrig);
$sizey = @ImageSY($this->_imgOrig);
$sx = $sy = $ex = $ey = -1;
for ($y = 0; $y < $sizey; $y++){
for ($x = 0; $x < $sizex; $x++){
if ($threshold >= $this->_getThresholdValue($this->_imgOrig, $x, $y)){
if ($sy == -1) $sy = $y;
else $ey = $y;
if ($sx == -1) $sx = $x;
else{
if ($x < $sx) $sx = $x;
else if ($x > $ex) $ex = $x;
}
}
}
}
$nx = abs($ex - $sx);
$ny = abs($ey - $sy);
return ($this->_cropSize($sx, $sy, $nx, $ny, ccTOPLEFT, 'cropByAuto'));
}
/*
*
* @return void
* @desc Destroy the resources used by the images
*/
function flushImages()
{
@ImageDestroy($this->_imgOrig);
@ImageDestroy($this->_imgFinal);
$this->_imgOrig = $this->_imgFinal = null;
}
/*
*
* @return bool
* @param int $ox Original image width
* @param int $oy Original image height
* @param int $nx New width
* @param int $ny New height
* @param int $position Where to place the crop
* @param string $function Name of the calling function
* @desc Creates the cropped image based on passed parameters
*/
function _cropSize($ox, $oy, $nx, $ny, $position, $function)
{
if ($this->_imgOrig == null){
$this->_debug($function, 'The original image has not been loaded.');
return false;
}
if (($nx <= 0) || ($ny <= 0)){
$this->_debug($function, 'The image could not be cropped because the size given is not valid.');
return false;
}
if (($nx > @ImageSX($this->_imgOrig)) || ($ny > @ImageSY($this->_imgOrig))){
$this->_debug($function, 'The image could not be cropped because the size given is larger than the original image.');
return false;
}
if ($ox == -1 || $oy == -1){
list($ox, $oy) = $this->_getCopyPosition($nx, $ny, $position);
}
if ($this->_gdVersion >= 2){
$this->_imgFinal = @ImageCreateTrueColor($nx, $ny);
imagefill($this->_imgFinal,0,0,imagecolorallocate($this->_imgFinal, 255, 255, 255));
@imagecopyresampled($this->_imgFinal, $this->_imgOrig, 0, 0, $ox, $oy, $nx, $ny, $nx, $ny);
}else{
$this->_imgFinal = @ImageCreate($nx, $ny);
@ImageCopyResized($this->_imgFinal, $this->_imgOrig, 0, 0, $ox, $oy, $nx, $ny, $nx, $ny);
}
return true;
}
/*
*
* @return array
* @param int $nx
* @param int $ny
* @param int $position
* @desc Determines dimensions of the crop
*/
function _getCopyPosition($nx, $ny, $position)
{
$ox = @ImageSX($this->_imgOrig);
$oy = @ImageSY($this->_imgOrig);
switch ($position){
case ccTOPLEFT:
return array(0, 0);
case ccTOP:
return array(ceil(($ox - $nx) / 2), 0);
case ccTOPRIGHT:
return array(($ox - $nx), 0);
case ccLEFT:
return array(0, ceil(($oy - $ny) / 2));
case ccCENTRE:
return array(ceil(($ox - $nx) / 2), ceil(($oy - $ny) / 2));
case ccRIGHT:
return array(($ox - $nx), ceil(($oy - $ny) / 2));
case ccBOTTOMLEFT:
return array(0, ($oy - $ny));
case ccBOTTOM:
return array(ceil(($ox - $nx) / 2), ($oy - $ny));
case ccBOTTOMRIGHT:
return array(($ox - $nx), ($oy - $ny));
}
}
/*
*
* @return float
* @param resource $im
* @param int $x
* @param int $y
* @desc Determines the intensity value of a pixel at the passed co-ordinates
*/
function _getThresholdValue($im, $x, $y)
{
$rgb = ImageColorAt($im, $x, $y);
$r = ($rgb >> 16) &0xFF;
$g = ($rgb >> 8) &0xFF;
$b = $rgb &0xFF;
$intensity = ($r + $g + $b) / 3;
return $intensity;
}
/*
*
* @return string
* @param string $filename
* @desc Get the extension of a file name
*/
function _getExtension($filename)
{
$ext = @strtolower(@substr($filename, (@strrpos($filename, ".") ? @strrpos($filename, ".") + 1 : @strlen($filename)), @strlen($filename)));
return ($ext == 'jpg') ? 'jpeg' : $ext;
}
/*
*
* @return void
* @param string $function
* @param string $string
* @desc Shows debugging information
*/
function _debug($function, $string)
{
if ($this->_showDebug){
echo "<p><strong style=\"color:#FF0000\">Error in function $function:</strong> $string</p>\n";
}
}
function version_compare_replacement_sub($version1, $version2, $operator = '')
{
// If you specify the third optional operator argument, you can test for a particular relationship.
// The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
// Using this argument, the function will return 1 if the relationship is the one specified by the operator, 0 otherwise.
// If a part contains special version strings these are handled in the following order: dev < (alpha = a) < (beta = b) < RC < pl
static $versiontype_lookup = array();
if (empty($versiontype_lookup)){
$versiontype_lookup['dev'] = 10001;
$versiontype_lookup['a'] = 10002;
$versiontype_lookup['alpha'] = 10002;
$versiontype_lookup['b'] = 10003;
$versiontype_lookup['beta'] = 10003;
$versiontype_lookup['RC'] = 10004;
$versiontype_lookup['pl'] = 10005;
}
if (isset($versiontype_lookup[$version1])){
$version1 = $versiontype_lookup[$version1];
}
if (isset($versiontype_lookup[$version2])){
$version2 = $versiontype_lookup[$version2];
}
switch ($operator){
case '<':
case 'lt':
return intval($version1 < $version2);
break;
case '<=':
case 'le':
return intval($version1 <= $version2);
break;
case '>':
case 'gt':
return intval($version1 > $version2);
break;
case '>=':
case 'ge':
return intval($version1 >= $version2);
break;
case '==':
case '=':
case 'eq':
return intval($version1 == $version2);
break;
case '!=':
case '<>':
case 'ne':
return intval($version1 != $version2);
break;
}
if ($version1 == $version2){
return 0;
}elseif ($version1 < $version2){
return -1;
}
return 1;
}
function version_compare_replacement($version1, $version2, $operator = '')
{
if (function_exists('version_compare')){
// built into PHP v4.1.0+
return version_compare($version1, $version2, $operator);
}
// The function first replaces _, - and + with a dot . in the version strings
$version1 = strtr($version1, '_-+', '...');
$version2 = strtr($version2, '_-+', '...');
// and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'.
// Then it splits the results like if you were using explode('.',$ver). Then it compares the parts starting from left to right.
$version1 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version1);
$version2 = eregi_replace('([0-9]+)([A-Z]+)([0-9]+)', '\\1.\\2.\\3', $version2);
$parts1 = explode('.', $version1);
$parts2 = explode('.', $version1);
$parts_count = max(count($parts1), count($parts2));
for ($i = 0; $i < $parts_count; $i++){
$comparison = $this->version_compare_replacement_sub($version1, $version2, $operator);
if ($comparison != 0){
return $comparison;
}
}
return 0;
}
function gd_version($fullstring = false)
{
static $cache_gd_version = array();
if (empty($cache_gd_version)){
$gd_info = $this->gd_info();
if (substr($gd_info['GD Version'], 0, strlen('bundled (')) == 'bundled ('){
$cache_gd_version[1] = $gd_info['GD Version']; // e.g. "bundled (2.0.15 compatible)"
$cache_gd_version[0] = (float) substr($gd_info['GD Version'], strlen('bundled ('), 3); // e.g. "2.0" (not "bundled (2.0.15 compatible)")
}else{
$cache_gd_version[1] = $gd_info['GD Version']; // e.g. "1.6.2 or higher"
$cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher")
}
}
return $cache_gd_version[intval($fullstring)];
}
function gd_info()
{
if (function_exists('gd_info')){
// built into PHP v4.3.0+ (with bundled GD2 library)
return gd_info();
}
static $gd_info = array();
if (empty($gd_info)){
// based on code by johnschaefer at gmx dot de
// from PHP help on gd_info()
$gd_info = array('GD Version' => '',
'FreeType Support' => false,
'FreeType Linkage' => '',
'T1Lib Support' => false,
'GIF Read Support' => false,
'GIF Create Support' => false,
'JPG Support' => false,
'PNG Support' => false,
'WBMP Support' => false,
'XBM Support' => false
);
$phpinfo_array = $this->phpinfo_array();
foreach ($phpinfo_array as $line){
$line = trim(strip_tags($line));
foreach ($gd_info as $key => $value){
// if (strpos($line, $key) !== false) {
if (strpos($line, $key) === 0){
$newvalue = trim(str_replace($key, '', $line));
$gd_info[$key] = $newvalue;
}
}
}
if (empty($gd_info['GD Version'])){
// probable cause: "phpinfo() disabled for security reasons"
if (function_exists('ImageTypes')){
$imagetypes = ImageTypes();
if ($imagetypes &IMG_PNG){
$gd_info['PNG Support'] = true;
}
if ($imagetypes &IMG_GIF){
$gd_info['GIF Create Support'] = true;
}
if ($imagetypes &IMG_JPG){
$gd_info['JPG Support'] = true;
}
if ($imagetypes &IMG_WBMP){
$gd_info['WBMP Support'] = true;
}
}
// to determine capability of GIF creation, try to use ImageCreateFromGIF on a 1px GIF
if (function_exists('ImageCreateFromGIF')){
if ($tempfilename = $this->GetTempName()){
if ($fp_tempfile = @fopen($tempfilename, 'wb')){
fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string
fclose($fp_tempfile);
// if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not
$gd_info['GIF Read Support'] = (bool) @ImageCreateFromGIF($tempfilename);
}
unlink($tempfilename);
}
}
if (function_exists('ImageCreateTrueColor') && @ImageCreateTrueColor(1, 1)){
$gd_info['GD Version'] = '2.0.1 or higher (assumed)';
}elseif (function_exists('ImageCreate') && @ImageCreate(1, 1)){
$gd_info['GD Version'] = '1.6.0 or higher (assumed)';
}
}
}
return $gd_info;
}
function phpinfo_array()
{
static $phpinfo_array = array();
if (empty($phpinfo_array)){
ob_start();
phpinfo();
$phpinfo = ob_get_contents();
ob_end_clean();
$phpinfo_array = explode("\n", $phpinfo);
}
return $phpinfo_array;
}
}
class imgRedim extends canvasCrop{
var $temp_directory;
var $improve_thumbs;
function imgRedim($debug = false, $filters = false, $temp_path)
{
$this->canvasCrop($debug);
if (empty($temp_path)){
$this->_debug($function, 'You must specify a temp directory.');
}
$this->improve_thumbs = $filters;
}
function redimToSize($x, $y, $crop = false)
{
return ($this->_reSize($x, $y, $crop, 'redimToSize'));
}
function _reSize($nx, $ny, $crop = false, $function)
{
if ($this->_imgOrig == null){
$this->_debug($function, 'The original image has not been loaded.');
return false;
}
if (($nx <= 0) || ($ny <= 0)){
$this->_debug($function, 'The image could not be resized because the size given is not valid.');
return false;
}
$ox = @ImageSX($this->_imgOrig);
$oy = @ImageSY($this->_imgOrig);
$nnx = $nx;
$nny = $ny;
if (($nx > $ox) || ($ny > $oy)){
$this->_debug($function, 'The image could not be resized because the size given is larger than the original image.');
return false;
}
if ($ox > $oy){
$nx = $ox * $ny / $oy;
}elseif ($ox < $oy){
$ny = $oy * $nx / $ox;
}
if ($this->_gdVersion >= 2){
$this->_imgFinal = @ImageCreateTrueColor($nx, $ny);
imagefill($this->_imgFinal, 0, 0, imagecolorallocate($this->_imgFinal, 255, 255, 255));
@ImageCopyResampled($this->_imgFinal, $this->_imgOrig, 0, 0, 0, 0, $nx, $ny, $ox, $oy);
}else{
$this->_imgFinal = @ImageCreate($nx, $ny);
@ImageCopyResized($this->_imgFinal, $this->_imgOrig, 0, 0, 0, 0, $nx, $ny, $ox, $oy);
}
$cropNecessary = false;
if (($nx != $nnx) || ($ny != $nny)) {
$cropNecessary = true;
}
if ($crop && $cropNecessary) {
$_imgOrigCopy = $this->_imgOrig;
$this->_imgOrig = $this->_imgFinal;
$result = $this->cropToSize($nnx, $nny);
if ($this->improve_thumbs){
$result = $this->WhiteBalance($this->_imgFinal);
$result = $this->AutoContrast($this->_imgFinal);
$result = $this->UnSharpMask($this->_imgFinal, "80|0.5|3");
$result = $this->Desaturate($this->_imgFinal, -10);
}
$this->_imgOrig = $_imgOrigCopy;
unset($_imgOrigCopy);
return $result;
}
if ($this->improve_thumbs){
$result = $this->WhiteBalance($this->_imgFinal);
$result = $this->AutoContrast($this->_imgFinal);
$result = $this->UnSharpMask($this->_imgFinal, "80|0.5|3");
$result = $this->Desaturate($this->_imgFinal, -10);
}
return true;
}
function IsHexColor($HexColorString)
{
return eregi('^[0-9A-F]{6}$', $HexColorString);
}
function GetPixelColor(&$img, $x, $y)
{
return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
}
function GrayscalePixel($OriginalPixel)
{
$gray = $this->GrayscaleValue($OriginalPixel['red'], $OriginalPixel['green'], $OriginalPixel['blue']);
return array('red' => $gray, 'green' => $gray, 'blue' => $gray);
}
function GrayscaleValue($r, $g, $b)
{
return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
}
function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha = false)
{
if ($this->version_compare_replacement(phpversion(), '4.3.2', '>=') && ($alpha !== false)){
return ImageColorAllocateAlpha($gdimg_hexcolorallocate, $R, $G, $B, intval($alpha));
}else{
return ImageColorAllocate($gdimg_hexcolorallocate, $R, $G, $B);
}
}
function Desaturate(&$gdimg, $amount, $color = '')
{
return $this->Colorize($gdimg, $amount, ($this->IsHexColor($color) ? $color : 'gray'));
}
//////////////////////////////////////////////////////////////
//// ////
//// p h p U n s h a r p M a s k ////
//// ////
//// Unsharp mask algorithm by Torstein H?nsi 2003. ////
//// thoensi_at_netcom_dot_no ////
//// Please leave this notice. ////
//// ////
//////////////////////////////////////////////////////////////
/// From: http://vikjavev.no/hovudsida/umtestside.php //
//////////////////////////////////////////////////////////////
function UnSharpMask(&$img, $parameter)
{
@list($amount, $radius, $threshold) = explode('|', $parameter);
$amount = ($amount ? $amount : 80);
$radius = ($radius ? $radius : 0.5);
$threshold = (strlen($threshold) ? $threshold : 3);
if ($this->gd_version() >= 2.0){
$this->applyUnsharpMask($img, $amount, $radius, $threshold);
}else{
$this->_debug($function, 'Skipping unsharp mask because gd_version is "' . $this->gd_version() . '"', __FILE__, __LINE__);
return false;
}
}
function applyUnsharpMask(&$img, $amount, $radius, $threshold)
{
// $img is an image that is already created within php using
// imgcreatetruecolor. No url! $img must be a truecolor image.
// Attempt to calibrate the parameters to Photoshop:
$amount = min($amount, 500);
$amount = $amount * 0.016;
if ($amount == 0){
return true;
}
$radius = min($radius, 50);
$radius = $radius * 2;
$threshold = min($threshold, 255);
$radius = abs(round($radius)); // Only integers make sense.
if ($radius == 0){
return true;
}
$w = ImageSX($img);
$h = ImageSY($img);
$imgCanvas = ImageCreateTrueColor($w, $h);
$imgCanvas2 = ImageCreateTrueColor($w, $h);
$imgBlur = ImageCreateTrueColor($w, $h);
$imgBlur2 = ImageCreateTrueColor($w, $h);
ImageCopy($imgCanvas, $img, 0, 0, 0, 0, $w, $h);
ImageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
// Gaussian blur matrix:
// 1 2 1
// 2 4 2
// 1 2 1
// ////////////////////////////////////////////////
// Move copies of the image around one pixel at the time and merge them with weight
// according to the matrix. The same matrix is simply repeated for higher radii.
for ($i = 0; $i < $radius; $i++){
ImageCopy ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1); // up left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50); // down right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25); // up right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25); // right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20); // up
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667); // down
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50); // center
ImageCopy ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
// During the loop above the blurred copy darkens, possibly due to a roundoff
// error. Therefore the sharp picture has to go through the same loop to
// produce a similar image for comparison. This is not a good thing, as processing
// time increases heavily.
ImageCopy ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopy ($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
}
// Calculate the difference between the blurred pixels and the original
// and set the pixels
for ($x = 0; $x < $w; $x++){ // each row
for ($y = 0; $y < $h; $y++){ // each pixel
$rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
$rOrig = (($rgbOrig >> 16) &0xFF);
$gOrig = (($rgbOrig >> 8) &0xFF);
$bOrig = ($rgbOrig &0xFF);
$rgbBlur = ImageColorAt($imgCanvas, $x, $y);
$rBlur = (($rgbBlur >> 16) &0xFF);
$gBlur = (($rgbBlur >> 8) &0xFF);
$bBlur = ($rgbBlur &0xFF);
// When the masked pixels differ less from the original
// than the threshold specifies, they are set to their original value.
$rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig;
$gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig;
$bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig;
if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)){
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
ImageSetPixel($img, $x, $y, $pixCol);
}
}
}
ImageDestroy($imgCanvas);
ImageDestroy($imgCanvas2);
ImageDestroy($imgBlur);
ImageDestroy($imgBlur2);
return true;
}
function Colorize(&$gdimg, $amount, $targetColor)
{
$amount = (is_numeric($amount) ? $amount : 25);
$targetColor = ($this->IsHexColor($targetColor) ? $targetColor : 'gray');
// overridden below for grayscale
if ($targetColor != 'gray'){
$TargetPixel['red'] = hexdec(substr($targetColor, 0, 2));
$TargetPixel['green'] = hexdec(substr($targetColor, 2, 2));
$TargetPixel['blue'] = hexdec(substr($targetColor, 4, 2));
}
for ($x = 0; $x < ImageSX($gdimg); $x++){
for ($y = 0; $y < ImageSY($gdimg); $y++){
$OriginalPixel = $this->GetPixelColor($gdimg, $x, $y);
if ($targetColor == 'gray'){
$TargetPixel = $this->GrayscalePixel($OriginalPixel);
}
foreach ($TargetPixel as $key => $value){
$NewPixel[$key] = round(max(0, min(255, ($OriginalPixel[$key] * ((100 - $amount) / 100)) + ($TargetPixel[$key] * ($amount / 100)))));
}
// $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']);
$newColor = ImageColorAllocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']);
ImageSetPixel($gdimg, $x, $y, $newColor);
}
}
return true;
}
function HistogramAnalysis(&$gdimg, $calculateGray = false)
{
$ImageSX = ImageSX($gdimg);
$ImageSY = ImageSY($gdimg);
for ($x = 0; $x < $ImageSX; $x++){
for ($y = 0; $y < $ImageSY; $y++){
$OriginalPixel = $this->GetPixelColor($gdimg, $x, $y);
@$Analysis['red'][$OriginalPixel['red']]++;
@$Analysis['green'][$OriginalPixel['green']]++;
@$Analysis['blue'][$OriginalPixel['blue']]++;
@$Analysis['alpha'][$OriginalPixel['alpha']]++;
if ($calculateGray){
$GrayPixel = $this->GrayscalePixel($OriginalPixel);
@$Analysis['gray'][$GrayPixel['red']]++;
}
}
}
$keys = array('red', 'green', 'blue', 'alpha');
if ($calculateGray){
$keys[] = 'gray';
}
foreach ($keys as $key){
ksort($Analysis[$key]);
}
return $Analysis;
}
function WhiteBalance(&$gdimg, $targetColor = '')
{
if ($this->IsHexColor($targetColor)){
$targetPixel = array('red' => hexdec(substr($targetColor, 0, 2)),
'green' => hexdec(substr($targetColor, 2, 2)),
'blue' => hexdec(substr($targetColor, 4, 2))
);
}else{
$Analysis = $this->HistogramAnalysis($gdimg, false);
$targetPixel = array('red' => max(array_keys($Analysis['red'])),
'green' => max(array_keys($Analysis['green'])),
'blue' => max(array_keys($Analysis['blue']))
);
}
$grayValue = $this->GrayscaleValue($targetPixel['red'], $targetPixel['green'], $targetPixel['blue']);
$scaleR = $grayValue / $targetPixel['red'];
$scaleG = $grayValue / $targetPixel['green'];
$scaleB = $grayValue / $targetPixel['blue'];
for ($x = 0; $x < ImageSX($gdimg); $x++){
for ($y = 0; $y < ImageSY($gdimg); $y++){
$currentPixel = $this->GetPixelColor($gdimg, $x, $y);
$newColor = $this->ImageColorAllocateAlphaSafe($gdimg,
max(0, min(255, round($currentPixel['red'] * $scaleR))),
max(0, min(255, round($currentPixel['green'] * $scaleG))),
max(0, min(255, round($currentPixel['blue'] * $scaleB))),
$currentPixel['alpha']
);
ImageSetPixel($gdimg, $x, $y, $newColor);
}
}
return true;
}
function AutoContrast(&$gdimg, $band = '*', $min = -1, $max = -1)
{
// equivalent of "Auto Contrast" in Adobe Photoshop
$Analysis = $this->HistogramAnalysis($gdimg, true);
$keys = array('r' => 'red', 'g' => 'green', 'b' => 'blue', 'a' => 'alpha', '*' => 'gray');
if (!isset($keys[$band])){
return false;
}
$key = $keys[$band];
// If the absolute brightest and darkest pixels are used then one random
// pixel in the image could throw off the whole system. Instead, count up/down
// from the limit and allow 0.1% of brightest/darkest pixels to be clipped to min/max
$clip_threshold = ImageSX($gdimg) * ImageSX($gdimg) * 0.001;
if ($min >= 0){
$range_min = min($min, 255);
}else{
$countsum = 0;
for ($i = 0; $i <= 255; $i++){
$countsum += @$Analysis[$key][$i];
if ($countsum >= $clip_threshold){
$range_min = $i - 1;
break;
}
}
$range_min = max($range_min, 0);
}
if ($max >= 0){
$range_max = max($max, 255);
}else{
$countsum = 0;
$threshold = ImageSX($gdimg) * ImageSX($gdimg) * 0.001; // 0.1% of brightest and darkest pixels can be clipped
for ($i = 255; $i >= 0; $i--){
$countsum += @$Analysis[$key][$i];
if ($countsum >= $clip_threshold){
$range_max = $i + 1;
break;
}
}
$range_max = min($range_max, 255);
}
$range_scale = (($range_max == $range_min) ? 1 : (255 / ($range_max - $range_min)));
if (($range_min == 0) && ($range_max == 255)){
// no adjustment neccesary - don't waste CPU time!
return true;
}
$ImageSX = ImageSX($gdimg);
$ImageSY = ImageSY($gdimg);
for ($x = 0; $x < $ImageSX; $x++){
for ($y = 0; $y < $ImageSY; $y++){
$OriginalPixel = $this->GetPixelColor($gdimg, $x, $y);
if ($band == '*'){
$new['red'] = min(255, max(0, ($OriginalPixel['red'] - $range_min) * $range_scale));
$new['green'] = min(255, max(0, ($OriginalPixel['green'] - $range_min) * $range_scale));
$new['blue'] = min(255, max(0, ($OriginalPixel['blue'] - $range_min) * $range_scale));
$new['alpha'] = min(255, max(0, ($OriginalPixel['alpha'] - $range_min) * $range_scale));
}else{
$new = $OriginalPixel;
$new[$key] = min(255, max(0, ($OriginalPixel[$key] - $range_min) * $range_scale));
}
$newColor = $this->ImageColorAllocateAlphaSafe($gdimg, $new['red'], $new['green'], $new['blue'], $new['alpha']);
ImageSetPixel($gdimg, $x, $y, $newColor);
}
}
return true;
}
function GetTempName()
{
return tempnam($this->temp_directory, 'temp_thumb');
}
} ?> |
|
|
| Back to top |
|
 |
amnuts Site Admin

Joined: 01 Sep 2002 Posts: 662 Location: East Sussex, England
|
|
| Back to top |
|
 |
nation-x Newbie
Joined: 28 Aug 2005 Posts: 6
|
Posted: Tue Aug 30, 2005 8:33 am Post subject: |
|
|
I like your classes, Andy. phpThumb is nice but I found that the shear immensity of it causes speed issues. I also had alot of other issues with it hanging on certain methods. He does have some great filters but I borrowed the ones that I thought had the best practical uses... other than those I would recommend your class for image effects. |
|
| Back to top |
|
 |
amnuts Site Admin

Joined: 01 Sep 2002 Posts: 662 Location: East Sussex, England
|
Posted: Tue Aug 30, 2005 9:32 am Post subject: |
|
|
That's very nice of you to sa. Cheers, mate!  |
|
| Back to top |
|
 |
nation-x Newbie
Joined: 28 Aug 2005 Posts: 6
|
Posted: Tue Jul 11, 2006 10:16 am Post subject: |
|
|
| bump for other people |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|