Image_Transform
[ class tree: Image_Transform ] [ index: Image_Transform ] [ all elements ]

Source for file GD.php

Documentation is available at GD.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  4.  
  5. /**
  6.  * GD implementation for Image_Transform package
  7.  *
  8.  * PHP versions 4 and 5
  9.  *
  10.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  11.  * that is available through the world-wide-web at the following URI:
  12.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  13.  * the PHP License and are unable to obtain it through the web, please
  14.  * send a note to license@php.net so we can mail you a copy immediately.
  15.  *
  16.  * @category   Image
  17.  * @package    Image_Transform
  18.  * @subpackage Image_Transform_Driver_GD
  19.  * @author     Alan Knowles <alan@akbkhome.com>
  20.  * @author     Peter Bowyer <peter@mapledesign.co.uk>
  21.  * @author     Philippe Jausions <Philippe.Jausions@11abacus.com>
  22.  * @copyright  2002-2005 The PHP Group
  23.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  24.  * @version    CVS: $Id: GD.php,v 1.32 2007/04/19 16:36:09 dufuz Exp $
  25.  * @link       http://pear.php.net/package/Image_Transform
  26.  */
  27.  
  28. require_once 'Image/Transform.php';
  29.  
  30. /**
  31.  * GD implementation for Image_Transform package
  32.  *
  33.  * Usage :
  34.  *    $img    =& Image_Transform::factory('GD');
  35.  *    $angle  = -78;
  36.  *    $img->load('magick.png');
  37.  *
  38.  *    if ($img->rotate($angle, array(
  39.  *               'autoresize' => true,
  40.  *               'color_mask' => array(255, 0, 0)))) {
  41.  *        $img->addText(array(
  42.  *               'text' => 'Rotation ' . $angle,
  43.  *               'x' => 0,
  44.  *               'y' => 100,
  45.  *               'font' => '/usr/share/fonts/default/TrueType/cogb____.ttf'));
  46.  *        $img->display();
  47.  *    } else {
  48.  *        echo "Error";
  49.  *    }
  50.  *    $img->free();
  51.  *
  52.  * @category   Image
  53.  * @package    Image_Transform
  54.  * @subpackage Image_Transform_Driver_GD
  55.  * @author     Alan Knowles <alan@akbkhome.com>
  56.  * @author     Peter Bowyer <peter@mapledesign.co.uk>
  57.  * @author     Philippe Jausions <Philippe.Jausions@11abacus.com>
  58.  * @copyright  2002-2005 The PHP Group
  59.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  60.  * @version    Release: @package_version@
  61.  * @link       http://pear.php.net/package/Image_Transform
  62.  * @since      PHP 4.0
  63.  */
  64. {
  65.     /**
  66.      * Holds the image resource for manipulation
  67.      *
  68.      * @var resource $imageHandle 
  69.      * @access protected
  70.      */
  71.     var $imageHandle = null;
  72.  
  73.     /**
  74.      * Holds the original image file
  75.      *
  76.      * @var resource $imageHandle 
  77.      * @access protected
  78.      */
  79.     var $old_image = null;
  80.  
  81.     /**
  82.      * Check settings
  83.      */
  84.     function Image_Transform_Driver_GD()
  85.     {
  86.         $this->__construct();
  87.     // End function Image
  88.  
  89.     
  90.     /**
  91.      * Check settings
  92.      *
  93.      * @since PHP 5
  94.      */
  95.     function __construct()
  96.     {
  97.         if (!PEAR::loadExtension('gd')) {
  98.             $this->isError(PEAR::raiseError("GD library is not available.",
  99.                 IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
  100.         else {
  101.             $types = ImageTypes();
  102.             if ($types IMG_PNG{
  103.                 $this->_supported_image_types['png''rw';
  104.             }
  105.             if (($types IMG_GIF)
  106.                 || function_exists('imagegif')) {
  107.                 $this->_supported_image_types['gif''rw';
  108.             elseif (function_exists('imagecreatefromgif')) {
  109.                 $this->_supported_image_types['gif''r';
  110.             }
  111.             if ($types IMG_JPG{
  112.                 $this->_supported_image_types['jpeg''rw';
  113.             }
  114.             if ($types IMG_WBMP{
  115.                 $this->_supported_image_types['wbmp''rw';
  116.             }
  117.             if (!$this->_supported_image_types{
  118.                 $this->isError(PEAR::raiseError("No supported image types available"IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
  119.             }
  120.         }
  121.  
  122.     // End function Image
  123.  
  124.     
  125.     /**
  126.      * Loads an image from file
  127.      *
  128.      * @param string $image filename
  129.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  130.      * @access public
  131.      */
  132.     function load($image)
  133.     {
  134.         $this->free();
  135.  
  136.         $this->image = $image;
  137.         $result $this->_get_image_details($image);
  138.         if (PEAR::isError($result)) {
  139.             return $result;
  140.         }
  141.         if (!$this->supportsType($this->type'r')) {
  142.             return PEAR::raiseError('Image type not supported for input',
  143.                 IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
  144.         }
  145.  
  146.         $functionName 'ImageCreateFrom' $this->type;
  147.         $this->imageHandle = $functionName($this->image);
  148.         if (!$this->imageHandle{
  149.             $this->imageHandle = null;
  150.             return PEAR::raiseError('Error while loading image file.',
  151.                 IMAGE_TRANSFORM_ERROR_IO);
  152.         }
  153.         return true;
  154.  
  155.     // End load
  156.  
  157.     
  158.     /**
  159.      * Adds a border of constant width around an image
  160.      *
  161.      * @param int $border_width Width of border to add
  162.      * @author Peter Bowyer
  163.      * @return bool TRUE
  164.      * @access public
  165.      */
  166.     function addBorder($border_width$color '')
  167.     {
  168.         $this->new_x = $this->img_x + 2 * $border_width;
  169.         $this->new_y = $this->img_y + 2 * $border_width;
  170.  
  171.         $new_img $this->_createImage($new_x$new_y$this->true_color);
  172.  
  173.         $options = array('pencilColor'$color);
  174.         $color $this->_getColor('pencilColor'$optionsarray(000));
  175.         if ($color{
  176.             if ($this->true_color{
  177.                 $c imagecolorresolve($this->imageHandle$color[0]$color[1]$color[2]);
  178.                 imagefill($new_img00$c);
  179.             else {
  180.                 imagecolorset($new_imgimagecolorat($new_img00)$color[0]$color[1]$color[2]);
  181.             }
  182.         }
  183.         ImageCopy($new_img$this->imageHandle$border_width$border_width00$this->img_x$this->img_y);
  184.         $this->imageHandle = $new_img;
  185.         $this->resized = true;
  186.  
  187.         return true;
  188.     }
  189.  
  190.     /**
  191.      * addText
  192.      *
  193.      * @param   array   $params     Array contains options
  194.      *                               array(
  195.      *                                   'text'  The string to draw
  196.      *                                   'x'     Horizontal position
  197.      *                                   'y'     Vertical Position
  198.      *                                   'color' Font color
  199.      *                                   'font'  Font to be used
  200.      *                                   'size'  Size of the fonts in pixel
  201.      *                                   'resize_first'  Tell if the image has to be resized
  202.      *                                                   before drawing the text
  203.      *                               )
  204.      *
  205.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  206.      */
  207.     function addText($params)
  208.     {
  209.         $params array_merge($this->_get_default_text_params()$params);
  210.         extract($params);
  211.  
  212.         $options = array('fontColor' => $color);
  213.         $color $this->_getColor('fontColor'$optionsarray(000));
  214.  
  215.         $c imagecolorresolve ($this->imageHandle$color[0]$color[1]$color[2]);
  216.  
  217.         if ('ttf' == substr($font-3)) {
  218.             ImageTTFText($this->imageHandle$size$angle$x$y$c$font$text);
  219.         else {
  220.             ImagePSText($this->imageHandle$size$angle$x$y$c$font$text);
  221.         }
  222.         return true;
  223.     // End addText
  224.  
  225.     
  226.     /**
  227.      * Rotates image by the given angle
  228.      *
  229.      * Uses a fast rotation algorythm for custom angles
  230.      * or lines copy for multiple of 90 degrees
  231.      *
  232.      * @param int   $angle   Rotation angle
  233.      * @param array $options array(
  234.      *                              'canvasColor' => array(r ,g, b), named color or #rrggbb
  235.      *                             )
  236.      * @author Pierre-Alain Joye
  237.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  238.      * @access public
  239.      */
  240.     function rotate($angle$options = null)
  241.     {
  242.         if (($angle % 360== 0{
  243.             return true;
  244.         }
  245.  
  246.         $color_mask $this->_getColor('canvasColor'$options,
  247.                                         array(255255255));
  248.  
  249.         $mask   imagecolorresolve($this->imageHandle$color_mask[0]$color_mask[1]$color_mask[2]);
  250.  
  251.         $this->old_image   = $this->imageHandle;
  252.  
  253.         // Multiply by -1 to change the sign, so the image is rotated clockwise
  254.         $this->imageHandle = ImageRotate($this->imageHandle$angle * -1$mask);
  255.         return true;
  256.     }
  257.  
  258.     /**
  259.      * Horizontal mirroring
  260.      *
  261.      * @return mixed TRUE or PEAR_Error object on error
  262.      * @access public
  263.      * @see flip()
  264.      ***/
  265.     function mirror()
  266.     {
  267.         $new_img $this->_createImage();
  268.         for ($x = 0; $x $this->new_x; ++$x{
  269.             imagecopy($new_img$this->imageHandle$x0,
  270.                 $this->new_x - $x - 101$this->new_y);
  271.         }
  272.         imagedestroy($this->imageHandle);
  273.         $this->imageHandle = $new_img;
  274.         return true;
  275.     }
  276.  
  277.     /**
  278.      * Vertical mirroring
  279.      *
  280.      * @return TRUE or PEAR Error object on error
  281.      * @access public
  282.      * @see mirror()
  283.      ***/
  284.     function flip()
  285.     {
  286.         $new_img $this->_createImage();
  287.         for ($y = 0; $y $this->new_y; ++$y{
  288.             imagecopy($new_img$this->imageHandle0$y,
  289.                 0$this->new_y - $y - 1$this->new_x1);
  290.         }
  291.         imagedestroy($this->imageHandle);
  292.         $this->imageHandle = $new_img;
  293.  
  294.         /* for very large images we may want to use the following
  295.            Needs to find out what is the threshhold
  296.         for ($x = 0; $x < $this->new_x; ++$x) {
  297.             for ($y1 = 0; $y1 < $this->new_y / 2; ++$y1) {
  298.                 $y2 = $this->new_y - 1 - $y1;
  299.                 $color1 = imagecolorat($this->imageHandle, $x, $y1);
  300.                 $color2 = imagecolorat($this->imageHandle, $x, $y2);
  301.                 imagesetpixel($this->imageHandle, $x, $y1, $color2);
  302.                 imagesetpixel($this->imageHandle, $x, $y2, $color1);
  303.             }
  304.         } */
  305.         return true;
  306.     }
  307.  
  308.     /**
  309.      * Crops image by size and start coordinates
  310.      *
  311.      * @param int width Cropped image width
  312.      * @param int height Cropped image height
  313.      * @param int x X-coordinate to crop at
  314.      * @param int y Y-coordinate to crop at
  315.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  316.      * @access public
  317.      */
  318.     function crop($width$height$x = 0$y = 0)
  319.     {
  320.         // Sanity check
  321.         if (!$this->intersects($width$height$x$y)) {
  322.             return PEAR::raiseError('Nothing to crop'IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
  323.         }
  324.         $x min($this->new_xmax(0$x));
  325.         $y min($this->new_ymax(0$y));
  326.         $width   min($width,  $this->new_x - $x);
  327.         $height  min($height$this->new_y - $y);
  328.         $new_img $this->_createImage($width$height);
  329.  
  330.         if (!imagecopy($new_img$this->imageHandle00$x$y$width$height)) {
  331.             imagedestroy($new_img);
  332.             return PEAR::raiseError('Failed transformation: crop()',
  333.                 IMAGE_TRANSFORM_ERROR_FAILED);
  334.         }
  335.  
  336.         $this->old_image = $this->imageHandle;
  337.         $this->imageHandle = $new_img;
  338.         $this->resized = true;
  339.  
  340.         $this->new_x = $width;
  341.         $this->new_y = $height;
  342.         return true;
  343.     }
  344.  
  345.     /**
  346.      * Converts the image to greyscale
  347.      *
  348.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  349.      * @access public
  350.      */
  351.     function greyscale({
  352.         imagecopymergegray($this->imageHandle$this->imageHandle0000$this->new_x$this->new_y0);
  353.         return true;
  354.     }
  355.  
  356.    /**
  357.     * Resize Action
  358.     *
  359.     * For GD 2.01+ the new copyresampled function is used
  360.     * It uses a bicubic interpolation algorithm to get far
  361.     * better result.
  362.     *
  363.     * Options:
  364.     *  - scaleMethod: "pixel" or "smooth"
  365.     *
  366.     * @param int   $new_x   New width
  367.     * @param int   $new_y   New height
  368.     * @param mixed $options Optional parameters
  369.     *
  370.     * @return bool|PEAR_ErrorTRUE on success or PEAR_Error object on error
  371.     * @access protected
  372.     */
  373.     function _resize($new_x$new_y$options = null)
  374.     {
  375.         if ($this->resized === true{
  376.             return PEAR::raiseError('You have already resized the image without saving it.  Your previous resizing will be overwritten'nullPEAR_ERROR_TRIGGERE_USER_NOTICE);
  377.         }
  378.  
  379.         if ($this->new_x == $new_x && $this->new_y == $new_y{
  380.             return true;
  381.         }
  382.  
  383.         $scaleMethod $this->_getOption('scaleMethod'$options'smooth');
  384.  
  385.         // Make sure to get a true color image if doing resampled resizing
  386.         // otherwise get the same type of image
  387.         $trueColor ($scaleMethod == 'pixel'? null : true;
  388.         $new_img $this->_createImage($new_x$new_y$trueColor);
  389.  
  390.         $icr_res = null;
  391.         if ($scaleMethod != 'pixel' && function_exists('ImageCopyResampled')) {
  392.             $icr_res = ImageCopyResampled($new_img$this->imageHandle0000$new_x$new_y$this->img_x$this->img_y);
  393.         }
  394.         if (!$icr_res{
  395.             ImageCopyResized($new_img$this->imageHandle0000$new_x$new_y$this->img_x$this->img_y);
  396.         }
  397.         $this->old_image = $this->imageHandle;
  398.         $this->imageHandle = $new_img;
  399.         $this->resized = true;
  400.  
  401.         $this->new_x = $new_x;
  402.         $this->new_y = $new_y;
  403.         return true;
  404.     }
  405.  
  406.     /**
  407.      * Adjusts the image gamma
  408.      *
  409.      * @param float $outputgamma 
  410.      *
  411.      * @return bool|PEAR_ErrorTRUE or a PEAR_Error object on error
  412.      * @access public
  413.      */
  414.     function gamma($outputgamma = 1.0)
  415.     {
  416.         if ($outputgamma != 1.0{
  417.             ImageGammaCorrect($this->imageHandle1.0$outputgamma);
  418.         }
  419.         return true;
  420.     }
  421.  
  422.     /**
  423.      * Helper method to save to a file or output the image
  424.      *
  425.      * @param string $filename the name of the file to write to (blank to output)
  426.      * @param string $types    define the output format, default
  427.      *                           is the current used format
  428.      * @param int    $quality  output DPI, default is 75
  429.      *
  430.      * @return bool|PEAR_ErrorTRUE on success or PEAR_Error object on error
  431.      * @access protected
  432.      */
  433.     function _generate($filename$type ''$quality = null)
  434.     {
  435.         $type strtolower(($type == ''$this->type : $type);
  436.         $options (is_array($quality)) $quality : array();
  437.         switch ($type{
  438.             case 'jpg':
  439.                 $type 'jpeg';
  440.             case 'jpeg':
  441.                 if (is_numeric($quality)) {
  442.                     $options['quality'$quality;
  443.                 }
  444.                 $quality $this->_getOption('quality'$options75);
  445.                 break;
  446.         }
  447.         if (!$this->supportsType($type'w')) {
  448.             return PEAR::raiseError('Image type not supported for output',
  449.                 IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
  450.         }
  451.  
  452.         if ($filename == ''{
  453.             header('Content-type: ' $this->getMimeType($type));
  454.             $action 'output image';
  455.         else {
  456.             $action 'save image to file';
  457.         }
  458.  
  459.         $functionName 'image' $type;
  460.         switch ($type{
  461.             case 'jpeg':
  462.                 $result $functionName($this->imageHandle$filename$quality);
  463.                 break;
  464.             default:
  465.                 if ($filename == ''{
  466.                     $result $functionName($this->imageHandle);
  467.                 else {
  468.                     $result $functionName($this->imageHandle$filename);
  469.                 }
  470.         }
  471.         if (!$result{
  472.             return PEAR::raiseError('Couldn\'t ' $action,
  473.                 IMAGE_TRANSFORM_ERROR_IO);
  474.         }
  475.         $this->imageHandle = $this->old_image;
  476.         if (!$this->keep_settings_on_save{
  477.             $this->free();
  478.         }
  479.         return true;
  480.  
  481.     // End save
  482.  
  483.     
  484.     /**
  485.      * Displays image without saving and lose changes.
  486.      *
  487.      * This method adds the Content-type HTTP header
  488.      *
  489.      * @param string $type (JPEG, PNG...);
  490.      * @param int    $quality 75
  491.      *
  492.      * @return bool|PEAR_ErrorTRUE or PEAR_Error object on error
  493.      * @access public
  494.      */
  495.     function display($type ''$quality = null)
  496.     {
  497.         return $this->_generate(''$type$quality);;
  498.     }
  499.  
  500.     /**
  501.      * Saves the image to a file
  502.      *
  503.      * @param string $filename the name of the file to write to
  504.      * @param string $type     the output format, default
  505.      *                           is the current used format
  506.      * @param int    $quality  default is 75
  507.      *
  508.      * @return bool|PEAR_ErrorTRUE on success or PEAR_Error object on error
  509.      * @access public
  510.      */
  511.     function save($filename$type ''$quality = null)
  512.     {
  513.         if (!trim($filename)) {
  514.             return PEAR::raiseError('Filename missing',
  515.                 IMAGE_TRANSFORM_ERROR_ARGUMENT);
  516.         }
  517.         return $this->_generate($filename$type$quality);
  518.     }
  519.  
  520.     /**
  521.      * Destroys image handle
  522.      *
  523.      * @access public
  524.      */
  525.     function free()
  526.     {
  527.         $this->resized = false;
  528.         if (is_resource($this->imageHandle)) {
  529.             ImageDestroy($this->imageHandle);
  530.         }
  531.         $this->imageHandle = null;
  532.         if (is_resource($this->old_image)){
  533.             ImageDestroy($this->old_image);
  534.         }
  535.         $this->old_image = null;
  536.     }
  537.  
  538.     /**
  539.      * Returns a new image for temporary processing
  540.      *
  541.      * @param int $width width of the new image
  542.      * @param int $height height of the new image
  543.      * @param bool $trueColor force which type of image to create
  544.      * @return resource a GD image resource
  545.      * @access protected
  546.      */
  547.     function _createImage($width = -1$height = -1$trueColor = null)
  548.     {
  549.         if ($width == -1{
  550.             $width $this->new_x;
  551.         }
  552.         if ($height == -1{
  553.             $height $this->new_y;
  554.         }
  555.  
  556.         $new_img = null;
  557.         if (is_null($trueColor)) {
  558.             if (function_exists('imageistruecolor')) {
  559.                 $createtruecolor imageistruecolor($this->