From a7c0248c576332e3317e1310f06481693d95b866 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Mon, 22 Dec 2014 10:58:12 +0100 Subject: [PATCH] Add API to overlay image --- .../image/adapter/GDImageAdapter.class.php | 22 ++++ .../image/adapter/IImageAdapter.class.php | 20 ++++ .../image/adapter/ImageAdapter.class.php | 100 ++++++++++++++++++ .../adapter/ImagickImageAdapter.class.php | 24 +++++ 4 files changed, 166 insertions(+) diff --git a/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php index 132acaa03e..f4420e30d1 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php @@ -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() */ diff --git a/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php index c97345cad3..aaa0670dcc 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php @@ -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. * diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php index d5daca8834..0433dac0e2 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php @@ -19,6 +19,22 @@ class ImageAdapter implements IImageAdapter { */ protected $adapter = null; + /** + * supported relative positions + * @var array + */ + 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() */ diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php index da7b7f6c33..42b3eaacb8 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php @@ -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() */ -- 2.20.1