mambax7/extgallery

View on GitHub
class/pear/Image/Transform/Driver/GD.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

/* vim: set expandtab tabstop=4 shiftwidth=4: */

/**
 * GD implementation for Image_Transform package
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category   Image
 * @package    Image_Transform
 * @subpackage Image_Transform_Driver_GD
 * @author     Alan Knowles <alan@akbkhome.com>
 * @author     Peter Bowyer <peter@mapledesign.co.uk>
 * @author     Philippe Jausions <Philippe.Jausions@11abacus.com>
 * @copyright  2002-2005 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id: GD.php 322661 2012-01-24 12:02:59Z clockwerx $
 * @link       http://pear.php.net/package/Image_Transform
 */

//require_once __DIR__ . '/Image/Transform.php';
require_once XOOPS_ROOT_PATH . '/modules/extgallery/class/pear/Image/Transform.php';

/**
 * GD implementation for Image_Transform package
 *
 * Usage :
 *    $img    =& Image_Transform::factory('GD');
 *    $angle  = -78;
 *    $img->load('magick.png');
 *
 *    if ($img->rotate($angle, array(
 *               'autoresize' => true,
 *               'color_mask' => array(255, 0, 0)))) {
 *        $img->addText(array(
 *               'text' => 'Rotation ' . $angle,
 *               'x' => 0,
 *               'y' => 100,
 *               'font' => '/usr/share/fonts/default/TrueType/cogb____.ttf'));
 *        $img->display();
 *    } else {
 *        echo "Error";
 *    }
 *    $img->free();
 *
 * @category   Image
 * @package    Image_Transform
 * @subpackage Image_Transform_Driver_GD
 * @author     Alan Knowles <alan@akbkhome.com>
 * @author     Peter Bowyer <peter@mapledesign.co.uk>
 * @author     Philippe Jausions <Philippe.Jausions@11abacus.com>
 * @copyright  2002-2005 The PHP Group
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    Release: @package_version@
 * @link       http://pear.php.net/package/Image_Transform
 * @since      PHP 4.0
 */
class Image_Transform_Driver_GD extends Image_Transform
{
    /**
     * Holds the image resource for manipulation
     *
     * @var resource $imageHandle
     * @access protected
     */
    public $imageHandle = null;
    /**
     * Holds the original image file
     *
     * @var resource $imageHandle
     * @access protected
     */
    public $oldImage = null;

    /**
     * Check settings
     */
    public function Image_Transform_Driver_GD()
    {
        $this->__construct();
    }

    // End function Image

