mirror of
https://github.com/mosbth/cimage.git
synced 2025-08-04 15:17:42 +02:00
A lot of updates when preparing for release.
This commit is contained in:
745
CImage.php
745
CImage.php
@@ -8,6 +8,18 @@
|
|||||||
*/
|
*/
|
||||||
class CImage {
|
class CImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants
|
||||||
|
*/
|
||||||
|
const PNG_GREYSCALE = 0;
|
||||||
|
const PNG_RGB = 2;
|
||||||
|
const PNG_RGB_PALETTE = 3;
|
||||||
|
const PNG_GREYSCALE_ALPHA = 4;
|
||||||
|
const PNG_RGB_ALPHA = 6;
|
||||||
|
const PNG_QUALITY_DEFAULT = -1;
|
||||||
|
const JPEG_QUALITY_DEFAULT = 60;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties
|
* Properties
|
||||||
*/
|
*/
|
||||||
@@ -29,13 +41,28 @@ class CImage {
|
|||||||
public $filters;
|
public $filters;
|
||||||
public $saveFolder;
|
public $saveFolder;
|
||||||
public $newName;
|
public $newName;
|
||||||
private $newFileName;
|
//private $newFileName; // OBSOLETE, using cacheFileName instead.
|
||||||
private $mime; // Calculated from source image
|
private $mime; // Calculated from source image
|
||||||
private $width; // Calculated from source image
|
private $width; // Calculated from source image
|
||||||
private $height; // Calculated from source image
|
private $height; // Calculated from source image
|
||||||
private $type; // Calculated from source image
|
private $type; // Calculated from source image
|
||||||
private $attr; // Calculated from source image
|
private $attr; // Calculated from source image
|
||||||
private $validExtensions = array('jpg', 'jpeg', 'png', 'gif');
|
private $validExtensions = array('jpg', 'jpeg', 'png', 'gif');
|
||||||
|
private $verbose; // Print out a trace together with original and created image
|
||||||
|
private $log; // Keep a log/trace on what happens.
|
||||||
|
private $cacheFileName; // Filename of the new image in the cache.
|
||||||
|
private $useCache; // Use the cache if true, set to false to ignore the cached file.
|
||||||
|
private $useOriginal; // Use original image if possible
|
||||||
|
private $saveAs; // Define a format to save image as, or null to use original format.
|
||||||
|
private $extension; // Extension to save image as.
|
||||||
|
|
||||||
|
// Specific for PNG
|
||||||
|
private $pngType; // Find out which type of PNG image it is.
|
||||||
|
private $pngFilter; // Path to command for filter optimize, for example optipng or null.
|
||||||
|
private $pngDeflate; // Path to command for deflate optimize, for example pngout or null.
|
||||||
|
|
||||||
|
// Specific for JPEG
|
||||||
|
private $jpegOptimize; // Path to command to optimize jpeg images, for example jpegtran or null.
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,11 +78,60 @@ class CImage {
|
|||||||
$this->imageFolder = rtrim($imageFolder, '/');
|
$this->imageFolder = rtrim($imageFolder, '/');
|
||||||
$this->pathToImage = $this->imageFolder . '/' . $this->imageName;
|
$this->pathToImage = $this->imageFolder . '/' . $this->imageName;
|
||||||
$this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION);
|
$this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION);
|
||||||
|
$this->extension = $this->fileExtension;
|
||||||
$this->saveFolder = $saveFolder;
|
$this->saveFolder = $saveFolder;
|
||||||
$this->newName = $newName;
|
$this->newName = $newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an event.
|
||||||
|
*
|
||||||
|
* @param string $message to log.
|
||||||
|
*/
|
||||||
|
public function Log($message) {
|
||||||
|
if($this->verbose) {
|
||||||
|
$this->log[] = $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do verbose output and print out the log and the actual images.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function VerboseOutput() {
|
||||||
|
$log = null;
|
||||||
|
$this->Log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M");
|
||||||
|
$this->Log("Memory limit: " . ini_get('memory_limit'));
|
||||||
|
foreach($this->log as $val) {
|
||||||
|
if(is_array($val)) {
|
||||||
|
foreach($val as $val1) {
|
||||||
|
$log .= $val1 . '<br/>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$log .= $val . '<br/>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = null; //print_r($this, 1);
|
||||||
|
|
||||||
|
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>
|
||||||
|
<pre>{$object}</pre>
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raise error, enables to implement a selection of error methods.
|
* Raise error, enables to implement a selection of error methods.
|
||||||
*
|
*
|
||||||
@@ -66,7 +142,8 @@ class CImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
/*
|
||||||
* Create filename to save file in cache.
|
* Create filename to save file in cache.
|
||||||
*/
|
*/
|
||||||
public function CreateFilename() {
|
public function CreateFilename() {
|
||||||
@@ -74,7 +151,8 @@ class CImage {
|
|||||||
$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;
|
||||||
$quality = $this->quality == 100 ? null : "_q{$this->quality}";
|
$quality = $this->quality ? "_q{$this->quality}" : null;
|
||||||
|
$offset = isset($this->offset) ? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left'] : null;
|
||||||
$crop = $this->crop ? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y'] : null;
|
$crop = $this->crop ? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y'] : null;
|
||||||
$filters = null;
|
$filters = null;
|
||||||
if(isset($this->filters)) {
|
if(isset($this->filters)) {
|
||||||
@@ -87,19 +165,39 @@ class CImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$sharpen = $this->sharpen ? 's' : null;
|
||||||
|
$emboss = $this->emboss ? 'e' : null;
|
||||||
|
$blur = $this->blur ? 'b' : null;
|
||||||
|
$palette = $this->palette ? 'p' : null;
|
||||||
|
|
||||||
|
$this->extension = isset($this->extension) ? $this->extension : $parts['extension'];
|
||||||
|
|
||||||
|
// Check optimizing options
|
||||||
|
$optimize = null;
|
||||||
|
if($this->extension == 'jpeg' || $this->extension == 'jpg') {
|
||||||
|
$optimize = $this->jpegOptimize ? 'o' : null;
|
||||||
|
}
|
||||||
|
else if($this->extension == 'png') {
|
||||||
|
$optimize .= $this->pngFilter ? 'f' : null;
|
||||||
|
$optimize .= $this->pngDeflate ? 'd' : null;
|
||||||
|
}
|
||||||
|
|
||||||
$subdir = str_replace('/', '-', dirname($this->imageName));
|
$subdir = str_replace('/', '-', dirname($this->imageName));
|
||||||
$subdir = ($subdir == '.') ? '_.' : $subdir;
|
$subdir = ($subdir == '.') ? '_.' : $subdir;
|
||||||
return $this->saveFolder . '/' . $subdir . '_' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . $cropToFit . $crop_x . $crop_y . $quality . $filters . '.' . $parts['extension'];
|
$this->cacheFileName = $this->saveFolder . '/' . $subdir . '_' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $offset . $crop . $cropToFit . $crop_x . $crop_y . $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize . '.' . $this->extension;
|
||||||
|
$this->Log("The cache file name is: " . $this->cacheFileName);
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init and do some sanity checks before any processing is done. Throws exception if not valid.
|
* Init and do some sanity checks before any processing is done. Throws exception if not valid.
|
||||||
*/
|
*/
|
||||||
public function Init() {
|
public function Init() {
|
||||||
is_null($this->newWidth) or is_numeric($this->newWidth) or $this->RaiseError('Width not numeric');
|
is_null($this->newWidth) or is_numeric($this->newWidth) or $this->RaiseError('Width not numeric');
|
||||||
is_null($this->newHeight) or is_numeric($this->newHeight) or $this->RaiseError('Height not numeric');
|
is_null($this->newHeight) or is_numeric($this->newHeight) or $this->RaiseError('Height not numeric');
|
||||||
is_numeric($this->quality) and $this->quality >= 0 and $this->quality <= 100 or $this->RaiseError('Quality not in range.');
|
is_null($this->quality) or (is_numeric($this->quality) and $this->quality >= 0 and $this->quality <= 100) or $this->RaiseError('Quality not in range.');
|
||||||
//is_numeric($this->crop_x) && is_numeric($this->crop_y) or $this->RaiseError('Quality not in range.');
|
//is_numeric($this->crop_x) && is_numeric($this->crop_y) or $this->RaiseError('Quality not in range.');
|
||||||
//filter
|
//filter
|
||||||
is_readable($this->pathToImage) or $this->RaiseError('File does not exist.');
|
is_readable($this->pathToImage) or $this->RaiseError('File does not exist.');
|
||||||
@@ -111,6 +209,12 @@ class CImage {
|
|||||||
!empty($info) or $this->RaiseError("The file doesn't seem to be an image.");
|
!empty($info) or $this->RaiseError("The file doesn't seem to be an image.");
|
||||||
$this->mime = $info['mime'];
|
$this->mime = $info['mime'];
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,34 +224,180 @@ class CImage {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function Output($file) {
|
protected function Output($file) {
|
||||||
|
if($this->verbose) {
|
||||||
|
$this->Log("Outputting image: $file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get details on image
|
||||||
|
$info = list($width, $height, $type, $attr) = getimagesize($file);
|
||||||
|
!empty($info) or $this->RaiseError("The file doesn't seem to be an image.");
|
||||||
|
$mime = $info['mime'];
|
||||||
$time = filemtime($file);
|
$time = filemtime($file);
|
||||||
|
|
||||||
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $time){
|
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $time){
|
||||||
|
if($this->verbose) {
|
||||||
|
$this->Log("304 not modified");
|
||||||
|
$this->VerboseOutput();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
header("HTTP/1.0 304 Not Modified");
|
header("HTTP/1.0 304 Not Modified");
|
||||||
} else {
|
} else {
|
||||||
header('Content-type: ' . $this->mime);
|
$gmdate = gmdate("D, d M Y H:i:s", $time);
|
||||||
header('Last-Modified: ' . gmdate("D, d M Y H:i:s",$time) . " GMT");
|
|
||||||
|
if($this->verbose) {
|
||||||
|
$this->Log("Last modified: " . $gmdate . " GMT");
|
||||||
|
$this->VerboseOutput();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-type: ' . $mime);
|
||||||
|
header('Last-Modified: ' . $gmdate . " GMT");
|
||||||
readfile($file);
|
readfile($file);
|
||||||
}
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of PNG image.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function GetPngType() {
|
||||||
|
$this->pngType = ord (file_get_contents ($this->pathToImage, false, null, 25, 1));
|
||||||
|
if($this->pngType == self::PNG_GREYSCALE) {
|
||||||
|
$this->Log("PNG is type 0, Greyscale.");
|
||||||
|
}
|
||||||
|
else if($this->pngType == self::PNG_RGB) {
|
||||||
|
$this->Log("PNG is type 2, RGB");
|
||||||
|
}
|
||||||
|
else if($this->pngType == self::PNG_RGB_PALETTE) {
|
||||||
|
$this->Log("PNG is type 3, RGB with palette");
|
||||||
|
}
|
||||||
|
else if($this->pngType == self::PNG_GREYSCALE_ALPHA) {
|
||||||
|
$this->Log("PNG is type 4, Greyscale with alpha channel");
|
||||||
|
}
|
||||||
|
else if($this->pngType == self::PNG_RGB_ALPHA) {
|
||||||
|
$this->Log("PNG is type 6, RGB with alpha channel (PNG 32-bit)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pngType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set quality of image
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function SetQuality() {
|
||||||
|
if(!$this->quality) {
|
||||||
|
switch($this->extension) {
|
||||||
|
case 'jpg':
|
||||||
|
$this->quality = self::JPEG_QUALITY_DEFAULT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'png':
|
||||||
|
$this->quality = self::PNG_QUALITY_DEFAULT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->quality = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->Log("Setting quality to {$this->quality}.");
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set optmizing options.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function SetOptimization() {
|
||||||
|
if(defined('JPEG_OPTIMIZE')) {
|
||||||
|
$this->jpegOptimize = JPEG_OPTIMIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(defined('PNG_FILTER')) {
|
||||||
|
$this->pngFilter = PNG_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(defined('PNG_DEFLATE')) {
|
||||||
|
$this->pngDeflate = PNG_DEFLATE;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calciulate number of colors in an image.
|
||||||
|
*
|
||||||
|
* @param resource $im the image.
|
||||||
|
*/
|
||||||
|
protected function ColorsTotal($im) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open image.
|
* Open image.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function Open() {
|
protected function Open() {
|
||||||
|
$this->Log("Opening file as {$this->fileExtension}.");
|
||||||
switch($this->fileExtension) {
|
switch($this->fileExtension) {
|
||||||
case 'jpg':
|
case 'jpg':
|
||||||
case 'jpeg': $this->image = @imagecreatefromjpeg($this->pathToImage); break;
|
case 'jpeg':
|
||||||
case 'gif': $this->image = @imagecreatefromgif($this->pathToImage); break;
|
$this->image = @imagecreatefromjpeg($this->pathToImage);
|
||||||
case 'png': $this->image = @imagecreatefrompng($this->pathToImage); break;
|
break;
|
||||||
|
|
||||||
|
case 'gif':
|
||||||
|
$this->image = @imagecreatefromgif($this->pathToImage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'png':
|
||||||
|
$this->image = @imagecreatefrompng($this->pathToImage);
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default: $this->image = false; $this->RaiseError('No support for this file extension.');
|
default: $this->image = false; $this->RaiseError('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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map filter name to PHP filter and id.
|
* Map filter name to PHP filter and id.
|
||||||
*
|
*
|
||||||
@@ -176,22 +426,63 @@ class CImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate new width and height of image.
|
* Calculate new width and height of image.
|
||||||
*/
|
*/
|
||||||
protected function CalculateNewWidthAndHeight() {
|
protected 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("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;
|
$width = $this->width;
|
||||||
$height = $this->height;
|
$height = $this->height;
|
||||||
|
|
||||||
if($this->crop) {
|
if($this->crop) {
|
||||||
if(empty($this->crop['width'])) {
|
$width = $this->crop['width'];
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$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.");
|
||||||
|
|
||||||
|
/*if(empty($this->crop['width'])) {
|
||||||
$this->crop['width'] = $this->width - $this->crop['start_x'];
|
$this->crop['width'] = $this->width - $this->crop['start_x'];
|
||||||
}
|
}
|
||||||
if(empty($this->crop['height'])) {
|
if(empty($this->crop['height'])) {
|
||||||
$this->crop['height'] = $this->height - $this->crop['start_y'];
|
$this->crop['height'] = $this->height - $this->crop['start_y'];
|
||||||
}
|
}*/
|
||||||
$width = $this->crop['width'];
|
|
||||||
$height = $this->crop['height'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate new width and height if keeping aspect-ratio.
|
// Calculate new width and height if keeping aspect-ratio.
|
||||||
@@ -244,31 +535,175 @@ class CImage {
|
|||||||
// 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}.");
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set extension for filename to save as.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function SetSaveAsExtension() {
|
||||||
|
if($this->saveAs) {
|
||||||
|
switch(strtolower($this->saveAs)) {
|
||||||
|
case 'jpg':
|
||||||
|
$this->extension = 'jpg';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'png':
|
||||||
|
$this->extension = 'png';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gif':
|
||||||
|
$this->extension = 'gif';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->extension = null;
|
||||||
|
}
|
||||||
|
$this->Log("Saving image as: " . $this->extension);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use original image if possible.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function UseOriginalIfPossible() {
|
||||||
|
if($this->useOriginal &&
|
||||||
|
($this->newWidth == $this->width) &&
|
||||||
|
($this->newHeight == $this->height) &&
|
||||||
|
!$this->quality &&
|
||||||
|
!$this->area &&
|
||||||
|
!$this->crop &&
|
||||||
|
!$this->filters &&
|
||||||
|
!$this->saveAs &&
|
||||||
|
!$this->sharpen &&
|
||||||
|
!$this->emboss &&
|
||||||
|
!$this->blur &&
|
||||||
|
!$this->palette) {
|
||||||
|
$this->Log("Using original image.");
|
||||||
|
$this->Output($this->pathToImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use cached version of image, if possible
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function UseCacheIfPossible() {
|
||||||
|
if(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.");
|
||||||
|
$this->Log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
|
||||||
|
}
|
||||||
|
$this->Output($this->cacheFileName);
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sharpen image as http://php.net/manual/en/ref.image.php#56144
|
||||||
|
* http://loriweb.pair.com/8udf-sharpen.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function SharpenImage() {
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emboss image as http://loriweb.pair.com/8udf-emboss.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function EmbossImage() {
|
||||||
|
$matrix = array(
|
||||||
|
array( 1, 1,-1,),
|
||||||
|
array( 1, 3,-1,),
|
||||||
|
array( 1,-1,-1,)
|
||||||
|
);
|
||||||
|
$divisor = 3;
|
||||||
|
$offset = 0;
|
||||||
|
imageconvolution($this->image, $matrix, $divisor, $offset);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blur image as http://loriweb.pair.com/8udf-basics.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function BlurImage() {
|
||||||
|
$matrix = array(
|
||||||
|
array( 1, 1, 1,),
|
||||||
|
array( 1,15, 1,),
|
||||||
|
array( 1, 1, 1,)
|
||||||
|
);
|
||||||
|
$divisor = 23;
|
||||||
|
$offset = 0;
|
||||||
|
imageconvolution($this->image, $matrix, $divisor, $offset);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize the image and optionally store/cache the new imagefile. Output the image.
|
* Resize the image and optionally store/cache the new imagefile. Output the image.
|
||||||
*
|
*
|
||||||
* @param integer $newWidth the new width or null. Default is null.
|
* @param array $args used when processing image.
|
||||||
* @param integer $newHeight the new width or null. Default is null.
|
|
||||||
* @param boolean $keepRatio true to keep aspect ratio else false. Default is true.
|
|
||||||
* @param boolean $cropToFit true to crop image to fit in box specified by $newWidth and $newHeight. Default is false.
|
|
||||||
* @param integer $quality the quality to use when saving the file, range 0-100, default is full quality which is 100.
|
|
||||||
* @param array/string $crop converts string of 1,2,3,4 to array 'width'=>1, 'height'=>2, 'start_x'=>3, 'start_y'=>4.
|
|
||||||
* @param array $filter.
|
|
||||||
*/
|
*/
|
||||||
public function ResizeAndOutput($args) {
|
public function ResizeAndOutput($args) {
|
||||||
$defaults = array(
|
$defaults = array(
|
||||||
'newWidth'=>null,
|
'newWidth' => null,
|
||||||
'newHeight'=>null,
|
'newHeight' => null,
|
||||||
'keepRatio'=>true,
|
'keepRatio' => true,
|
||||||
'cropToFit'=>false,
|
'area' => null, //'0,0,0,0',
|
||||||
'quality'=>100,
|
'cropToFit' => false,
|
||||||
'crop'=>null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
|
'quality' => null,
|
||||||
'filters'=>null,
|
'crop' => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
|
||||||
|
'filters' => null,
|
||||||
|
'verbose' => false,
|
||||||
|
'useCache' => true,
|
||||||
|
'useOriginal' => true,
|
||||||
|
'saveAs' => null,
|
||||||
|
'sharpen' => null,
|
||||||
|
'emboss' => null,
|
||||||
|
'blur' => null,
|
||||||
|
'palette' => null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert crop settins from string to array
|
// Convert crop settins from string to array
|
||||||
@@ -282,6 +717,17 @@ class CImage {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert area settins from string to array
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert filter settins from array of string to array of array
|
// Convert filter settins from array of string to array of array
|
||||||
if(isset($args['filters']) && is_array($args['filters'])) {
|
if(isset($args['filters']) && is_array($args['filters'])) {
|
||||||
foreach($args['filters'] as $key => $filterStr) {
|
foreach($args['filters'] as $key => $filterStr) {
|
||||||
@@ -298,7 +744,6 @@ class CImage {
|
|||||||
$args['filters'][$key] = $filter;
|
$args['filters'][$key] = $filter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//echo "<pre>" . print_r($args['filters'], true) . "</pre>";
|
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -306,31 +751,51 @@ class CImage {
|
|||||||
foreach($defaults as $key=>$val) {
|
foreach($defaults as $key=>$val) {
|
||||||
$this->{$key} = $args[$key];
|
$this->{$key} = $args[$key];
|
||||||
}
|
}
|
||||||
//echo "<pre>" . print_r($this, true) . "</pre>";
|
$this->Log("Resize and output image.");
|
||||||
|
|
||||||
// Init the object and do sanity checks on arguments
|
// Init the object and do sanity checks on arguments
|
||||||
$this->Init()->CalculateNewWidthAndHeight();
|
$this->Init()
|
||||||
//echo "<pre>" . print_r($this, true) . "</pre>";
|
->CalculateNewWidthAndHeight()
|
||||||
|
->UseOriginalIfPossible();
|
||||||
// Use original image?
|
|
||||||
if(is_null($this->newWidth) && is_null($this->newHeight)) {
|
|
||||||
$this->Output($this->pathToImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cache before resizing.
|
// Check cache before resizing.
|
||||||
$this->newFileName = $this->CreateFilename();
|
$this->SetSaveAsExtension()
|
||||||
if(is_readable($this->newFileName)) {
|
->SetQuality()
|
||||||
$fileTime = filemtime($this->pathToImage);
|
->SetOptimization()
|
||||||
$cacheTime = filemtime($this->newFileName);
|
->CreateFilename()
|
||||||
if($fileTime <= $cacheTime) {
|
->UseCacheIfPossible();
|
||||||
$this->Output($this->newFileName);
|
|
||||||
}
|
// Resize and output
|
||||||
|
$this->Open()
|
||||||
|
->Resize()
|
||||||
|
->SaveToCache()
|
||||||
|
->Output($this->cacheFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize and output and save new to cache
|
|
||||||
$this->Open()->ResizeAndSave();
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
protected function TrueColorToPalette() {
|
||||||
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->image = $img;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a image and keep transparency for png and gifs.
|
* Create a image and keep transparency for png and gifs.
|
||||||
@@ -340,10 +805,33 @@ class CImage {
|
|||||||
* @returns image resource.
|
* @returns image resource.
|
||||||
*/
|
*/
|
||||||
public function CreateImageKeepTransparency($width, $height) {
|
public function CreateImageKeepTransparency($width, $height) {
|
||||||
//echo $width . "x" . $height . "<br>";
|
$this->Log("Creating a new working image width={$width}px, height={$height}px.");
|
||||||
$img = imagecreatetruecolor($width, $height);
|
$img = imagecreatetruecolor($width, $height);
|
||||||
|
imagealphablending($img, false);
|
||||||
|
imagesavealpha($img, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
$this->Log("Filling image with background color.");
|
||||||
|
$bg = imagecolorallocate($img, 255, 255, 255);
|
||||||
|
imagefill($img, 0, 0, $bg);
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
I have had success doing it like this in the past:
|
||||||
|
|
||||||
|
$thumb = imagecreatetruecolor($newwidth, $newheight);
|
||||||
|
imagealphablending($thumb, false);
|
||||||
|
imagesavealpha($thumb, true);
|
||||||
|
|
||||||
|
$source = imagecreatefrompng($fileName);
|
||||||
|
imagealphablending($source, true);
|
||||||
|
|
||||||
|
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
|
||||||
|
|
||||||
|
imagepng($thumb,$newFilename);
|
||||||
|
|
||||||
|
I found the output image quality much better using imagecopyresampled() than imagecopyresized()
|
||||||
|
|
||||||
if($this->fileExtension == 'png' || ($this->fileExtension == 'gif')) {
|
if($this->fileExtension == 'png' || ($this->fileExtension == 'gif')) {
|
||||||
imagealphablending($img, false);
|
imagealphablending($img, false);
|
||||||
imagesavealpha($img, true);
|
imagesavealpha($img, true);
|
||||||
@@ -356,14 +844,36 @@ class CImage {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize, crop and output the image.
|
* Resize and or crop the image.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function ResizeAndSave() {
|
public function Resize() {
|
||||||
|
|
||||||
|
$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'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Do as crop, take only part of image
|
// Do as crop, take only part of image
|
||||||
if($this->crop) {
|
if($this->crop) {
|
||||||
//echo "Cropping";
|
$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']);
|
$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']);
|
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->image = $img;
|
||||||
@@ -373,73 +883,146 @@ class CImage {
|
|||||||
|
|
||||||
// Resize by crop to fit
|
// Resize by crop to fit
|
||||||
if($this->cropToFit) {
|
if($this->cropToFit) {
|
||||||
//echo "Crop to fit";
|
$this->Log("Crop to fit");
|
||||||
$cropX = ($this->cropWidth/2) - ($this->newWidth/2);
|
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
|
||||||
$cropY = ($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);
|
||||||
imagecopyresampled($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight, $this->newWidth, $this->newHeight);
|
imagecopyresampled($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight, $this->newWidth, $this->newHeight);
|
||||||
|
$this->image = $imageResized;
|
||||||
}
|
}
|
||||||
|
|
||||||
// as is
|
// Resize it
|
||||||
else {
|
else if(!($this->newWidth == $this->width && $this->newHeight == $this->height)) {
|
||||||
|
$this->Log("Resizing, new height and/or width");
|
||||||
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
|
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
|
||||||
imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply filters
|
// Apply filters
|
||||||
if(isset($this->filters) && is_array($this->filters)) {
|
if(isset($this->filters) && is_array($this->filters)) {
|
||||||
foreach($this->filters as $filter) {
|
foreach($this->filters as $filter) {
|
||||||
|
$this->Log("Applying filter $filter.");
|
||||||
switch($filter['argc']) {
|
switch($filter['argc']) {
|
||||||
case 0: imagefilter($imageResized, $filter['type']); break;
|
case 0: imagefilter($this->image, $filter['type']); break;
|
||||||
case 1: imagefilter($imageResized, $filter['type'], $filter['arg1']); break;
|
case 1: imagefilter($this->image, $filter['type'], $filter['arg1']); break;
|
||||||
case 2: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2']); break;
|
case 2: imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2']); break;
|
||||||
case 3: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); break;
|
case 3: imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); break;
|
||||||
case 4: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); break;
|
case 4: imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch($this->fileExtension)
|
// Convert to palette image
|
||||||
{
|
if($this->palette) {
|
||||||
case 'jpg':
|
$this->Log("Converting to palette image.");
|
||||||
case 'jpeg':
|
$this->TrueColorToPalette();
|
||||||
if(imagetypes() & IMG_JPG) {
|
}
|
||||||
if($this->saveFolder) {
|
|
||||||
imagejpeg($imageResized, $this->newFileName, $this->quality);
|
// Blur the image
|
||||||
|
if($this->blur) {
|
||||||
|
$this->Log("Blur.");
|
||||||
|
$this->BlurImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emboss the image
|
||||||
|
if($this->emboss) {
|
||||||
|
$this->Log("Emboss.");
|
||||||
|
$this->EmbossImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sharpen the image
|
||||||
|
if($this->sharpen) {
|
||||||
|
$this->Log("Sharpen.");
|
||||||
|
$this->SharpenImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save image to cache
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function SaveToCache() {
|
||||||
|
switch($this->extension) {
|
||||||
|
case 'jpg':
|
||||||
|
if($this->saveFolder) {
|
||||||
|
$this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
|
||||||
|
imagejpeg($this->image, $this->cacheFileName, $this->quality);
|
||||||
|
|
||||||
|
// Use JPEG optimize if defined
|
||||||
|
if($this->jpegOptimize) {
|
||||||
|
if($this->verbose) { clearstatcache(); $this->Log("Filesize before optimize: " . filesize($this->cacheFileName) . " bytes."); }
|
||||||
|
$res = array();
|
||||||
|
$cmd = $this->jpegOptimize . " -outfile $this->cacheFileName $this->cacheFileName";
|
||||||
|
exec($cmd, $res);
|
||||||
|
$this->Log($cmd);
|
||||||
|
$this->Log($res);
|
||||||
}
|
}
|
||||||
$imgFunction = 'imagejpeg';
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'gif':
|
case 'gif':
|
||||||
if (imagetypes() & IMG_GIF) {
|
|
||||||
if($this->saveFolder) {
|
if($this->saveFolder) {
|
||||||
imagegif($imageResized, $this->newFileName);
|
$this->Log("Saving image as GIF to cache.");
|
||||||
}
|
imagegif($this->image, $this->cacheFileName);
|
||||||
$imgFunction = 'imagegif';
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'png':
|
case 'png':
|
||||||
// Scale quality from 0-100 to 0-9 and invert setting as 0 is best, not 9
|
|
||||||
$quality = 9 - round(($this->quality/100) * 9);
|
|
||||||
if (imagetypes() & IMG_PNG) {
|
|
||||||
if($this->saveFolder) {
|
if($this->saveFolder) {
|
||||||
imagepng($imageResized, $this->newFileName, $quality);
|
$this->Log("Saving image as PNG to cache using quality = {$this->quality}.");
|
||||||
|
|
||||||
|
// Turn off alpha blending and set alpha flag
|
||||||
|
imagealphablending($this->image, false);
|
||||||
|
imagesavealpha($this->image, true);
|
||||||
|
|
||||||
|
imagepng($this->image, $this->cacheFileName, $this->quality);
|
||||||
|
|
||||||
|
// Use external program to filter PNG, if defined
|
||||||
|
if($this->pngFilter) {
|
||||||
|
if($this->verbose) { clearstatcache(); $this->Log("Filesize before filter optimize: " . filesize($this->cacheFileName) . " bytes."); }
|
||||||
|
$res = array();
|
||||||
|
$cmd = $this->pngFilter . " $this->cacheFileName";
|
||||||
|
exec($cmd, $res);
|
||||||
|
$this->Log($cmd);
|
||||||
|
$this->Log($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use external program to deflate PNG, if defined
|
||||||
|
if($this->pngDeflate) {
|
||||||
|
if($this->verbose) { clearstatcache(); $this->Log("Filesize before deflate optimize: " . filesize($this->cacheFileName) . " bytes."); }
|
||||||
|
$res = array();
|
||||||
|
$cmd = $this->pngDeflate . " $this->cacheFileName";
|
||||||
|
exec($cmd, $res);
|
||||||
|
$this->Log($cmd);
|
||||||
|
$this->Log($res);
|
||||||
}
|
}
|
||||||
$imgFunction = 'imagepng';
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->RaiseError('No support for this file extension.');
|
$this->RaiseError('No support for this file extension.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
header('Content-type: ' . $this->mime);
|
|
||||||
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", time()) . " GMT");
|
if($this->verbose) {
|
||||||
$imgFunction($imageResized);
|
clearstatcache();
|
||||||
exit;
|
$this->Log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
|
||||||
|
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
33
README.md
33
README.md
@@ -26,6 +26,12 @@ Enjoy!
|
|||||||
Mikael Roos (me@mikaelroos.se)
|
Mikael Roos (me@mikaelroos.se)
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
Free and opensource software. License according to MIT.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
@@ -52,6 +58,24 @@ RewriteRule ^image/(.*)$ img/img.php?src=$1 [QSA,NC,L]
|
|||||||
Now you can access and resize your images through `/image/someimage.jpg?w=80`. Very handy.
|
Now you can access and resize your images through `/image/someimage.jpg?w=80`. Very handy.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
`img.php?src=image.jpg&sharpen`
|
||||||
|
|
||||||
|
-v, -verbose, Do verbose output and print out a log what happens.
|
||||||
|
-no-cache, Do not use the cached version, do all conversions.
|
||||||
|
-skip-original, Skip using the original image, always resize and use cached image.
|
||||||
|
-save-as, Save image as jpg, png or gif, loosing transparency.
|
||||||
|
|
||||||
|
-sharpen to appy a filter that sharpens the image. Good to apply when resizing to smaller dimensions.
|
||||||
|
-emboss to apply a emboss effect.
|
||||||
|
-blur to apply a blur effect.
|
||||||
|
|
||||||
|
-palette to create a palette version of the image with up to 256 colors.
|
||||||
|
|
||||||
|
|
||||||
Revision history
|
Revision history
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
@@ -64,13 +88,16 @@ center of the image from which the crop is done.
|
|||||||
* Support for resizing opaque images.
|
* Support for resizing opaque images.
|
||||||
* Clean up code in `CImage.php`.
|
* Clean up code in `CImage.php`.
|
||||||
* Better errorhandling for invalid dimensions.
|
* Better errorhandling for invalid dimensions.
|
||||||
* Crop-to-fit does not work.
|
* Crop-to-fit with offste top, bottom, center, left, right, center.
|
||||||
|
* Define the color of the background of the resulting image, when loosing transparency.
|
||||||
|
|
||||||
|
|
||||||
v0.3x (latest)
|
v0.3.x (latest)
|
||||||
|
|
||||||
|
* Adding grid column size as predefined size, c1-c24 for a 24 column grid. Configure in `img.php`.
|
||||||
* Corrected error on naming cache-files using subdir.
|
* Corrected error on naming cache-files using subdir.
|
||||||
* Corrected calculation error on width & height for crop-to-fit.
|
* Corrected calculation error on width & height for crop-to-fit.
|
||||||
|
* Adding effects for sharpen, emboss and blur through imageconvolution using matrixes.
|
||||||
|
|
||||||
|
|
||||||
v0.3 (2012-10-02)
|
v0.3 (2012-10-02)
|
||||||
@@ -101,4 +128,4 @@ v0.1 (2012-04-25)
|
|||||||
* Initial release after rewriting some older code I had lying around.
|
* Initial release after rewriting some older code I had lying around.
|
||||||
|
|
||||||
.
|
.
|
||||||
..: Copyright 2012 by Mikael Roos (me@mikaelroos.se)
|
..: Copyright 2012-2013 by Mikael Roos (me@mikaelroos.se)
|
||||||
|
1
cache/README.md
vendored
1
cache/README.md
vendored
@@ -1 +0,0 @@
|
|||||||
Make this directory writable by the webserver.
|
|
96
img.php
96
img.php
@@ -10,30 +10,68 @@
|
|||||||
error_reporting(-1);
|
error_reporting(-1);
|
||||||
set_time_limit(20);
|
set_time_limit(20);
|
||||||
|
|
||||||
|
|
||||||
|
// Use preprocessing of images
|
||||||
|
define('PNG_FILTER', '/usr/local/bin/optipng -q');
|
||||||
|
define('PNG_DEFLATE', '/usr/local/bin/pngout -q');
|
||||||
|
define('JPEG_OPTIMIZE', '/usr/local/bin/jpegtran -copy none -optimize');
|
||||||
|
|
||||||
|
|
||||||
// Append ending slash
|
// Append ending slash
|
||||||
$cimageClassFile = __DIR__ .'/CImage.php';
|
$cimageClassFile = __DIR__ .'/CImage.php';
|
||||||
$pathToImages = __DIR__.'/img/';
|
$pathToImages = __DIR__.'/img/';
|
||||||
$pathToCache = __DIR__.'/cache/';
|
$pathToCache = __DIR__.'/cache/';
|
||||||
$maxWidth = $maxHeight = 2000;
|
$maxWidth = $maxHeight = 2000;
|
||||||
|
$gridColumnWidth = 30;
|
||||||
|
$gridGutterWidth = 10;
|
||||||
|
$gridColumns = 24;
|
||||||
|
// settings for do not largen smaller images
|
||||||
|
// settings for max image dimensions
|
||||||
|
|
||||||
// Set areas to map constant to value, easier to use with width or height
|
// Set sizes to map constant to value, easier to use with width or height
|
||||||
$area = array(
|
$sizes = array(
|
||||||
'w1' => 613,
|
'w1' => 613,
|
||||||
|
'w2' => 630,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Add column width to $area, useful for use as predefined size for width (or height).
|
||||||
|
for($i = 1; $i <= $gridColumns; $i++) {
|
||||||
|
$sizes['c' . $i] = ($gridColumnWidth + $gridGutterWidth) * $i - $gridGutterWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Get input from querystring
|
// Get input from querystring
|
||||||
$srcImage = isset($_GET['src']) ? $_GET['src'] : null;
|
$srcImage = isset($_GET['src']) ? $_GET['src'] : null;
|
||||||
$newWidth = isset($_GET['width']) ? $_GET['width'] : (isset($_GET['w']) ? $_GET['w'] : null);
|
$newWidth = isset($_GET['width']) ? $_GET['width'] : (isset($_GET['w']) ? $_GET['w'] : null);
|
||||||
$newHeight = isset($_GET['height']) ? $_GET['height'] : (isset($_GET['h']) ? $_GET['h'] : null);
|
$newHeight = isset($_GET['height']) ? $_GET['height'] : (isset($_GET['h']) ? $_GET['h'] : null);
|
||||||
$keepRatio = isset($_GET['no-ratio']) ? false : true;
|
$keepRatio = isset($_GET['no-ratio']) ? false : true;
|
||||||
$cropToFit = isset($_GET['crop-to-fit']) ? true : false;
|
$cropToFit = isset($_GET['crop-to-fit']) ? true : false;
|
||||||
|
$area = isset($_GET['area']) ? $_GET['area'] : null;
|
||||||
$crop = isset($_GET['crop']) ? $_GET['crop'] : (isset($_GET['c']) ? $_GET['c'] : null);
|
$crop = isset($_GET['crop']) ? $_GET['crop'] : (isset($_GET['c']) ? $_GET['c'] : null);
|
||||||
$quality = isset($_GET['quality']) ? $_GET['quality'] : (isset($_GET['q']) ? $_GET['q'] : 100);
|
$quality = isset($_GET['quality']) ? $_GET['quality'] : (isset($_GET['q']) ? $_GET['q'] : null);
|
||||||
|
$verbose = (isset($_GET['verbose']) || isset($_GET['v'])) ? true : false;
|
||||||
|
$useCache = isset($_GET['no-cache']) ? false : true;
|
||||||
|
$useOriginal = isset($_GET['skip-original']) ? false : true;
|
||||||
|
$saveAs = isset($_GET['save-as']) ? $_GET['save-as'] : null;
|
||||||
|
$sharpen = isset($_GET['sharpen']) ? true : null;
|
||||||
|
$emboss = isset($_GET['emboss']) ? true : null;
|
||||||
|
$blur = isset($_GET['blur']) ? true : null;
|
||||||
|
$palette = isset($_GET['palette']) ? true : null;
|
||||||
|
|
||||||
// Check to replace area
|
|
||||||
if(isset($area[$newWidth])) {
|
|
||||||
$newWidth = $area[$newWidth];
|
// Check to replace predefined size
|
||||||
|
if(isset($sizes[$newWidth])) {
|
||||||
|
$newWidth = $sizes[$newWidth];
|
||||||
}
|
}
|
||||||
|
if(isset($sizes[$newHeight])) {
|
||||||
|
$newHeight = $sizes[$newHeight];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add all filters to an array
|
// Add all filters to an array
|
||||||
$filters = array();
|
$filters = array();
|
||||||
@@ -44,6 +82,8 @@ for($i=0; $i<10;$i++) {
|
|||||||
if($filter) { $filters[] = $filter; }
|
if($filter) { $filters[] = $filter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Do some sanity checks
|
// Do some sanity checks
|
||||||
function errorPage($msg) {
|
function errorPage($msg) {
|
||||||
header("Status: 404 Not Found");
|
header("Status: 404 Not Found");
|
||||||
@@ -58,10 +98,44 @@ is_null($newWidth) or ($newWidth > 10 && $newWidth <= $maxWidth) or errorPage('W
|
|||||||
is_null($newHeight) or ($newHeight > 10 && $newHeight <= $maxHeight) or errorPage('Hight out of range.');
|
is_null($newHeight) or ($newHeight > 10 && $newHeight <= $maxHeight) or errorPage('Hight out of range.');
|
||||||
$quality >= 0 and $quality <= 100 or errorPage('Quality out of range');
|
$quality >= 0 and $quality <= 100 or errorPage('Quality out of range');
|
||||||
|
|
||||||
// Create the image object
|
|
||||||
|
|
||||||
|
// Display image if vebose mode
|
||||||
|
if($verbose) {
|
||||||
|
$query = array();
|
||||||
|
parse_str($_SERVER['QUERY_STRING'], $query);
|
||||||
|
unset($query['verbose']);
|
||||||
|
unset($query['v']);
|
||||||
|
unset($query['nocache']);
|
||||||
|
$url1 = '?' . http_build_query($query);
|
||||||
|
echo <<<EOD
|
||||||
|
<a href=$url1><code>$url1</code></a><br>
|
||||||
|
<img src='{$url1}' />
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Create and output the image
|
||||||
require($cimageClassFile);
|
require($cimageClassFile);
|
||||||
$img = new CImage($srcImage, $pathToImages, $pathToCache);
|
$img = new CImage($srcImage, $pathToImages, $pathToCache);
|
||||||
$img->ResizeAndOutput(array('newWidth'=>$newWidth, 'newHeight'=>$newHeight, 'keepRatio'=>$keepRatio,
|
$img->ResizeAndOutput(array(
|
||||||
'cropToFit'=>$cropToFit, 'quality'=>$quality,
|
'newWidth' => $newWidth,
|
||||||
'crop'=>$crop, 'filters'=>$filters,
|
'newHeight' => $newHeight,
|
||||||
));
|
'keepRatio' => $keepRatio,
|
||||||
|
'cropToFit' => $cropToFit,
|
||||||
|
'area' => $area,
|
||||||
|
'quality' => $quality,
|
||||||
|
'crop' => $crop,
|
||||||
|
'filters' => $filters,
|
||||||
|
'verbose' => $verbose,
|
||||||
|
'useCache' => $useCache,
|
||||||
|
'useOriginal' => $useOriginal,
|
||||||
|
'saveAs' => $saveAs,
|
||||||
|
'sharpen' => $sharpen,
|
||||||
|
'emboss' => $emboss,
|
||||||
|
'blur' => $blur,
|
||||||
|
'palette' => $palette,
|
||||||
|
));
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user