1
0
mirror of https://github.com/mosbth/cimage.git synced 2025-09-03 12:42:36 +02:00

Compare commits

..

19 Commits
v0.6 ... v0.6.1

Author SHA1 Message Date
Mikael Roos
254ddf11f0 pre 0.7 before integrating with remote images 2015-01-08 18:03:15 +01:00
Mikael Roos
d21daa298c pre 0.7 before integrating with remote images
* Adding compare-page for comparing images. Issue #20.
* Added option `no-upscale, nu` as resizing strategy to decline upscaling of smaller images. Fix #61.
* Minor change in `CImage::resize()`, crop now does imagecopy without resamling.
* Correcting internal details for save-as and response json which indicated wrong colors. Fix #62.
* Fixed fill-to-fit that failed when using aspect-ratio. Fix #52.
* JSON returns correct values for resulting image. Fix #58.
* Corrected behaviour for skip-original. Fix #60.
2015-01-08 18:01:48 +01:00
Mikael Roos
f4b3f16443 Adding compare-page for comparing images. 2014-12-19 15:23:21 +01:00
Mikael Roos
b5100845e9 Adding compare-page for comparing images. Issue #20. 2014-12-19 15:21:47 +01:00
Mikael Roos
8c4b22ca66 Prepare to rewrite the resize-method with respect to no-upscale code. 2014-12-15 17:45:21 +01:00
Mikael Roos
d509067759 updating strategies to use for no-upscale, #61 2014-12-15 17:24:49 +01:00
Mikael Roos
5fa05e8282 updating strategies to use for no-upscale 2014-12-15 17:16:41 +01:00
Mikael Roos
1144c8e105 updating strategies to use for no-upscale 2014-12-15 17:14:06 +01:00
Mikael Roos
90bfced741 Added option - no-upscale, nu - as resizing strategy to decline upscaling of smaller images. Fix #61. 2014-12-15 15:41:51 +01:00
Mikael Roos
61e0473f47 CImage::resize now crops using imagecopy without resamling. 2014-12-15 09:41:11 +01:00
Mikael Roos
2dbe33bd8e Correcting internal details for save-as and response json which indicated wrong colors. Fix #62. 2014-12-15 09:24:14 +01:00
Mikael Roos
ece0be086c Correcting internal details for save-as and response json which indicated wrong colors. Fix #62. 2014-12-15 09:19:23 +01:00
Mikael Roos
e236757e5d adding testcases for resizing strategies 2014-12-15 09:18:41 +01:00
Mikael Roos
951231f360 Fixed fill-to-fit that failed when using aspect-ratio. Fix #52. 2014-12-14 17:09:29 +01:00
Mikael Roos
34124e687f Removed &nc 2014-12-14 17:09:12 +01:00
Mikael Roos
ea06d5cfe6 Fixed fill-to-fit that failed when using aspect-ratio. Fix #52. 2014-12-14 17:04:51 +01:00
Mikael Roos
cfc4a5f0ed JSON returns correct values for resulting image. Fix #58. 2014-12-13 00:23:40 +01:00
Mikael Roos
00dca21be0 Corrected behaviour for skip-original. Fix #60 2014-12-13 00:12:39 +01:00
Mikael Roos
9680cd421b spelling 2014-12-06 15:53:14 +01:00
17 changed files with 880 additions and 66 deletions

View File

