4
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
# Cache files #
|
||||
######################
|
||||
cache/_*
|
||||
|
||||
cache/*
|
||||
|
||||
|
194
CImage.php
@@ -151,24 +151,31 @@ class CImage
|
||||
private $jpegOptimize;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Image dimensions, calculated from loaded image.
|
||||
*/
|
||||
private $width; // Calculated from source image
|
||||
private $height; // Calculated from source image
|
||||
|
||||
|
||||
/**
|
||||
* New image dimensions, incoming as argument or calculated.
|
||||
*/
|
||||
private $newWidth;
|
||||
private $newWidthOrig; // Save original value
|
||||
private $newHeight;
|
||||
private $newHeightOrig; // Save original value
|
||||
|
||||
|
||||
/**
|
||||
* Properties (clean up these)
|
||||
*/
|
||||
private $offset;
|
||||
|
||||
public $keepRatio;
|
||||
public $cropToFit;
|
||||
public $crop;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
public $newWidth;
|
||||
public $newHeight;
|
||||
private $cropWidth;
|
||||
private $cropHeight;
|
||||
public $crop_x;
|
||||
@@ -315,6 +322,10 @@ class CImage
|
||||
|
||||
// Pre-processing, before resizing is done
|
||||
'scale' => null,
|
||||
'rotateBefore' => null,
|
||||
|
||||
// General options
|
||||
'bgColor' => 0,
|
||||
|
||||
// Post-processing, after resizing is done
|
||||
'palette' => null,
|
||||
@@ -322,6 +333,8 @@ class CImage
|
||||
'sharpen' => null,
|
||||
'emboss' => null,
|
||||
'blur' => null,
|
||||
'rotateAfter' => null,
|
||||
'autoRotate' => false,
|
||||
|
||||
// Options for saving
|
||||
//'quality' => null,
|
||||
@@ -375,6 +388,9 @@ class CImage
|
||||
$this->{$key} = $args[$key];
|
||||
}
|
||||
|
||||
$this->newWidthOrig = $this->newWidth;
|
||||
$this->newHeightOrig = $this->newHeight;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -405,9 +421,9 @@ class CImage
|
||||
'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE),
|
||||
);
|
||||
|
||||
if (isset($map[$name]))
|
||||
if (isset($map[$name])) {
|
||||
return $map[$name];
|
||||
else {
|
||||
} else {
|
||||
throw new Exception('No such filter.');
|
||||
}
|
||||
}
|
||||
@@ -415,13 +431,12 @@ class CImage
|
||||
|
||||
|
||||
/**
|
||||
* Init new width and height and do some sanity checks on constraints, before any
|
||||
* processing can be done.
|
||||
* Load image details from original image file.
|
||||
*
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function initDimensions()
|
||||
public function loadImageDetails()
|
||||
{
|
||||
is_readable($this->pathToImage)
|
||||
or $this->raiseError('Image file does not exist.');
|
||||
@@ -436,6 +451,20 @@ class CImage
|
||||
$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 %
|
||||
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
|
||||
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
|
||||
@@ -539,11 +568,16 @@ class CImage
|
||||
// Calculate new width and height if keeping aspect-ratio.
|
||||
if ($this->keepRatio) {
|
||||
|
||||
$this->log("Keep aspect ratio.");
|
||||
|
||||
// Crop-to-fit and both new width and height are set.
|
||||
if ($this->cropToFit && isset($this->newWidth) && isset($this->newHeight)) {
|
||||
|
||||
// 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)) {
|
||||
|
||||
// Both new width and height are set.
|
||||
// Use newWidth and newHeigh as max width/height, image should not be larger.
|
||||
$ratioWidth = $width / $this->newWidth;
|
||||
@@ -551,18 +585,27 @@ class CImage
|
||||
$ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight;
|
||||
$this->newWidth = round($width / $ratio);
|
||||
$this->newHeight = round($height / $ratio);
|
||||
$this->log("New width and height was set.");
|
||||
|
||||
} elseif (isset($this->newWidth)) {
|
||||
|
||||
// Use new width as max-width
|
||||
$factor = (float)$this->newWidth / (float)$width;
|
||||
$this->newHeight = round($factor * $height);
|
||||
$this->log("New width was set.");
|
||||
|
||||
} elseif (isset($this->newHeight)) {
|
||||
|
||||
// Use new height as max-hight
|
||||
$factor = (float)$this->newHeight / (float)$height;
|
||||
$this->newWidth = round($factor * $width);
|
||||
$this->log("New height was set.");
|
||||
|
||||
}
|
||||
|
||||
// Use newWidth and newHeigh as defined width/height, image should fit the area.
|
||||
if ($this->cropToFit) {
|
||||
$this->log("Crop to fit.");
|
||||
$ratioWidth = $width / $this->newWidth;
|
||||
$ratioHeight = $height / $this->newHeight;
|
||||
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
|
||||
@@ -573,6 +616,7 @@ class CImage
|
||||
|
||||
// Crop, ensure to set new width and height
|
||||
if ($this->crop) {
|
||||
$this->log("Crop.");
|
||||
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
|
||||
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
@@ -677,6 +741,10 @@ class CImage
|
||||
&& !$this->quality
|
||||
&& !$this->compress
|
||||
&& !$this->saveAs
|
||||
&& !$this->rotateBefore
|
||||
&& !$this->rotateAfter
|
||||
&& !$this->autoRotate
|
||||
&& !$this->bgColor
|
||||
) {
|
||||
$this->log("Using original image.");
|
||||
$this->output($this->pathToImage);
|
||||
@@ -700,8 +768,11 @@ class CImage
|
||||
$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']
|
||||
@@ -728,6 +799,8 @@ class CImage
|
||||
$blur = $this->blur ? 'b' : null;
|
||||
$palette = $this->palette ? 'p' : null;
|
||||
|
||||
$autoRotate = $this->autoRotate ? 'ar' : null;
|
||||
|
||||
$this->extension = isset($this->extension)
|
||||
? $this->extension
|
||||
: $parts['extension'];
|
||||
@@ -746,7 +819,7 @@ class CImage
|
||||
$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 . '.' . $this->extension;
|
||||
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor . '.' . $this->extension;
|
||||
|
||||
return $this->setTarget($file, $base);
|
||||
}
|
||||
@@ -955,6 +1028,20 @@ class CImage
|
||||
{
|
||||
$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
|
||||
if (isset($this->scale)) {
|
||||
$this->log("Scale by {$this->scale}%");
|
||||
@@ -1055,6 +1142,12 @@ class CImage
|
||||
{
|
||||
$this->log("Post-process after resizing");
|
||||
|
||||
// Rotate image
|
||||
if ($this->rotateAfter) {
|
||||
$this->log("Rotating image.");
|
||||
$this->rotate($this->rotateAfter, $this->bgColor);
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
if (isset($this->filters) && is_array($this->filters)) {
|
||||
|
||||
@@ -1115,6 +1208,73 @@ class CImage
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@@ -133,6 +133,13 @@ Revision history
|
||||
|
||||
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.
|
||||
* Setting gd.jpeg_ignore_warning to true as default #34.
|
||||
* `webroot/check_system.php` now outputs version of PHP and GD.
|
||||
|
@@ -306,7 +306,7 @@ verbose("save as = $saveAs");
|
||||
$scale = get(array('scale', 's'));
|
||||
|
||||
is_null($scale)
|
||||
or ($scale >= 0 and $quality <= 400)
|
||||
or ($scale >= 0 and $scale <= 400)
|
||||
or errorPage('Scale out of range');
|
||||
|
||||
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()
|
||||
*/
|
||||
@@ -411,6 +473,10 @@ $img->setVerbose($verbose)
|
||||
|
||||
// Pre-processing, before resizing is done
|
||||
'scale' => $scale,
|
||||
'rotateBefore' => $rotateBefore,
|
||||
|
||||
// General processing options
|
||||
'bgColor' => $bgColor,
|
||||
|
||||
// Post-processing, after resizing is done
|
||||
'palette' => $palette,
|
||||
@@ -418,8 +484,11 @@ $img->setVerbose($verbose)
|
||||
'sharpen' => $sharpen,
|
||||
'emboss' => $emboss,
|
||||
'blur' => $blur,
|
||||
'rotateAfter' => $rotateAfter,
|
||||
'autoRotate' => $autoRotate,
|
||||
)
|
||||
)
|
||||
->loadImageDetails()
|
||||
->initDimensions()
|
||||
->calculateNewWidthAndHeight()
|
||||
->setSaveAsExtension($saveAs)
|
||||
|
BIN
webroot/img/issue36/flower-0.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
webroot/img/issue36/flower-180.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
webroot/img/issue36/flower-270.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
webroot/img/issue36/flower-90.jpg
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
webroot/img/issue36/me-0.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
webroot/img/issue36/me-180.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
webroot/img/issue36/me-270.jpg
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
webroot/img/issue36/me-90.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
77
webroot/test_issue36.php
Normal 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; ?>
|
||||
|