title: Magento + Imagemagick Proportional Images
body: <p>Implemented Imagemagick instead of GD2 in Magento 1.7.0.2 as suggested here:</p>
  
  <p><a href="http://www.magentocommerce.com/boards/v/viewthread/6158/P135/#t328638" rel="nofollow">http://www.magentocommerce.com/boards/v/viewthread/6158/P135/#t328638</a></p>
  
  <p>However the new images are not proportional, causing stretching. They were perfect with GD2</p>
  
  <p>Instructions:</p>
  
  <p>In the Gd2.php file, in the save() function comment out</p>
  
  <pre><code>call_user_func_array($this-&gt;_getCallback('output'), $functionParameters);
  </code></pre>
  
  <p>This is the where Magento calls the imagejpeg() function. Yuck. </p>
  
  <p>Now add the following code just below the commented out line.</p>
  
  <pre><code>$source_file = $this-&gt;_fileSrcPath.'/'.$this-&gt;_fileSrcName;
  $image_info = getimagesize($source_file);
  $im = new Imagick();
  $im-&gt;readImage($source_file);
  
  //check to see if image needs to be resized (eg. thumbnails)
  if ($image_info[0] != $this-&gt;_imageSrcWidth) {
   $im-&gt;thumbnailImage($this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight);
  }
  $im-&gt;writeImage($fileName);
  </code></pre>
  
  <p>Here is the implemented code</p>
  
  <pre><code>        &lt;?php
          /**
           * Magento
           *
           * NOTICE OF LICENSE
           *
           * This source file is subject to the Open Software License (OSL 3.0)
           * that is bundled with this package in the file LICENSE.txt.
           * It is also available through the world-wide-web at this URL:
           * http://opensource.org/licenses/osl-3.0.php
           * If you did not receive a copy of the license and are unable to
           * obtain it through the world-wide-web, please send an email
           * to license@magentocommerce.com so we can send you a copy immediately.
           *
           * DISCLAIMER
           *
           * Do not edit or add to this file if you wish to upgrade Magento to newer
           * versions in the future. If you wish to customize Magento for your
           * needs please refer to http://www.magentocommerce.com for more information.
           *
           * @category   Varien
           * @package    Varien_Image
           * @copyright  Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
           * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
           */
  
  
          class Varien_Image_Adapter_Gd2 extends Varien_Image_Adapter_Abstract
          {
              protected $_requiredExtensions = Array("gd");
              private static $_callbacks = array(
                  IMAGETYPE_GIF  =&gt; array('output' =&gt; 'imagegif',  'create' =&gt; 'imagecreatefromgif'),
                  IMAGETYPE_JPEG =&gt; array('output' =&gt; 'imagejpeg', 'create' =&gt; 'imagecreatefromjpeg'),
                  IMAGETYPE_PNG  =&gt; array('output' =&gt; 'imagepng',  'create' =&gt; 'imagecreatefrompng'),
                  IMAGETYPE_XBM  =&gt; array('output' =&gt; 'imagexbm',  'create' =&gt; 'imagecreatefromxbm'),
                  IMAGETYPE_WBMP =&gt; array('output' =&gt; 'imagewbmp', 'create' =&gt; 'imagecreatefromxbm'),
              );
  
              /**
               * Whether image was resized or not
               *
               * @var bool
               */
              protected $_resized = false;
  
              public function open($filename)
              {
                  $this-&gt;_fileName = $filename;
                  $this-&gt;getMimeType();
                  $this-&gt;_getFileAttributes();
                  $this-&gt;_imageHandler = call_user_func($this-&gt;_getCallback('create'), $this-&gt;_fileName);
              }
  
              public function save($destination=null, $newName=null)
              {
                  $fileName = ( !isset($destination) ) ? $this-&gt;_fileName : $destination;
  
                  if( isset($destination) &amp;&amp; isset($newName) ) {
                      $fileName = $destination . "/" . $newName;
                  } elseif( isset($destination) &amp;&amp; !isset($newName) ) {
                      $info = pathinfo($destination);
                      $fileName = $destination;
                      $destination = $info['dirname'];
                  } elseif( !isset($destination) &amp;&amp; isset($newName) ) {
                      $fileName = $this-&gt;_fileSrcPath . "/" . $newName;
                  } else {
                      $fileName = $this-&gt;_fileSrcPath . $this-&gt;_fileSrcName;
                  }
  
                  $destinationDir = ( isset($destination) ) ? $destination : $this-&gt;_fileSrcPath;
  
                  if( !is_writable($destinationDir) ) {
                      try {
                          $io = new Varien_Io_File();
                          $io-&gt;mkdir($destination);
                      } catch (Exception $e) {
                          throw new Exception("Unable to write file into directory '{$destinationDir}'. Access forbidden.");
                      }
                  }
  
                  if (!$this-&gt;_resized) {
                      // keep alpha transparency
                      $isAlpha     = false;
                      $isTrueColor = false;
                      $this-&gt;_getTransparency($this-&gt;_imageHandler, $this-&gt;_fileType, $isAlpha, $isTrueColor);
                      if ($isAlpha) {
                          if ($isTrueColor) {
                              $newImage = imagecreatetruecolor($this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight);
                          } else {
                              $newImage = imagecreate($this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight);
                          }
                          $this-&gt;_fillBackgroundColor($newImage);
                          imagecopy(
                              $newImage,
                              $this-&gt;_imageHandler,
                              0, 0,
                              0, 0,
                              $this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight
                          );
                          $this-&gt;_imageHandler = $newImage;
                      }
                  }
  
                  $functionParameters = array();
                  $functionParameters[] = $this-&gt;_imageHandler;
                  $functionParameters[] = $fileName;
  
                  // set quality param for JPG file type
                  if (!is_null($this-&gt;quality()) &amp;&amp; $this-&gt;_fileType == IMAGETYPE_JPEG)
                  {
                      $functionParameters[] = $this-&gt;quality();
                  }
  
                  // set quality param for PNG file type
                  if (!is_null($this-&gt;quality()) &amp;&amp; $this-&gt;_fileType == IMAGETYPE_PNG)
                  {
                      $quality = round(($this-&gt;quality() / 100) * 10);
                      if ($quality &lt; 1) {
                          $quality = 1;
                      } elseif ($quality &gt; 10) {
                          $quality = 10;
                      }
                      $quality = 10 - $quality;
                      $functionParameters[] = $quality;
                  }
  
                  //call_user_func_array($this-&gt;_getCallback('output'), $functionParameters);
  
                  $source_file = $this-&gt;_fileSrcPath.'/'.$this-&gt;_fileSrcName;
                  $image_info = getimagesize($source_file);
                  $im = new Imagick();
                  $im-&gt;readImage($source_file);
  
                  //check to see if image needs to be resized (eg. thumbnails)
                  if ($image_info[0] != $this-&gt;_imageSrcWidth) {
                       $im-&gt;thumbnailImage($this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight);
                  }
                  $im-&gt;writeImage($fileName);
              }
  
              public function display()
              {
                  header("Content-type: ".$this-&gt;getMimeType());
                  call_user_func($this-&gt;_getCallback('output'), $this-&gt;_imageHandler);
              }
  
              /**
               * Obtain function name, basing on image type and callback type
               *
               * @param string $callbackType
               * @param int $fileType
               * @return string
               * @throws Exception
               */
              private function _getCallback($callbackType, $fileType = null, $unsupportedText = 'Unsupported image format.')
              {
                  if (null === $fileType) {
                      $fileType = $this-&gt;_fileType;
                  }
                  if (empty(self::$_callbacks[$fileType])) {
                      throw new Exception($unsupportedText);
                  }
                  if (empty(self::$_callbacks[$fileType][$callbackType])) {
                      throw new Exception('Callback not found.');
                  }
                  return self::$_callbacks[$fileType][$callbackType];
              }
  
              private function _fillBackgroundColor(&amp;$imageResourceTo)
              {
                  // try to keep transparency, if any
                  if ($this-&gt;_keepTransparency) {
                      $isAlpha = false;
                      $transparentIndex = $this-&gt;_getTransparency($this-&gt;_imageHandler, $this-&gt;_fileType, $isAlpha);
                      try {
                          // fill truecolor png with alpha transparency
                          if ($isAlpha) {
  
                              if (!imagealphablending($imageResourceTo, false)) {
                                  throw new Exception('Failed to set alpha blending for PNG image.');
                              }
                              $transparentAlphaColor = imagecolorallocatealpha($imageResourceTo, 0, 0, 0, 127);
                              if (false === $transparentAlphaColor) {
                                  throw new Exception('Failed to allocate alpha transparency for PNG image.');
                              }
                              if (!imagefill($imageResourceTo, 0, 0, $transparentAlphaColor)) {
                                  throw new Exception('Failed to fill PNG image with alpha transparency.');
                              }
                              if (!imagesavealpha($imageResourceTo, true)) {
                                  throw new Exception('Failed to save alpha transparency into PNG image.');
                              }
  
                              return $transparentAlphaColor;
                          }
                          // fill image with indexed non-alpha transparency
                          elseif (false !== $transparentIndex) {
                              $transparentColor = false;
                              if ($transparentIndex &gt;=0 &amp;&amp; $transparentIndex &lt;= imagecolorstotal($this-&gt;_imageHandler)) {
                                  list($r, $g, $b)  = array_values(imagecolorsforindex($this-&gt;_imageHandler, $transparentIndex));
                                  $transparentColor = imagecolorallocate($imageResourceTo, $r, $g, $b);
                              }
                              if (false === $transparentColor) {
                                  throw new Exception('Failed to allocate transparent color for image.');
                              }
                              if (!imagefill($imageResourceTo, 0, 0, $transparentColor)) {
                                  throw new Exception('Failed to fill image with transparency.');
                              }
                              imagecolortransparent($imageResourceTo, $transparentColor);
                              return $transparentColor;
                          }
                      }
                      catch (Exception $e) {
                          // fallback to default background color
                      }
                  }
                  list($r, $g, $b) = $this-&gt;_backgroundColor;
                  $color = imagecolorallocate($imageResourceTo, $r, $g, $b);
                  if (!imagefill($imageResourceTo, 0, 0, $color)) {
                      throw new Exception("Failed to fill image background with color {$r} {$g} {$b}.");
                  }
  
                  return $color;
              }
  
              /**
               * Gives true for a PNG with alpha, false otherwise
               *
               * @param string $fileName
               * @return boolean
               */
  
              public function checkAlpha($fileName)
              {
                  return ((ord(file_get_contents($fileName, false, null, 25, 1)) &amp; 6) &amp; 4) == 4;
              }
  
              private function _getTransparency($imageResource, $fileType, &amp;$isAlpha = false, &amp;$isTrueColor = false)
              {
                  $isAlpha     = false;
                  $isTrueColor = false;
                  // assume that transparency is supported by gif/png only
                  if ((IMAGETYPE_GIF === $fileType) || (IMAGETYPE_PNG === $fileType)) {
                      // check for specific transparent color
                      $transparentIndex = imagecolortransparent($imageResource);
                      if ($transparentIndex &gt;= 0) {
                          return $transparentIndex;
                      }
                      // assume that truecolor PNG has transparency
                      elseif (IMAGETYPE_PNG === $fileType) {
                          $isAlpha     = $this-&gt;checkAlpha($this-&gt;_fileName);
                          $isTrueColor = true;
                          return $transparentIndex; // -1
                      }
                  }
                  if (IMAGETYPE_JPEG === $fileType) {
                      $isTrueColor = true;
                  }
                  return false;
              }
  
              /**
               * Change the image size
               *
               * @param int $frameWidth
               * @param int $frameHeight
               */
              public function resize($frameWidth = null, $frameHeight = null)
              {
                  if (empty($frameWidth) &amp;&amp; empty($frameHeight)) {
                      throw new Exception('Invalid image dimensions.');
                  }
  
                  // calculate lacking dimension
                  if (!$this-&gt;_keepFrame) {
                      if (null === $frameWidth) {
                          $frameWidth = round($frameHeight * ($this-&gt;_imageSrcWidth / $this-&gt;_imageSrcHeight));
                      }
                      elseif (null === $frameHeight) {
                          $frameHeight = round($frameWidth * ($this-&gt;_imageSrcHeight / $this-&gt;_imageSrcWidth));
                      }
                  }
                  else {
                      if (null === $frameWidth) {
                          $frameWidth = $frameHeight;
                      }
                      elseif (null === $frameHeight) {
                          $frameHeight = $frameWidth;
                      }
                  }
  
                  // define coordinates of image inside new frame
                  $srcX = 0;
                  $srcY = 0;
                  $dstX = 0;
                  $dstY = 0;
                  $dstWidth  = $frameWidth;
                  $dstHeight = $frameHeight;
                  if ($this-&gt;_keepAspectRatio) {
                      // do not make picture bigger, than it is, if required
                      if ($this-&gt;_constrainOnly) {
                          if (($frameWidth &gt;= $this-&gt;_imageSrcWidth) &amp;&amp; ($frameHeight &gt;= $this-&gt;_imageSrcHeight)) {
                              $dstWidth  = $this-&gt;_imageSrcWidth;
                              $dstHeight = $this-&gt;_imageSrcHeight;
                          }
                      }
                      // keep aspect ratio
                      if ($this-&gt;_imageSrcWidth / $this-&gt;_imageSrcHeight &gt;= $frameWidth / $frameHeight) {
                          $dstHeight = round(($dstWidth / $this-&gt;_imageSrcWidth) * $this-&gt;_imageSrcHeight);
                      } else {
                          $dstWidth = round(($dstHeight / $this-&gt;_imageSrcHeight) * $this-&gt;_imageSrcWidth);
                      }
                  }
                  // define position in center (TODO: add positions option)
                  $dstY = round(($frameHeight - $dstHeight) / 2);
                  $dstX = round(($frameWidth - $dstWidth) / 2);
  
                  // get rid of frame (fallback to zero position coordinates)
                  if (!$this-&gt;_keepFrame) {
                      $frameWidth  = $dstWidth;
                      $frameHeight = $dstHeight;
                      $dstY = 0;
                      $dstX = 0;
                  }
  
                  // create new image
                  $isAlpha     = false;
                  $isTrueColor = false;
                  $this-&gt;_getTransparency($this-&gt;_imageHandler, $this-&gt;_fileType, $isAlpha, $isTrueColor);
                  if ($isTrueColor) {
                      $newImage = imagecreatetruecolor($frameWidth, $frameHeight);
                  }
                  else {
                      $newImage = imagecreate($frameWidth, $frameHeight);
                  }
  
                  // fill new image with required color
                  $this-&gt;_fillBackgroundColor($newImage);
  
                  // resample source image and copy it into new frame
                  imagecopyresampled(
                      $newImage,
                      $this-&gt;_imageHandler,
                      $dstX, $dstY,
                      $srcX, $srcY,
                      $dstWidth, $dstHeight,
                      $this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight
                  );
                  $this-&gt;_imageHandler = $newImage;
                  $this-&gt;refreshImageDimensions();
                  $this-&gt;_resized = true;
              }
  
              public function rotate($angle)
              {
          /*
                  $isAlpha = false;
                  $backgroundColor = $this-&gt;_getTransparency($this-&gt;_imageHandler, $this-&gt;_fileType, $isAlpha);
                  list($r, $g, $b) = $this-&gt;_backgroundColor;
                  if ($isAlpha) {
                      $backgroundColor = imagecolorallocatealpha($this-&gt;_imageHandler, 0, 0, 0, 127);
                  }
                  elseif (false === $backgroundColor) {
                      $backgroundColor = imagecolorallocate($this-&gt;_imageHandler, $r, $g, $b);
                  }
                  $this-&gt;_imageHandler = imagerotate($this-&gt;_imageHandler, $angle, $backgroundColor);
          //*/
                  $this-&gt;_imageHandler = imagerotate($this-&gt;_imageHandler, $angle, $this-&gt;imageBackgroundColor);
                  $this-&gt;refreshImageDimensions();
              }
  
              public function watermark($watermarkImage, $positionX=0, $positionY=0, $watermarkImageOpacity=30, $repeat=false)
              {
                  list($watermarkSrcWidth, $watermarkSrcHeight, $watermarkFileType, ) = getimagesize($watermarkImage);
                  $this-&gt;_getFileAttributes();
                  $watermark = call_user_func($this-&gt;_getCallback(
                      'create',
                      $watermarkFileType,
                      'Unsupported watermark image format.'
                  ), $watermarkImage);
  
                  $merged = false;
  
                  if ($this-&gt;getWatermarkWidth() &amp;&amp;
                      $this-&gt;getWatermarkHeigth() &amp;&amp;
                      ($this-&gt;getWatermarkPosition() != self::POSITION_STRETCH)
                  ) {
                      $newWatermark = imagecreatetruecolor($this-&gt;getWatermarkWidth(), $this-&gt;getWatermarkHeigth());
                      imagealphablending($newWatermark, false);
                      $col = imagecolorallocate($newWatermark, 255, 255, 255);
                      imagecolortransparent($newWatermark, $col);
                      imagefilledrectangle($newWatermark, 0, 0, $this-&gt;getWatermarkWidth(), $this-&gt;getWatermarkHeigth(), $col);
                      imagealphablending($newWatermark, true);
                      imageSaveAlpha($newWatermark, true);
                      imagecopyresampled(
                          $newWatermark,
                          $watermark,
                          0, 0, 0, 0,
                          $this-&gt;getWatermarkWidth(), $this-&gt;getWatermarkHeigth(),
                          imagesx($watermark), imagesy($watermark)
                      );
                      $watermark = $newWatermark;
                  }
  
                  if( $this-&gt;getWatermarkPosition() == self::POSITION_TILE ) {
                      $repeat = true;
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_STRETCH ) {
  
                      $newWatermark = imagecreatetruecolor($this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight);
                      imagealphablending($newWatermark, false);
                      $col = imagecolorallocate($newWatermark, 255, 255, 255);
                      imagecolortransparent($newWatermark, $col);
                      imagefilledrectangle($newWatermark, 0, 0, $this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight, $col);
                      imagealphablending($newWatermark, true);
                      imageSaveAlpha($newWatermark, true);
                      imagecopyresampled(
                          $newWatermark,
                          $watermark,
                          0, 0, 0, 0,
                          $this-&gt;_imageSrcWidth, $this-&gt;_imageSrcHeight,
                          imagesx($watermark), imagesy($watermark)
                      );
                      $watermark = $newWatermark;
  
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_CENTER ) {
                      $positionX = ($this-&gt;_imageSrcWidth/2 - imagesx($watermark)/2);
                      $positionY = ($this-&gt;_imageSrcHeight/2 - imagesy($watermark)/2);
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_TOP_RIGHT ) {
                      $positionX = ($this-&gt;_imageSrcWidth - imagesx($watermark));
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_TOP_LEFT  ) {
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_BOTTOM_RIGHT ) {
                      $positionX = ($this-&gt;_imageSrcWidth - imagesx($watermark));
                      $positionY = ($this-&gt;_imageSrcHeight - imagesy($watermark));
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  } elseif( $this-&gt;getWatermarkPosition() == self::POSITION_BOTTOM_LEFT ) {
                      $positionY = ($this-&gt;_imageSrcHeight - imagesy($watermark));
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  }
  
                  if( $repeat === false &amp;&amp; $merged === false ) {
                      imagecopymerge(
                          $this-&gt;_imageHandler,
                          $watermark,
                          $positionX, $positionY,
                          0, 0,
                          imagesx($watermark), imagesy($watermark),
                          $this-&gt;getWatermarkImageOpacity()
                      );
                  } else {
                      $offsetX = $positionX;
                      $offsetY = $positionY;
                      while( $offsetY &lt;= ($this-&gt;_imageSrcHeight+imagesy($watermark)) ) {
                          while( $offsetX &lt;= ($this-&gt;_imageSrcWidth+imagesx($watermark)) ) {
                              imagecopymerge(
                                  $this-&gt;_imageHandler,
                                  $watermark,
                                  $offsetX, $offsetY,
                                  0, 0,
                                  imagesx($watermark), imagesy($watermark),
                                  $this-&gt;getWatermarkImageOpacity()
                              );
                              $offsetX += imagesx($watermark);
                          }
                          $offsetX = $positionX;
                          $offsetY += imagesy($watermark);
                      }
                  }
  
                  imagedestroy($watermark);
                  $this-&gt;refreshImageDimensions();
              }
  
              public function crop($top=0, $left=0, $right=0, $bottom=0)
              {
                  if( $left == 0 &amp;&amp; $top == 0 &amp;&amp; $right == 0 &amp;&amp; $bottom == 0 ) {
                      return;
                  }
  
                  $newWidth = $this-&gt;_imageSrcWidth - $left - $right;
                  $newHeight = $this-&gt;_imageSrcHeight - $top - $bottom;
  
                  $canvas = imagecreatetruecolor($newWidth, $newHeight);
  
                  if ($this-&gt;_fileType == IMAGETYPE_PNG) {
                      $this-&gt;_saveAlpha($canvas);
                  }
  
                  imagecopyresampled(
                      $canvas,
                      $this-&gt;_imageHandler,
                      0, 0, $left, $top,
                      $newWidth, $newHeight,
                      $newWidth, $newHeight
                  );
  
                  $this-&gt;_imageHandler = $canvas;
                  $this-&gt;refreshImageDimensions();
              }
  
              public function checkDependencies()
              {
                  foreach( $this-&gt;_requiredExtensions as $value ) {
                      if( !extension_loaded($value) ) {
                          throw new Exception("Required PHP extension '{$value}' was not loaded.");
                      }
                  }
              }
  
              private function refreshImageDimensions()
              {
                  $this-&gt;_imageSrcWidth = imagesx($this-&gt;_imageHandler);
                  $this-&gt;_imageSrcHeight = imagesy($this-&gt;_imageHandler);
              }
  
              function __destruct()
              {
                  @imagedestroy($this-&gt;_imageHandler);
              }
  
              /*
               * Fixes saving PNG alpha channel
               */
              private function _saveAlpha($imageHandler)
              {
                  $background = imagecolorallocate($imageHandler, 0, 0, 0);
                  ImageColorTransparent($imageHandler, $background);
                  imagealphablending($imageHandler, false);
                  imagesavealpha($imageHandler, true);
              }
          }
  </code></pre>