Image manipulation support for GD and ImageMagick
authorAlexander Ebert <ebert@woltlab.com>
Thu, 21 Jul 2011 19:28:37 +0000 (21:28 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 21 Jul 2011 19:28:37 +0000 (21:28 +0200)
Classes are not finished yet, this is a work in progress, please do not modify.

wcfsetup/install/files/lib/system/image/ImageHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php [new file with mode: 0644]

diff --git a/wcfsetup/install/files/lib/system/image/ImageHandler.class.php b/wcfsetup/install/files/lib/system/image/ImageHandler.class.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php
new file mode 100644 (file)
index 0000000..7759012
--- /dev/null
@@ -0,0 +1,258 @@
+<?php
+class GDImageAdapter {
+       /**
+        * image height
+        * @var integer
+        */     
+       protected $height = 0;
+       
+       /**
+        * loaded image
+        * @var resource
+        */     
+       protected $image = null;
+       
+       /**
+        * image type
+        * @var integer
+        */
+       protected $type = 0;
+       
+       /**
+        * image width
+        * @var integer
+        */     
+       protected $width = 0;
+       
+       /**
+        * Loads an image from a resource.
+        * 
+        * @param       resource        $image
+        * @param       integer         $type
+        */
+       public function load($image, $type = '') {
+               if (!is_resource($image)) {
+                       throw new SystemException("Image resource is invalid.");
+               }
+               
+               if (empty($type)) {
+                       throw new SystemException("Image type is missing.");
+               }
+               
+               $this->image = $image;
+               $this->type = $type;
+               
+               $this->height = imageSY($this->image);
+               $this->width = imageSX($this->image);
+       }
+       
+       /**
+        * Loads an image from file.
+        * 
+        * @param       string          $file
+        */     
+       public function loadFile($file) {
+               list($this->width, $this->height, $this->type) = getImageSize($file);
+               
+               switch ($this->type) {
+                       case IMAGETYPE_GIF:
+                               $this->image = imageCreateFromGif($file);
+                       break;
+                       
+                       case IMAGETYPE_JPEG:
+                               $this->image = imageCreateFromJpeg($file);
+                       break;
+                       
+                       case IMAGETYPE_PNG:
+                               $this->image = imageCreateFromPng($file);
+                       break;
+                       
+                       default:
+                               throw new SystemException("Could not read image '".$file."', format is not recognized.");
+                       break;
+               }
+       }
+       
+       /**
+        * Creates a thumbnail from previously loaded image.
+        * 
+        * @param       integer         $maxWidth
+        * @param       integer         $maxHeight
+        * @param       boolean         $obtainDimensions
+        * @return      resource
+        */     
+       public function createThumbnail($maxWidth, $maxHeight, $obtainDimensions = true) {
+               if ($maxWidth > $this->width || $maxHeight > $this->height) {
+                       throw new SystemException("Dimensions for thumbnail can not exceed image dimensions.");
+               }
+               
+               $width = $height = $x = $y = 0;
+               
+               if ($obtainDimensions) {
+                       $widthScale = $maxWidth / $this->width;
+                       $heightScale = $maxHeight / $this->height;
+                       
+                       if ($widthScale > $heightScale) {
+                               $width = round($this->width * $heightScale, 0);
+                               $height = $maxHeight;
+                       }
+                       else {
+                               $width = $maxWidth;
+                               $height = round($this->height * $widthScale, 0);
+                       }
+               }
+               else {
+                       if ($this->width > $this->height) {
+                               $x = ceil(($this->width - $this->height) / 2);
+                               $width = $height = $this->height;
+                       }
+                       else {
+                               $y = ceil(($this->height - $this->width) / 2);
+                               $height = $width = $this->width;
+                       }
+               }
+               
+               // resize image
+               $image = imageCreateTrueColor($width, $height);
+               imageAlphaBlending($image, false);
+               imageCopyResampled($image, $this->image, 0, 0, $x, $y, $width, $height, $this->width, $this->height);
+               imageSaveAlpha($image, true);
+               
+               return $image;
+       }
+       
+       /**
+        * Clips a part of currently loaded image, overwrites image resource within instance.
+        * 
+        * @param       integer         $originX
+        * @param       integer         $originY
+        * @param       integer         $width
+        * @param       integer         $height
+        * @see wcf\system\image\adapter\GDImageAdapter::getImage()
+        */
+       public function clip($originX, $originY, $width, $height) {
+               // validate if coordinates and size are within bounds
+               if ($originX < 0 || $originY < 0) {
+                       throw new SystemException("Clipping an image requires valid offsets, an offset below zero is invalid.");
+               }
+               if ($width <= 0 || $height <= 0) {
+                       throw new SystemException("Clipping an image requires valid dimensions, width or height below or equal zero are invalid.");
+               }
+               if ((($originX + $width) > $this->width) || (($originY + $height) > $this->height)) {
+                       throw new SystemException("Offset and dimension can not exceed image dimensions.");
+               }
+               
+               $image = imageCreateTrueColor($width, $height);
+               imageAlphaBlending($image, false);
+               
+               imageCopy($image, $this->image, 0, 0, $originX, $originY, $width, $height);
+               imageSaveAlpha($image, true);
+               
+               $this->image = $image;
+       }
+       
+       /**
+        * Resizes an image with optional scaling, overwrites image resource within instance.
+        * 
+        * @param       integer         $originX
+        * @param       integer         $originY
+        * @param       integer         $originWidth
+        * @param       integer         $originHeight
+        * @param       integer         $targetX
+        * @param       integer         $targetY
+        * @param       integer         $targetWidth
+        * @param       integer         $targetHeight
+        * @see wcf\system\image\adapter\GDImageAdapter::getImage()
+        */
+       public function resize($originX, $originY, $originWidth, $originHeight, $targetX = 0, $targetY = 0, $targetWidth = 0, $targetHeight = 0) {
+               // use origin dimensions if target dimensions are both zero
+               if ($targetWidth == 0 && $targetHeight == 0) {
+                       $targetWidth = $originWidth;
+                       $targetHeight = $originHeight;
+               }
+               
+               $image = imageCreateTrueColor($targetWidth, $targetHeight);
+               imageAlphaBlending($image, false);
+               
+               imageCopyResampled($image, $this->image, $targetX, $targetY, $originX, $originY, $targetWidth, $targetHeight, $originWidth, $originHeight);
+               imageSaveAlpha($image, true);
+               
+               $this->image = $image;
+       }
+       
+       /**
+        * Draws a rectangle, overwrites image resource within instance.
+        * 
+        * @param       integer         $startX
+        * @param       integer         $startY
+        * @param       integer         $endX
+        * @param       integer         $endY
+        * @param       integer         $color
+        * @see wcf\system\image\adapter\GDImageAdapter::getColor()
+        * @see wcf\system\image\adapter\GDImageAdapter::getImage()
+        */
+       public function drawRectangle($startX, $startY, $endX, $endY, $color) {
+               imageFilledRectangle($this->image, $startX, $startY, $endX, $endY, $color);
+       }
+       
+       /**
+        * Draws a line of text, overwrites image resource within instance.
+        * 
+        * @param       string          $string
+        * @param       integer         $x
+        * @param       integer         $y
+        * @param       integer         $color
+        * @param       integer         $font
+        * @see wcf\system\image\adapter\GDImageAdapter::getColor()
+        * @see wcf\system\image\adapter\GDImageAdapter::getImage()
+        */
+       public function drawText($string, $x, $y, $color, $font = 3) {
+               imageString($this->image, $font, $x, $y, $string, $color);
+       }
+       
+       /**
+        * Creates a color value based upon RGB.
+        * 
+        * @param       integer         $red
+        * @param       integer         $green
+        * @param       integer         $blue
+        * @return      integer
+        */     
+       public function getColor($red, $green, $blue) {
+               return imageColorAllocate($this->image, $red, $green, $blue);
+       }
+       
+       /**
+        * Writes an image to disk.
+        * 
+        * @param       resource        $image
+        * @param       string          $filename
+        */     
+       public function writeImage($image, $filename) {
+               ob_start();
+               
+               if ($this->type == IMAGETYPE_GIF && function_exists('imageGIF')) {
+                       imageGIF($image);
+               }
+               else if (($this->type == IMAGETYPE_GIF || $this->type == IMAGETYPE_PNG) && function_exists('imagePNG')) {
+                       imagePNG($image);
+               }
+               else if (function_exists('imageJPEG')) {
+                       imageJPEG($image, '', 90);
+               }
+               
+               $thumbnail = ob_get_contents();
+               ob_end_clean();
+               
+               file_put_contents($filename, $thumbnail);
+       }
+       
+       /**
+        * Returns image resource.
+        * 
+        * @return      resource
+        */
+       public function getImage() {
+               return $this->image;
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php
new file mode 100644 (file)
index 0000000..47c1bf1
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+namespace wcf\system\image\adapter;
+use wcf\system\session\SystemException;
+
+class ImageAdapter {
+       protected $adapter = null;
+       
+       public function __construct($adapterClassName) {
+               $this->adapter = new $adapterClassName();
+       }
+       
+       public function load($image, $type = 0) {
+               $this->adapter->load($image, $type);
+       }
+       
+       public function loadFile($file) {
+               if (!file_exists($file) || !is_readable($file)) {
+                       throw new SystemException("Image '".$file."' is not readable or does not exists.");
+               }
+               
+               $this->adapter->loadFile($file);
+       }
+       
+       public function createThumbnail($maxWidth, $maxHeight, $obtainDimensions = true) {
+               if ($maxWidth > $this->getWidth() || $maxHeight > $this->getHeight()) {
+                       throw new SystemException("Dimensions for thumbnail can not exceed image dimensions.");
+               }
+               
+               return $this->adapter->createThumbnail($maxWidth, $maxHeight, $obtainDimensions);
+       }
+       
+       public function clip($originX, $originY, $width, $height) {
+               // validate if coordinates and size are within bounds
+               if ($originX < 0 || $originY < 0) {
+                       throw new SystemException("Clipping an image requires valid offsets, an offset below zero is invalid.");
+               }
+               if ($width <= 0 || $height <= 0) {
+                       throw new SystemException("Clipping an image requires valid dimensions, width or height below or equal zero are invalid.");
+               }
+               if ((($originX + $width) > $this->getWidth()) || (($originY + $height) > $this->getHeight())) {
+                       throw new SystemException("Offset and dimension can not exceed image dimensions.");
+               }
+               
+               $this->adapter->clip($originX, $originY, $width, $height);
+       }
+       
+       public function resize($originX, $originY, $originWidth, $originHeight, $targetX, $targetY, $targetWidth, $targetHeight) {
+               // use origin dimensions if target dimensions are both zero
+               if ($targetWidth == 0 && $targetHeight == 0) {
+                       $targetWidth = $originWidth;
+                       $targetHeight = $originHeight;
+               }
+               
+               $this->adapter->resize($originX, $originY, $originWidth, $originHeight, $targetX, $targetY, $targetWidth, $targetHeight);
+       }
+       
+       public function drawRectangle($startX, $startY, $endX, $endY) {
+               if (!$this->adapter->hasColor()) {
+                       throw new SystemException("Cannot draw a rectangle unless a color has been specified with setColor().");
+               }
+               
+               $this->adapter->drawRectangle($startX, $startY, $endX, $endY);
+       }
+       
+       public function drawText($string, $x, $y) {
+               if (!$this->adapter->hasColor()) {
+                       throw new SystemException("Cannot draw text unless a color has been specified with setColor().");
+               }
+               
+               $this->adapter->drawText($string, $x, $y);
+       }
+       
+       public function setColor($red, $green, $blue) {
+               $this->adapter->setColor($red, $green, $blue);
+       }
+       
+       public function getWidth() {
+               return $this->adapter->getWidth();
+       }
+       
+       public function getHeight() {
+               return $this->adapter->getHeight();
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php
new file mode 100644 (file)
index 0000000..497a299
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+require_once('GD.php');
+
+class ImagickImageAdapter extends GDImageAdapter {
+       protected $imagick = null;
+       protected $color = null;
+       
+       
+       public function __construct() {
+               $this->imagick = new \Imagick();
+       }
+       
+       public function load($image, $type = '') {
+               if (!($image instanceof \Imagick)) {
+                       throw new SystemException("Object must be an instance of Imagick");
+               }
+               
+               $this->imagick = $imagick;
+               $this->height = $this->imagick->getImageHeight();
+               $this->width = $this->imagick->getImageWidth();
+       }
+       
+       public function loadFile($file) {
+               try {
+                       $this->imagick->readImage($file);
+               }
+               catch (\ImagickException $e) {
+                       throw new SystemException("Image '".$file."' is not readable or does not exist.");
+               }
+               $this->height = $this->imagick->getImageHeight();
+               $this->width = $this->imagick->getImageWidth();
+       }
+       
+       public function createThumbnail($maxWidth, $maxHeight, $obtainDimensions = true) {
+               $thumbnail = $this->imagick;
+               
+               $thumbnail = $this->imagick;
+               $thumbnail->cropThumbnailImage($maxWidth, $maxHeight);
+               
+               return $thumbnail;
+       }
+       
+       public function clip($originX, $originY, $width, $height) {
+               // validate if coordinates and size are within bounds
+               if ($originX < 0 || $originY < 0) {
+                       throw new SystemException("Clipping an image requires valid offsets, an offset below zero is invalid.");
+               }
+               if ($width <= 0 || $height <= 0) {
+                       throw new SystemException("Clipping an image requires valid dimensions, width or height below or equal zero are invalid.");
+               }
+               if ((($originX + $width) > $this->width) || (($originY + $height) > $this->height)) {
+                       throw new SystemException("Offset and dimension can not exceed image dimensions.");
+               }
+               
+               $this->imagick->cropImage($width, $height, $originX, $originY);
+       }
+       
+       public function drawRectangle($startX, $startY, $endX, $endY, $color) {
+               $draw = new \ImagickDraw();
+               $draw->setFillColor($this->color);
+               $draw->setStrokeColor($this->color);
+               $draw->rectangle($startX, $startY, $endX, $endY);
+               
+               $this->imagick->drawImage($draw);
+       }
+       
+       public function drawText($string, $x, $y, $color, $font = 4) {
+               $draw = new \ImagickDraw();
+               $draw->setFillColor($this->color);
+               $draw->setTextAntialias(true);
+               
+               // draw text
+               $draw->annotation($x, $y, $string);
+               $this->imagick->drawImage($draw);
+       }
+       
+       public function setColor($red, $green, $blue) {
+               $this->color = new \ImagickPixel();
+               $this->color->setColor('rgb('.$red.','.$green.','.$blue.')');
+               
+       }
+       
+       public function hasColor() {
+               if ($this->color instanceof \ImagickPixel) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
+       public function getImage() {
+               return $this->imagick;
+       }
+       
+       public function writeImage($image, $filename) {
+               $image->writeImage($filename);
+       }
+}