    /**
     * Check settings
     *
     * @since PHP 5
     */
    public function __construct()
    {
        if (!PEAR::loadExtension('gd')) {
            $this->isError(PEAR::raiseError('GD library is not available.', IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
        } else {
            $types = imagetypes();
            if ($types & IMG_PNG) {
                $this->_supported_image_types['png'] = 'rw';
            }
            if (($types & IMG_GIF)
                || function_exists('imagegif')) {
                $this->_supported_image_types['gif'] = 'rw';
            } elseif (function_exists('imagecreatefromgif')) {
                $this->_supported_image_types['gif'] = 'rw';
            }
            if ($types & IMG_JPG) {
                $this->_supported_image_types['jpeg'] = 'rw';
            }
            if ($types & IMG_WBMP) {
                $this->_supported_image_types['wbmp'] = 'rw';
            }
            if (!$this->_supported_image_types) {
                $this->isError(PEAR::raiseError('No supported image types available', IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
            }
        }
    }

    // End function Image

    /**
     * Loads an image from file
     *
     * @param string $image filename
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     * @access public
     */
    public function load($image)
    {
        $this->free();

        $this->image = $image;
        $result      = $this->_get_image_details($image);
        if (PEAR::isError($result)) {
            return $result;
        }
        if (!$this->supportsType($this->type, 'r')) {
            return PEAR::raiseError('Image type not supported for input', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
        }

        $functionName      = 'ImageCreateFrom' . $this->type;
        $this->imageHandle = $functionName($this->image);
        if (!$this->imageHandle) {
            $this->imageHandle = null;

            return PEAR::raiseError('Error while loading image file.', IMAGE_TRANSFORM_ERROR_IO);
        }

        return true;
    }

    // End load

    /**
     * Returns the GD image handle
     *
     * @return resource
     *
     * @access public
     */
    public function getHandle()
    {
        return $this->imageHandle;
    }

    //function getHandle()

    /**
     * Adds a border of constant width around an image
     *
     * @param int    $border_width Width of border to add
     * @param string $color
     * @return bool TRUE
     * @author Peter Bowyer
     * @access public
     */
    public function addBorder($border_width = null, $color = '')
    {
        $this->new_x = $this->img_x + 2 * $border_width;
        $this->new_y = $this->img_y + 2 * $border_width;

        $new_img = $this->_createImage($new_x, $new_y, $this->true_color);

        $options = ['pencilColor', $color];
        $color   = $this->_getColor('pencilColor', $options, [0, 0, 0]);
        if ($color) {
            if ($this->true_color) {
                $c = imagecolorresolve($this->imageHandle, $color[0], $color[1], $color[2]);
                imagefill($new_img, 0, 0, $c);
            } else {
                imagecolorset($new_img, imagecolorat($new_img, 0, 0), $color[0], $color[1], $color[2]);
            }
        }
        imagecopy($new_img, $this->imageHandle, $border_width, $border_width, 0, 0, $this->img_x, $this->img_y);
        $this->imageHandle = $new_img;
        $this->resized     = true;

        return true;
    }

    /**
     * addText
     *
     * @param array $params                             Array contains options
     *                                                  array(
     *                                                  'text'  The string to draw
     *                                                  'x'     Horizontal position
     *                                                  'y'     Vertical Position
     *                                                  'color' Font color
     *                                                  'font'  Font to be used
     *                                                  'size'  Size of the fonts in pixel
     *                                                  'resize_first'  Tell if the image has to be resized
     *                                                  before drawing the text
     *                                                  )
     *
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     */
    public function addText($params = null)
    {
        $this->oldImage = $this->imageHandle;
        $params         = array_merge($this->_get_default_text_params(), $params);
        extract($params);

        $options = ['fontColor' => $color];
        $color   = $this->_getColor('fontColor', $options, [0, 0, 0]);

        $c = imagecolorresolve($this->imageHandle, $color[0], $color[1], $color[2]);

        if ('ttf' == mb_substr($font, -3)) {
            imagettftext($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text);
        } else {
            imagepstext($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text);
        }

        return true;
    }

    // End addText

    /**
     * Rotates image by the given angle
     *
     * Uses a fast rotation algorythm for custom angles
     * or lines copy for multiple of 90 degrees
     *
     * @param int   $angle         Rotation angle
     * @param array $options       array(
     *                             'canvasColor' => array(r ,g, b), named color or #rrggbb
     *                             )
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     * @access public
     * @author Pierre-Alain Joye
     */
    public function rotate($angle, $options = null)
    {
        if (0 == ($angle % 360)) {
            return true;
        }

        $color_mask = $this->_getColor('canvasColor', $options, [255, 255, 255]);

        $mask = imagecolorresolve($this->imageHandle, $color_mask[0], $color_mask[1], $color_mask[2]);

        $this->oldImage = $this->imageHandle;

        // Multiply by -1 to change the sign, so the image is rotated clockwise
        $this->imageHandle = imagerotate($this->imageHandle, $angle * -1, $mask);

        return true;
    }

    /**
     * Horizontal mirroring
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     * @see    flip()
     **/
    public function mirror()
    {
        $new_img = $this->_createImage();
        for ($x = 0; $x < $this->new_x; ++$x) {
            imagecopy($new_img, $this->imageHandle, $x, 0, $this->new_x - $x - 1, 0, 1, $this->new_y);
        }
        imagedestroy($this->imageHandle);
        $this->imageHandle = $new_img;

        return true;
    }

    /**
     * Vertical mirroring
     *
     * @return true or PEAR Error object on error
     * @access public
     * @see    mirror()
     **/
    public function flip()
    {
        $new_img = $this->_createImage();
        for ($y = 0; $y < $this->new_y; ++$y) {
            imagecopy($new_img, $this->imageHandle, 0, $y, 0, $this->new_y - $y - 1, $this->new_x, 1);
        }
        imagedestroy($this->imageHandle);
        $this->imageHandle = $new_img;

        /* for very large images we may want to use the following
           Needs to find out what is the threshhold
        for ($x = 0; $x < $this->new_x; ++$x) {
            for ($y1 = 0; $y1 < $this->new_y / 2; ++$y1) {
                $y2 = $this->new_y - 1 - $y1;
                $color1 = imagecolorat($this->imageHandle, $x, $y1);
                $color2 = imagecolorat($this->imageHandle, $x, $y2);
                imagesetpixel($this->imageHandle, $x, $y1, $color2);
                imagesetpixel($this->imageHandle, $x, $y2, $color1);
            }
        } */

        return true;
    }

    /**
     * Crops image by size and start coordinates
     *
     * @param mixed $width
     * @param mixed $height
     * @param mixed $x
     * @param mixed $y
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     * @access public
     */
    public function crop($width, $height, $x = 0, $y = 0)
    {
        // Sanity check
        if (!$this->intersects($width, $height, $x, $y)) {
            return PEAR::raiseError('Nothing to crop', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
        }
        $x       = min($this->new_x, max(0, $x));
        $y       = min($this->new_y, max(0, $y));
        $width   = min($width, $this->new_x - $x);
        $height  = min($height, $this->new_y - $y);
        $new_img = $this->_createImage($width, $height);

        if (!imagecopy($new_img, $this->imageHandle, 0, 0, $x, $y, $width, $height)) {
            imagedestroy($new_img);

            return PEAR::raiseError('Failed transformation: crop()', IMAGE_TRANSFORM_ERROR_FAILED);
        }

        $this->oldImage    = $this->imageHandle;
        $this->imageHandle = $new_img;
        $this->resized     = true;

        $this->new_x = $width;
        $this->new_y = $height;

        return true;
    }

    /**
     * Converts the image to greyscale
     *
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     * @access public
     */
    public function greyscale()
    {
        imagecopymergegray($this->imageHandle, $this->imageHandle, 0, 0, 0, 0, $this->new_x, $this->new_y, 0);

        return true;
    }

    /**
     * Resize Action
     *
     * For GD 2.01+ the new copyresampled function is used
     * It uses a bicubic interpolation algorithm to get far
     * better result.
     *
     * @param int   $new_x   New width
     * @param int   $new_y   New height
     * @param array $options Optional parameters
     *                       <ul>
     *                       <li>'scaleMethod': "pixel" or "smooth"</li>
     *                       </ul>
     *
     * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error
     * @access protected
     */
    public function _resize($new_x = null, $new_y = null, $options = null)
    {
        if (true === $this->resized) {
            return PEAR::raiseError('You have already resized the image without saving it.  Your previous resizing will be overwritten', null, PEAR_ERROR_TRIGGER, E_USER_NOTICE);
        }

        if ($this->new_x == $new_x && $this->new_y == $new_y) {
            return true;
        }

        $scaleMethod = $this->_getOption('scaleMethod', $options, 'smooth');

        // Make sure to get a true color image if doing resampled resizing
        // otherwise get the same type of image
        $trueColor = ('pixel' == $scaleMethod) ? null : true;
        $new_img   = $this->_createImage($new_x, $new_y, $trueColor);

        $icr_res = null;
        if ('pixel' != $scaleMethod && function_exists('ImageCopyResampled')) {
            $icr_res = imagecopyresampled($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y);
        }
        if (!$icr_res) {
            imagecopyresized($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y);
        }
        $this->oldImage    = $this->imageHandle;
        $this->imageHandle = $new_img;
        $this->resized     = true;

        $this->new_x = $new_x;
        $this->new_y = $new_y;

        return true;
    }

    /**
     * Adjusts the image gamma
     *
     * @param float $outputgamma
     *
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
     * @access public
     */
    public function gamma($outputgamma = 1.0)
    {
        if (1.0 != $outputgamma) {
            imagegammacorrect($this->imageHandle, 1.0, $outputgamma);
        }

        return true;
    }

    /**
     * Helper method to save to a file or output the image
     *
     * @param string $filename the name of the file to write to (blank to output)
     * @param string $type
     * @param int    $quality  output DPI, default is 75
     *
     * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error
     * @access protected
     */
    public function _generate($filename, $type = '', $quality = null)
    {
        $type    = mb_strtolower(('' == $type) ? $this->type : $type);
        $options = is_array($quality) ? $quality : [];
        switch ($type) {
            case 'jpg':
                $type = 'jpeg';
            // no break
            case 'jpeg':
                if (is_numeric($quality)) {
                    $options['quality'] = $quality;
                }
                $quality = $this->_getOption('quality', $options, 75);
                break;
        }
        if (!$this->supportsType($type, 'w')) {
            return PEAR::raiseError('Image type not supported for output', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
        }

        if ('' == $filename) {
            header('Content-type: ' . $this->getMimeType($type));
            $action = 'output image';
        } else {
            $action = 'save image to file';
        }

        $functionName = 'image' . $type;
        switch ($type) {
            case 'jpeg':
                $result = $functionName($this->imageHandle, $filename, $quality);
                break;
            default:
                if ('' == $filename) {
                    $result = $functionName($this->imageHandle);
                } else {
                    $result = $functionName($this->imageHandle, $filename);
                }
        }
        if (!$result) {
            return PEAR::raiseError('Couldn\'t ' . $action, IMAGE_TRANSFORM_ERROR_IO);
        }
        $this->imageHandle = $this->oldImage;
        if (!$this->keep_settings_on_save) {
            $this->free();
        }

        return true;
    }

    // End save

    /**
     * Displays image without saving and lose changes.
     *
     * This method adds the Content-type HTTP header
     *
     * @param string $type    (JPEG, PNG...);
     * @param int    $quality 75
     *
     * @return bool|PEAR_Error TRUE or PEAR_Error object on error
     * @access public
     */
    public function display($type = '', $quality = null)
    {
        return $this->_generate('', $type, $quality);
    }

    /**
     * Saves the image to a file
     *
     * @param string $filename  the name of the file to write to
     * @param string $type      the output format, default
     *                          is the current used format
     * @param int    $quality   default is 75
     *
     * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error
     * @access public
     */
    public function save($filename, $type = '', $quality = null)
    {
        if (!trim($filename)) {
            return PEAR::raiseError('Filename missing', IMAGE_TRANSFORM_ERROR_ARGUMENT);
        }

        return $this->_generate($filename, $type, $quality);
    }

    /**
     * Destroys image handle
     *
     * @access public
     */
    public function free()
    {
        $this->resized = false;
        if (is_resource($this->imageHandle)) {
            imagedestroy($this->imageHandle);
        }
        $this->imageHandle = null;
        if (is_resource($this->oldImage)) {
            imagedestroy($this->oldImage);
        }
        $this->oldImage = null;
    }

    /**
     * Returns a new image for temporary processing
     *
     * @param int  $width     width of the new image
     * @param int  $height    height of the new image
     * @param bool $trueColor force which type of image to create
     * @return resource a GD image resource
     * @access protected
     */
    public function _createImage($width = -1, $height = -1, $trueColor = null)
    {
        if (-1 == $width) {
            $width = $this->new_x;
        }
        if (-1 == $height) {
            $height = $this->new_y;
        }

        $new_img = null;
        if (null === $trueColor) {
            if (function_exists('imageistruecolor')) {
                $createtruecolor = imageistruecolor($this->imageHandle);
            } else {
                $createtruecolor = true;
            }
        } else {
            $createtruecolor = $trueColor;
        }
        if ($createtruecolor
            && function_exists('ImageCreateTrueColor')) {
            $new_img = @imagecreatetruecolor($width, $height);
            //GIF Transparent Patch
            if ('gif' != $this->type) {
                imagealphablending($new_img, false);
                imagesavealpha($new_img, true);
            }
            //End GIF Transparent Patch
        }
        if (!$new_img) {
            $new_img = imagecreate($width, $height);
            imagepalettecopy($new_img, $this->imageHandle);
            $color = imagecolortransparent($this->imageHandle);
            if (-1 != $color) {
                imagecolortransparent($new_img, $color);
                imagefill($new_img, 0, 0, $color);
            }
        }

        //GIF Transparent Patch
        if ('gif' == $this->type) {
            $transparencyIndex = imagecolortransparent($this->imageHandle);
            $transparencyColor = ['red' => 255, 'green' => 255, 'blue' => 255];

            if ($transparencyIndex >= 0) {
                $transparencyColor = imagecolorsforindex($this->imageHandle, $transparencyIndex);
            }

            $transparencyIndex = imagecolorallocate($new_img, $transparencyColor['red'], $transparencyColor['green'], $transparencyColor['blue']);
            imagefill($new_img, 0, 0, $transparencyIndex);
            imagecolortransparent($new_img, $transparencyIndex);
        }

        //End GIF Transparent Patch

        return $new_img;
    }
}