1
0
mirror of https://github.com/mosbth/cimage.git synced 2025-08-02 14:17:44 +02:00

Cleaning up code #23, added rotate and autoRotate #36

This commit is contained in:
Mikael Roos
2014-08-31 23:57:34 +02:00
parent 67524bd090
commit c96ed10f80
13 changed files with 480 additions and 167 deletions

4
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# Cache files # # Cache files #
###################### ######################
cache/_* cache/*


View File

@@ -6,7 +6,7 @@
* @example http://dbwebb.se/opensource/cimage * @example http://dbwebb.se/opensource/cimage
* @link https://github.com/mosbth/cimage * @link https://github.com/mosbth/cimage
*/ */
class CImage class CImage
{ {
/** /**
@@ -107,7 +107,7 @@ class CImage
/** /**
* Keep a log/trace on what happens * Keep a log/trace on what happens
*/ */
private $log = array(); private $log = array();
@@ -148,36 +148,43 @@ class CImage
/** /**
* Path to command to optimize jpeg images, for example jpegtran or null. * Path to command to optimize jpeg images, for example jpegtran or null.
*/ */
private $jpegOptimize; private $jpegOptimize;
/**
* Image dimensions, calculated from loaded image.
*/
private $width; // Calculated from source image
private $height; // Calculated from source image
private $width; // Calculated from source image /**
private $height; // Calculated from source image * New image dimensions, incoming as argument or calculated.
private $offset; */
private $newWidth;
public $keepRatio; private $newWidthOrig; // Save original value
public $cropToFit; private $newHeight;
public $crop; private $newHeightOrig; // Save original value
/**
* Properties (clean up these)
*/
private $offset;
public $keepRatio;
public $cropToFit;
public $crop;
/** private $cropWidth;
* Properties private $cropHeight;
*/ public $crop_x;
public $newWidth; public $crop_y;
public $newHeight; public $filters;
private $cropWidth; private $type; // Calculated from source image
private $cropHeight; private $attr; // Calculated from source image
public $crop_x; private $useCache; // Use the cache if true, set to false to ignore the cached file.
public $crop_y; private $useOriginal; // Use original image if possible
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
@@ -190,7 +197,7 @@ class CImage
* @param string $saveFolder path to folder where to save the new file or null to skip saving. * @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. * @param string $saveName name of target file when saveing.
*/ */
public function __construct($imageSrc = null, $imageFolder = null, $saveFolder = null, $saveName = null) public function __construct($imageSrc = null, $imageFolder = null, $saveFolder = null, $saveName = null)
{ {
$this->setSource($imageSrc, $imageFolder); $this->setSource($imageSrc, $imageFolder);
$this->setTarget($saveFolder, $saveName); $this->setTarget($saveFolder, $saveName);
@@ -205,7 +212,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function setVerbose($mode = true) public function setVerbose($mode = true)
{ {
$this->verbose = $mode; $this->verbose = $mode;
return $this; return $this;
@@ -220,11 +227,11 @@ class CImage
* *
* @return $this * @return $this
*/ */
private function checkFileExtension($extension) private function checkFileExtension($extension)
{ {
$valid = array('jpg', 'jpeg', 'png', 'gif'); $valid = array('jpg', 'jpeg', 'png', 'gif');
in_array($extension, $valid) in_array($extension, $valid)
or $this->raiseError('Not a valid file extension.'); or $this->raiseError('Not a valid file extension.');
return $this; return $this;
@@ -267,7 +274,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function setTarget($src = null, $dir = null) public function setTarget($src = null, $dir = null)
{ {
if (!(isset($src) && isset($dir))) { if (!(isset($src) && isset($dir))) {
return $this; return $this;
@@ -295,9 +302,9 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function setOptions($args) public function setOptions($args)
{ {
$this->log("Set new options for processing image."); $this->log("Set new options for processing image.");
$defaults = array( $defaults = array(
// Options for calculate dimensions // Options for calculate dimensions
@@ -306,15 +313,19 @@ class CImage
'aspectRatio' => null, 'aspectRatio' => null,
'keepRatio' => true, 'keepRatio' => true,
'cropToFit' => false, 'cropToFit' => false,
'crop' => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0), 'crop' => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
'area' => null, //'0,0,0,0', 'area' => null, //'0,0,0,0',
// Options for caching or using original // Options for caching or using original
'useCache' => true, 'useCache' => true,
'useOriginal' => true, 'useOriginal' => true,
// Pre-processing, before resizing is done // Pre-processing, before resizing is done
'scale' => null, 'scale' => null,
'rotateBefore' => null,
// General options
'bgColor' => 0,
// Post-processing, after resizing is done // Post-processing, after resizing is done
'palette' => null, 'palette' => null,
@@ -322,6 +333,8 @@ class CImage
'sharpen' => null, 'sharpen' => null,
'emboss' => null, 'emboss' => null,
'blur' => null, 'blur' => null,
'rotateAfter' => null,
'autoRotate' => false,
// Options for saving // Options for saving
//'quality' => null, //'quality' => null,
@@ -357,11 +370,11 @@ class CImage
$parts = explode(',', $filterStr); $parts = explode(',', $filterStr);
$filter = $this->mapFilter($parts[0]); $filter = $this->mapFilter($parts[0]);
$filter['str'] = $filterStr; $filter['str'] = $filterStr;
for ($i=1;$i<=$filter['argc'];$i++) { for ($i=1; $i<=$filter['argc']; $i++) {
if (isset($parts[$i])) { if (isset($parts[$i])) {
$filter["arg{$i}"] = $parts[$i]; $filter["arg{$i}"] = $parts[$i];
} else { } else {
throw new Exception('Missing arg to filter, review how many arguments are needed at http://php.net/manual/en/function.imagefilter.php'); throw new Exception('Missing arg to filter, review how many arguments are needed at http://php.net/manual/en/function.imagefilter.php');
} }
} }
$args['filters'][$key] = $filter; $args['filters'][$key] = $filter;
@@ -371,10 +384,13 @@ class CImage
// Merge default arguments with incoming and set properties. // Merge default arguments with incoming and set properties.
//$args = array_merge_recursive($defaults, $args); //$args = array_merge_recursive($defaults, $args);
$args = array_merge($defaults, $args); $args = array_merge($defaults, $args);
foreach ($defaults as $key=>$val) { foreach ($defaults as $key => $val) {
$this->{$key} = $args[$key]; $this->{$key} = $args[$key];
} }
$this->newWidthOrig = $this->newWidth;
$this->newHeightOrig = $this->newHeight;
return $this; return $this;
} }
@@ -388,10 +404,10 @@ class CImage
* @return array with filter settings * @return array with filter settings
* @throws Exception * @throws Exception
*/ */
private function mapFilter($name) private function mapFilter($name)
{ {
$map = array( $map = array(
'negate' => array('id'=>0, 'argc'=>0, 'type'=>IMG_FILTER_NEGATE), 'negate' => array('id'=>0, 'argc'=>0, 'type'=>IMG_FILTER_NEGATE),
'grayscale' => array('id'=>1, 'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE), 'grayscale' => array('id'=>1, 'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE),
'brightness' => array('id'=>2, 'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS), 'brightness' => array('id'=>2, 'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS),
'contrast' => array('id'=>3, 'argc'=>1, 'type'=>IMG_FILTER_CONTRAST), 'contrast' => array('id'=>3, 'argc'=>1, 'type'=>IMG_FILTER_CONTRAST),
@@ -405,9 +421,9 @@ class CImage
'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE), 'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE),
); );
if (isset($map[$name])) if (isset($map[$name])) {
return $map[$name]; return $map[$name];
else { } else {
throw new Exception('No such filter.'); throw new Exception('No such filter.');
} }
} }
@@ -415,15 +431,14 @@ class CImage
/** /**
* Init new width and height and do some sanity checks on constraints, before any * Load image details from original image file.
* processing can be done.
* *
* @return $this * @return $this
* @throws Exception * @throws Exception
*/ */
public function initDimensions() public function loadImageDetails()
{ {
is_readable($this->pathToImage) is_readable($this->pathToImage)
or $this->raiseError('Image file does not exist.'); or $this->raiseError('Image file does not exist.');
// Get details on image // Get details on image
@@ -436,6 +451,20 @@ class CImage
$this->log("Image filesize: " . filesize($this->pathToImage) . " bytes."); $this->log("Image filesize: " . filesize($this->pathToImage) . " bytes.");
} }
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()
{
// width as % // width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') { if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100; $this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
@@ -473,12 +502,12 @@ class CImage
} }
// Check values to be within domain // Check values to be within domain
is_null($this->newWidth) is_null($this->newWidth)
or is_numeric($this->newWidth) or is_numeric($this->newWidth)
or $this->raiseError('Width not numeric'); or $this->raiseError('Width not numeric');
is_null($this->newHeight) is_null($this->newHeight)
or is_numeric($this->newHeight) or is_numeric($this->newHeight)
or $this->raiseError('Height not numeric'); or $this->raiseError('Height not numeric');
return $this; return $this;
@@ -491,7 +520,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function calculateNewWidthAndHeight() public function calculateNewWidthAndHeight()
{ {
// Crop, use cropped width and height as base for calulations // Crop, use cropped width and height as base for calulations
$this->log("Calculate new width and height."); $this->log("Calculate new width and height.");
@@ -523,7 +552,7 @@ class CImage
$this->crop['start_x'] = $this->width - $width; $this->crop['start_x'] = $this->width - $width;
} elseif ($this->crop['start_x'] == 'center') { } elseif ($this->crop['start_x'] == 'center') {
$this->crop['start_x'] = round($this->width / 2) - round($width / 2); $this->crop['start_x'] = round($this->width / 2) - round($width / 2);
} }
if ($this->crop['start_y'] == 'top') { if ($this->crop['start_y'] == 'top') {
$this->crop['start_y'] = 0; $this->crop['start_y'] = 0;
@@ -531,19 +560,24 @@ class CImage
$this->crop['start_y'] = $this->height - $height; $this->crop['start_y'] = $this->height - $height;
} elseif ($this->crop['start_y'] == 'center') { } elseif ($this->crop['start_y'] == 'center') {
$this->crop['start_y'] = round($this->height / 2) - round($height / 2); $this->crop['start_y'] = round($this->height / 2) - round($height / 2);
} }
$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."); $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.");
} }
// Calculate new width and height if keeping aspect-ratio. // Calculate new width and height if keeping aspect-ratio.
if ($this->keepRatio) { if ($this->keepRatio) {
$this->log("Keep aspect ratio.");
// Crop-to-fit and both new width and height are set. // Crop-to-fit and both new width and height are set.
if ($this->cropToFit && isset($this->newWidth) && isset($this->newHeight)) { if ($this->cropToFit && isset($this->newWidth) && isset($this->newHeight)) {
// Use newWidth and newHeigh as width/height, image should fit in box. // Use newWidth and newHeigh as width/height, image should fit in box.
; $this->log("Use newWidth and newHeigh as width/height, image should fit in box.");
} elseif (isset($this->newWidth) && isset($this->newHeight)) { } elseif (isset($this->newWidth) && isset($this->newHeight)) {
// Both new width and height are set. // Both new width and height are set.
// Use newWidth and newHeigh as max width/height, image should not be larger. // Use newWidth and newHeigh as max width/height, image should not be larger.
$ratioWidth = $width / $this->newWidth; $ratioWidth = $width / $this->newWidth;
@@ -551,35 +585,45 @@ class CImage
$ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight; $ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight;
$this->newWidth = round($width / $ratio); $this->newWidth = round($width / $ratio);
$this->newHeight = round($height / $ratio); $this->newHeight = round($height / $ratio);
$this->log("New width and height was set.");
} elseif (isset($this->newWidth)) { } elseif (isset($this->newWidth)) {
// Use new width as max-width // Use new width as max-width
$factor = (float)$this->newWidth / (float)$width; $factor = (float)$this->newWidth / (float)$width;
$this->newHeight = round($factor * $height); $this->newHeight = round($factor * $height);
$this->log("New width was set.");
} elseif (isset($this->newHeight)) { } elseif (isset($this->newHeight)) {
// Use new height as max-hight // Use new height as max-hight
$factor = (float)$this->newHeight / (float)$height; $factor = (float)$this->newHeight / (float)$height;
$this->newWidth = round($factor * $width); $this->newWidth = round($factor * $width);
$this->log("New height was set.");
} }
// Use newWidth and newHeigh as defined width/height, image should fit the area. // Use newWidth and newHeigh as defined width/height, image should fit the area.
if ($this->cropToFit) { if ($this->cropToFit) {
$this->log("Crop to fit.");
$ratioWidth = $width / $this->newWidth; $ratioWidth = $width / $this->newWidth;
$ratioHeight = $height / $this->newHeight; $ratioHeight = $height / $this->newHeight;
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight; $ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
$this->cropWidth = round($width / $ratio); $this->cropWidth = round($width / $ratio);
$this->cropHeight = round($height / $ratio); $this->cropHeight = round($height / $ratio);
} }
} }
// Crop, ensure to set new width and height // Crop, ensure to set new width and height
if ($this->crop) { if ($this->crop) {
$this->log("Crop.");
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']); $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']); $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
} }
// No new height or width is set, use existing measures. // No new height or width is set, use existing measures.
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->width); $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->width);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height); $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height);
$this->log("Calculated new width x height as {$this->newWidth} x {$this->newHeight}."); $this->log("Calculated new width x height as {$this->newWidth} x {$this->newHeight}.");
return $this; return $this;
@@ -587,6 +631,26 @@ class CImage
/**
* 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;
$this->initDimensions()
->calculateNewWidthAndHeight();
return $this;
}
/** /**
* Set extension for filename to save as. * Set extension for filename to save as.
* *
@@ -594,7 +658,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function setSaveAsExtension($saveAs = null) public function setSaveAsExtension($saveAs = null)
{ {
if (isset($saveAs)) { if (isset($saveAs)) {
$this->checkFileExtension($saveAs); $this->checkFileExtension($saveAs);
@@ -622,7 +686,7 @@ class CImage
? $quality ? $quality
: self::JPEG_QUALITY_DEFAULT; : self::JPEG_QUALITY_DEFAULT;
(is_numeric($this->quality) and $this->quality > 0 and $this->quality <= 100) (is_numeric($this->quality) and $this->quality > 0 and $this->quality <= 100)
or $this->raiseError('Quality not in range.'); or $this->raiseError('Quality not in range.');
$this->log("Setting JPEG quality to {$this->quality}."); $this->log("Setting JPEG quality to {$this->quality}.");
@@ -645,7 +709,7 @@ class CImage
? $compress ? $compress
: self::PNG_COMPRESSION_DEFAULT; : self::PNG_COMPRESSION_DEFAULT;
(is_numeric($this->compress) and $this->compress >= -1 and $this->compress <= 9) (is_numeric($this->compress) and $this->compress >= -1 and $this->compress <= 9)
or $this->raiseError('Quality not in range.'); or $this->raiseError('Quality not in range.');
$this->log("Setting PNG compression level to {$this->compress}."); $this->log("Setting PNG compression level to {$this->compress}.");
@@ -662,21 +726,25 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function useOriginalIfPossible($useOrig = true) public function useOriginalIfPossible($useOrig = true)
{ {
if ($useOrig if ($useOrig
&& ($this->newWidth == $this->width) && ($this->newWidth == $this->width)
&& ($this->newHeight == $this->height) && ($this->newHeight == $this->height)
&& !$this->area && !$this->area
&& !$this->crop && !$this->crop
&& !$this->filters && !$this->filters
&& !$this->sharpen && !$this->sharpen
&& !$this->emboss && !$this->emboss
&& !$this->blur && !$this->blur
&& !$this->palette && !$this->palette
&& !$this->quality && !$this->quality
&& !$this->compress && !$this->compress
&& !$this->saveAs && !$this->saveAs
&& !$this->rotateBefore
&& !$this->rotateAfter
&& !$this->autoRotate
&& !$this->bgColor
) { ) {
$this->log("Using original image."); $this->log("Using original image.");
$this->output($this->pathToImage); $this->output($this->pathToImage);
@@ -693,21 +761,24 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function generateFilename($base) public function generateFilename($base)
{ {
$parts = pathinfo($this->pathToImage); $parts = pathinfo($this->pathToImage);
$cropToFit = $this->cropToFit ? '_cf' : null; $cropToFit = $this->cropToFit ? '_cf' : null;
$crop_x = $this->crop_x ? "_x{$this->crop_x}" : null; $crop_x = $this->crop_x ? "_x{$this->crop_x}" : null;
$crop_y = $this->crop_y ? "_y{$this->crop_y}" : null; $crop_y = $this->crop_y ? "_y{$this->crop_y}" : null;
$scale = $this->scale ? "_s{$this->scale}" : null; $scale = $this->scale ? "_s{$this->scale}" : null;
$quality = $this->quality ? "_q{$this->quality}" : null; $bgColor = $this->bgColor ? "_bgc{$this->bgColor}" : null;
$compress = $this->compress ? "_co{$this->compress}" : 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) $offset = isset($this->offset)
? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left'] ? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left']
: null; : null;
$crop = $this->crop $crop = $this->crop
? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y'] ? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y']
: null; : null;
@@ -716,7 +787,7 @@ class CImage
foreach ($this->filters as $filter) { foreach ($this->filters as $filter) {
if (is_array($filter)) { if (is_array($filter)) {
$filters .= "_f{$filter['id']}"; $filters .= "_f{$filter['id']}";
for ($i=1;$i<=$filter['argc'];$i++) { for ($i=1; $i<=$filter['argc']; $i++) {
$filters .= ":".$filter["arg{$i}"]; $filters .= ":".$filter["arg{$i}"];
} }
} }
@@ -727,9 +798,11 @@ class CImage
$emboss = $this->emboss ? 'e' : null; $emboss = $this->emboss ? 'e' : null;
$blur = $this->blur ? 'b' : null; $blur = $this->blur ? 'b' : null;
$palette = $this->palette ? 'p' : null; $palette = $this->palette ? 'p' : null;
$autoRotate = $this->autoRotate ? 'ar' : null;
$this->extension = isset($this->extension) $this->extension = isset($this->extension)
? $this->extension ? $this->extension
: $parts['extension']; : $parts['extension'];
// Check optimizing options // Check optimizing options
@@ -738,15 +811,15 @@ class CImage
$optimize = $this->jpegOptimize ? 'o' : null; $optimize = $this->jpegOptimize ? 'o' : null;
} elseif ($this->extension == 'png') { } elseif ($this->extension == 'png') {
$optimize .= $this->pngFilter ? 'f' : null; $optimize .= $this->pngFilter ? 'f' : null;
$optimize .= $this->pngDeflate ? 'd' : null; $optimize .= $this->pngDeflate ? 'd' : null;
} }
$subdir = str_replace('/', '-', dirname($this->imageSrc)); $subdir = str_replace('/', '-', dirname($this->imageSrc));
$subdir = ($subdir == '.') ? '_.' : $subdir; $subdir = ($subdir == '.') ? '_.' : $subdir;
$file = $subdir . '_' . $parts['filename'] . '_' . round($this->newWidth) . '_' $file = $subdir . '_' . $parts['filename'] . '_' . round($this->newWidth) . '_'
. round($this->newHeight) . $offset . $crop . $cropToFit . $crop_x . $crop_y . round($this->newHeight) . $offset . $crop . $cropToFit . $crop_x . $crop_y
. $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize . $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize
. $scale . '.' . $this->extension; . $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor . '.' . $this->extension;
return $this->setTarget($file, $base); return $this->setTarget($file, $base);
} }
@@ -760,7 +833,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function useCacheIfPossible($useCache = true) public function useCacheIfPossible($useCache = true)
{ {
if ($useCache && is_readable($this->cacheFileName)) { if ($useCache && is_readable($this->cacheFileName)) {
$fileTime = filemtime($this->pathToImage); $fileTime = filemtime($this->pathToImage);
@@ -770,7 +843,7 @@ class CImage
if ($this->useCache) { if ($this->useCache) {
if ($this->verbose) { if ($this->verbose) {
$this->log("Use cached file."); $this->log("Use cached file.");
$this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes."); $this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
} }
$this->output($this->cacheFileName); $this->output($this->cacheFileName);
} else { } else {
@@ -885,9 +958,9 @@ class CImage
* @return int as the type of the png-image * @return int as the type of the png-image
* *
*/ */
private function getPngType() private function getPngType()
{ {
$pngType = ord (file_get_contents ($this->pathToImage, false, null, 25, 1)); $pngType = ord(file_get_contents($this->pathToImage, false, null, 25, 1));
switch ($pngType) { switch ($pngType) {
@@ -895,11 +968,11 @@ class CImage
$this->log("PNG is type 0, Greyscale."); $this->log("PNG is type 0, Greyscale.");
break; break;
case self::PNG_RGB: case self::PNG_RGB:
$this->log("PNG is type 2, RGB"); $this->log("PNG is type 2, RGB");
break; break;
case self::PNG_RGB_PALETTE: case self::PNG_RGB_PALETTE:
$this->log("PNG is type 3, RGB with palette"); $this->log("PNG is type 3, RGB with palette");
break; break;
@@ -927,7 +1000,7 @@ class CImage
* *
* @return int * @return int
*/ */
private function colorsTotal($im) private function colorsTotal($im)
{ {
if (imageistruecolor($im)) { if (imageistruecolor($im)) {
$h = imagesy($im); $h = imagesy($im);
@@ -951,10 +1024,24 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function preResize() public function preResize()
{ {
$this->log("Pre-process before resizing"); $this->log("Pre-process before resizing");
// 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();
}
// Scale the original image before starting // Scale the original image before starting
if (isset($this->scale)) { if (isset($this->scale)) {
$this->log("Scale by {$this->scale}%"); $this->log("Scale by {$this->scale}%");
@@ -965,9 +1052,9 @@ class CImage
$this->image = $img; $this->image = $img;
$this->width = $newWidth; $this->width = $newWidth;
$this->height = $newHeight; $this->height = $newHeight;
} }
return $this; return $this;
} }
@@ -977,7 +1064,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function resize() public function resize()
{ {
$this->log("Starting to Resize()"); $this->log("Starting to Resize()");
@@ -991,7 +1078,7 @@ class CImage
$this->image = $img; $this->image = $img;
$this->width = $this->offset['width']; $this->width = $this->offset['width'];
$this->height = $this->offset['height']; $this->height = $this->offset['height'];
} }
// SaveAs need to copy image to remove transparency, if any // SaveAs need to copy image to remove transparency, if any
if ($this->saveAs) { if ($this->saveAs) {
@@ -1013,14 +1100,14 @@ class CImage
$this->image = $img; $this->image = $img;
$this->width = $this->crop['width']; $this->width = $this->crop['width'];
$this->height = $this->crop['height']; $this->height = $this->crop['height'];
} }
// Resize by crop to fit // Resize by crop to fit
if ($this->cropToFit) { if ($this->cropToFit) {
$this->log("Crop to fit"); $this->log("Crop to fit");
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2)); $cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2)); $cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight); $imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height); imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
@@ -1051,10 +1138,16 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function postResize() public function postResize()
{ {
$this->log("Post-process after resizing"); $this->log("Post-process after resizing");
// Rotate image
if ($this->rotateAfter) {
$this->log("Rotating image.");
$this->rotate($this->rotateAfter, $this->bgColor);
}
// Apply filters // Apply filters
if (isset($this->filters) && is_array($this->filters)) { if (isset($this->filters) && is_array($this->filters)) {
@@ -1063,54 +1156,121 @@ class CImage
switch ($filter['argc']) { switch ($filter['argc']) {
case 0: case 0:
imagefilter($this->image, $filter['type']); imagefilter($this->image, $filter['type']);
break; break;
case 1: case 1:
imagefilter($this->image, $filter['type'], $filter['arg1']); imagefilter($this->image, $filter['type'], $filter['arg1']);
break; break;
case 2: case 2:
imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2']); imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2']);
break; break;
case 3: case 3:
imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']);
break; break;
case 4: case 4:
imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']);
break; break;
} }
} }
} }
// Convert to palette image // Convert to palette image
if($this->palette) { if ($this->palette) {
$this->log("Converting to palette image."); $this->log("Converting to palette image.");
$this->trueColorToPalette(); $this->trueColorToPalette();
} }
// Blur the image // Blur the image
if($this->blur) { if ($this->blur) {
$this->log("Blur."); $this->log("Blur.");
$this->blurImage(); $this->blurImage();
} }
// Emboss the image // Emboss the image
if($this->emboss) { if ($this->emboss) {
$this->log("Emboss."); $this->log("Emboss.");
$this->embossImage(); $this->embossImage();
} }
// Sharpen the image // Sharpen the image
if($this->sharpen) { if ($this->sharpen) {
$this->log("Sharpen."); $this->log("Sharpen.");
$this->sharpenImage(); $this->sharpenImage();
} }
return $this; 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;
} }
@@ -1121,7 +1281,7 @@ class CImage
* *
* @return void * @return void
*/ */
public function trueColorToPalette() public function trueColorToPalette()
{ {
$img = imagecreatetruecolor($this->width, $this->height); $img = imagecreatetruecolor($this->width, $this->height);
$bga = imagecolorallocatealpha($img, 0, 0, 0, 127); $bga = imagecolorallocatealpha($img, 0, 0, 0, 127);
@@ -1147,7 +1307,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function sharpenImage() public function sharpenImage()
{ {
$matrix = array( $matrix = array(
array(-1,-1,-1,), array(-1,-1,-1,),
@@ -1170,7 +1330,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function embossImage() public function embossImage()
{ {
$matrix = array( $matrix = array(
array( 1, 1,-1,), array( 1, 1,-1,),
@@ -1193,7 +1353,7 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function blurImage() public function blurImage()
{ {
$matrix = array( $matrix = array(
array( 1, 1, 1,), array( 1, 1, 1,),
@@ -1218,12 +1378,12 @@ class CImage
* @param int $height of the new image. * @param int $height of the new image.
* @return image resource. * @return image resource.
*/ */
private function createImageKeepTransparency($width, $height) private function createImageKeepTransparency($width, $height)
{ {
$this->log("Creating a new working image width={$width}px, height={$height}px."); $this->log("Creating a new working image width={$width}px, height={$height}px.");
$img = imagecreatetruecolor($width, $height); $img = imagecreatetruecolor($width, $height);
imagealphablending($img, false); imagealphablending($img, false);
imagesavealpha($img, true); imagesavealpha($img, true);
/* /*
$this->Log("Filling image with background color."); $this->Log("Filling image with background color.");
@@ -1243,12 +1403,12 @@ class CImage
* *
* @return $this * @return $this
*/ */
public function setPostProcessingOptions($options) public function setPostProcessingOptions($options)
{ {
if (isset($options['jpeg_optimize']) && $options['jpeg_optimize']) { if (isset($options['jpeg_optimize']) && $options['jpeg_optimize']) {
$this->jpegOptimizeCmd = $options['jpeg_optimize_cmd']; $this->jpegOptimizeCmd = $options['jpeg_optimize_cmd'];
} else { } else {
$this->jpegOptimizeCmd = null; $this->jpegOptimizeCmd = null;
} }
if (isset($options['png_filter']) && $options['png_filter']) { if (isset($options['png_filter']) && $options['png_filter']) {
@@ -1291,9 +1451,9 @@ class CImage
// Use JPEG optimize if defined // Use JPEG optimize if defined
if ($this->jpegOptimizeCmd) { if ($this->jpegOptimizeCmd) {
if ($this->verbose) { if ($this->verbose) {
clearstatcache(); clearstatcache();
$this->log("Filesize before optimize: " . filesize($this->cacheFileName) . " bytes."); $this->log("Filesize before optimize: " . filesize($this->cacheFileName) . " bytes.");
} }
$res = array(); $res = array();
$cmd = $this->jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName"; $cmd = $this->jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName";
@@ -1301,28 +1461,28 @@ class CImage
$this->log($cmd); $this->log($cmd);
$this->log($res); $this->log($res);
} }
break; break;
case 'gif': case 'gif':
if ($this->saveFolder) { if ($this->saveFolder) {
$this->Log("Saving image as GIF to cache."); $this->Log("Saving image as GIF to cache.");
imagegif($this->image, $this->cacheFileName); imagegif($this->image, $this->cacheFileName);
} }
break; break;
case 'png': case 'png':
$this->Log("Saving image as PNG to cache using compression = {$this->compress}."); $this->Log("Saving image as PNG to cache using compression = {$this->compress}.");
// Turn off alpha blending and set alpha flag // Turn off alpha blending and set alpha flag
imagealphablending($this->image, false); imagealphablending($this->image, false);
imagesavealpha($this->image, true); imagesavealpha($this->image, true);
imagepng($this->image, $this->cacheFileName, $this->compress); imagepng($this->image, $this->cacheFileName, $this->compress);
// Use external program to filter PNG, if defined // Use external program to filter PNG, if defined
if ($this->pngFilterCmd) { if ($this->pngFilterCmd) {
if ($this->verbose) { if ($this->verbose) {
clearstatcache(); clearstatcache();
$this->Log("Filesize before filter optimize: " . filesize($this->cacheFileName) . " bytes."); $this->Log("Filesize before filter optimize: " . filesize($this->cacheFileName) . " bytes.");
} }
$res = array(); $res = array();
$cmd = $this->pngFilterCmd . " $this->cacheFileName"; $cmd = $this->pngFilterCmd . " $this->cacheFileName";
@@ -1333,9 +1493,9 @@ class CImage
// Use external program to deflate PNG, if defined // Use external program to deflate PNG, if defined
if ($this->pngDeflateCmd) { if ($this->pngDeflateCmd) {
if ($this->verbose) { if ($this->verbose) {
clearstatcache(); clearstatcache();
$this->Log("Filesize before deflate optimize: " . filesize($this->cacheFileName) . " bytes."); $this->Log("Filesize before deflate optimize: " . filesize($this->cacheFileName) . " bytes.");
} }
$res = array(); $res = array();
$cmd = $this->pngDeflateCmd . " $this->cacheFileName"; $cmd = $this->pngDeflateCmd . " $this->cacheFileName";
@@ -1343,16 +1503,16 @@ class CImage
$this->Log($cmd); $this->Log($cmd);
$this->Log($res); $this->Log($res);
} }
break; break;
default: default:
$this->RaiseError('No support for this file extension.'); $this->RaiseError('No support for this file extension.');
break; break;
} }
if ($this->verbose) { if ($this->verbose) {
clearstatcache(); clearstatcache();
$this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes."); $this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
$this->log("imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false')); $this->log("imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false'));
$this->log("imagecolorstotal() : " . imagecolorstotal($this->image)); $this->log("imagecolorstotal() : " . imagecolorstotal($this->image));
$this->log("Number of colors in image = " . $this->ColorsTotal($this->image)); $this->log("Number of colors in image = " . $this->ColorsTotal($this->image));

View File

@@ -133,6 +133,13 @@ Revision history
v0.5.x (latest) v0.5.x (latest)
* Broke API when `initDimensions()` split into two methods, new `initDimensions()` and `loadImageDetails()`.
* Added `autoRotate,`aro` to auto rotate image based on EXIF information.
* Added `bgColor,`bgc` to use as backgroundcolor when needing a filler color.
* Added `rotate,`r` as an alias to `rotateAfter`.
* Added `rotateBefore,`rb` to rotate image a certain angle before processing.
* Added `rotateAfter,`ra` to rotate image a certain angle after processing.
* Cleaned up code formatting, removed trailing spaces.
* Removed @ from opening images, better to display correct warning when failing #34, but put it back again. * Removed @ from opening images, better to display correct warning when failing #34, but put it back again.
* Setting gd.jpeg_ignore_warning to true as default #34. * Setting gd.jpeg_ignore_warning to true as default #34.
* `webroot/check_system.php` now outputs version of PHP and GD. * `webroot/check_system.php` now outputs version of PHP and GD.

View File

@@ -306,7 +306,7 @@ verbose("save as = $saveAs");
$scale = get(array('scale', 's')); $scale = get(array('scale', 's'));
is_null($scale) is_null($scale)
or ($scale >= 0 and $quality <= 400) or ($scale >= 0 and $scale <= 400)
or errorPage('Scale out of range'); or errorPage('Scale out of range');
verbose("scale = $scale"); verbose("scale = $scale");
@@ -349,6 +349,68 @@ verbose("blur = $blur");
/**
* rotate - Rotate the image with an angle, before processing
*/
/*
$rotate = get(array('rotate', 'r'));
is_null($rotate)
or ($rotate >= -360 and $rotate <= 360)
or errorPage('Rotate out of range');
verbose("rotate = $rotate");
*/
/**
* rotateBefore - Rotate the image with an angle, before processing
*/
$rotateBefore = get(array('rotateBefore', 'rb'));
is_null($rotateBefore)
or ($rotateBefore >= -360 and $rotateBefore <= 360)
or errorPage('RotateBefore out of range');
verbose("rotateBefore = $rotateBefore");
/**
* rotateAfter - Rotate the image with an angle, before processing
*/
$rotateAfter = get(array('rotateAfter', 'ra', 'rotate', 'r'));
is_null($rotateAfter)
or ($rotateAfter >= -360 and $rotateAfter <= 360)
or errorPage('RotateBefore out of range');
verbose("rotateAfter = $rotateAfter");
/**
* bgColor - Default background color to use
*/
$bgColor = hexdec(get(array('bgColor', 'bgc')));
is_null($bgColor)
or ($bgColor >= 0 and $bgColor <= hexdec("FFFFFF"))
or errorPage('Background color needs a hex value');
verbose("bgColor = $bgColor");
/**
* autoRotate - Auto rotate based on EXIF information
*/
$autoRotate = getDefined(array('autoRotate', 'aro'), true, false);
verbose("autoRotate = $autoRotate");
/** /**
* filter, f, f0-f9 - Processing option, post filter for various effects using imagefilter() * filter, f, f0-f9 - Processing option, post filter for various effects using imagefilter()
*/ */
@@ -400,26 +462,33 @@ $img->setVerbose($verbose)
->setSource($srcImage, $config['image_path']) ->setSource($srcImage, $config['image_path'])
->setOptions( ->setOptions(
array( array(
// Options for calculate dimensions // Options for calculate dimensions
'newWidth' => $newWidth, 'newWidth' => $newWidth,
'newHeight' => $newHeight, 'newHeight' => $newHeight,
'aspectRatio' => $aspectRatio, 'aspectRatio' => $aspectRatio,
'keepRatio' => $keepRatio, 'keepRatio' => $keepRatio,
'cropToFit' => $cropToFit, 'cropToFit' => $cropToFit,
'crop' => $crop, 'crop' => $crop,
'area' => $area, 'area' => $area,
// Pre-processing, before resizing is done // Pre-processing, before resizing is done
'scale' => $scale, 'scale' => $scale,
'rotateBefore' => $rotateBefore,
// Post-processing, after resizing is done // General processing options
'palette' => $palette, 'bgColor' => $bgColor,
'filters' => $filters,
'sharpen' => $sharpen, // Post-processing, after resizing is done
'emboss' => $emboss, 'palette' => $palette,
'blur' => $blur, 'filters' => $filters,
'sharpen' => $sharpen,
'emboss' => $emboss,
'blur' => $blur,
'rotateAfter' => $rotateAfter,
'autoRotate' => $autoRotate,
) )
) )
->loadImageDetails()
->initDimensions() ->initDimensions()
->calculateNewWidthAndHeight() ->calculateNewWidthAndHeight()
->setSaveAsExtension($saveAs) ->setSaveAsExtension($saveAs)

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

77
webroot/test_issue36.php Normal file
View File

@@ -0,0 +1,77 @@
<!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36</title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36</h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "img.php?src=";
$images = array(
'issue36/me-0.jpg',
'issue36/me-90.jpg',
'issue36/me-180.jpg',
'issue36/me-270.jpg',
'issue36/flower-0.jpg',
'issue36/flower-90.jpg',
'issue36/flower-180.jpg',
'issue36/flower-270.jpg',
);
$testcase = array(
'&aro&nc',
'&aro&nc&w=200',
'&aro&nc&h=200',
'&aro&nc&w=200&h=200&cf',
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>