@@ -34,6 +34,13 @@ class CImage
/**
* Is the quality level set from external use (true) or is it default (false)?
*/
private $useQuality = false;
/**
* Constant for default image quality when not set
*/
@@ -48,6 +55,14 @@ class CImage
/**
* Is the compress level set from external use (true) or is it default (false)?
*/
private $useCompress = false;
/**
* Default background color, red, green, blue, alpha.
*
@@ -216,6 +231,14 @@ class CImage
private $dpr = 1;
/**
* Always upscale images, even if they are smaller than target image.
*/
const UPSCALE_DEFAULT = true;
private $upscale = self::UPSCALE_DEFAULT;
/**
* Array with details on how to crop, incoming as argument and calculated.
*/
@@ -265,6 +288,15 @@ class CImage
private $offset;
/**
* Calculate target dimension for image when using fill-to-fit resize strategy.
*/
private $fillWidth;
private $fillHeight;
/**
* Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created.
@@ -345,10 +377,13 @@ class CImage
*
* @return $this
*/
public function setSource($src = null, $dir = null)
public function setSource($src, $dir = null)
{
if (!(isset($src) && isset($dir))) {
if (!isset($src)) {
return $this;
} else if (!isset($dir)) {
$dir = dirname($src);
$src = basename($src);
}
$this->imageSrc = ltrim($src, '/');
@@ -416,6 +451,7 @@ class CImage
'fillToFit' => null,
'crop' => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
'area' => null, //'0,0,0,0',
'upscale' => self::UPSCALE_DEFAULT,
// Options for caching or using original
'useCache' => true,
@@ -548,22 +584,26 @@ class CImage
/**
* Load image details from original image file.
*
* @param string $file the file to load or null to use $this->pathToImage.
*
* @return $this
* @throws Exception
*/
public function loadImageDetails()
public function loadImageDetails($file = null)
{
is_readable($this->pathToImage)
$file = $file ? $file : $this->pathToImage;
is_readable($file)
or $this->raiseError('Image file does not exist.');
// Get details on image
$info = list($this->width, $this->height, $this->type, $this->attr) = getimagesize($this->pathToImage);
$info = list($this->width, $this->height, $this->type, $this->attr) = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
if ($this->verbose) {
$this->log("Image file: {$this->pathToImage}");
$this->log("Image file: {$file}");
$this->log("Image width x height (type): {$this->width} x {$this->height} ({$this->type}).");
$this->log("Image filesize: " . filesize($this->pathToImage) . " bytes.");
$this->log("Image filesize: " . filesize($file) . " bytes.");
}
return $this;
@@ -580,6 +620,8 @@ class CImage
*/
public function initDimensions()
{
$this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
@@ -637,6 +679,8 @@ class CImage
or is_numeric($this->newHeight)
or $this->raiseError('Height not numeric');
$this->log("Init dimension (after) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
return $this;
}
@@ -652,7 +696,9 @@ class CImage
// 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}.");
$this->log("Target dimension (before calculating) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// Check if there is an area to crop off
if (isset($this->area)) {
$this->offset['top'] = round($this->area['top'] / 100 * $this->height);
$this->offset['right'] = round($this->area['right'] / 100 * $this->width);
@@ -669,6 +715,7 @@ class CImage
$width = $this->width;
$height = $this->height;
// Check if crop is set
if ($this->crop) {
$width = $this->crop['width'] = $this->crop['width'] <= 0 ? $this->width + $this->crop['width'] : $this->crop['width'];
$height = $this->crop['height'] = $this->crop['height'] <= 0 ? $this->height + $this->crop['height'] : $this->crop['height'];
@@ -698,7 +745,7 @@ class CImage
$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)) {
if (($this->cropToFit || $this->fillToFit) && 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.");
@@ -730,16 +777,33 @@ class CImage
}
if ($this->cropToFit) {
// Use newWidth and newHeigh as defined width/height,
// image should fit the area.
$this->log("Crop to fit.");
// Get image dimensions for pre-resize image.
if ($this->cropToFit || $this->fillToFit) {
// Get relations of original & target image
$ratioWidth = $width / $this->newWidth;
$ratioHeight = $height / $this->newHeight;
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
$this->cropWidth = round($width / $ratio);
$this->cropHeight = round($height / $ratio);
if ($this->cropToFit) {
// Use newWidth and newHeigh as defined width/height,
// image should fit the area.
$this->log("Crop to fit.");
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
$this->cropWidth = round($width / $ratio);
$this->cropHeight = round($height / $ratio);
$this->log("Crop width, height, ratio: $this->cropWidth x $this->cropHeight ($ratio).");
} else if ($this->fillToFit) {
// Use newWidth and newHeigh as defined width/height,
// image should fit the area.
$this->log("Fill to fit.");
$ratio = ($ratioWidth < $ratioHeight) ? $ratioHeight : $ratioWidth;
$this->fillWidth = round($width / $ratio);
$this->fillHeight = round($height / $ratio);
$this->log("Fill width, height, ratio: $this->fillWidth x $this->fillHeight ($ratio).");
}
}
}
@@ -750,6 +814,13 @@ class CImage
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
}
// Fill to fit, ensure to set new width and height
/*if ($this->fillToFit) {
$this->log("FillToFit.");
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
}*/
// No new height or width is set, use existing measures.
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->width);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height);
@@ -813,6 +884,10 @@ class CImage
*/
public function setJpegQuality($quality = null)
{
if ($quality) {
$this->useQuality = true;
}
$this->quality = isset($quality)
? $quality
: self::JPEG_QUALITY_DEFAULT;
@@ -836,6 +911,10 @@ class CImage
*/
public function setPngCompression($compress = null)
{
if ($compress) {
$this->useCompress = true;
}
$this->compress = isset($compress)
? $compress
: self::PNG_COMPRESSION_DEFAULT;
@@ -872,17 +951,19 @@ class CImage
&& !$this->blur
&& !$this->convolve
&& !$this->palette
&& !$this->quality
&& !$this->compress
&& !$this->useQuality
&& !$this->useCompress
&& !$this->saveAs
&& !$this->rotateBefore
&& !$this->rotateAfter
&& !$this->autoRotate
&& !$this->bgColor
&& ($this->upscale === self::UPSCALE_DEFAULT)
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
}
return $this;
}
@@ -909,13 +990,8 @@ class CImage
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
if ($fillToFit) {
$width = $this->newWidthOrig;
$height = $this->newHeightOrig;
} else {
$width = $this->newWidth;
$height = $this->newHeight;
}
$width = $this->newWidth;
$height = $this->newHeight;
$offset = isset($this->offset)
? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left']
@@ -958,14 +1034,19 @@ class CImage
$convolve = null;
if ($this->convolve) {
$convolve = 'convolve' . preg_replace('/[^a-zA-Z0-9]/', '', $this->convolve);
$convolve = '_conv' . preg_replace('/[^a-zA-Z0-9]/', '', $this->convolve);
}
$upscale = null;
if ($this->upscale !== self::UPSCALE_DEFAULT) {
$upscale = '_nu';
}
$subdir = str_replace('/', '-', dirname($this->imageSrc));
$subdir = ($subdir == '.') ? '_.' : $subdir;
$file = $subdir . '_' . $parts['filename'] . '_' . $width . '_'
. $height . $offset . $crop . $cropToFit . $fillToFit
. $crop_x . $crop_y
. $crop_x . $crop_y . $upscale
. $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor . $convolve
. '.' . $this->extension;
@@ -1152,6 +1233,7 @@ class CImage
private function colorsTotal($im)
{
if (imageistruecolor($im)) {
$this->log("Colors as true color.");
$h = imagesy($im);
$w = imagesx($im);
$c = array();
@@ -1162,6 +1244,7 @@ class CImage
}
return count($c);
} else {
$this->log("Colors as palette.");
return imagecolorstotal($im);
}
}
@@ -1217,6 +1300,7 @@ class CImage
{
$this->log("Starting to Resize()");
$this->log("Upscale = '$this->upscale'");
// Only use a specified area of the image, $this->offset is defining the area to use
if (isset($this->offset)) {
@@ -1229,38 +1313,56 @@ class CImage
$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;
}
if ($this->crop) {
// Do as crop, take only part of image
$this->log("Cropping area width={$this->crop['width']}, height={$this->crop['height']}, start_x={$this->crop['start_x']}, start_y={$this->crop['start_y']}");
$img = $this->CreateImageKeepTransparency($this->crop['width'], $this->crop['height']);
imagecopyresampled($img, $this->image, 0, 0, $this->crop['start_x'], $this->crop['start_y'], $this->crop['width'], $this->crop['height'], $this->crop['width'], $this->crop['height']);
imagecopy($img, $this->image, 0, 0, $this->crop['start_x'], $this->crop['start_y'], $this->crop['width'], $this->crop['height']);
$this->image = $img;
$this->width = $this->crop['width'];
$this->height = $this->crop['height'];
}
if (!$this->upscale) {
// Consider rewriting the no-upscale code to fit within this if-statement,
// likely to be more readable code.
// The code is more or leass equal in below crop-to-fit, fill-to-fit and stretch
}
if ($this->cropToFit) {
// Resize by crop to fit
$this->log("Crop to fit");
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
imagecopyresampled($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight, $this->newWidth, $this->newHeight);
$this->log("Resizing using strategy - Crop to fit");
if (!$this->upscale && ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
$this->log("Resizing - smaller image, do not upscale.");
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$posX = 0;
$posY = 0;
if ($this->newWidth > $this->width) {
$posX = round(($this->newWidth - $this->width) / 2);
}
if ($this->newHeight > $this->height) {
$posY = round(($this->newHeight - $this->height) / 2);
}
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->newWidth, $this->newHeight);
} else {
$cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
$cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
$imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
imagecopy($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight);
}
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
@@ -1268,33 +1370,85 @@ class CImage
} else if ($this->fillToFit) {
// Resize by fill to fit
$this->log("Fill to fit");
$this->log("Resizing using strategy - Fill to fit");
$posX = 0;
$posY = 0;
if ($this->newWidth == $this->newWidthOrig) {
$posY = round(($this->newHeightOrig - $this->newHeight) / 2);
$ratioOrig = $this->width / $this->height;
$ratioNew = $this->newWidth / $this->newHeight;
// Check ratio for landscape or portrait
if ($ratioOrig < $ratioNew) {
$posX = round(($this->newWidth - $this->fillWidth) / 2);
} else {
$posX = round(($this->newWidthOrig - $this->newWidth) / 2);
$posY = round(($this->newHeight - $this->fillHeight) / 2);
}
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)
) {
$this->log("Resizing - smaller image, do not upscale.");
$posX = round(($this->fillWidth - $this->width) / 2);
$posY = round(($this->fillHeight - $this->height) / 2);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, 0, 0, $this->fillWidth, $this->fillHeight);
} else {
$imgPreFill = $this->CreateImageKeepTransparency($this->fillWidth, $this->fillHeight);
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($imgPreFill, $this->image, 0, 0, 0, 0, $this->fillWidth, $this->fillHeight, $this->width, $this->height);
imagecopy($imageResized, $imgPreFill, $posX, $posY, 0, 0, $this->fillWidth, $this->fillHeight);
}
$imageResized = $this->CreateImageKeepTransparency($this->newWidthOrig, $this->newHeightOrig);
imagecopyresampled($imageResized, $this->image, $posX, $posY, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
$this->image = $imageResized;
$this->width = $this->newWidthOrig;
$this->height = $this->newHeightOrig;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
} else if (!($this->newWidth == $this->width && $this->newHeight == $this->height)) {
// Resize it
$this->log("Resizing, new height and/or width");
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
//imagecopyresized($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)
) {
$this->log("Resizing - smaller image, do not upscale.");
if (!$this->keepRatio) {
$this->log("Resizing - stretch to fit selected.");
$posX = 0;
$posY = 0;
$cropX = 0;
$cropY = 0;
if ($this->newWidth > $this->width && $this->newHeight > $this->height) {
$posX = round(($this->newWidth - $this->width) / 2);
$posY = round(($this->newHeight - $this->height) / 2);
} else if ($this->newWidth > $this->width) {
$posX = round(($this->newWidth - $this->width) / 2);
$cropY = round(($this->height - $this->newHeight) / 2);
} else if ($this->newHeight > $this->height) {
$posY = round(($this->newHeight - $this->height) / 2);
$cropX = round(($this->width - $this->newWidth) / 2);
}
//$this->log("posX=$posX, posY=$posY, cropX=$cropX, cropY=$cropY.");
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->newWidth, $this->newHeight);
$this->image = $imageResized;
$this->width = $this->newWidth;
$this->height = $this->newHeight;
}
} else {
$imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
imagecopyresampled($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;
}
}
return $this;
@@ -1919,10 +2073,14 @@ class CImage
/**
* Create a JSON object from the image details.
*
* @param string $file the file to output.
*
* @return string json-encoded representation of the image.
*/
public function json()
public function json($file = null)
{
$file = $file ? $file : $this->cacheFileName;
$details = array();
clearstatcache();
@@ -1935,9 +2093,16 @@ class CImage
$lastModified = filemtime($this->cacheFileName);
$details['cacheGmdate'] = gmdate("D, d M Y H:i:s", $lastModified);
$this->loadImageDetails($file);
$details['filename'] = basename($file);
$details['width'] = $this->width;
$details['height'] = $this->height;
$details['aspectRatio'] = round($this->width / $this->height, 3);
$details['size'] = filesize($file);
$this->load($file);
$details['colors'] = $this->colorsTotal($this->image);
$options = null;
if (defined("JSON_PRETTY_PRINT") && defined("JSON_UNESCAPED_SLASHES")) {

View File

@@ -19,7 +19,7 @@ You got an image from your friend who took it with the iPhone and you want to pu
<img src="http://dbwebb.se/kod-exempel/cimage/webroot/img.php?src=issue36/me-270.jpg&w=300" alt="">
The original image is looking like the one to the right, scaled down to a width of 300 pixels.
The original image is looking like this one, scaled down to a width of 300 pixels.
So, you need to rotate it and crop off some parts to make it intresting.
@@ -49,12 +49,12 @@ The [sourcode is available on GitHub](https://github.com/mosbth/cimage). Clone,
I prefer cloning like this. Do switch to the latest stable version.
**Latest stable version is v0.6 released 2014-12-06.**
**Latest stable version is v0.6.1 released 2015-01-08.**
```bash
git clone git://github.com/mosbth/cimage.git
cd cimage
git checkout v0.6
git checkout v0.6.1
```
Make the cache-directory writable by the webserver.
@@ -212,6 +212,7 @@ These options affect strategy to use when resizing an image into a target image
| `nr, no-ratio, stretch` | Do *not* keep aspect ratio when resizing and using both width & height constraints. Results in stretching the image, if needed, to fit in the resulting box. |
| `cf, crop-to-fit` | Set together with both `h` and `w` to make the image fit into dimensions, and crop out the rest of the image. |
| `ff, fill-to-fit` | Set together with both `h` and `w` to make the image fit into dimensions, and fill the rest using a background color. You can optionally supply a background color as this `ff=00ff00`, or `ff=00ff007f` when using the alpha channel. |
| `nu, no-upscale` | Avoid smaller images from being upscaled to larger ones. Combine with `stretch`, `crop-to-fit` or `fill-to-fit` to get the smaller image centered on a larger canvas. The requested dimension for the target image are thereby met. |
@@ -278,6 +279,17 @@ Revision history
-------------------------------------
v0.6.1 (2015-01-08)
* Adding compare-page for comparing images. Issue #20.
* Added option `no-upscale, nu` as resizing strategy to decline upscaling of smaller images. Fix #61.
* Minor change in `CImage::resize()`, crop now does imagecopy without resamling.
* Correcting internal details for save-as and response json which indicated wrong colors. Fix #62.
* Fixed fill-to-fit that failed when using aspect-ratio. Fix #52.
* JSON returns correct values for resulting image. Fix #58.
* Corrected behaviour for skip-original. Fix #60.
v0.6 (2014-12-06)
* Rewrote and added documentation.

View File

@@ -0,0 +1,13 @@
<?php
$script = <<<EOD
CImage.compare({
"input1": "../img.php?src=car.png",
"input2": "../img.php?src=car.png&sharpen",
"input3": "../img.php?src=car.png&blur",
"input4": "../img.php?src=car.png&emboss",
"json": true,
"stack": false
});
EOD;
include __DIR__ . "/compare.php";

106
webroot/compare/compare.php Normal file
View File

@@ -0,0 +1,106 @@
<!doctype html>
<html lang=en>
<head>
<style>
body {
}
input[type=text] {
width: 400px;
}
.hidden {
display: none;
}
#wrap {
position: relative;
overflow: visible;
}
.stack {
position: absolute;
left: 0;
top: 0;
}
.area {
float: left;
padding: 1em;
background-color: #fff;
}
.top {
z-index: 10;
}
</style>
</head>
<body>
<h1>Compare images</h1>
<p>Add link to images and visually compare them. Change the link och press return to load the image. <a href="http://dbwebb.se/opensource/cimage">Read more...</a></p>
<form>
<p>
<label>Image 1: <input type="text" id="input1" data-id="1"></label> <img id="thumb1"></br>
<label>Image 2: <input type="text" id="input2" data-id="2"></label> <img id="thumb2"></br>
<label>Image 3: <input type="text" id="input3" data-id="3"></label> <img id="thumb3"></br>
<label>Image 4: <input type="text" id="input4" data-id="4"></label> <img id="thumb4"></br>
<label><input type="checkbox" id="viewDetails">Hide image details?</label><br/>
<label><input type="checkbox" id="stack">Stack images?</label>
</p>
</form>
<div id="buttonWrap" class="hidden">
<button id="button1" class="button" data-id="1">Image 1</button>
<button id="button2" class="button" data-id="2">Image 2</button>
<button id="button3" class="button" data-id="3">Image 3</button>
<button id="button4" class="button" data-id="4">Image 4</button>
</div>
<div id="wrap">
<div id="area1" class="area">
<code>Image 1</code><br>
<img id="img1">
<pre id="json1" class="json"></pre>
</div>
<div id="area2" class="area">
<code>Image 2</code><br>
<img id="img2">
<pre id="json2" class="json"></pre>
</div>
<div id="area3" class="area">
<code>Image 3</code><br>
<img id="img3">
<pre id="json3" class="json"></pre>
</div>
<div id="area4" class="area">
<code>Image 4</code><br>
<img id="img4">
<pre id="json4" class="json"></pre>
</div>
</div>
</body>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="../js/cimage.js"></script>
<script>
<?php
if (isset($script)) {
echo $script;
} else {
echo "CImage.compare({});";
} ?>
</script>
</html>

View File

@@ -341,7 +341,7 @@ verbose("area = $area");
/**
* skip-original, so - skip the original image and always process a new image
*/
$useOriginal = getDefined(array('save-as', 'sa'), false, true);
$useOriginal = getDefined(array('skip-original', 'so'), false, true);
verbose("use original = $useOriginal");
@@ -529,6 +529,15 @@ verbose("convolve = " . print_r($convolve, 1));
/**
* no-upscale, nu - Do not upscale smaller image to larger dimension.
*/
$upscale = getDefined(array('no-upscale', 'nu'), false, true);
verbose("upscale = $upscale");
/**
* Display image if verbose mode
*/
@@ -541,9 +550,21 @@ if ($verbose) {
unset($query['nc']);
unset($query['json']);
$url1 = '?' . htmlentities(urldecode(http_build_query($query)));
$url2 = '?' . urldecode(http_build_query($query));
echo <<<EOD
<a href=$url1><code>$url1</code></a><br>
<img src='{$url1}' />
<pre id="json"></pre>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
window.getDetails = function (url, id) {
$.getJSON(url, function(data) {
element = document.getElementById(id);
element.innerHTML = "filename: " + data.filename + "\\ncolors: " + data.colors + "\\nsize: " + data.size + "\\nwidth: " + data.width + "\\nheigh: " + data.height + "\\naspect-ratio: " + data.aspectRatio;
});
}
</script>
<script type="text/javascript">window.getDetails("{$url2}&json", "json")</script>
EOD;
}
@@ -565,6 +586,7 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
'fillToFit' => $fillToFit,
'crop' => $crop,
'area' => $area,
'upscale' => $upscale,
// Pre-processing, before resizing is done
'scale' => $scale,

BIN
webroot/img/car.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
webroot/img/car.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

163
webroot/js/cimage.js Normal file
View File

@@ -0,0 +1,163 @@
/**
* JavaScript utilities for CImage and img.php.
*/
window.CImage = (function(){
"use strict";
/**
* Init the compare page with details.
*/
function compareLoadImage(event) {
var img, json, button, area, deck, id;
id = this.dataset.id;
img = document.getElementById("img" + id);
json = document.getElementById("json" + id);
button = document.getElementById("button" + id);
area = document.getElementById("area" + id);
deck = document.getElementById("deck" + id);
if (this.value == "") {
// Clear image if input is cleared
button.setAttribute("disabled", "disabled");
area.classList.add("hidden");
button.classList.remove("selected");
return;
}
// Display image in its area
img.src = this.value;
area.classList.remove("hidden");
$.getJSON(this.value + "&json", function(data) {
json.innerHTML = "filename: " + data.filename + "\ncolors: " + data.colors + "\nsize: " + data.size + "\nwidth: " + data.width + "\nheigh: " + data.height + "\naspect-ratio: " + data.aspectRatio;
});
// Display image in overlay
button.removeAttribute("disabled");
button.classList.add("selected");
};
/**
* Init the compare page with details.
*/
function compareInit(options)
{
var elements, id, onTop, myEvent,
input1 = document.getElementById("input1"),
input2 = document.getElementById("input2"),
input3 = document.getElementById("input3"),
input4 = document.getElementById("input4"),
details = document.getElementById("viewDetails"),
stack = document.getElementById("stack"),
buttons = document.getElementById("buttonWrap");
/* img1 = document.getElementById("img1"),
img2 = document.getElementById("img2"),
img3 = document.getElementById("img3"),
img4 = document.getElementById("img4"),
img01 = document.getElementById("img01"),
img02 = document.getElementById("img02"),
img03 = document.getElementById("img03"),
img04 = document.getElementById("img04"),
json1 = document.getElementById("json1"),
json2 = document.getElementById("json2"),
json3 = document.getElementById("json3"),
json4 = document.getElementById("json4"),
json01 = document.getElementById("json01"),
json02 = document.getElementById("json02"),
json03 = document.getElementById("json03"),
json04 = document.getElementById("json04"),
button1 = document.getElementById("button1"),
button2 = document.getElementById("button2"),
button3 = document.getElementById("button3"),
button4 = document.getElementById("button4"),
area1 = document.getElementById("area1"),
area2 = document.getElementById("area2"),
area3 = document.getElementById("area3"),
area4 = document.getElementById("area4");*/
input1.addEventListener("change", compareLoadImage);
input2.addEventListener("change", compareLoadImage);
input3.addEventListener("change", compareLoadImage);
input4.addEventListener("change", compareLoadImage);
// Toggle json
details.addEventListener("change", function() {
var elements = document.querySelectorAll(".json");
for (var element of elements) {
element.classList.toggle("hidden");
}
});
// Do not show json as default
if (options.json === false) {
details.setAttribute("checked", "checked");
myEvent = new CustomEvent("change");
details.dispatchEvent(myEvent);
}
// Toggle stack
stack.addEventListener("change", function() {
var element,
elements = document.querySelectorAll(".area");
buttons.classList.toggle("hidden");
for (element of elements) {
element.classList.toggle("stack");
if (!element.classList.contains('hidden')) {
onTop = element;
}
}
onTop.classList.toggle("top");
console.log("Stacking");
});
// Stack as default
if (options.stack === true) {
stack.setAttribute("checked", "checked");
myEvent = new CustomEvent("change");
stack.dispatchEvent(myEvent);
}
// Button clicks
elements = document.querySelectorAll(".button");
for (var element of elements) {
element.addEventListener("click", function() {
var id = this.dataset.id,
area = document.getElementById("area" + id);
area.classList.toggle("top");
onTop.classList.toggle("top");
onTop = area;
console.log("button" + id);
});
}
input1.value = options.input1 || null;
input2.value = options.input2 || null;
input3.value = options.input3 || null;
input4.value = options.input4 || null;
compareLoadImage.call(input1);
compareLoadImage.call(input2);
compareLoadImage.call(input3);
compareLoadImage.call(input4);
console.log(options);
}
return {
"compare": compareInit
};
}());

View File

@@ -11,7 +11,7 @@
window.getDetails = function (url, id) {
$.getJSON(url, function(data) {
element = document.getElementById(id);
element.innerHTML = "width: " + data.width + "\nheigh: " + data.height + "\naspect-ratio: " + data.aspectRatio;
element.innerHTML = "filename: " + data.filename + "\ncolors: " + data.colors + "\nsize: " + data.size + "\nwidth: " + data.width + "\nheigh: " + data.height + "\naspect-ratio: " + data.aspectRatio;
});
}
</script>

View File

@@ -0,0 +1,41 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing issue 52 - Fill to fit fails with aspect ratio";
// Provide a short description of the testcase.
$description = "Verify that Fill To Fit resize strategy works with all variants of sizes.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = '&nc';
$testcase = array(
$nc . '&w=300&h=300&crop-to-fit',
$nc . '&w=300&ar=1&crop-to-fit',
$nc . '&w=300&ar=3&crop-to-fit',
$nc . '&h=300&ar=1&crop-to-fit',
$nc . '&h=300&ar=3&crop-to-fit',
$nc . '&w=50%&ar=1&crop-to-fit',
$nc . '&w=50%&ar=3&crop-to-fit',
$nc . '&h=50%&ar=1&crop-to-fit',
$nc . '&h=50%&ar=3&crop-to-fit',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,41 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing issue 52 - Fill to fit fails with aspect ratio";
// Provide a short description of the testcase.
$description = "Verify that Fill To Fit resize strategy works with all variants of sizes.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = '&nc';
$testcase = array(
$nc . '&w=300&h=300&stretch',
$nc . '&w=300&ar=1&stretch',
$nc . '&w=300&ar=3&stretch',
$nc . '&h=300&ar=1&stretch',
$nc . '&h=300&ar=3&stretch',
$nc . '&w=50%&ar=1&stretch',
$nc . '&w=50%&ar=3&stretch',
$nc . '&h=50%&ar=1&stretch',
$nc . '&h=50%&ar=3&stretch',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,41 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing issue 52 - Fill to fit fails with aspect ratio";
// Provide a short description of the testcase.
$description = "Verify that Fill To Fit resize strategy works with all variants of sizes.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = null; //&nc';
$testcase = array(
$nc . '&w=300&h=300&fill-to-fit',
$nc . '&w=300&ar=1&fill-to-fit',
$nc . '&w=300&ar=2&fill-to-fit',
$nc . '&h=300&ar=1&fill-to-fit',
$nc . '&h=300&ar=2&fill-to-fit',
$nc . '&w=50%&ar=1&fill-to-fit',
$nc . '&w=50%&ar=2&fill-to-fit',
$nc . '&h=50%&ar=1&fill-to-fit',
$nc . '&h=50%&ar=2&fill-to-fit',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,36 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing issue 58 - JSON reporting filesize";
// Provide a short description of the testcase.
$description = "Verify that JSON returns filesize of actual image.";
// Use these images in the test
$images = array(
'kodim08.png',
);
// For each image, apply these testcases
$testcase = array(
'&w=300',
'&w=300&h=300',
'&w=300&h=300&stretch',
'&w=300&h=300&crop-to-fit',
'&w=300&h=300&fill-to-fit',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,33 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing issue 60 - skip original";
// Provide a short description of the testcase.
$description = "Always use the original if suitable, use skip-original to force using the cached version.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$testcase = array(
'',
'&so',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,41 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing option crop";
// Provide a short description of the testcase.
$description = "Cropping parts of image";
// Use these images in the test
$images = array(
'kodim04.png',
);
// For each image, apply these testcases
$nc = "&nc"; //null; //&nc';
$testcase = array(
$nc . '&w=300',
$nc . '&w=300&crop=0,0,0,0',
$nc . '&crop=300,200,0,0',
$nc . '&crop=300,200,left,top',
$nc . '&crop=300,200,right,top',
$nc . '&crop=300,200,right,bottom',
$nc . '&crop=300,200,left,bottom',
$nc . '&crop=300,200,center,center',
$nc . '&crop=200,220,190,300',
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,58 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing option no-upscale";
// Provide a short description of the testcase.
$description = "Do not upscale image when original image (slice) is smaller than target image.";
// Use these images in the test
$images = array(
'car.png',
);
// For each image, apply these testcases
$nc = null; //"&nc"; //null; //&nc';
$testcase = array(
$nc . '&w=600',
$nc . '&w=600&no-upscale',
$nc . '&h=400',
$nc . '&h=400&no-upscale',
$nc . '&w=600&h=400',
$nc . '&w=600&h=400&no-upscale',
$nc . '&w=700&h=400&stretch',
$nc . '&w=700&h=400&no-upscale&stretch',
$nc . '&w=700&h=200&stretch',
$nc . '&w=700&h=200&no-upscale&stretch',
$nc . '&w=300&h=400&stretch',
$nc . '&w=300&h=400&no-upscale&stretch',
$nc . '&w=600&h=400&crop-to-fit',
$nc . '&w=600&h=400&no-upscale&crop-to-fit',
$nc . '&w=600&h=200&crop-to-fit',
$nc . '&w=600&h=200&no-upscale&crop-to-fit',
$nc . '&w=300&h=400&crop-to-fit',
$nc . '&w=300&h=400&no-upscale&crop-to-fit',
$nc . '&w=600&h=400&fill-to-fit',
$nc . '&w=600&h=400&no-upscale&fill-to-fit',
/*
$nc . '&w=600&ar=1.6',
$nc . '&w=600&ar=1.6&no-upscale',
$nc . '&h=400&ar=1.6',
$nc . '&h=400&ar=1.6&no-upscale',
*/
);
// Apply testcases and present results
include __DIR__ . "/template.php";

View File

@@ -0,0 +1,42 @@
<?php
// Include config for all testcases
include __DIR__ . "/config.php";
// The title of the test case
$title = "Testing option save-as - save image to another format";
// Provide a short description of the testcase.
$description = "";
// Use these images in the test
$images = array(
'car.png',
'car.gif',
'car.jpg',
'ball24.png',
'wider.jpg',
);
// For each image, apply these testcases
$nc = "&nc"; //null; //&nc';
$testcase = array(
$nc . '&w=300',
$nc . '&w=300&sa=jpg',
$nc . '&w=300&sa=png',
$nc . '&w=300&sa=gif',
$nc . '&w=300&palette',
$nc . '&w=300&sa=png&palette',
);
// Apply testcases and present results
include __DIR__ . "/template.php";