From c3b75b7edba5c56cd4dab24a323167b23814c6bc Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Fri, 3 Oct 2014 01:53:27 +0200 Subject: Rework image manipulation class This is the first of hopefully more classes using namespaces and proper classes that can be used as objects rather than CI's singleton approach. The namespace is mainly used to gain nice autoloading capabilities and it's not really yet used for separation. Signed-off-by: Florian Pritz --- application/controllers/file.php | 6 +- application/core/MY_Controller.php | 1 + application/libraries/Customautoloader.php | 26 +++ application/libraries/Image.php | 267 +++++++++++++++++++++++++++++ application/libraries/Imglib.php | 169 ------------------ 5 files changed, 297 insertions(+), 172 deletions(-) create mode 100644 application/libraries/Customautoloader.php create mode 100644 application/libraries/Image.php delete mode 100644 application/libraries/Imglib.php diff --git a/application/controllers/file.php b/application/controllers/file.php index 1bdab2c9f..57e9d4d1d 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -316,7 +316,6 @@ class File extends MY_Controller { private function _tooltip_for_image($filedata) { - $this->load->library("imglib"); $filesize = format_bytes($filedata["filesize"]); list($width, $height) = getimagesize($this->mfile->file($filedata["hash"])); $upload_date = date("r", $filedata["date"]); @@ -517,8 +516,9 @@ class File extends MY_Controller { $thumb = cache_function($cache_key, 100, function() use ($filedata, $thumb_size){ $CI =& get_instance(); - $this->load->library("imglib"); - $thumb = $CI->imglib->makeThumb($this->mfile->file($filedata["hash"]), $thumb_size, IMAGETYPE_JPEG); + $img = new libraries\Image($this->mfile->file($filedata["hash"])); + $img->makeThumb($thumb_size, $thumb_size); + $thumb = $img->get(IMAGETYPE_JPEG); if ($thumb === false) { show_error("Failed to generate thumbnail"); diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index bba8725a9..53aad9145 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -44,6 +44,7 @@ class MY_Controller extends CI_Controller { mb_internal_encoding('UTF-8'); $this->load->helper(array('form', 'filebin')); + $this->load->library('customautoloader'); // TODO: proper accept header handling or is this enough? if (isset($_SERVER["HTTP_ACCEPT"])) { diff --git a/application/libraries/Customautoloader.php b/application/libraries/Customautoloader.php new file mode 100644 index 000000000..1904977d7 --- /dev/null +++ b/application/libraries/Customautoloader.php @@ -0,0 +1,26 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +// Original source: http://stackoverflow.com/a/9526005/953022 +class CustomAutoloader{ + public function __construct() + { + spl_autoload_register(array($this, 'loader')); + } + + public function loader($className) + { + $path = APPPATH.str_replace('\\', DIRECTORY_SEPARATOR, $className).'.php'; + if (file_exists($path)) { + require $path; + return; + } + } +} +?> diff --git a/application/libraries/Image.php b/application/libraries/Image.php new file mode 100644 index 000000000..6d6c9e563 --- /dev/null +++ b/application/libraries/Image.php @@ -0,0 +1,267 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace libraries; + +/** + * This class deals with a single image and provides useful operations that + * operate on this image. + */ +class Image { + private $img; + private $source_type; + private $exif; + + /** + * Create a new object and load the contents of file. + * @param file file to read + */ + public function __construct($file) + { + $this->read($file); + } + + /** + * Replace the current image by reading in a file + * @param file file to read + */ + public function read($file) + { + $img = imagecreatefromstring(file_get_contents($file)); + if ($img === false) { + show_error("Unsupported image type"); + } + $this->set_img_object($img); + $this->fix_alpha(); + + $this->source_type = getimagesize($file)[2]; + + switch ($this->source_type) { + case IMAGETYPE_JPEG: + $this->exif = exif_read_data($file); + break; + default: + } + } + + /** + * Return the current image rendered to a specific format. Passing null as + * the target_type returns the image in the format of the source image + * (loaded with read()). + * + * @param target_type one of IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG or null + * @return binary image data + */ + public function get($target_type = null) + { + if ($target_type === null) { + $target_type = $this->source_type; + } + + ob_start(); + switch ($target_type) { + case IMAGETYPE_GIF: + $ret = imagegif($this->img); + break; + case IMAGETYPE_JPEG: + $ret = imagejpeg($this->img); + break; + case IMAGETYPE_PNG: + $ret = imagepng($this->img); + break; + default: + assert(0); + } + $result = ob_get_clean(); + + if (!$ret) { + show_error("Failed to create thumbnail"); + } + + return $result; + } + + /** + * Resize the image. + * @param width + * @param height + */ + public function resize($width, $height) + { + $temp_gdim = imagecreatetruecolor($width, $height); + $this->fix_alpha(); + imagecopyresampled( + $temp_gdim, + $this->img, + 0, 0, + 0, 0, + $width, $height, + imagesx($this->img), imagesy($this->img) + ); + + $this->set_img_object($temp_gdim); + } + + /** + * Crop the image to the area defined by x, y, width and height. + * + * @param x starting coordinate + * @param y starting coordinate + * @param width width of the area + * @param height height of the area + */ + public function crop($x, $y, $width, $height) + { + $thumb = imagecreatetruecolor($width, $height); + $this->fix_alpha(); + imagecopy( + $thumb, + $this->img, + 0, 0, + $x, $y, + $width, $height + ); + + $this->set_img_object($thumb); + } + + /** + * Crop/resize the image to fit into the desired size. This also rotates + * the image if the source image had an EXIF orientation tag. + * + * @param width width of the resulting image + * @param height height of the resulting image + */ + // Source: http://salman-w.blogspot.co.at/2009/04/crop-to-fit-image-using-aspphp.html + public function makeThumb($target_width, $target_height) + { + $source_aspect_ratio = imagesx($this->img) / imagesy($this->img); + $desired_aspect_ratio = $target_width / $target_height; + + if ($source_aspect_ratio > $desired_aspect_ratio) { + // Triggered when source image is wider + $temp_height = $target_height; + $temp_width = round(($target_height * $source_aspect_ratio)); + } else { + // Triggered otherwise (i.e. source image is similar or taller) + $temp_width = $target_width; + $temp_height = round(($target_width / $source_aspect_ratio)); + } + + $this->resize($temp_width, $temp_height); + + $x0 = ($temp_width - $target_width) / 2; + $y0 = ($temp_height - $target_height) / 2; + $this->crop($x0, $y0, $target_width, $target_height); + + $this->apply_exif_orientation(); + } + + static public function get_exif_orientation($file) + { + $type = getimagesize($file)[2]; + switch ($type) { + case IMAGETYPE_JPEG: + $exif = exif_read_data($file); + if (isset($exif["Orientation"])) { + return $exif["Orientation"]; + } + return 0; + break; + default: + return 0; + } + } + + /** + * Rotate the image according to the sources EXIF orientation tag if any. + */ + public function apply_exif_orientation() + { + if (isset($this->exif['Orientation'])) { + $mirror = false; + $deg = 0; + + switch ($this->exif['Orientation']) { + case 2: + $mirror = true; + break; + case 3: + $deg = 180; + break; + case 4: + $deg = 180; + $mirror = true; + break; + case 5: + $deg = 270; + $mirror = true; + break; + case 6: + $deg = 270; + break; + case 7: + $deg = 90; + $mirror = true; + break; + case 8: + $deg = 90; + break; + } + + if ($deg) { + $this->set_img_object(imagerotate($this->img, $deg, 0)); + } + + if ($mirror) { + $this->mirror(); + } + } + } + + /** + * Mirror the image along the x axis. + */ + public function mirror() + { + $width = imagesx($this->img); + $height = imagesy($this->img); + + $src_x = $width -1; + $src_y = 0; + $src_width = -$width; + $src_height = $height; + + $imgdest = imagecreatetruecolor($width, $height); + imagealphablending($imgdest,false); + imagesavealpha($imgdest,true); + + imagecopyresampled($imgdest, $this->img, 0, 0, $src_x, $src_y, $width, $height, $src_width, $src_height); + $this->set_img_object($imgdest); + } + + private function set_img_object($new) + { + assert($new !== false); + + $old = $this->img; + $this->img = $new; + + if ($old != null) { + imagedestroy($old); + } + } + + private function fix_alpha() + { + imagealphablending($this->img,false); + imagesavealpha($this->img,true); + } + +} diff --git a/application/libraries/Imglib.php b/application/libraries/Imglib.php deleted file mode 100644 index 4d6de21d3..000000000 --- a/application/libraries/Imglib.php +++ /dev/null @@ -1,169 +0,0 @@ - - * - * Licensed under AGPLv3 - * (see COPYING for full license text) - * - */ - -class Imglib { - /* - * This returns a square thumbnail for the input image - * Source: http://salman-w.blogspot.co.at/2009/04/crop-to-fit-image-using-aspphp.html - */ - public function makeThumb($file, $size = 150, $target_type = null) - { - $source_gdim = imagecreatefromstring(file_get_contents($file)); - if ($source_gdim === false) { - show_error("Unsupported image type"); - } - imagealphablending($source_gdim,false); - imagesavealpha($source_gdim,true); - - list($source_width, $source_height, $source_type) = getimagesize($file); - - if ($target_type === null) { - $target_type = $source_type; - } - - $target_width = $size; - $target_height = $size; - - $source_aspect_ratio = $source_width / $source_height; - $desired_aspect_ratio = $target_width / $target_height; - - if ($source_aspect_ratio > $desired_aspect_ratio) { - // Triggered when source image is wider - $temp_height = $target_height; - $temp_width = round(($target_height * $source_aspect_ratio)); - } else { - // Triggered otherwise (i.e. source image is similar or taller) - $temp_width = $target_width; - $temp_height = round(($target_width / $source_aspect_ratio)); - } - - /* - * Resize the image into a temporary GD image - */ - - $temp_gdim = imagecreatetruecolor($temp_width, $temp_height); - imagealphablending($temp_gdim,false); - imagesavealpha($temp_gdim,true); - imagecopyresampled( - $temp_gdim, - $source_gdim, - 0, 0, - 0, 0, - $temp_width, $temp_height, - $source_width, $source_height - ); - - /* - * Copy cropped region from temporary image into the desired GD image - */ - - $x0 = ($temp_width - $target_width) / 2; - $y0 = ($temp_height - $target_height) / 2; - $thumb = imagecreatetruecolor($target_width, $target_height); - imagealphablending($thumb,false); - imagesavealpha($thumb,true); - imagecopy( - $thumb, - $temp_gdim, - 0, 0, - $x0, $y0, - $target_width, $target_height - ); - - /* - * Fix orientation according to exif tag - */ - try { - $exif = exif_read_data($file); - } catch (ErrorException $e) { - } - - if (isset($exif['Orientation'])) { - $mirror = false; - $deg = 0; - - switch ($exif['Orientation']) { - case 2: - $mirror = true; - break; - case 3: - $deg = 180; - break; - case 4: - $deg = 180; - $mirror = true; - break; - case 5: - $deg = 270; - $mirror = true; - break; - case 6: - $deg = 270; - break; - case 7: - $deg = 90; - $mirror = true; - break; - case 8: - $deg = 90; - break; - } - if ($deg) $thumb = imagerotate($thumb, $deg, 0); - if ($mirror) $thumb = $this->_mirrorImage($thumb); - } - - ob_start(); - switch ($target_type) { - case IMAGETYPE_GIF: - $ret = imagegif($thumb); - break; - case IMAGETYPE_JPEG: - $ret = imagejpeg($thumb); - break; - case IMAGETYPE_PNG: - $ret = imagepng($thumb); - break; - default: - assert(0); - } - $result = ob_get_clean(); - - if (!$ret) { - show_error("Failed to create thumbnail"); - } - - imagedestroy($thumb); - imagedestroy($temp_gdim); - imagedestroy($source_gdim); - - return $result; - } - - private function _mirrorImage($imgsrc) - { - $width = imagesx($imgsrc); - $height = imagesy($imgsrc); - - $src_x = $width -1; - $src_y = 0; - $src_width = -$width; - $src_height = $height; - - $imgdest = imagecreatetruecolor($width, $height); - imagealphablending($imgdest,false); - imagesavealpha($imgdest,true); - - if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $src_x, $src_y, $width, $height, $src_width, $src_height)) { - return $imgdest; - } - - return $imgsrc; - } - -} -- cgit v1.2.3-24-g4f1b