/**
  * Draw a line around an image
  *
  * Draw a line around an image. If $offset is 0, the line is just around the image.
  * If $offset is positive, the line is outside the image, and the gad is filled with background color.
  * If $offset is -width / 2, the line is centered on the border of the image.
  * If $offset is -$width, the line in just inside the image.
  * If $offset is negative, the line is inside the image.
  *
  * @param   int $width Line width
  * @param   mixed $color Line color
  * @param   int $offset Distance between line and image.
  *
  * @return  bool|PEAR_Error TRUE on success or PEAR_Error on failure.
  * @access  private
  * @author  Charles Brunet <*****@*****.**>
  */
 function _line($width = 2, $color = '000000', $offset = 0, $background = 'FFFFFF')
 {
     // Agrandir l'image si nécessaire
     $mag = $width + $offset;
     if ($mag > 0) {
         $w = $this->_iWidth + 2 * $mag;
         $h = $this->_iHeight + 2 * $mag;
         // Create the target image
         if (function_exists('imagecreatetruecolor')) {
             $target = imagecreatetruecolor($w, $h);
         } else {
             $target = imagecreate($w, $h);
         }
         if (!Image_Tools::isGDImageResource($target)) {
             return PEAR::raiseError('Cannot initialize new GD image stream');
         }
         $background = Image_Tools_Utils::colorToRGBA($background);
         $background = imagecolorallocate($target, $background['r'], $background['g'], $background['b']);
         imagefilledrectangle($target, 0, 0, $w - 1, $h - 1, $background);
         imagecopy($target, $this->resultImage, $mag, $mag, 0, 0, $this->_iWidth, $this->_iHeight);
         $this->_iWidth = $w;
         $this->_iHeight = $h;
         imagedestroy($this->resultImage);
         $this->resultImage = $target;
     }
     if (!Image_Tools::isGDImageResource($this->resultImage)) {
         return PEAR::raiseError('Invalid image resource Image_Tools_Mask::$_resultImage');
     }
     $color = Image_Tools_Utils::colorToRGBA($color);
     $color = imagecolorallocate($this->resultImage, $color['r'], $color['g'], $color['b']);
     $a = $mag < 0 ? 0 - $mag : 0;
     for ($i = $a; $i < $a + $width; ++$i) {
         imagerectangle($this->resultImage, $i, $i, $this->_iWidth - $i - 1, $this->_iHeight - $i - 1, $color);
     }
     return true;
 }
 /**
  * Compare the "PHP-standardized" version number string with the
  * current loaded GD.
  *
  * @param string $version
  * @param string $operator
  * @return boolean
  * @static
  */
 function compareGDVersion($version, $operator)
 {
     return version_compare(Image_Tools_Utils::getGDVersion(), $version, $operator);
 }
 /**
  * Draw thumbnail result to resource.
  *
  * @return  bool|PEAR_Error TRUE on success or PEAR_Error on failure.
  * @access  public
  */
 function render()
 {
     if (!Image_Tools::isGDImageResource($this->resultImage)) {
         return PEAR::raiseError('Invalid image resource');
     }
     // Estimate a rectangular portion of the source image and a size of the target image
     if ($this->options['method'] == IMAGE_TOOLS_THUMBNAIL_METHOD_CROP) {
         if ($this->options['percent']) {
             $W = floor($this->options['percent'] / 100 * $this->imageInfo['width']);
             $H = floor($this->options['percent'] / 100 * $this->imageInfo['height']);
         } else {
             $W = $this->options['width'];
             $H = $this->options['height'];
         }
         $width = $W;
         $height = $H;
         $Y = $this->_coord('valign', 'height', $H);
         $X = $this->_coord('halign', 'width', $W);
     } else {
         $W = $this->imageInfo['width'];
         $H = $this->imageInfo['height'];
         $X = 0;
         $Y = 0;
         if ($this->options['percent']) {
             $width = floor($this->options['percent'] / 100 * $W);
             $height = floor($this->options['percent'] / 100 * $H);
         } else {
             $width = $this->options['width'];
             $height = $this->options['height'];
             if ($this->options['method'] == IMAGE_TOOLS_THUMBNAIL_METHOD_SCALE_MIN) {
                 $Ww = $W / $width;
                 $Hh = $H / $height;
                 if ($Ww > $Hh) {
                     $W = floor($width * $Hh);
                     $X = $this->_coord('halign', 'width', $W);
                 } else {
                     $H = floor($height * $Ww);
                     $Y = $this->_coord('valign', 'height', $H);
                 }
             } else {
                 if ($H > $W) {
                     $width = floor($height / $H * $W);
                 } else {
                     $height = floor($width / $W * $H);
                 }
             }
         }
     }
     // Create the target image
     if (function_exists('imagecreatetruecolor')) {
         $target = imagecreatetruecolor($width, $height);
     } else {
         $target = imagecreate($width, $height);
     }
     if (!Image_Tools::isGDImageResource($target)) {
         return PEAR::raiseError('Cannot initialize new GD image stream');
     }
     // enable transparency if supported
     if (Image_Tools_Utils::compareGDVersion('2.0.28', '>=')) {
         // imagealphablending() and imagesavealpha() requires GD 2.0.38
         imagealphablending($target, false);
         imagesavealpha($target, true);
     }
     // Copy the source image to the target image
     if ($this->options['method'] == IMAGE_TOOLS_THUMBNAIL_METHOD_CROP) {
         $result = imagecopy($target, $this->resultImage, 0, 0, $X, $Y, $W, $H);
     } elseif (function_exists('imagecopyresampled')) {
         $result = imagecopyresampled($target, $this->resultImage, 0, 0, $X, $Y, $width, $height, $W, $H);
     } else {
         $result = imagecopyresized($target, $this->resultImage, 0, 0, $X, $Y, $width, $height, $W, $H);
     }
     if (!$result) {
         return PEAR::raiseError('Cannot resize image');
     }
     // Free a memory from the source image and save the resulting thumbnail
     imagedestroy($this->resultImage);
     $this->resultImage = $target;
     return true;
 }
 /**
  * Apply the blend mode.
  *
  * @return true|PEAR_Error
  * @access protected
  */
 function render()
 {
     $method = array($this, "_{$this->options['mode']}");
     if (!is_callable($method)) {
         return PEAR::raiseError('Invalid mode or not supported');
     }
     $width1 = imagesx($this->_image1);
     $height1 = imagesy($this->_image1);
     $width = imagesx($this->_image2);
     $height = imagesy($this->_image2);
     // sets out of bound flags
     $x_outbound = $y_outbound = false;
     // walking through pixels
     for ($x2 = 0, $x1 = $this->options['x']; $x2 < $width; $x2++, $x1++) {
         // x and y already out of bound, nothing to process
         if ($x_outbound && $y_outbound) {
             break;
         }
         $x_outbound = $y_outbound = false;
         // x is out bound
         if ($x1 >= $width1) {
             $x_outbound = true;
             continue;
         }
         for ($y2 = 0, $y1 = $this->options['y']; $y2 < $height; $y2++, $y1++) {
             // y is out bound
             if ($y1 >= $height1) {
                 $y_outbound = true;
                 continue;
             }
             $color1 = Image_Tools_Utils::colorToRGBA(imagecolorat($this->_image1, $x1, $y1));
             $color2 = Image_Tools_Utils::colorToRGBA(imagecolorat($this->_image2, $x2, $y2));
             // ignore transparencies
             if ($color2['a'] == 127) {
                 continue;
             }
             $params = array_merge(array($color1['r'], $color2['r']), $this->options['params']);
             $red = call_user_func_array($method, $params);
             $params = array_merge(array($color1['g'], $color2['g']), $this->options['params']);
             $green = call_user_func_array($method, $params);
             $params = array_merge(array($color1['b'], $color2['b']), $this->options['params']);
             $blue = call_user_func_array($method, $params);
             $color = imagecolorallocatealpha($this->_image1, $red, $green, $blue, $color2['a']);
             imagesetpixel($this->_image1, $x1, $y1, $color);
         }
     }
     $this->resultImage = $this->_image1;
     // frees up memory
     imagedestroy($this->_image2);
     return true;
 }