From b86ca09cc59db979de9d95f7d236f4c39f44d532 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 21 Jul 2011 21:28:37 +0200 Subject: [PATCH] Image manipulation support for GD and ImageMagick Classes are not finished yet, this is a work in progress, please do not modify. --- .../lib/system/image/ImageHandler.class.php | 0 .../image/adapter/GDImageAdapter.class.php | 258 ++++++++++++++++++ .../image/adapter/IImageAdapter.class.php | 0 .../image/adapter/ImageAdapter.class.php | 84 ++++++ .../adapter/ImagickImageAdapter.class.php | 98 +++++++ 5 files changed, 440 insertions(+) create mode 100644 wcfsetup/install/files/lib/system/image/ImageHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php create mode 100644 wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php create mode 100644 wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php create mode 100644 wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php 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 index 0000000000..e69de29bb2 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 index 0000000000..775901234b --- /dev/null +++ b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php @@ -0,0 +1,258 @@ +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 index 0000000000..e69de29bb2 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 index 0000000000..47c1bf1848 --- /dev/null +++ b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php @@ -0,0 +1,84 @@ +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 index 0000000000..497a299a64 --- /dev/null +++ b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php @@ -0,0 +1,98 @@ +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); + } +} -- 2.20.1