2012-04-25 15:49:09 +02:00
< ? php
/**
2014-02-12 14:45:25 +01:00
* Resize and crop images on the fly , store generated images in a cache .
2012-04-25 15:49:09 +02:00
*
* @ author Mikael Roos mos @ dbwebb . se
2014-02-12 14:45:25 +01:00
* @ example http :// dbwebb . se / opensource / cimage
2012-04-25 15:53:46 +02:00
* @ link https :// github . com / mosbth / cimage
2012-04-25 15:49:09 +02:00
*/
2014-08-31 23:57:34 +02:00
class CImage
2014-02-12 14:45:25 +01:00
{
/**
* Constants type of PNG image
*/
const PNG_GREYSCALE = 0 ;
const PNG_RGB = 2 ;
const PNG_RGB_PALETTE = 3 ;
const PNG_GREYSCALE_ALPHA = 4 ;
const PNG_RGB_ALPHA = 6 ;
/**
* Constant for default image quality when not set
*/
const JPEG_QUALITY_DEFAULT = 60 ;
/**
* Quality level for JPEG images .
*/
private $quality ;
/**
* Constant for default image quality when not set
*/
const PNG_COMPRESSION_DEFAULT = - 1 ;
/**
* Compression level for PNG images .
*/
private $compress ;
/**
* Where to save the target file .
*/
private $saveFolder ;
/**
* The working image object .
*/
private $image ;
/**
* The root folder of images ( only used in constructor to create $pathToImage ? ) .
*/
private $imageFolder ;
/**
* Image filename , may include subdirectory , relative from $imageFolder
*/
private $imageSrc ;
/**
* Actual path to the image , $imageFolder . '/' . $imageSrc
*/
private $pathToImage ;
/**
* Original file extension
*/
private $fileExtension ;
/**
2014-11-21 21:01:22 +01:00
* File extension to use when saving image .
2014-02-12 14:45:25 +01:00
*/
private $extension ;
2014-11-21 22:20:30 +01:00
/**
* Output format , supports null ( image ) or json .
*/
private $outputFormat = null ;
2014-02-12 14:45:25 +01:00
/**
* Verbose mode to print out a trace and display the created image
*/
private $verbose = false ;
/**
* Keep a log / trace on what happens
*/
2014-08-31 23:57:34 +02:00
private $log = array ();
2014-02-12 14:45:25 +01:00
/**
* Handle image as palette image
*/
private $palette ;
/**
* Target filename , with path , to save resulting image in .
*/
private $cacheFileName ;
/**
* Set a format to save image as , or null to use original format .
*/
private $saveAs ;
/**
* Path to command for filter optimize , for example optipng or null .
*/
private $pngFilter ;
/**
* Path to command for deflate optimize , for example pngout or null .
*/
private $pngDeflate ;
/**
* Path to command to optimize jpeg images , for example jpegtran or null .
*/
2014-08-31 23:57:34 +02:00
private $jpegOptimize ;
2014-02-12 14:45:25 +01:00
2014-08-31 23:57:34 +02:00
/**
* Image dimensions , calculated from loaded image .
*/
private $width ; // Calculated from source image
private $height ; // Calculated from source image
2014-02-12 14:45:25 +01:00
2014-08-31 23:57:34 +02:00
/**
* New image dimensions , incoming as argument or calculated .
*/
private $newWidth ;
private $newWidthOrig ; // Save original value
private $newHeight ;
private $newHeightOrig ; // Save original value
2014-02-12 14:45:25 +01:00
2012-04-25 15:49:09 +02:00
2014-09-01 00:38:26 +02:00
/**
* Array with details on how to crop , incoming as argument and calculated .
*/
public $crop ;
public $cropOrig ; // Save original value
2014-08-31 23:57:34 +02:00
/**
* Properties ( clean up these )
*/
private $offset ;
2013-10-03 18:16:33 +02:00
2014-08-31 23:57:34 +02:00
public $keepRatio ;
public $cropToFit ;
2013-10-03 18:16:33 +02:00
2014-08-31 23:57:34 +02:00
private $cropWidth ;
private $cropHeight ;
public $crop_x ;
public $crop_y ;
public $filters ;
private $type ; // Calculated from source image
private $attr ; // Calculated from source image
private $useCache ; // Use the cache if true, set to false to ignore the cached file.
private $useOriginal ; // Use original image if possible
2013-10-03 18:16:33 +02:00
2012-04-25 15:49:09 +02:00
2014-02-12 14:45:25 +01:00
/**
* Constructor , can take arguments to init the object .
*
* @ param string $imageSrc filename which may contain subdirectory .
* @ param string $imageFolder path to root folder for images .
* @ param string $saveFolder path to folder where to save the new file or null to skip saving .
* @ param string $saveName name of target file when saveing .
*/
2014-08-31 23:57:34 +02:00
public function __construct ( $imageSrc = null , $imageFolder = null , $saveFolder = null , $saveName = null )
2014-02-12 14:45:25 +01:00
{
$this -> setSource ( $imageSrc , $imageFolder );
$this -> setTarget ( $saveFolder , $saveName );
}
2012-04-25 15:49:09 +02:00
2014-02-12 14:45:25 +01:00
/**
* Set verbose mode .
*
* @ param boolean $mode true or false to enable and disable versbose mode , default is true .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function setVerbose ( $mode = true )
2014-02-12 14:45:25 +01:00
{
$this -> verbose = $mode ;
return $this ;
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Check if file extension is valid as a file extension .
*
* @ param string $extension of image file .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
private function checkFileExtension ( $extension )
2014-02-12 14:45:25 +01:00
{
$valid = array ( 'jpg' , 'jpeg' , 'png' , 'gif' );
2014-11-21 21:01:22 +01:00
in_array ( strtolower ( $extension ), $valid )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Not a valid file extension.' );
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Set src file .
*
* @ param string $src of image .
* @ param string $dir as base directory where images are .
*
* @ return $this
*/
public function setSource ( $src = null , $dir = null )
{
if ( ! ( isset ( $src ) && isset ( $dir ))) {
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
$this -> imageSrc = ltrim ( $src , '/' );
$this -> imageFolder = rtrim ( $dir , '/' );
$this -> pathToImage = $this -> imageFolder . '/' . $this -> imageSrc ;
2014-11-21 21:01:22 +01:00
$this -> fileExtension = strtolower ( pathinfo ( $this -> pathToImage , PATHINFO_EXTENSION ));
2014-11-21 22:20:30 +01:00
//$this->extension = $this->fileExtension;
2014-02-12 14:45:25 +01:00
$this -> checkFileExtension ( $this -> fileExtension );
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
}
2013-10-03 18:16:33 +02:00
2012-04-25 15:49:09 +02:00
2014-02-12 14:45:25 +01:00
/**
* Set target file .
*
* @ param string $src of target image .
* @ param string $dir as base directory where images are stored .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function setTarget ( $src = null , $dir = null )
2014-02-12 14:45:25 +01:00
{
if ( ! ( isset ( $src ) && isset ( $dir ))) {
return $this ;
2012-05-09 17:57:48 +02:00
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
$this -> saveFolder = $dir ;
$this -> cacheFileName = $dir . '/' . $src ;
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
is_writable ( $this -> saveFolder )
or $this -> raiseError ( 'Target directory is not writable.' );
// Sanitize filename
$this -> cacheFileName = preg_replace ( '/^a-zA-Z0-9\.-_/' , '' , $this -> cacheFileName );
$this -> log ( " The cache file name is: " . $this -> cacheFileName );
return $this ;
}
2012-04-25 15:49:09 +02:00
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Set options to use when processing image .
*
* @ param array $args used when processing image .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function setOptions ( $args )
2014-02-12 14:45:25 +01:00
{
2014-08-31 23:57:34 +02:00
$this -> log ( " Set new options for processing image. " );
2014-02-12 14:45:25 +01:00
$defaults = array (
// Options for calculate dimensions
'newWidth' => null ,
'newHeight' => null ,
'aspectRatio' => null ,
'keepRatio' => true ,
'cropToFit' => false ,
2014-08-31 23:57:34 +02:00
'crop' => null , //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
2014-02-12 14:45:25 +01:00
'area' => null , //'0,0,0,0',
// Options for caching or using original
'useCache' => true ,
2014-08-31 23:57:34 +02:00
'useOriginal' => true ,
2014-02-12 14:45:25 +01:00
// Pre-processing, before resizing is done
2014-08-31 23:57:34 +02:00
'scale' => null ,
'rotateBefore' => null ,
// General options
'bgColor' => 0 ,
2014-02-12 14:45:25 +01:00
// Post-processing, after resizing is done
'palette' => null ,
'filters' => null ,
'sharpen' => null ,
'emboss' => null ,
'blur' => null ,
2014-08-31 23:57:34 +02:00
'rotateAfter' => null ,
'autoRotate' => false ,
2014-02-12 14:45:25 +01:00
2014-11-21 22:20:30 +01:00
// Output format
'outputFormat' => null ,
2014-02-12 14:45:25 +01:00
// Options for saving
//'quality' => null,
//'compress' => null,
//'saveAs' => null,
);
2014-08-15 02:07:07 -04:00
// Convert crop settings from string to array
2014-02-12 14:45:25 +01:00
if ( isset ( $args [ 'crop' ]) && ! is_array ( $args [ 'crop' ])) {
$pices = explode ( ',' , $args [ 'crop' ]);
$args [ 'crop' ] = array (
'width' => $pices [ 0 ],
'height' => $pices [ 1 ],
'start_x' => $pices [ 2 ],
'start_y' => $pices [ 3 ],
);
}
2013-10-03 18:16:33 +02:00
2014-08-15 02:07:07 -04:00
// Convert area settings from string to array
2014-02-12 14:45:25 +01:00
if ( isset ( $args [ 'area' ]) && ! is_array ( $args [ 'area' ])) {
$pices = explode ( ',' , $args [ 'area' ]);
$args [ 'area' ] = array (
'top' => $pices [ 0 ],
'right' => $pices [ 1 ],
'bottom' => $pices [ 2 ],
'left' => $pices [ 3 ],
);
}
2013-10-07 23:50:53 +02:00
2014-08-15 02:07:07 -04:00
// Convert filter settings from array of string to array of array
2014-02-12 14:45:25 +01:00
if ( isset ( $args [ 'filters' ]) && is_array ( $args [ 'filters' ])) {
foreach ( $args [ 'filters' ] as $key => $filterStr ) {
$parts = explode ( ',' , $filterStr );
$filter = $this -> mapFilter ( $parts [ 0 ]);
$filter [ 'str' ] = $filterStr ;
2014-08-31 23:57:34 +02:00
for ( $i = 1 ; $i <= $filter [ 'argc' ]; $i ++ ) {
2014-02-12 14:45:25 +01:00
if ( isset ( $parts [ $i ])) {
$filter [ " arg { $i } " ] = $parts [ $i ];
} else {
2014-08-31 23:57:34 +02:00
throw new Exception ( 'Missing arg to filter, review how many arguments are needed at http://php.net/manual/en/function.imagefilter.php' );
2014-02-12 14:45:25 +01:00
}
}
$args [ 'filters' ][ $key ] = $filter ;
}
}
2013-10-07 23:50:53 +02:00
2014-02-12 14:45:25 +01:00
// Merge default arguments with incoming and set properties.
//$args = array_merge_recursive($defaults, $args);
$args = array_merge ( $defaults , $args );
2014-08-31 23:57:34 +02:00
foreach ( $defaults as $key => $val ) {
2014-02-12 14:45:25 +01:00
$this -> { $key } = $args [ $key ];
2013-10-07 23:50:53 +02:00
}
2014-02-12 14:45:25 +01:00
2014-09-01 00:38:26 +02:00
// Save original values to enable re-calculating
2014-08-31 23:57:34 +02:00
$this -> newWidthOrig = $this -> newWidth ;
$this -> newHeightOrig = $this -> newHeight ;
2014-09-01 00:38:26 +02:00
$this -> cropOrig = $this -> crop ;
2014-08-31 23:57:34 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
}
/**
* Map filter name to PHP filter and id .
*
* @ param string $name the name of the filter .
*
* @ return array with filter settings
* @ throws Exception
*/
2014-08-31 23:57:34 +02:00
private function mapFilter ( $name )
2014-02-12 14:45:25 +01:00
{
$map = array (
2014-08-31 23:57:34 +02:00
'negate' => array ( 'id' => 0 , 'argc' => 0 , 'type' => IMG_FILTER_NEGATE ),
2014-02-12 14:45:25 +01:00
'grayscale' => array ( 'id' => 1 , 'argc' => 0 , 'type' => IMG_FILTER_GRAYSCALE ),
'brightness' => array ( 'id' => 2 , 'argc' => 1 , 'type' => IMG_FILTER_BRIGHTNESS ),
'contrast' => array ( 'id' => 3 , 'argc' => 1 , 'type' => IMG_FILTER_CONTRAST ),
'colorize' => array ( 'id' => 4 , 'argc' => 4 , 'type' => IMG_FILTER_COLORIZE ),
'edgedetect' => array ( 'id' => 5 , 'argc' => 0 , 'type' => IMG_FILTER_EDGEDETECT ),
'emboss' => array ( 'id' => 6 , 'argc' => 0 , 'type' => IMG_FILTER_EMBOSS ),
'gaussian_blur' => array ( 'id' => 7 , 'argc' => 0 , 'type' => IMG_FILTER_GAUSSIAN_BLUR ),
'selective_blur' => array ( 'id' => 8 , 'argc' => 0 , 'type' => IMG_FILTER_SELECTIVE_BLUR ),
'mean_removal' => array ( 'id' => 9 , 'argc' => 0 , 'type' => IMG_FILTER_MEAN_REMOVAL ),
'smooth' => array ( 'id' => 10 , 'argc' => 1 , 'type' => IMG_FILTER_SMOOTH ),
'pixelate' => array ( 'id' => 11 , 'argc' => 2 , 'type' => IMG_FILTER_PIXELATE ),
);
2014-08-31 23:57:34 +02:00
if ( isset ( $map [ $name ])) {
2014-02-12 14:45:25 +01:00
return $map [ $name ];
2014-08-31 23:57:34 +02:00
} else {
2014-02-12 14:45:25 +01:00
throw new Exception ( 'No such filter.' );
2013-10-07 23:50:53 +02:00
}
}
2014-02-12 14:45:25 +01:00
2012-04-25 15:49:09 +02:00
2014-02-12 14:45:25 +01:00
/**
2014-08-31 23:57:34 +02:00
* Load image details from original image file .
2014-02-12 14:45:25 +01:00
*
* @ return $this
* @ throws Exception
*/
2014-08-31 23:57:34 +02:00
public function loadImageDetails ()
2014-02-12 14:45:25 +01:00
{
2014-08-31 23:57:34 +02:00
is_readable ( $this -> pathToImage )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Image file does not exist.' );
// Get details on image
$info = list ( $this -> width , $this -> height , $this -> type , $this -> attr ) = getimagesize ( $this -> pathToImage );
! empty ( $info ) or $this -> raiseError ( " The file doesn't seem to be an image. " );
if ( $this -> verbose ) {
$this -> log ( " Image file: { $this -> pathToImage } " );
$this -> log ( " Image width x height (type): { $this -> width } x { $this -> height } ( { $this -> type } ). " );
$this -> log ( " Image filesize: " . filesize ( $this -> pathToImage ) . " bytes. " );
}
2014-08-31 23:57:34 +02:00
return $this ;
}
/**
* Init new width and height and do some sanity checks on constraints , before any
* processing can be done .
*
* @ return $this
* @ throws Exception
*/
public function initDimensions ()
{
2014-02-12 14:45:25 +01:00
// width as %
if ( $this -> newWidth [ strlen ( $this -> newWidth ) - 1 ] == '%' ) {
$this -> newWidth = $this -> width * substr ( $this -> newWidth , 0 , - 1 ) / 100 ;
$this -> log ( " Setting new width based on % to { $this -> newWidth } " );
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// height as %
if ( $this -> newHeight [ strlen ( $this -> newHeight ) - 1 ] == '%' ) {
$this -> newHeight = $this -> height * substr ( $this -> newHeight , 0 , - 1 ) / 100 ;
$this -> log ( " Setting new height based on % to { $this -> newHeight } " );
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
is_null ( $this -> aspectRatio ) or is_numeric ( $this -> aspectRatio ) or $this -> raiseError ( 'Aspect ratio out of range' );
2013-10-14 09:22:21 +02:00
2014-02-12 14:45:25 +01:00
// width & height from aspect ratio
if ( $this -> aspectRatio && is_null ( $this -> newWidth ) && is_null ( $this -> newHeight )) {
if ( $this -> aspectRatio >= 1 ) {
$this -> newWidth = $this -> width ;
$this -> newHeight = $this -> width / $this -> aspectRatio ;
$this -> log ( " Setting new width & height based on width & aspect ratio (>=1) to (w x h) { $this -> newWidth } x { $this -> newHeight } " );
} else {
$this -> newHeight = $this -> height ;
$this -> newWidth = $this -> height * $this -> aspectRatio ;
$this -> log ( " Setting new width & height based on width & aspect ratio (<1) to (w x h) { $this -> newWidth } x { $this -> newHeight } " );
}
} elseif ( $this -> aspectRatio && is_null ( $this -> newWidth )) {
$this -> newWidth = $this -> newHeight * $this -> aspectRatio ;
$this -> log ( " Setting new width based on aspect ratio to { $this -> newWidth } " );
} elseif ( $this -> aspectRatio && is_null ( $this -> newHeight )) {
$this -> newHeight = $this -> newWidth / $this -> aspectRatio ;
$this -> log ( " Setting new height based on aspect ratio to { $this -> newHeight } " );
}
// Check values to be within domain
2014-08-31 23:57:34 +02:00
is_null ( $this -> newWidth )
or is_numeric ( $this -> newWidth )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Width not numeric' );
2014-08-31 23:57:34 +02:00
is_null ( $this -> newHeight )
or is_numeric ( $this -> newHeight )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Height not numeric' );
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
2012-04-25 15:49:09 +02:00
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Calculate new width and height of image , based on settings .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function calculateNewWidthAndHeight ()
2014-02-12 14:45:25 +01:00
{
// Crop, use cropped width and height as base for calulations
$this -> log ( " Calculate new width and height. " );
$this -> log ( " Original width x height is { $this -> width } x { $this -> height } . " );
if ( isset ( $this -> area )) {
$this -> offset [ 'top' ] = round ( $this -> area [ 'top' ] / 100 * $this -> height );
$this -> offset [ 'right' ] = round ( $this -> area [ 'right' ] / 100 * $this -> width );
$this -> offset [ 'bottom' ] = round ( $this -> area [ 'bottom' ] / 100 * $this -> height );
$this -> offset [ 'left' ] = round ( $this -> area [ 'left' ] / 100 * $this -> width );
$this -> offset [ 'width' ] = $this -> width - $this -> offset [ 'left' ] - $this -> offset [ 'right' ];
$this -> offset [ 'height' ] = $this -> height - $this -> offset [ 'top' ] - $this -> offset [ 'bottom' ];
$this -> width = $this -> offset [ 'width' ];
$this -> height = $this -> offset [ 'height' ];
$this -> log ( " The offset for the area to use is top { $this -> area [ 'top' ] } %, right { $this -> area [ 'right' ] } %, bottom { $this -> area [ 'bottom' ] } %, left { $this -> area [ 'left' ] } %. " );
$this -> log ( " The offset for the area to use is top { $this -> offset [ 'top' ] } px, right { $this -> offset [ 'right' ] } px, bottom { $this -> offset [ 'bottom' ] } px, left { $this -> offset [ 'left' ] } px, width { $this -> offset [ 'width' ] } px, height { $this -> offset [ 'height' ] } px. " );
}
$width = $this -> width ;
$height = $this -> height ;
if ( $this -> crop ) {
$width = $this -> crop [ 'width' ] = $this -> crop [ 'width' ] <= 0 ? $this -> width + $this -> crop [ 'width' ] : $this -> crop [ 'width' ];
$height = $this -> crop [ 'height' ] = $this -> crop [ 'height' ] <= 0 ? $this -> height + $this -> crop [ 'height' ] : $this -> crop [ 'height' ];
if ( $this -> crop [ 'start_x' ] == 'left' ) {
$this -> crop [ 'start_x' ] = 0 ;
} elseif ( $this -> crop [ 'start_x' ] == 'right' ) {
$this -> crop [ 'start_x' ] = $this -> width - $width ;
} elseif ( $this -> crop [ 'start_x' ] == 'center' ) {
$this -> crop [ 'start_x' ] = round ( $this -> width / 2 ) - round ( $width / 2 );
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
if ( $this -> crop [ 'start_y' ] == 'top' ) {
$this -> crop [ 'start_y' ] = 0 ;
} elseif ( $this -> crop [ 'start_y' ] == 'bottom' ) {
$this -> crop [ 'start_y' ] = $this -> height - $height ;
} elseif ( $this -> crop [ 'start_y' ] == 'center' ) {
$this -> crop [ 'start_y' ] = round ( $this -> height / 2 ) - round ( $height / 2 );
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
$this -> log ( " Crop area is width { $width } px, height { $height } px, start_x { $this -> crop [ 'start_x' ] } px, start_y { $this -> crop [ 'start_y' ] } px. " );
}
2013-10-03 18:16:33 +02:00
2014-08-31 23:57:34 +02:00
// Calculate new width and height if keeping aspect-ratio.
2014-02-12 14:45:25 +01:00
if ( $this -> keepRatio ) {
2014-08-31 23:57:34 +02:00
$this -> log ( " Keep aspect ratio. " );
2014-02-12 14:45:25 +01:00
// Crop-to-fit and both new width and height are set.
if ( $this -> cropToFit && isset ( $this -> newWidth ) && isset ( $this -> newHeight )) {
2014-08-31 23:57:34 +02:00
2014-02-12 14:45:25 +01:00
// Use newWidth and newHeigh as width/height, image should fit in box.
2014-08-31 23:57:34 +02:00
$this -> log ( " Use newWidth and newHeigh as width/height, image should fit in box. " );
2014-02-12 14:45:25 +01:00
} elseif ( isset ( $this -> newWidth ) && isset ( $this -> newHeight )) {
2014-08-31 23:57:34 +02:00
2014-02-12 14:45:25 +01:00
// Both new width and height are set.
// Use newWidth and newHeigh as max width/height, image should not be larger.
$ratioWidth = $width / $this -> newWidth ;
$ratioHeight = $height / $this -> newHeight ;
$ratio = ( $ratioWidth > $ratioHeight ) ? $ratioWidth : $ratioHeight ;
$this -> newWidth = round ( $width / $ratio );
$this -> newHeight = round ( $height / $ratio );
2014-08-31 23:57:34 +02:00
$this -> log ( " New width and height was set. " );
2014-02-12 14:45:25 +01:00
} elseif ( isset ( $this -> newWidth )) {
2014-08-31 23:57:34 +02:00
2014-02-12 14:45:25 +01:00
// Use new width as max-width
$factor = ( float ) $this -> newWidth / ( float ) $width ;
$this -> newHeight = round ( $factor * $height );
2014-08-31 23:57:34 +02:00
$this -> log ( " New width was set. " );
2014-02-12 14:45:25 +01:00
} elseif ( isset ( $this -> newHeight )) {
2014-08-31 23:57:34 +02:00
2014-02-12 14:45:25 +01:00
// Use new height as max-hight
$factor = ( float ) $this -> newHeight / ( float ) $height ;
$this -> newWidth = round ( $factor * $width );
2014-08-31 23:57:34 +02:00
$this -> log ( " New height was set. " );
2014-02-12 14:45:25 +01:00
}
// Use newWidth and newHeigh as defined width/height, image should fit the area.
if ( $this -> cropToFit ) {
2014-08-31 23:57:34 +02:00
$this -> log ( " Crop to fit. " );
2014-02-12 14:45:25 +01:00
$ratioWidth = $width / $this -> newWidth ;
$ratioHeight = $height / $this -> newHeight ;
$ratio = ( $ratioWidth < $ratioHeight ) ? $ratioWidth : $ratioHeight ;
$this -> cropWidth = round ( $width / $ratio );
$this -> cropHeight = round ( $height / $ratio );
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
}
// Crop, ensure to set new width and height
if ( $this -> crop ) {
2014-08-31 23:57:34 +02:00
$this -> log ( " Crop. " );
2014-02-12 14:45:25 +01:00
$this -> newWidth = round ( isset ( $this -> newWidth ) ? $this -> newWidth : $this -> crop [ 'width' ]);
2014-08-31 23:57:34 +02:00
$this -> newHeight = round ( isset ( $this -> newHeight ) ? $this -> newHeight : $this -> crop [ 'height' ]);
2014-02-12 14:45:25 +01:00
}
// No new height or width is set, use existing measures.
$this -> newWidth = round ( isset ( $this -> newWidth ) ? $this -> newWidth : $this -> width );
2014-08-31 23:57:34 +02:00
$this -> newHeight = round ( isset ( $this -> newHeight ) ? $this -> newHeight : $this -> height );
2014-02-12 14:45:25 +01:00
$this -> log ( " Calculated new width x height as { $this -> newWidth } x { $this -> newHeight } . " );
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-08-31 23:57:34 +02:00
/**
* Re - calculate image dimensions when original image dimension has changed .
*
* @ return $this
*/
public function reCalculateDimensions ()
{
$this -> log ( " Re-calculate image dimensions, newWidth x newHeigh was: " . $this -> newWidth . " x " . $this -> newHeight );
$this -> newWidth = $this -> newWidthOrig ;
$this -> newHeight = $this -> newHeightOrig ;
2014-09-01 00:38:26 +02:00
$this -> crop = $this -> cropOrig ;
2014-08-31 23:57:34 +02:00
$this -> initDimensions ()
-> calculateNewWidthAndHeight ();
return $this ;
}
2014-02-12 14:45:25 +01:00
/**
* Set extension for filename to save as .
*
* @ param string $saveas extension to save image as
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function setSaveAsExtension ( $saveAs = null )
2014-02-12 14:45:25 +01:00
{
if ( isset ( $saveAs )) {
2014-11-21 21:01:22 +01:00
$saveAs = strtolower ( $saveAs );
2014-02-12 14:45:25 +01:00
$this -> checkFileExtension ( $saveAs );
$this -> saveAs = $saveAs ;
$this -> extension = $saveAs ;
}
$this -> log ( " Prepare to save image using as: " . $this -> extension );
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Set JPEG quality to use when saving image
*
* @ param int $quality as the quality to set .
*
* @ return $this
*/
public function setJpegQuality ( $quality = null )
{
$this -> quality = isset ( $quality )
? $quality
: self :: JPEG_QUALITY_DEFAULT ;
2014-08-31 23:57:34 +02:00
( is_numeric ( $this -> quality ) and $this -> quality > 0 and $this -> quality <= 100 )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Quality not in range.' );
$this -> log ( " Setting JPEG quality to { $this -> quality } . " );
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Set PNG compressen algorithm to use when saving image
*
* @ param int $compress as the algorithm to use .
*
* @ return $this
*/
public function setPngCompression ( $compress = null )
{
$this -> compress = isset ( $compress )
? $compress
: self :: PNG_COMPRESSION_DEFAULT ;
2014-08-31 23:57:34 +02:00
( is_numeric ( $this -> compress ) and $this -> compress >= - 1 and $this -> compress <= 9 )
2014-02-12 14:45:25 +01:00
or $this -> raiseError ( 'Quality not in range.' );
$this -> log ( " Setting PNG compression level to { $this -> compress } . " );
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
2014-08-06 19:02:55 -04:00
* Use original image if possible , check options which affects image processing .
2014-02-12 14:45:25 +01:00
*
* @ param boolean $useOrig default is to use original if possible , else set to false .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function useOriginalIfPossible ( $useOrig = true )
2014-02-12 14:45:25 +01:00
{
2014-08-31 23:57:34 +02:00
if ( $useOrig
&& ( $this -> newWidth == $this -> width )
&& ( $this -> newHeight == $this -> height )
&& ! $this -> area
&& ! $this -> crop
&& ! $this -> filters
&& ! $this -> sharpen
&& ! $this -> emboss
&& ! $this -> blur
2014-02-12 14:45:25 +01:00
&& ! $this -> palette
2014-08-31 23:57:34 +02:00
&& ! $this -> quality
&& ! $this -> compress
&& ! $this -> saveAs
&& ! $this -> rotateBefore
&& ! $this -> rotateAfter
&& ! $this -> autoRotate
&& ! $this -> bgColor
2014-02-12 14:45:25 +01:00
) {
$this -> log ( " Using original image. " );
$this -> output ( $this -> pathToImage );
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Generate filename to save file in cache .
*
* @ param string $base as basepath for storing file .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function generateFilename ( $base )
2014-02-12 14:45:25 +01:00
{
2014-08-31 23:57:34 +02:00
$parts = pathinfo ( $this -> pathToImage );
$cropToFit = $this -> cropToFit ? '_cf' : null ;
$crop_x = $this -> crop_x ? " _x { $this -> crop_x } " : null ;
$crop_y = $this -> crop_y ? " _y { $this -> crop_y } " : null ;
$scale = $this -> scale ? " _s { $this -> scale } " : null ;
$bgColor = $this -> bgColor ? " _bgc { $this -> bgColor } " : null ;
$quality = $this -> quality ? " _q { $this -> quality } " : null ;
$compress = $this -> compress ? " _co { $this -> compress } " : null ;
$rotateBefore = $this -> rotateBefore ? " _rb { $this -> rotateBefore } " : null ;
$rotateAfter = $this -> rotateAfter ? " _ra { $this -> rotateAfter } " : null ;
$offset = isset ( $this -> offset )
? '_o' . $this -> offset [ 'top' ] . '-' . $this -> offset [ 'right' ] . '-' . $this -> offset [ 'bottom' ] . '-' . $this -> offset [ 'left' ]
2014-02-12 14:45:25 +01:00
: null ;
2014-08-31 23:57:34 +02:00
$crop = $this -> crop
2014-02-12 14:45:25 +01:00
? '_c' . $this -> crop [ 'width' ] . '-' . $this -> crop [ 'height' ] . '-' . $this -> crop [ 'start_x' ] . '-' . $this -> crop [ 'start_y' ]
: null ;
$filters = null ;
if ( isset ( $this -> filters )) {
foreach ( $this -> filters as $filter ) {
if ( is_array ( $filter )) {
$filters .= " _f { $filter [ 'id' ] } " ;
2014-08-31 23:57:34 +02:00
for ( $i = 1 ; $i <= $filter [ 'argc' ]; $i ++ ) {
2014-02-12 14:45:25 +01:00
$filters .= " : " . $filter [ " arg { $i } " ];
}
}
}
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
$sharpen = $this -> sharpen ? 's' : null ;
$emboss = $this -> emboss ? 'e' : null ;
$blur = $this -> blur ? 'b' : null ;
$palette = $this -> palette ? 'p' : null ;
2014-08-31 23:57:34 +02:00
$autoRotate = $this -> autoRotate ? 'ar' : null ;
2014-02-12 14:45:25 +01:00
2014-08-31 23:57:34 +02:00
$this -> extension = isset ( $this -> extension )
? $this -> extension
2014-02-12 14:45:25 +01:00
: $parts [ 'extension' ];
// Check optimizing options
$optimize = null ;
if ( $this -> extension == 'jpeg' || $this -> extension == 'jpg' ) {
$optimize = $this -> jpegOptimize ? 'o' : null ;
} elseif ( $this -> extension == 'png' ) {
$optimize .= $this -> pngFilter ? 'f' : null ;
2014-08-31 23:57:34 +02:00
$optimize .= $this -> pngDeflate ? 'd' : null ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
$subdir = str_replace ( '/' , '-' , dirname ( $this -> imageSrc ));
$subdir = ( $subdir == '.' ) ? '_.' : $subdir ;
2014-08-31 23:57:34 +02:00
$file = $subdir . '_' . $parts [ 'filename' ] . '_' . round ( $this -> newWidth ) . '_'
. round ( $this -> newHeight ) . $offset . $crop . $cropToFit . $crop_x . $crop_y
. $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor . '.' . $this -> extension ;
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
return $this -> setTarget ( $file , $base );
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Use cached version of image , if possible .
*
* @ param boolean $useCache is default true , set to false to avoid using cached object .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function useCacheIfPossible ( $useCache = true )
2014-02-12 14:45:25 +01:00
{
if ( $useCache && is_readable ( $this -> cacheFileName )) {
$fileTime = filemtime ( $this -> pathToImage );
$cacheTime = filemtime ( $this -> cacheFileName );
if ( $fileTime <= $cacheTime ) {
if ( $this -> useCache ) {
if ( $this -> verbose ) {
$this -> log ( " Use cached file. " );
2014-08-31 23:57:34 +02:00
$this -> log ( " Cached image filesize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
2014-02-12 14:45:25 +01:00
}
2014-11-21 22:20:30 +01:00
$this -> output ( $this -> cacheFileName , $this -> outputFormat );
2014-02-12 14:45:25 +01:00
} else {
$this -> log ( " Cache is valid but ignoring it by intention. " );
}
} else {
$this -> log ( " Original file is modified, ignoring cache. " );
}
} else {
$this -> log ( " Cachefile does not exists or ignoring it. " );
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
2012-10-02 22:49:43 +02:00
}
2014-02-12 14:45:25 +01:00
2012-04-25 15:49:09 +02:00
2014-08-21 02:21:32 +02:00
/**
* Error message when failing to load somehow corrupt image .
*
* @ return void
*
*/
public function failedToLoad ()
{
header ( " HTTP/1.0 404 Not Found " );
2014-08-21 02:23:15 +02:00
echo ( " CImage.php says 404: Fatal error when opening image.<br> " );
2014-08-21 02:21:32 +02:00
switch ( $this -> fileExtension ) {
case 'jpg' :
case 'jpeg' :
$this -> image = imagecreatefromjpeg ( $this -> pathToImage );
break ;
case 'gif' :
$this -> image = imagecreatefromgif ( $this -> pathToImage );
break ;
case 'png' :
$this -> image = imagecreatefrompng ( $this -> pathToImage );
break ;
}
exit ();
}
2014-02-12 14:45:25 +01:00
/**
* Load image from disk .
*
* @ param string $src of image .
* @ param string $dir as base directory where images are .
*
* @ return $this
*
*/
2014-08-21 02:21:32 +02:00
public function load ( $src = null , $dir = null )
2014-02-12 14:45:25 +01:00
{
if ( isset ( $src )) {
$this -> setSource ( $src , $dir );
}
$this -> log ( " Opening file as { $this -> fileExtension } . " );
2014-08-21 02:21:32 +02:00
switch ( $this -> fileExtension ) {
2014-02-12 14:45:25 +01:00
case 'jpg' :
2014-08-21 02:21:32 +02:00
case 'jpeg' :
2014-08-21 02:13:01 +02:00
$this -> image = @ imagecreatefromjpeg ( $this -> pathToImage );
2014-08-21 02:22:20 +02:00
$this -> image or $this -> failedToLoad ();
2014-08-21 02:21:32 +02:00
break ;
2012-10-02 22:49:43 +02:00
2014-02-12 14:45:25 +01:00
case 'gif' :
2014-08-21 02:21:32 +02:00
$this -> image = @ imagecreatefromgif ( $this -> pathToImage );
2014-08-21 02:22:20 +02:00
$this -> image or $this -> failedToLoad ();
2014-08-21 02:21:32 +02:00
break ;
2014-02-12 14:45:25 +01:00
2014-08-21 02:21:32 +02:00
case 'png' :
$this -> image = @ imagecreatefrompng ( $this -> pathToImage );
2014-08-21 02:22:20 +02:00
$this -> image or $this -> failedToLoad ();
2014-08-21 02:08:49 +02:00
2014-02-12 14:45:25 +01:00
$type = $this -> getPngType ();
$hasFewColors = imagecolorstotal ( $this -> image );
if ( $type == self :: PNG_RGB_PALETTE || ( $hasFewColors > 0 && $hasFewColors <= 256 )) {
if ( $this -> verbose ) {
$this -> log ( " Handle this image as a palette image. " );
}
$this -> palette = true ;
}
2014-08-21 02:21:32 +02:00
break ;
2014-02-12 14:45:25 +01:00
2014-08-21 02:21:32 +02:00
default :
$this -> image = false ;
2014-02-12 14:45:25 +01:00
throw new Exception ( 'No support for this file extension.' );
}
if ( $this -> verbose ) {
$this -> log ( " imageistruecolor() : " . ( imageistruecolor ( $this -> image ) ? 'true' : 'false' ));
$this -> log ( " imagecolorstotal() : " . imagecolorstotal ( $this -> image ));
$this -> log ( " Number of colors in image = " . $this -> colorsTotal ( $this -> image ));
}
return $this ;
2012-10-02 22:49:43 +02:00
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Get the type of PNG image .
*
* @ return int as the type of the png - image
*
*/
2014-08-31 23:57:34 +02:00
private function getPngType ()
2014-02-12 14:45:25 +01:00
{
2014-08-31 23:57:34 +02:00
$pngType = ord ( file_get_contents ( $this -> pathToImage , false , null , 25 , 1 ));
2014-02-12 14:45:25 +01:00
switch ( $pngType ) {
case self :: PNG_GREYSCALE :
$this -> log ( " PNG is type 0, Greyscale. " );
break ;
2014-08-31 23:57:34 +02:00
case self :: PNG_RGB :
2014-02-12 14:45:25 +01:00
$this -> log ( " PNG is type 2, RGB " );
break ;
2014-08-31 23:57:34 +02:00
case self :: PNG_RGB_PALETTE :
2014-02-12 14:45:25 +01:00
$this -> log ( " PNG is type 3, RGB with palette " );
break ;
case self :: PNG_GREYSCALE_ALPHA :
$this -> Log ( " PNG is type 4, Greyscale with alpha channel " );
break ;
case self :: PNG_RGB_ALPHA :
$this -> Log ( " PNG is type 6, RGB with alpha channel (PNG 32-bit) " );
break ;
default :
$this -> Log ( " PNG is UNKNOWN type, is it really a PNG image? " );
}
return $pngType ;
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Calculate number of colors in an image .
*
* @ param resource $im the image .
*
* @ return int
*/
2014-08-31 23:57:34 +02:00
private function colorsTotal ( $im )
2014-02-12 14:45:25 +01:00
{
if ( imageistruecolor ( $im )) {
$h = imagesy ( $im );
$w = imagesx ( $im );
$c = array ();
for ( $x = 0 ; $x < $w ; $x ++ ) {
for ( $y = 0 ; $y < $h ; $y ++ ) {
@ $c [ 'c' . imagecolorat ( $im , $x , $y )] ++ ;
}
}
return count ( $c );
} else {
return imagecolorstotal ( $im );
}
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Preprocess image before rezising it .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function preResize ()
2014-02-12 14:45:25 +01:00
{
$this -> log ( " Pre-process before resizing " );
2014-08-31 23:57:34 +02:00
// Rotate image
if ( $this -> rotateBefore ) {
$this -> log ( " Rotating image. " );
$this -> rotate ( $this -> rotateBefore , $this -> bgColor )
-> reCalculateDimensions ();
}
// Auto-rotate image
if ( $this -> autoRotate ) {
$this -> log ( " Auto rotating image. " );
$this -> rotateExif ()
-> reCalculateDimensions ();
}
2014-02-12 14:45:25 +01:00
// Scale the original image before starting
if ( isset ( $this -> scale )) {
$this -> log ( " Scale by { $this -> scale } % " );
$newWidth = $this -> width * $this -> scale / 100 ;
$newHeight = $this -> height * $this -> scale / 100 ;
$img = $this -> CreateImageKeepTransparency ( $newWidth , $newHeight );
imagecopyresampled ( $img , $this -> image , 0 , 0 , 0 , 0 , $newWidth , $newHeight , $this -> width , $this -> height );
$this -> image = $img ;
$this -> width = $newWidth ;
$this -> height = $newHeight ;
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
2014-08-31 23:57:34 +02:00
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Resize and or crop the image .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function resize ()
2014-02-12 14:45:25 +01:00
{
$this -> log ( " Starting to Resize() " );
// Only use a specified area of the image, $this->offset is defining the area to use
if ( isset ( $this -> offset )) {
$this -> log ( " Offset for area to use, cropping it width= { $this -> offset [ 'width' ] } , height= { $this -> offset [ 'height' ] } , start_x= { $this -> offset [ 'left' ] } , start_y= { $this -> offset [ 'top' ] } " );
$img = $this -> CreateImageKeepTransparency ( $this -> offset [ 'width' ], $this -> offset [ 'height' ]);
imagecopy ( $img , $this -> image , 0 , 0 , $this -> offset [ 'left' ], $this -> offset [ 'top' ], $this -> offset [ 'width' ], $this -> offset [ 'height' ]);
$this -> image = $img ;
$this -> width = $this -> offset [ 'width' ];
$this -> height = $this -> offset [ 'height' ];
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
// SaveAs need to copy image to remove transparency, if any
if ( $this -> saveAs ) {
$this -> log ( " Copying image before saving as another format, loosing transparency, width= { $this -> width } , height= { $this -> height } . " );
$img = imagecreatetruecolor ( $this -> width , $this -> height );
$bg = imagecolorallocate ( $img , 255 , 255 , 255 );
imagefill ( $img , 0 , 0 , $bg );
imagecopy ( $img , $this -> image , 0 , 0 , 0 , 0 , $this -> width , $this -> height );
$this -> image = $img ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
// Do as crop, take only part of image
if ( $this -> crop ) {
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
$this -> log ( " Cropping area width= { $this -> crop [ 'width' ] } , height= { $this -> crop [ 'height' ] } , start_x= { $this -> crop [ 'start_x' ] } , start_y= { $this -> crop [ 'start_y' ] } " );
$img = $this -> CreateImageKeepTransparency ( $this -> crop [ 'width' ], $this -> crop [ 'height' ]);
imagecopyresampled ( $img , $this -> image , 0 , 0 , $this -> crop [ 'start_x' ], $this -> crop [ 'start_y' ], $this -> crop [ 'width' ], $this -> crop [ 'height' ], $this -> crop [ 'width' ], $this -> crop [ 'height' ]);
$this -> image = $img ;
$this -> width = $this -> crop [ 'width' ];
$this -> height = $this -> crop [ 'height' ];
2014-08-31 23:57:34 +02:00
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Resize by crop to fit
if ( $this -> cropToFit ) {
$this -> log ( " Crop to fit " );
2014-08-31 23:57:34 +02:00
$cropX = round (( $this -> cropWidth / 2 ) - ( $this -> newWidth / 2 ));
$cropY = round (( $this -> cropHeight / 2 ) - ( $this -> newHeight / 2 ));
2014-02-12 14:45:25 +01:00
$imgPreCrop = $this -> CreateImageKeepTransparency ( $this -> cropWidth , $this -> cropHeight );
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
imagecopyresampled ( $imgPreCrop , $this -> image , 0 , 0 , 0 , 0 , $this -> cropWidth , $this -> cropHeight , $this -> width , $this -> height );
imagecopyresampled ( $imageResized , $imgPreCrop , 0 , 0 , $cropX , $cropY , $this -> newWidth , $this -> newHeight , $this -> newWidth , $this -> newHeight );
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
} else if ( ! ( $this -> newWidth == $this -> width && $this -> newHeight == $this -> height )) {
// Resize it
$this -> log ( " Resizing, new height and/or width " );
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
imagecopyresampled ( $imageResized , $this -> image , 0 , 0 , 0 , 0 , $this -> newWidth , $this -> newHeight , $this -> width , $this -> height );
//imagecopyresized($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Postprocess image after rezising image .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function postResize ()
2014-02-12 14:45:25 +01:00
{
$this -> log ( " Post-process after resizing " );
2014-08-31 23:57:34 +02:00
// Rotate image
if ( $this -> rotateAfter ) {
$this -> log ( " Rotating image. " );
$this -> rotate ( $this -> rotateAfter , $this -> bgColor );
}
2014-02-12 14:45:25 +01:00
// Apply filters
if ( isset ( $this -> filters ) && is_array ( $this -> filters )) {
foreach ( $this -> filters as $filter ) {
$this -> log ( " Applying filter $filter . " );
switch ( $filter [ 'argc' ]) {
2014-08-31 23:57:34 +02:00
case 0 :
imagefilter ( $this -> image , $filter [ 'type' ]);
2014-02-12 14:45:25 +01:00
break ;
2014-08-31 23:57:34 +02:00
case 1 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ]);
2014-02-12 14:45:25 +01:00
break ;
2014-08-31 23:57:34 +02:00
case 2 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ]);
2014-02-12 14:45:25 +01:00
break ;
2014-08-31 23:57:34 +02:00
case 3 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ], $filter [ 'arg3' ]);
2014-02-12 14:45:25 +01:00
break ;
2014-08-31 23:57:34 +02:00
case 4 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ], $filter [ 'arg3' ], $filter [ 'arg4' ]);
2014-02-12 14:45:25 +01:00
break ;
}
}
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Convert to palette image
2014-08-31 23:57:34 +02:00
if ( $this -> palette ) {
2014-02-12 14:45:25 +01:00
$this -> log ( " Converting to palette image. " );
$this -> trueColorToPalette ();
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Blur the image
2014-08-31 23:57:34 +02:00
if ( $this -> blur ) {
2014-02-12 14:45:25 +01:00
$this -> log ( " Blur. " );
$this -> blurImage ();
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Emboss the image
2014-08-31 23:57:34 +02:00
if ( $this -> emboss ) {
2014-02-12 14:45:25 +01:00
$this -> log ( " Emboss. " );
$this -> embossImage ();
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Sharpen the image
2014-08-31 23:57:34 +02:00
if ( $this -> sharpen ) {
2014-02-12 14:45:25 +01:00
$this -> log ( " Sharpen. " );
$this -> sharpenImage ();
2012-05-09 17:57:48 +02:00
}
2013-10-07 23:50:53 +02:00
2014-08-31 23:57:34 +02:00
return $this ;
}
/**
* Rotate image using angle .
*
* @ param float $angle to rotate image .
* @ param int $anglebgColor to fill image with if needed .
*
* @ return $this
*/
public function rotate ( $angle , $bgColor )
{
$this -> log ( " Rotate image " . $angle . " degrees with filler color " . $bgColor );
$this -> image = imagerotate ( $this -> image , $angle , $bgColor );
$this -> width = imagesx ( $this -> image );
$this -> height = imagesy ( $this -> image );
$this -> log ( " New image dimension width x height: " . $this -> width . " x " . $this -> height );
return $this ;
}
/**
* Rotate image using information in EXIF .
*
* @ return $this
*/
public function rotateExif ()
{
if ( ! in_array ( $this -> fileExtension , array ( 'jpg' , 'jpeg' ))) {
$this -> log ( " Autorotate ignored, EXIF not supported by this filetype. " );
return $this ;
}
$exif = exif_read_data ( $this -> pathToImage );
if ( ! empty ( $exif [ 'Orientation' ])) {
switch ( $exif [ 'Orientation' ]) {
case 3 :
$this -> log ( " Autorotate 180. " );
$this -> rotate ( 180 , $this -> bgColor );
break ;
case 6 :
$this -> log ( " Autorotate -90. " );
$this -> rotate ( - 90 , $this -> bgColor );
break ;
case 8 :
$this -> log ( " Autorotate 90. " );
$this -> rotate ( 90 , $this -> bgColor );
break ;
default :
$this -> log ( " Autorotate ignored, unknown value as orientation. " );
}
} else {
$this -> log ( " Autorotate ignored, no orientation in EXIF. " );
}
return $this ;
2012-05-09 17:57:48 +02:00
}
2012-04-25 15:49:09 +02:00
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Convert true color image to palette image , keeping alpha .
* http :// stackoverflow . com / questions / 5752514 / how - to - convert - png - to - 8 - bit - png - using - php - gd - library
*
* @ return void
*/
2014-08-31 23:57:34 +02:00
public function trueColorToPalette ()
2014-02-12 14:45:25 +01:00
{
$img = imagecreatetruecolor ( $this -> width , $this -> height );
$bga = imagecolorallocatealpha ( $img , 0 , 0 , 0 , 127 );
imagecolortransparent ( $img , $bga );
imagefill ( $img , 0 , 0 , $bga );
imagecopy ( $img , $this -> image , 0 , 0 , 0 , 0 , $this -> width , $this -> height );
imagetruecolortopalette ( $img , false , 255 );
imagesavealpha ( $img , true );
if ( imageistruecolor ( $this -> image )) {
$this -> log ( " Matching colors with true color image. " );
imagecolormatch ( $this -> image , $img );
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
$this -> image = $img ;
}
2013-10-03 18:16:33 +02:00
2012-10-02 22:49:43 +02:00
2014-02-12 14:45:25 +01:00
/**
* Sharpen image as http :// php . net / manual / en / ref . image . php #56144
* http :// loriweb . pair . com / 8 udf - sharpen . html
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function sharpenImage ()
2014-02-12 14:45:25 +01:00
{
$matrix = array (
array ( - 1 , - 1 , - 1 ,),
array ( - 1 , 16 , - 1 ,),
array ( - 1 , - 1 , - 1 ,),
);
$divisor = 8 ;
$offset = 0 ;
imageconvolution ( $this -> image , $matrix , $divisor , $offset );
return $this ;
}
2012-10-02 22:49:43 +02:00
2013-10-07 23:50:53 +02:00
2014-02-12 14:45:25 +01:00
/**
* Emboss image as http :// loriweb . pair . com / 8 udf - emboss . html
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function embossImage ()
2014-02-12 14:45:25 +01:00
{
$matrix = array (
array ( 1 , 1 , - 1 ,),
array ( 1 , 3 , - 1 ,),
array ( 1 , - 1 , - 1 ,),
);
2013-10-07 23:50:53 +02:00
2014-02-12 14:45:25 +01:00
$divisor = 3 ;
$offset = 0 ;
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
imageconvolution ( $this -> image , $matrix , $divisor , $offset );
return $this ;
2013-10-03 18:16:33 +02:00
}
2012-10-02 22:49:43 +02:00
2014-02-12 14:45:25 +01:00
/**
* Blur image as http :// loriweb . pair . com / 8 udf - basics . html
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function blurImage ()
2014-02-12 14:45:25 +01:00
{
$matrix = array (
array ( 1 , 1 , 1 ,),
array ( 1 , 15 , 1 ,),
array ( 1 , 1 , 1 ,),
);
2012-10-02 22:49:43 +02:00
2014-02-12 14:45:25 +01:00
$divisor = 23 ;
$offset = 0 ;
2012-10-02 22:49:43 +02:00
2014-02-12 14:45:25 +01:00
imageconvolution ( $this -> image , $matrix , $divisor , $offset );
2012-05-09 17:57:48 +02:00
2014-02-12 14:45:25 +01:00
return $this ;
2012-05-09 17:57:48 +02:00
}
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
/**
* Create a image and keep transparency for png and gifs .
*
* @ param int $width of the new image .
* @ param int $height of the new image .
* @ return image resource .
*/
2014-08-31 23:57:34 +02:00
private function createImageKeepTransparency ( $width , $height )
2014-02-12 14:45:25 +01:00
{
$this -> log ( " Creating a new working image width= { $width } px, height= { $height } px. " );
$img = imagecreatetruecolor ( $width , $height );
imagealphablending ( $img , false );
2014-08-31 23:57:34 +02:00
imagesavealpha ( $img , true );
2014-02-12 14:45:25 +01:00
/*
$this -> Log ( " Filling image with background color. " );
$bg = imagecolorallocate ( $img , 255 , 255 , 255 );
imagefill ( $img , 0 , 0 , $bg );
*/
return $img ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Set optimizing and post - processing options . CHANGE FROM DEFINE TO INJECT INTO CLASS , TO BE ABLE TO SET OFF POSTPROCESSING
*
* @ param array $options with config for postprocessing with external tools .
*
* @ return $this
*/
2014-08-31 23:57:34 +02:00
public function setPostProcessingOptions ( $options )
2014-02-12 14:45:25 +01:00
{
if ( isset ( $options [ 'jpeg_optimize' ]) && $options [ 'jpeg_optimize' ]) {
$this -> jpegOptimizeCmd = $options [ 'jpeg_optimize_cmd' ];
2014-04-01 08:11:49 +02:00
} else {
2014-08-31 23:57:34 +02:00
$this -> jpegOptimizeCmd = null ;
2014-02-12 14:45:25 +01:00
}
if ( isset ( $options [ 'png_filter' ]) && $options [ 'png_filter' ]) {
$this -> pngFilterCmd = $options [ 'png_filter_cmd' ];
2014-04-01 08:11:49 +02:00
} else {
$this -> pngFilterCmd = null ;
2014-02-12 14:45:25 +01:00
}
if ( isset ( $options [ 'png_deflate' ]) && $options [ 'png_deflate' ]) {
$this -> pngDeflateCmd = $options [ 'png_deflate_cmd' ];
2014-04-01 08:11:49 +02:00
} else {
$this -> pngDeflateCmd = null ;
2014-02-12 14:45:25 +01:00
}
return $this ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
/**
* Save image .
*
* @ param string $src as target filename .
* @ param string $base as base directory where to store images .
*
* @ return $this or false if no folder is set .
*/
2014-08-21 02:02:50 +02:00
public function save ( $src = null , $base = null )
2014-02-12 14:45:25 +01:00
{
if ( isset ( $src )) {
$this -> setTarget ( $src , $base );
}
2013-10-03 18:16:33 +02:00
2014-11-21 22:20:30 +01:00
switch ( strtolower ( $this -> extension )) {
2014-02-12 14:45:25 +01:00
2014-08-21 02:02:50 +02:00
case 'jpeg' :
case 'jpg' :
2014-02-12 14:45:25 +01:00
$this -> Log ( " Saving image as JPEG to cache using quality = { $this -> quality } . " );
imagejpeg ( $this -> image , $this -> cacheFileName , $this -> quality );
2013-10-03 18:16:33 +02:00
2014-02-12 14:45:25 +01:00
// Use JPEG optimize if defined
2014-02-12 15:09:29 +01:00
if ( $this -> jpegOptimizeCmd ) {
2014-08-31 23:57:34 +02:00
if ( $this -> verbose ) {
clearstatcache ();
$this -> log ( " Filesize before optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
2014-02-12 14:45:25 +01:00
}
$res = array ();
2014-02-12 15:09:29 +01:00
$cmd = $this -> jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName " ;
2014-02-12 14:45:25 +01:00
exec ( $cmd , $res );
$this -> log ( $cmd );
$this -> log ( $res );
}
2014-08-31 23:57:34 +02:00
break ;
2014-02-12 14:45:25 +01:00
case 'gif' :
if ( $this -> saveFolder ) {
$this -> Log ( " Saving image as GIF to cache. " );
2014-08-31 23:57:34 +02:00
imagegif ( $this -> image , $this -> cacheFileName );
2014-02-12 14:45:25 +01:00
}
2014-08-31 23:57:34 +02:00
break ;
2014-02-12 14:45:25 +01:00
2014-08-31 23:57:34 +02:00
case 'png' :
2014-02-12 14:45:25 +01:00
$this -> Log ( " Saving image as PNG to cache using compression = { $this -> compress } . " );
// Turn off alpha blending and set alpha flag
imagealphablending ( $this -> image , false );
imagesavealpha ( $this -> image , true );
2014-08-31 23:57:34 +02:00
imagepng ( $this -> image , $this -> cacheFileName , $this -> compress );
2014-02-12 14:45:25 +01:00
// Use external program to filter PNG, if defined
2014-02-12 15:09:29 +01:00
if ( $this -> pngFilterCmd ) {
2014-08-31 23:57:34 +02:00
if ( $this -> verbose ) {
clearstatcache ();
$this -> Log ( " Filesize before filter optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
2014-02-12 14:45:25 +01:00
}
$res = array ();
2014-02-12 15:09:29 +01:00
$cmd = $this -> pngFilterCmd . " $this->cacheFileName " ;
2014-02-12 14:45:25 +01:00
exec ( $cmd , $res );
$this -> Log ( $cmd );
$this -> Log ( $res );
}
// Use external program to deflate PNG, if defined
2014-02-12 15:09:29 +01:00
if ( $this -> pngDeflateCmd ) {
2014-08-31 23:57:34 +02:00
if ( $this -> verbose ) {
clearstatcache ();
$this -> Log ( " Filesize before deflate optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
2014-02-12 14:45:25 +01:00
}
$res = array ();
2014-02-12 15:09:29 +01:00
$cmd = $this -> pngDeflateCmd . " $this->cacheFileName " ;
2014-02-12 14:45:25 +01:00
exec ( $cmd , $res );
$this -> Log ( $cmd );
$this -> Log ( $res );
}
2014-08-31 23:57:34 +02:00
break ;
2014-02-12 14:45:25 +01:00
default :
$this -> RaiseError ( 'No support for this file extension.' );
break ;
2014-08-31 23:57:34 +02:00
}
2014-02-12 14:45:25 +01:00
if ( $this -> verbose ) {
clearstatcache ();
2014-08-31 23:57:34 +02:00
$this -> log ( " Cached image filesize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
2014-02-12 14:45:25 +01:00
$this -> log ( " imageistruecolor() : " . ( imageistruecolor ( $this -> image ) ? 'true' : 'false' ));
$this -> log ( " imagecolorstotal() : " . imagecolorstotal ( $this -> image ));
$this -> log ( " Number of colors in image = " . $this -> ColorsTotal ( $this -> image ));
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
return $this ;
}
/**
* Output image to browser using caching .
*
2014-11-21 22:20:30 +01:00
* @ param string $file to read and output , default is to use $this -> cacheFileName
* @ param string $format set to json to output file as json object with details
2014-02-12 14:45:25 +01:00
*
* @ return void
*/
2014-11-21 22:20:30 +01:00
public function output ( $file = null , $format = null )
2014-02-12 14:45:25 +01:00
{
if ( is_null ( $file )) {
$file = $this -> cacheFileName ;
2013-10-03 18:16:33 +02:00
}
2014-11-21 22:20:30 +01:00
if ( is_null ( $format )) {
$format = $this -> outputFormat ;
}
$this -> log ( " Output format is: $format " );
if ( ! $this -> verbose && $format == 'json' ) {
header ( 'Content-type: application/json' );
echo $this -> json ( $file );
exit ;
}
2014-02-12 14:45:25 +01:00
$this -> log ( " Outputting image: $file " );
2013-10-03 18:16:33 +02:00
2014-11-21 20:20:35 +01:00
// Get image modification time
clearstatcache ();
2014-05-20 00:55:43 +02:00
$lastModified = filemtime ( $file );
2014-02-12 14:45:25 +01:00
$gmdate = gmdate ( " D, d M Y H:i:s " , $lastModified );
2014-05-20 00:55:43 +02:00
if ( ! $this -> verbose ) {
header ( 'Last-Modified: ' . $gmdate . " GMT " );
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
if ( isset ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) && strtotime ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) == $lastModified ) {
if ( $this -> verbose ) {
$this -> log ( " 304 not modified " );
$this -> verboseOutput ();
exit ;
}
header ( " HTTP/1.0 304 Not Modified " );
2014-05-20 00:55:43 +02:00
} else {
2014-02-12 14:45:25 +01:00
if ( $this -> verbose ) {
$this -> log ( " Last modified: " . $gmdate . " GMT " );
$this -> verboseOutput ();
exit ;
}
2014-11-21 20:20:35 +01:00
// Get details on image
$info = getimagesize ( $file );
! empty ( $info ) or $this -> raiseError ( " The file doesn't seem to be an image. " );
$mime = $info [ 'mime' ];
2014-05-20 00:55:43 +02:00
header ( 'Content-type: ' . $mime );
2014-02-12 14:45:25 +01:00
readfile ( $file );
}
exit ;
2013-10-03 18:16:33 +02:00
}
2014-02-12 14:45:25 +01:00
2014-11-21 22:20:30 +01:00
/**
* Create a JSON object from the image details .
*
* @ return string json - encoded representation of the image .
*/
public function json ()
{
$details = array ();
clearstatcache ();
$details [ 'src' ] = $this -> imageSrc ;
$lastModified = filemtime ( $this -> pathToImage );
$details [ 'src-gmdate' ] = gmdate ( " D, d M Y H:i:s " , $lastModified );
$details [ 'cache' ] = basename ( $this -> cacheFileName );
$lastModified = filemtime ( $this -> cacheFileName );
$details [ 'cache-gmdate' ] = gmdate ( " D, d M Y H:i:s " , $lastModified );
$details [ 'width' ] = $this -> width ;
$details [ 'height' ] = $this -> height ;
return json_encode ( $details , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
}
2014-02-12 14:45:25 +01:00
/**
* Log an event if verbose mode .
*
* @ param string $message to log .
*
2014-05-20 00:55:43 +02:00
* @ return this
2014-02-12 14:45:25 +01:00
*/
2014-05-20 00:55:43 +02:00
public function log ( $message )
2014-02-12 14:45:25 +01:00
{
if ( $this -> verbose ) {
2014-05-20 00:55:43 +02:00
$this -> log [] = $message ;
2014-02-12 14:45:25 +01:00
}
2014-05-20 00:55:43 +02:00
return $this ;
2014-02-12 14:45:25 +01:00
}
2012-04-25 15:49:09 +02:00
2014-02-12 14:45:25 +01:00
/**
* Do verbose output and print out the log and the actual images .
*
* @ return void
*/
2014-05-20 00:55:43 +02:00
private function verboseOutput ()
2014-02-12 14:45:25 +01:00
{
$log = null ;
2014-11-21 22:20:30 +01:00
$this -> log ( " As JSON: \n " . $this -> json ());
2014-02-12 14:45:25 +01:00
$this -> log ( " Memory peak: " . round ( memory_get_peak_usage () / 1024 / 1024 ) . " M " );
$this -> log ( " Memory limit: " . ini_get ( 'memory_limit' ));
$included = get_included_files ();
$this -> log ( " Included files: " . count ( $included ));
foreach ( $this -> log as $val ) {
if ( is_array ( $val )) {
foreach ( $val as $val1 ) {
$log .= htmlentities ( $val1 ) . '<br/>' ;
}
} else {
$log .= htmlentities ( $val ) . '<br/>' ;
}
}
echo <<< EOD
<! doctype html >
< html lang = en >
< meta charset = utf - 8 >
< title > CImage verbose output </ title >
< style > body { background - color : #ddd}</style>
< h1 > CImage Verbose Output </ h1 >
< pre > { $log } </ pre >
EOD ;
}
/**
* Raise error , enables to implement a selection of error methods .
*
* @ param string $message the error message to display .
*
* @ return void
* @ throws Exception
*/
2014-05-20 00:55:43 +02:00
private function raiseError ( $message )
2014-02-12 14:45:25 +01:00
{
throw new Exception ( $message );
}
2014-05-20 00:55:43 +02:00
}