mirror of
https://github.com/mosbth/cimage.git
synced 2025-08-08 09:06:35 +02:00
initial release
This commit is contained in:
273
CImage.php
Normal file
273
CImage.php
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Resizing images on the fly.
|
||||||
|
*
|
||||||
|
* @author Mikael Roos mos@dbwebb.se
|
||||||
|
* @example http://dbwebb.se/example/lessphp/
|
||||||
|
* @link https://github.com/mosbth/Utility/blob/master/style.php
|
||||||
|
*/
|
||||||
|
class CImage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties
|
||||||
|
*/
|
||||||
|
private $image = null; // Object for open image
|
||||||
|
public $pathToImage;
|
||||||
|
private $fileExtension;
|
||||||
|
public $newWidth;
|
||||||
|
public $newHeight;
|
||||||
|
private $cropWidth;
|
||||||
|
private $cropHeight;
|
||||||
|
public $keepRatio;
|
||||||
|
public $crop;
|
||||||
|
public $saveFolder;
|
||||||
|
public $newName;
|
||||||
|
private $newFileName;
|
||||||
|
private $mime; // Calculated from source image
|
||||||
|
private $width; // Calculated from source image
|
||||||
|
private $height; // Calculated from source image
|
||||||
|
private $type; // Calculated from source image
|
||||||
|
private $attr; // Calculated from source image
|
||||||
|
private $validExtensions = array('jpg', 'jpeg', 'png', 'gif');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, can take arguments to init the object.
|
||||||
|
*
|
||||||
|
* @param $pathToImage string the filepath to the image.
|
||||||
|
* @param $newWidth integer the new width or null.
|
||||||
|
* @param $newHeight integer the new width or null.
|
||||||
|
* @param $keepRatio boolean true to keep aspect ratio else false.
|
||||||
|
* @param $saveFolder string path to folder where to save the new file or null to skip saving.
|
||||||
|
* @param $newName string new filename or leave to null to autogenerate filename.
|
||||||
|
*/
|
||||||
|
public function __construct($pathToImage=null, $newWidth=null, $newHeight=null,
|
||||||
|
$keepRatio=true, $crop=false, $saveFolder=null, $newName=null) {
|
||||||
|
$this->pathToImage = $pathToImage;
|
||||||
|
$this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION);
|
||||||
|
$this->newWidth = $newWidth;
|
||||||
|
$this->newHeight = $newHeight;
|
||||||
|
$this->keepRatio = $keepRatio;
|
||||||
|
$this->crop = $crop;
|
||||||
|
$this->saveFolder = $saveFolder;
|
||||||
|
$this->newName = $newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raise error, enables to implement a selection of error methods.
|
||||||
|
*
|
||||||
|
* @param $message string the error message to display.
|
||||||
|
*/
|
||||||
|
public function RaiseError($message) {
|
||||||
|
throw new Exception($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create filename to save file in cache.
|
||||||
|
*/
|
||||||
|
public function CreateFilename() {
|
||||||
|
$parts = pathinfo($this->pathToImage);
|
||||||
|
$crop = $this->crop ? '_c_' : null;
|
||||||
|
return $this->saveFolder . '/' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . '.' . $parts['extension'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init and do some sanity checks before any processing is done. Throws exception if not valid.
|
||||||
|
*/
|
||||||
|
public function Init() {
|
||||||
|
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_readable($this->pathToImage) or $this->RaiseError('File does not exist.');
|
||||||
|
in_array($this->fileExtension, $this->validExtensions) or $this->RaiseError('Not a valid file extension.');
|
||||||
|
is_null($this->saveFolder) or is_writable($this->saveFolder) or $this->RaiseError('Save directory does not exist or is not writable.');
|
||||||
|
|
||||||
|
// Get details on image
|
||||||
|
$info = list($this->width, $this->height, $this->type, $this->attr) = getimagesize($this->pathToImage);
|
||||||
|
!empty($info) or $this->RaiseError("The file doesn't seem to be an image.");
|
||||||
|
$this->mime = $info['mime'];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output image using caching.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function Output($file) {
|
||||||
|
$time = filemtime($file);
|
||||||
|
if(isset($_SERVER['If-Modified-Since']) && strtotime($_SERVER['If-Modified-Since']) >= $time){
|
||||||
|
header("HTTP/1.0 304 Not Modified");
|
||||||
|
} else {
|
||||||
|
header('Content-type: ' . $this->mime);
|
||||||
|
header('Last-Modified: ' . gmdate("D, d M Y H:i:s",$time) . " GMT");
|
||||||
|
readfile($file);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open image.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function Open() {
|
||||||
|
switch($this->fileExtension) {
|
||||||
|
case 'jpg':
|
||||||
|
case 'jpeg': $this->image = @imagecreatefromjpeg($this->pathToImage); break;
|
||||||
|
case 'gif': $this->image = @imagecreatefromgif($this->pathToImage); break;
|
||||||
|
case 'png': $this->image = @imagecreatefrompng($this->pathToImage); break;
|
||||||
|
default: $this->image = false; $this->RaiseError('No support for this file extension.');
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate new width and height of image.
|
||||||
|
*/
|
||||||
|
protected function CalculateNewWidthAndHeight() {
|
||||||
|
// Only calculate new width and height if keeping aspect-ratio.
|
||||||
|
if($this->keepRatio) {
|
||||||
|
|
||||||
|
// Both new width and height are set.
|
||||||
|
if(isset($this->newWidth) && isset($this->newHeight)) {
|
||||||
|
|
||||||
|
// Use newWidth and newHeigh as min width/height, image should fit the area.
|
||||||
|
if($this->crop) {
|
||||||
|
$ratioWidth = $this->width/$this->newWidth;
|
||||||
|
$ratioHeight = $this->height/$this->newHeight;
|
||||||
|
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
|
||||||
|
$this->cropWidth = $this->width / $ratio;
|
||||||
|
$this->cropHeight = $this->height / $ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use newWidth and newHeigh as max width/height, image should not be larger.
|
||||||
|
else {
|
||||||
|
if($this->width > $this->height) {
|
||||||
|
$factor = (float)$this->newWidth / (float)$this->width;
|
||||||
|
$this->newHeight = $factor * $this->height;
|
||||||
|
} else {
|
||||||
|
$factor = (float)$this->newHeight / (float)$this->height;
|
||||||
|
$this->newWidth = $factor * $this->width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use new width as max-width
|
||||||
|
elseif(isset($this->newWidth)) {
|
||||||
|
$factor = (float)$this->newWidth / (float)$this->width;
|
||||||
|
$this->newHeight = $factor * $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use new height as max-hight
|
||||||
|
elseif(isset($this->newHeight)) {
|
||||||
|
$factor = (float)$this->newHeight / (float)$this->height;
|
||||||
|
$this->newWidth = $factor * $this->width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not keep aspect ratio, but both newWidth and newHeight must be set
|
||||||
|
else {
|
||||||
|
$this->newWidth = isset($this->newWidth) ? $this->newWidth : $this->width;
|
||||||
|
$this->newHeight = isset($this->newHeight) ? $this->newHeight : $this->height;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the image and optionally store/cache the new imagefile. Output the image.
|
||||||
|
*/
|
||||||
|
public function ResizeAndOutput() {
|
||||||
|
$this->Init()->CalculateNewWidthAndHeight();
|
||||||
|
|
||||||
|
// Use original image?
|
||||||
|
if(is_null($this->newWidth) && is_null($this->newHeight)) {
|
||||||
|
$this->Output($this->pathToImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cache before resizing.
|
||||||
|
$this->newFileName = $this->CreateFilename();
|
||||||
|
if(is_readable($this->newFileName)) {
|
||||||
|
$fileTime = filemtime($this->pathToImage);
|
||||||
|
$cacheTime = filemtime($this->newFileName);
|
||||||
|
if($fileTime <= $cacheTime) {
|
||||||
|
$this->Output($this->newFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize and output
|
||||||
|
$this->Open()->ResizeAndSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize, crop and output the image.
|
||||||
|
*
|
||||||
|
* @param $imageQuality number the quality to use when saving the file, default is full quality.
|
||||||
|
*/
|
||||||
|
public function ResizeAndSave($imageQuality="100") {
|
||||||
|
|
||||||
|
//echo "{$this->width}:{$this->height}:{$this->mime}</br>";
|
||||||
|
//echo "{$this->cropWidth}:{$this->cropHeight}</br>";
|
||||||
|
//echo "{$this->newWidth}:{$this->newHeight}</br>";
|
||||||
|
|
||||||
|
if($this->crop) {
|
||||||
|
$cropX = ($this->cropWidth/2) - ($this->newWidth/2);
|
||||||
|
$cropY = ($this->cropHeight/2) - ($this->newHeight/2);
|
||||||
|
$imgPreCrop = imagecreatetruecolor($this->cropWidth, $this->cropHeight);
|
||||||
|
$imageResized = imagecreatetruecolor($this->newWidth, $this->newHeight);
|
||||||
|
imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
|
||||||
|
imagecopyresampled($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight, $this->newWidth, $this->newHeight);
|
||||||
|
} else {
|
||||||
|
$imageResized = imagecreatetruecolor($this->newWidth, $this->newHeight);
|
||||||
|
imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($this->fileExtension)
|
||||||
|
{
|
||||||
|
case 'jpg':
|
||||||
|
case 'jpeg':
|
||||||
|
if(imagetypes() & IMG_JPG) {
|
||||||
|
if($this->saveFolder) {
|
||||||
|
imagejpeg($imageResized, $this->newFileName, $imageQuality);
|
||||||
|
}
|
||||||
|
$imgFunction = 'imagejpeg';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gif':
|
||||||
|
if (imagetypes() & IMG_GIF) {
|
||||||
|
if($this->saveFolder) {
|
||||||
|
imagegif($imageResized, $this->newFileName);
|
||||||
|
}
|
||||||
|
$imgFunction = 'imagegif';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'png':
|
||||||
|
// Scale quality from 0-100 to 0-9 and invert setting as 0 is best, not 9
|
||||||
|
$quality = 9 - round(($imageQuality/100) * 9);
|
||||||
|
if (imagetypes() & IMG_PNG) {
|
||||||
|
if($this->saveFolder) {
|
||||||
|
imagepng($imageResized, $this->newFileName, $quality);
|
||||||
|
}
|
||||||
|
$imgFunction = 'imagepng';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->RaiseError('No support for this file extension.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
header('Content-type: ' . $this->mime);
|
||||||
|
header('Last-Modified: ' . gmdate("D, d M Y H:i:s",time()) . " GMT");
|
||||||
|
$imgFunction($imageResized);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
2
LICENSE.txt
Normal file
2
LICENSE.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This is a software solution to a general known problem.
|
||||||
|
Free software. Use at own risk.
|
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Image conversion on the fly using PHP
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
The `CImage.php` contains a class that can resize and crop images and output them to
|
||||||
|
a webpage. The class has cache of generated images.
|
||||||
|
|
||||||
|
The file `img.php` uses `CImage.php` to resize images. It is a usecase on how to use
|
||||||
|
the class.
|
||||||
|
|
||||||
|
The file `test.php` has some testcases that show the results of `img.php` with different
|
||||||
|
settings.
|
||||||
|
|
||||||
|
Start by reviewing the `test.php`, then have a look at `img.php` and finally go through
|
||||||
|
`CImage.php`.
|
||||||
|
|
||||||
|
Enjoy.
|
||||||
|
|
||||||
|
Mikael Roos (mos@dbwebb.se)
|
||||||
|
|
||||||
|
|
||||||
|
Revision history
|
||||||
|
----------------
|
||||||
|
|
||||||
|
v0.1 (2012-04-25)
|
||||||
|
|
||||||
|
* Initial release after rewriting some older code I had lying around.
|
34
img.php
Normal file
34
img.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Resize images on the fly using cache.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
error_reporting(-1);
|
||||||
|
set_time_limit(20);
|
||||||
|
|
||||||
|
// Append ending slash
|
||||||
|
$pathToImages = __DIR__.'/img/';
|
||||||
|
$pathToCache = __DIR__.'/cache/';
|
||||||
|
|
||||||
|
// Get input from querystring
|
||||||
|
$srcImage = isset($_GET['src']) ? $pathToImages . basename($_GET['src']) : 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);
|
||||||
|
$keepRatio = isset($_GET['no-ratio']) ? false : true; // Keep Aspect Ratio?
|
||||||
|
$crop = isset($_GET['crop']) ? true : false; // Crop image?
|
||||||
|
|
||||||
|
// Do some sanity checks
|
||||||
|
!preg_match('/^[\w-\.]+$/', $srcImage) or die('Filename contains invalid characters.');
|
||||||
|
if(isset($newWidth)) {
|
||||||
|
$newWidth < 1000 or die('To large width.');
|
||||||
|
$newWidth > 10 or die('To small width.');
|
||||||
|
}
|
||||||
|
if(isset($newHeight)) {
|
||||||
|
$newHeight < 1000 or die('To large height.');
|
||||||
|
$newHeight > 10 or die('To small height.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the image object
|
||||||
|
require(__DIR__.'/CImage.php');
|
||||||
|
$img = new CImage($srcImage, $newWidth, $newHeight, $keepRatio, $crop, null/*$pathToCache*/);
|
||||||
|
$img->ResizeAndOutput();
|
BIN
img/higher.jpg
Normal file
BIN
img/higher.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 165 KiB |
BIN
img/wider.jpg
Normal file
BIN
img/wider.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
39
test.php
Normal file
39
test.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'/>
|
||||||
|
<title>Testing img resizing using CImage.php</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Testing <code>CImage.php</code> through <code>img.php</code></h1>
|
||||||
|
<p>You can test any variation of resizing the images through <a href='img.php?src=wider.jpg&w=200&h=200'>img.php?src=wider.jpg&w=200&h=200</a> or <a href='img.php?src=higher.jpg&w=200&h=200'>img.php?src=higher.jpg&w=200&h=200</a></p>
|
||||||
|
<p>Supported arguments throught the querystring are:</p>
|
||||||
|
<ul>
|
||||||
|
<li>h, height: h=200 sets the height to 200px.
|
||||||
|
<li>w, width: w=200 sets the width to 200px.
|
||||||
|
<li>crop: together with both h & w makes the image fit in the box.
|
||||||
|
<li>no-ratio: do not keep aspect ratio.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<caption>Test cases</caption>
|
||||||
|
<thead><tr><th>Testcase:</th><th>Result:</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>Original image of wider.jpg.</td><td><img src='img.php?src=wider.jpg' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max width 200.</td><td><img src='img.php?src=wider.jpg&w=200' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max height 200.</td><td><img src='img.php?src=wider.jpg&h=200' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max width 200 and max height 200.</td><td><img src='img.php?src=wider.jpg&w=200&h=200' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max width 200 and max height 200 and no-ratio.</td><td><img src='img.php?src=wider.jpg&w=200&h=200&no-ratio' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max width 200 and max height 200 and cropped.</td><td><img src='img.php?src=wider.jpg&w=200&h=200&crop' /></td></tr>
|
||||||
|
<tr><td>wider.jpg max width 200 and max height 100 and cropped.</td><td><img src='img.php?src=wider.jpg&w=200&h=100&crop' /></td></tr>
|
||||||
|
<tr><td>Original image of higher.jpg.</td><td><img src='img.php?src=higher.jpg' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max width 200.</td><td><img src='img.php?src=higher.jpg&w=200' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max height 200.</td><td><img src='img.php?src=higher.jpg&h=200' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max width 200 and max height 200.</td><td><img src='img.php?src=higher.jpg&w=200&h=200' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max width 200 and max height 200 and no-ratio.</td><td><img src='img.php?src=higher.jpg&w=200&h=200&no-ratio' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max width 200 and max height 200 and cropped.</td><td><img src='img.php?src=higher.jpg&w=200&h=200&crop' /></td></tr>
|
||||||
|
<tr><td>higher.jpg max width 200 and max height 100 and cropped.</td><td><img src='img.php?src=higher.jpg&w=200&h=100&crop' /></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user