/core/classes/framework/image/image.php
PHP | 774 lines | 408 code | 111 blank | 255 comment | 44 complexity | 8de680d0aee18b2f89ef2a899592f99c MD5 | raw file
- <?php
- class Image {
- const MIME_TYPE_JPEG = "image/jpeg";
- const MIME_TYPE_PNG = "image/png";
- const MIME_TYPE_GIF = "image/gif";
- const MIME_TYPE_WBMP = "image/wbmp";
-
- const FLIP_HORIZONTAL = "y";
- const FLIP_VERTICAL = "x";
- const ORIENTATION_PORTRAIT = 1;
- const ORIENTATION_LANDSCAPE = 2;
- const ORIENTATION_SQUARE = 3;
-
- const OUTPUT_JPEG = "jpeg";
- const OUTPUT_JPG = "jpg";
- const OUTPUT_PNG = "png";
- const OUTPUT_GIF = "gif";
-
- const DEFAULT_IMAGE_QUALITY = 90;
- /**
- *
- * @var string
- */
- private $imagePath = "";
-
- /**
- *
- * @var resource
- */
- private $image;
-
- /**
- *
- * @var array
- */
- private $originalInfo = array();
- /**
- *
- * @param string $imageFile path to the image file
- *
- * @return \Image
- * @throws Exception
- */
- public function __construct($imageFile = null) {
- if (!$imageFile){
- throw new Exception('Path to the image file required!');
- }
-
- if(!extension_loaded('gd')){
- throw new Exception('Required extension GD is not loaded!');
- }
- if (!is_readable($imageFile)){
- throw new Exception('File is not readable!');
- }
- if (!$this->_load($imageFile)){
- throw new Exception('Can not load image file!');
- } else {
- return $this;
- }
- }
- public function __destruct() {
- if($this->image){
- imagedestroy($this->image);
- }
- }
- public function _load($filename) {
- $this->imagePath = $filename;
- $info = getimagesize($this->imagePath);
- if ($info) {
- switch($info['mime']) {
- case self::MIME_TYPE_GIF:
- $this->image = imagecreatefromgif($this->imagePath);
- break;
- case self::MIME_TYPE_JPEG:
- $this->image = imagecreatefromjpeg($this->imagePath);
- break;
- case self::MIME_TYPE_PNG:
- $this->image = imagecreatefrompng($this->imagePath);
- break;
- case self::MIME_TYPE_WBMP:
- $this->image = imagecreatefromwbmp($this->imagePath);
- break;
- default:
- throw new Exception('Invalid image: ' . $this->imagePath);
- break;
- }
-
- imagesavealpha($this->image, true);
- imagealphablending($this->image, true);
- $this->originalInfo = array(
- 'image' => $this->image,
- 'width' => $info[0],
- 'height' => $info[1],
- 'orientation' => $this->getOrientation(),
- 'exif' => function_exists('exif_read_data') ? @exif_read_data($this->imagePath) : array(),
- 'format' => $this->fileExt($this->imagePath),
- 'mime' => $info['mime']
- );
- return true;
- } else {
- return false;
- }
- }
- /**
- *
- * Restores original image
- *
- * @return \Image
- */
- public function restore()
- {
- $this->image = $this->originalInfo['image'];
-
- return $this;
- }
- /**
- *
- * Saves image or outputs it to the browser.
- * Available oprions are:
- * - filename - name of destination file. Outputs to the browser if no file
- * - format - image save format. Will be set from 'filename' option if there is one
- * or from original image info
- * - quality - quality of the image from 0 to 100. Effects on 'jpg' and 'png' output
- *
- * @param array $options
- * @return \Image
- * @throws Exception
- */
- public function save(array $options = array()) {
- $filename = "";
- $format = "";
- $quality = self::DEFAULT_IMAGE_QUALITY;
- extract($options, EXTR_IF_EXISTS | EXTR_OVERWRITE);
- if (!$format){
- if ($filename){
- $format = $this->fileExt($filename);
- } else {
- $format = $this->originalInfo['format'];
- }
- }
- switch(strtolower($format)) {
- case self::OUTPUT_GIF: {
- $result = imagegif($this->image, $filename);
- break;
- }
- case self::OUTPUT_JPEG:
- case self::OUTPUT_JPG: {
- $quality = $this->keepWithin($quality, 0, 100);
- $result = imagejpeg($this->image, $filename, $quality);
- break;
- }
- case self::OUTPUT_PNG: {
- if($quality >= 10) {
- $quality = ($quality - $quality % 10) / 10;;
- }
- $quality = $this->keepWithin($quality, 0, 9);
- $filters = null;
- if (isset($options['pngfilters'])){
- $filters = $options['pngfilters'];
- }
- $result = imagepng($this->image, $filename, $quality, $filters);
- break;
- }
- default:
- throw new Exception("Unsupported format image format: '{$format}'");
- }
- if(!$result)
- throw new Exception('Unable to save image: ' . $filename);
- return $this;
- }
- /**
- *
- * Returns information about original image
- *
- * @return array
- */
- public function getOriginalInfo(){
- return $this->originalInfo;
- }
-
- /**
- *
- * Returns mime type of the image
- *
- * @return string
- */
- public function getMimeType(){
- return $this->originalInfo['mime'];
- }
- /**
- *
- * Get current image width
- *
- * @return int
- */
- public function getWidth() {
- return imagesx($this->image);
- }
- /**
- *
- * Get current image height
- *
- * @return int
- */
- public function getHeight() {
- return imagesy($this->image);
- }
- /**
- *
- * Get current image orientation
- *
- * @return string
- */
- public function getOrientation() {
- if($this->getWidth() > $this->getHeight()){
- return self::ORIENTATION_LANDSCAPE;
- }
- if($this->getWidth() < $this->getHeight()){
- return self::ORIENTATION_PORTRAIT;
- }
- return self::ORIENTATION_SQUARE;
- }
- /**
- *
- * @param string $direction
- * @return \Image
- */
- private function flip($direction) {
- $height = $this->getHeight();
- $width = $this->getWidth();
- $new = imagecreatetruecolor($width, $height);
- imagealphablending($new, false);
- imagesavealpha($new, true);
- switch( strtolower($direction) ) {
- case self::FLIP_VERTICAL: {
- for($y = 0; $y < $height; $y++){
- imagecopy($new, $this->image, 0, $y, 0, $height - $y - 1, $width, 1);
- }
- break;
- }
- case self::FLIP_HORIZONTAL: {
- for($x = 0; $x < $width; $x++){
- imagecopy($new, $this->image, $x, 0, $width - $x - 1, 0, 1, $height);
- }
- break;
- }
- }
- $this->image = $new;
- return $this;
- }
-
- public function flipHorizontal(){
- return $this->flip(self::FLIP_HORIZONTAL);
- }
- public function flipVertical(){
- return $this->flip(self::FLIP_VERTICAL);
- }
-
- /**
- *
- * Rotates image by $angle degrees
- *
- * @param int $angle
- * @param string $bgColor hex color value
- * @return \Image
- * @throws Exception
- */
- public function rotate($angle, $bgColor = '#000000') {
- if (function_exists('imagerotate')){
- $rgb = $this->hex2rgb($bgColor);
- $bgColor = imagecolorallocate($this->image, $rgb['r'], $rgb['g'], $rgb['b']);
- $new = imagerotate($this->image, ($this->keepWithin($angle, -360, 360)), $bgColor);
- imagesavealpha($new, true);
- imagealphablending($new, true);
- $this->image = $new;
- return $this;
- } else {
- throw new Exception("Image rotate function is not supported");
- }
- }
- /**
- *
- * Uses Exif orientation tag to orient image respectively
- *
- * @return \Image
- */
- public function autoOrient() {
- if (isset($this->original_info['exif']['Orientation'])){
- switch( $this->original_info['exif']['Orientation'] ) {
- case 1:{
- // Do nothing
- break;
- }
- case 2: {
- // Flip horizontal
- $this->flipHorizontal();
- break;
- }
- case 3: {
- // Rotate 180 counterclockwise
- $this->rotate(180);
- break;
- }
- case 4: {
- // vertical flip
- $this->flipVertical();
- break;
- }
- case 5: {
- // flip vertically and rotate 90 clockwise
- $this->flipVertical()->rotate(-90);
- break;
- }
- case 6: {
- // Rotate 90 clockwise
- $this->rotate(-90);
- break;
- }
- case 7: {
- // flip horizontally and rotate 90 clockwise
- $this->flipHorizontal()->rotate(-90);
- break;
- }
- case 8: {
- // Rotate 90 counterclockwise
- $this->rotate(90);
- break;
- }
- }
- }
- return $this;
- }
- /**
- *
- * Resizes image to the specified dimensions
- *
- * @param type $width
- * @param type $height
- * @return \Image
- */
- public function resize($width, $height, $keepAspectRatio = false) {
- $new = imagecreatetruecolor($width, $height);
- imagealphablending($new, false);
- imagesavealpha($new, true);
-
- $dstX = 0;
- $dstY = 0;
- $dstWidth = $width;
- $dstHeight = $height;
-
- if($keepAspectRatio){
- $srcAspectRatio = $this->getWidth() / $this->getHeight();
- $dstAspectRatio = $width / $height;
- if ($srcAspectRatio > $dstAspectRatio) {
- $dstHeight = $width / $srcAspectRatio;
- $dstY = round(($height - $dstHeight) / 2);
- } elseif($srcAspectRatio < $dstAspectRatio) {
- $dstWidth = $height * $srcAspectRatio;
- $dstX = round(($width - $dstWidth) / 2);
- }
- }
-
- imagecopyresampled($new, $this->image, $dstX, $dstY, 0, 0, $dstWidth, $dstHeight, $this->getWidth(), $this->getHeight());
- $this->image = $new;
- return $this;
- }
- /**
- *
- * @param type $width
- * @return type
- */
- public function fitToWidth($width) {
- $aspectRatio = $this->getHeight() / $this->getWidth();
- $height = $width * $aspectRatio;
- return $this->resize($width, $height);
- }
- /**
- *
- * @param type $height
- * @return type
- */
- public function fitToHeight($height) {
- $aspectRatio = $this->getHeight() / $this->getWidth();
- $width = $height / $aspectRatio;
- return $this->resize($width, $height);
- }
- /**
- *
- * Best fit to specified dimensions
- *
- * @param type $maxWidth
- * @param type $maxHeight
- * @return \Image
- */
- public function bestFit($maxWidth, $maxHeight) {
- if($this->getWidth() == $maxWidth && $this->getHeight() == $maxHeight){
- return $this;
- }
- $aspectRatio = $this->getHeight() / $this->getWidth();
- // Make width fit into new dimensions
- if( $this->getWidth() > $maxWidth ) {
- $width = $maxWidth;
- $height = $width * $aspectRatio;
- } else {
- $width = $this->getWidth();
- $height = $this->getHeight();
- }
- // Make height fit into new dimensions
- if( $height > $maxHeight ) {
- $height = $maxHeight;
- $width = $height / $aspectRatio;
- }
- return $this->resize($width, $height);
- }
- /**
- *
- * Crops the image
- *
- * @param int $x1 left
- * @param int $y1 top
- * @param int $x2 right
- * @param int $y2 bottom
- * @return \Image
- */
- public function crop($x1, $y1, $x2, $y2) {
- if( $x2 < $x1 ) list($x1, $x2) = array($x2, $x1);
- if( $y2 < $y1 ) list($y1, $y2) = array($y2, $y1);
- $cropWidth = $this->keepWithin($x2 - $x1, 0, $this->getWidth() - $x1);
- $cropHeight = $this->keepWithin($y2 - $y1, 0, $this->getHeight() - $y1);
- $new = imagecreatetruecolor($cropWidth, $cropHeight);
- imagealphablending($new, false);
- imagesavealpha($new, true);
- imagecopyresampled($new, $this->image, 0, 0, $x1, $y1, $cropWidth, $cropHeight, $cropWidth, $cropHeight);
- $this->image = $new;
- return $this;
- }
- /**
- *
- * Crops the biggest part of the image best for $xWidth, $yWidth.
- *
- * @param int $xWidth
- * @param int $yWidth
- * @return \Image
- */
- public function centerCrop($xWidth, $yWidth) {
- $height = $this->getHeight();
- $width = $this->getWidth();
- $srcAspectRatio = $this->getWidth() / $this->getHeight();
- $dstAspectRatio = $xWidth / $yWidth;
- if($srcAspectRatio > $dstAspectRatio) {
- $xOffsetWidth = $height * $dstAspectRatio;
- $yOffsetHeight = $height;
- $xOffset = ($width - $xOffsetWidth) / 2;
- $yOffset = 0;
- } elseif ($srcAspectRatio <= $dstAspectRatio) {
- $xOffsetWidth = $width;
- $yOffsetHeight = $width / $dstAspectRatio;
- $xOffset = 0;
- $yOffset = ($height - $yOffsetHeight) / 2;
- }
- $this->crop($xOffset, $yOffset, $xOffset + $xOffsetWidth, $yOffset + $yOffsetHeight);
- $this->resize($xWidth, $yWidth);
- return $this;
- }
- /**
- *
- * Grayscale the image
- *
- * @return \Image
- */
- public function grayscale() {
- imagefilter($this->image, IMG_FILTER_GRAYSCALE);
- return $this;
- }
- /**
- *
- * Negate the image
- *
- * @return \Image
- */
- public function negate() {
- imagefilter($this->image, IMG_FILTER_NEGATE);
- return $this;
- }
- /**
- *
- * Changes brightness level
- *
- * @param int $level
- * @return \Image
- */
- public function brightness($level) {
- imagefilter($this->image, IMG_FILTER_BRIGHTNESS, $this->keepWithin($level, -255, 255));
- return $this;
- }
- /**
- *
- * Changes contrast level
- *
- * @param int $level
- * @return \Image
- */
- public function contrast($level) {
- imagefilter($this->image, IMG_FILTER_CONTRAST, $this->keepWithin($level, -100, 100));
- return $this;
- }
- /**
- *
- * Colorizes
- *
- * @param string $color hex color
- * @param float $opacity 0 to 1 value
- * @return \Image
- */
- public function colorize($color, $opacity = 0) {
- $rgb = $this->hex2rgb($color);
- $alpha = $this->keepWithin(127 - (127 * $opacity), 0, 127);
- imagefilter($this->image, IMG_FILTER_COLORIZE, $this->keepWithin($rgb['r'], 0, 255), $this->keepWithin($rgb['g'], 0, 255), $this->keepWithin($rgb['b'], 0, 255), $alpha);
- return $this;
- }
- /**
- *
- * Uses edge detection to highlight the edges in the image
- *
- * @return \Image
- */
- public function edges() {
- imagefilter($this->image, IMG_FILTER_EDGEDETECT);
- return $this;
- }
- /**
- *
- * Embosses the image
- *
- * @return \Image
- */
- public function emboss() {
- imagefilter($this->image, IMG_FILTER_EMBOSS);
- return $this;
- }
- /**
- *
- * Uses mean removal to achieve a "sketchy" effect
- *
- * @return \Image
- */
- public function meanRemove() {
- imagefilter($this->image, IMG_FILTER_MEAN_REMOVAL);
- return $this;
- }
- /**
- *
- * Alias of meanRemove()
- *
- * @return type
- */
- public function sketch(){
- return $this->meanRemove();
- }
- /**
- * Blurs the image using the Gaussian method
- *
- * @param int $passes
- * @return \Image
- */
- public function gaussianBlur($passes = 1){
- for($i = 0; $i < $passes; $i++){
- imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
- }
- return $this;
- }
- /**
- * Blurs the image
- *
- * @param int $passes
- * @return \Image
- */
- public function selectiveBlur($passes = 1){
- for($i = 0; $i < $passes; $i++){
- imagefilter($this->image, IMG_FILTER_SELECTIVE_BLUR);
- }
- return $this;
- }
- /**
- *
- * Makes the image smoother
- *
- * @param type $level
- * @return \Image
- */
- public function smooth($level) {
- imagefilter($this->image, IMG_FILTER_SMOOTH, $this->keepWithin($level, -10, 10));
- return $this;
- }
- /**
- *
- * Applies pixelation effect to the image
- *
- * @param type $blockSize
- * @return \Image
- */
- public function pixelate($blockSize = 10) {
- imagefilter($this->image, IMG_FILTER_PIXELATE, $blockSize, true);
- return $this;
- }
- /**
- *
- * Makes sepia effect
- *
- * @return \Image
- */
- public function sepia() {
- imagefilter($this->image, IMG_FILTER_GRAYSCALE);
- $this->colorize('#704214');
- return $this;
- }
- /**
- *
- * Ensure that the value is always between $min and $max
- *
- * @param mixed $value
- * @param mixed $min
- * @param mixed $max
- * @return mixed
- */
- private function keepWithin($value, $min, $max) {
- if( $value < $min ) return $min;
- if( $value > $max ) return $max;
- return $value;
- }
- /**
- *
- * Returns file extension
- *
- * @param string $filename
- * @return string
- */
- private function fileExt($filename) {
- if(!preg_match('/\./', $filename)) {
- return '';
- }
- return preg_replace('/^.*\./', '', $filename);
- }
- /**
- *
- * Converts hex color to its RGB representation
- *
- * @param type $hex_color
- * @return boolean
- */
- private function hex2rgb($hex_color) {
- if($hex_color[0] == '#'){
- $hex_color = substr($hex_color, 1);
- }
- if(strlen($hex_color) == 6) {
- list($r, $g, $b) = array(
- $hex_color[0] . $hex_color[1],
- $hex_color[2] . $hex_color[3],
- $hex_color[4] . $hex_color[5]
- );
- } elseif(strlen($hex_color) == 3) {
- list($r, $g, $b) = array(
- $hex_color[0] . $hex_color[0],
- $hex_color[1] . $hex_color[1],
- $hex_color[2] . $hex_color[2]
- );
- } else {
- trigger_error("Hex color format is incorrect", E_USER_WARNING);
- return false;
- }
- return array(
- 'r' => hexdec($r),
- 'g' => hexdec($g),
- 'b' => hexdec($b)
- );
- }
- }