Add API to overlay image
authorMatthias Schmidt <gravatronics@live.com>
Mon, 22 Dec 2014 09:58:12 +0000 (10:58 +0100)
committerMatthias Schmidt <gravatronics@live.com>
Mon, 22 Dec 2014 09:58:12 +0000 (10:58 +0100)
wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php
wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php
wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php
wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php

index 132acaa03e931d6d80c7a9fbbc9df1258c6c4bfe..f4420e30d193f1bfa9e7a0174aeb948b4da3bf53 100644 (file)
@@ -276,6 +276,28 @@ class GDImageAdapter implements IImageAdapter {
                return imagerotate($this->image, (360.0 - $degrees), ($this->color ?: 0));
        }
        
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImage()
+        */
+       public function overlayImage($file, $x, $y, $opacity) {
+               $overlayImage = new self();
+               $overlayImage->loadFile($file);
+               
+               // fix PNG alpha channel handling
+               // see http://php.net/manual/en/function.imagecopymerge.php#92787
+               $cut = imagecreatetruecolor($overlayImage->getWidth(), $overlayImage->getHeight());
+               imagecopy($cut, $this->image, 0, 0, $x, $y, $overlayImage->getWidth(), $overlayImage->getHeight());
+               imagecopy($cut, $overlayImage->image, 0, 0, 0, 0, $overlayImage->getWidth(), $overlayImage->getHeight());
+               imagecopymerge($this->image, $cut, $x, $y, 0, 0, $overlayImage->getWidth(), $overlayImage->getHeight(), $opacity * 100);
+       }
+       
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImageRelative()
+        */
+       public function overlayImageRelative($file, $position, $margin, $opacity) {
+               // does nothing
+       }
+       
        /**
         * @see \wcf\system\image\adapter\IImageAdapter::isSupported()
         */
index c97345cad3935e949869b0745d1d5fa79f5a69bb..aaa0670dcca1fa77170967f43d62f9825a2a80f9 100644 (file)
@@ -161,6 +161,26 @@ interface IImageAdapter {
         */
        public function rotate($degrees);
        
+       /**
+        * Overlays the given image at an absolute position.
+        * 
+        * @param       string          $file
+        * @param       integer         $x
+        * @param       integer         $y
+        * @param       float           $opacity
+        */
+       public function overlayImage($file, $x, $y, $opacity);
+       
+       /**
+        * Overlays the given image at a relative position.
+        * 
+        * @param       string          $file
+        * @param       string          $position
+        * @param       integer         $margin
+        * @param       float           $opacity
+        */
+       public function overlayImageRelative($file, $position, $margin, $opacity);
+       
        /**
         * Determines if an image adapter is supported.
         * 
index d5daca883490affee98c08186ae499420957eb25..0433dac0e203dfb6fc8185f42bd900a0ec45f4b1 100644 (file)
@@ -19,6 +19,22 @@ class ImageAdapter implements IImageAdapter {
         */
        protected $adapter = null;
        
+       /**
+        * supported relative positions
+        * @var array<string>
+        */
+       protected $relativePositions = array(
+               'topLeft',
+               'topCenter',
+               'topRight',
+               'middleLeft',
+               'middleCenter',
+               'middleRight',
+               'bottomLeft',
+               'bottomCenter',
+               'bottomRight'
+       );
+       
        /**
         * Creates a new ImageAdapter instance.
         * 
@@ -192,6 +208,90 @@ class ImageAdapter implements IImageAdapter {
                return $this->adapter->rotate($degrees);
        }
        
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImage()
+        */
+       public function overlayImage($file, $x, $y, $opacity) {
+               // validate file
+               if (!file_exists($file)) {
+                       throw new SystemException("Image '".$file."' does not exist.");
+               }
+               
+               $this->adapter->overlayImage($file, $x, $y, $opacity);
+       }
+       
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImage()
+        */
+       public function overlayImageRelative($file, $position, $margin, $opacity) {
+               // validate file
+               if (!file_exists($file)) {
+                       throw new SystemException("Image '".$file."' does not exist.");
+               }
+               
+               // validate position
+               if (!in_array($position, $this->relativePositions)) {
+                       throw new SystemException("Unknown relative position '".$position."'.");
+               }
+               
+               // validate margin
+               if ($margin < 0 || $margin >= $this->getHeight() / 2 || $margin >= $this->getWidth() / 2) {
+                       throw new SystemException("Margin has to be positive and respect image dimensions.");
+               }
+               
+               $adapterClassName = get_class($this->adapter);
+               $overlayImage = new $adapterClassName();
+               $overlayImage->loadFile($file);
+               $overlayHeight = $overlayImage->getHeight();
+               $overlayWidth = $overlayImage->getWidth();
+               
+               // calculate y coordinate
+               $x = 0;
+               switch ($position) {
+                       case 'topLeft':
+                       case 'middleLeft':
+                       case 'bottomLeft':
+                               $x = $margin;
+                       break;
+                       
+                       case 'topCenter':
+                       case 'middleCenter':
+                       case 'bottomCenter':
+                               $x = floor(($this->getWidth() - $overlayWidth) / 2);
+                       break;
+                       
+                       case 'topRight':
+                       case 'middleRight':
+                       case 'bottomRight':
+                               $x = $this->getWidth() - $overlayWidth - $margin;
+                       break;
+               }
+               
+               // calculate y coordinate
+               $y = 0;
+               switch ($position) {
+                       case 'topLeft':
+                       case 'topCenter':
+                       case 'topRight':
+                               $y = $margin;
+                       break;
+                       
+                       case 'middleLeft':
+                       case 'middleCenter':
+                       case 'middleRight':
+                               $y = floor(($this->getHeight() - $overlayHeight) / 2);
+                       break;
+                       
+                       case 'bottomLeft':
+                       case 'bottomCenter':
+                       case 'bottomRight':
+                               $y = $this->getHeight() - $overlayHeight - $margin;
+                       break;
+               }
+               
+               $this->overlayImage($file, $x, $y, $opacity);
+       }
+       
        /**
         * @see \wcf\system\image\adapter\IImageAdapter::isSupported()
         */
index da7b7f6c3303c68df7b32d427ebd0727f3b832c4..42b3eaacb8bd627916af72d3e4cde09580141c74 100644 (file)
@@ -76,6 +76,7 @@ class ImagickImageAdapter implements IImageAdapter {
         */
        public function loadFile($file) {
                try {
+                       $this->imagick->clear();
                        $this->imagick->readImage($file);
                }
                catch (\ImagickException $e) {
@@ -280,6 +281,29 @@ class ImagickImageAdapter implements IImageAdapter {
                return $image;
        }
        
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImage()
+        */
+       public function overlayImage($file, $x, $y, $opacity) {
+               try {
+                       $overlayImage = new \Imagick($file);
+               }
+               catch (\ImagickException $e) {
+                       throw new SystemException("Image '".$file."' is not readable or does not exist.");
+               }
+               
+               $overlayImage->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $opacity, \Imagick::CHANNEL_OPACITY);
+               $this->imagick->compositeImage($overlayImage, \Imagick::COMPOSITE_OVER, $x, $y);
+               $this->imagick = $this->imagick->flattenImages();
+       }
+       
+       /**
+        * @see \wcf\system\image\adapter\IImageAdapter::overlayImageRelative()
+        */
+       public function overlayImageRelative($file, $position, $margin, $opacity) {
+               // does nothing
+       }
+       
        /**
         * @see \wcf\system\image\adapter\IImageAdapter::isSupported()
         */