<?php
class ScmsImage extends canvasCrop
{
    function scmsImage($debug = FALSE)
    {
        $this->canvasCrop($debug);
    }

    function applyChangesToOriginal() {
         $this->_imgOrig = $this->_imgFinal;
    }

    function getInfo() {
        return array( @ImageSX($this->_imgOrig) , @ImageSY($this->_imgOrig) );
    }

    function redimToPercent($percentx, $percenty, $crop = FALSE)
    {
        $percentx = ($percentx > 100)?100:$percentx;
        $percenty = ($percenty > 100)?100:$percenty;
        $x = (($percentx / 100) * @ImageSX($this->_imgOrig));
        $y = (($percenty / 100) * @ImageSY($this->_imgOrig));
        return ($this->_reSize($x, $y, $crop, 'redimToPercent'));
    }

    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);
            @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);
        }
        if($crop AND $nx!=$nnx AND $ny!=$nny)
        {
            $_imgOrigCopy = $this->_imgOrig;
            $this->_imgOrig = $this->_imgFinal;
            $result = $this->cropToSize($nnx, $nny);
            $this->_imgOrig = $_imgOrigCopy;
            unset($_imgOrigCopy);
            return $result;
        }
        return true;
    }
}

//
// class.cropcanvas.php
// version 1.2.0, 26th November, 2003
//
// Description
//
// This is a class allows you to crop an image in a variety of ways.
// You can crop in an absolute or relative way (to a certain size or
// by a certain size), both as a pixel number or a percentage.  You
// can also save or display the cropped image.  The cropping can be
// done in 9 different positions: top left, top, top right, left,
// centre, right, bottom left, bottom, or bottom right.  Or you can
// crop automatically based on a threshold limit.  The original
// image can be loaded from the file system or from a string (for
// example, data returned from a database.)
//
// Author
//
// Andrew Collington, 2003
// php@amnuts.com, http://php.amnuts.com/
//
//
// Feedback
//
// There is message board at the following address:
//
//    http://php.amnuts.com/forums/index.php
//
// Please use that to post up any comments, questions, bug reports, etc.  You
// can also use the board to show off your use of the script.
//
// Support
//
// If you like this script, or any of my others, then please take a moment
// to consider giving a donation.  This will encourage me to make updates and
// create new scripts which I would make available to you.  If you would like
// to donate anything, then there is a link from my website to PayPal.
//
// Example of use
//
//	require 'class.cropcanvas.php';
//	$cc = new canvasCrop();
//
//	$cc->loadImage('original1.png');
//	$cc->cropBySize(100, 100, ccBOTTOMRIGHT);
//	$cc->saveImage('final1.png');
//
//	$cc->flushImages();
//
//	$cc->loadImage('original2.png');
//	$cc->cropByPercent(15, 50, ccCENTER);
//	$cc->saveImage('final2.jpg', 90);
//
//	$cc->flushImages();
//
//	$cc->loadImage('original3.png');
//	$cc->cropToDimensions(67, 37, 420, 255);
//	$cc->showImage('png');
//


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
{
    public $_imgOrig;
    public $_imgFinal;
    public $_showDebug;
    public $_gdVersion;


    /**
    * @return canvasCrop
    * @param bool $debug
    * @desc Class initializer
    */
    function canvasCrop($debug = false)
    {
        $this->_showDebug = ($debug ? true : false);
        $this->_gdVersion = (function_exists('imagecreatetruecolor')) ? 2 : 1;
    }


    /**
    * @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)
    {
        $sx = ($sx < 0)?0:$sx;
        $sy = ($sy < 0)?0:$sy;
        $ex = ($ex > @ImageSX($this->_imgOrig))?@ImageSX($this->_imgOrig):$ex;
        $ey = ($ey > @ImageSY($this->_imgOrig))?@ImageSY($this->_imgOrig):$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);
            @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";
        }
    }


    /**
    * @return array
    * @desc Try to ascertain what the version of GD being used is, based on phpinfo output
    */
    function _getGDVersion()
    {
        static $version = array();

        if (empty($version))
        {
            ob_start();
            phpinfo();
            $buffer = ob_get_contents();
            ob_end_clean();
            if (preg_match("|<B>GD Version</B></td><TD ALIGN=\"left\">([^<]*)</td>|i", $buffer, $matches))
            {
                $version = explode('.', $matches[1]);
            }
            else if (preg_match("|GD Version </td><td class=\"v\">bundled \(([^ ]*)|i", $buffer, $matches))
            {
                $version = explode('.', $matches[1]);
            }
            else if (preg_match("|GD Version </td><td class=\"v\">([^ ]*)|i", $buffer, $matches))
            {
                $version = explode('.', $matches[1]);
            }
        }
        return $version;
    }

}

?>