From 90bfced741cdd34ad3e1ad78e7e27f8f97699195 Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Mon, 15 Dec 2014 15:41:51 +0100 Subject: [PATCH] Added option - no-upscale, nu - as resizing strategy to decline upscaling of smaller images. Fix #61. --- CImage.php | 89 +++++++++++++++++-------- README.md | 2 + webroot/img.php | 10 +++ webroot/test/test_option-no-upscale.php | 50 ++++++++++++++ 4 files changed, 125 insertions(+), 26 deletions(-) create mode 100644 webroot/test/test_option-no-upscale.php diff --git a/CImage.php b/CImage.php index a4cecae..9880fef 100644 --- a/CImage.php +++ b/CImage.php @@ -231,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. */ @@ -443,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, @@ -949,6 +958,7 @@ class CImage && !$this->rotateAfter && !$this->autoRotate && !$this->bgColor + && ($this->upscale === self::UPSCALE_DEFAULT) ) { $this->log("Using original image."); $this->output($this->pathToImage); @@ -1027,11 +1037,16 @@ class CImage $convolve = 'convolve' . 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; @@ -1285,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)) { @@ -1302,7 +1318,6 @@ class CImage // 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']; @@ -1312,22 +1327,26 @@ class CImage 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); - imagecopy($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight); - $this->image = $imageResized; - $this->width = $this->newWidth; - $this->height = $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."); + } 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; + } } 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; @@ -1342,23 +1361,41 @@ class CImage $posY = round(($this->newHeight - $this->fillHeight) / 2); } - $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); - $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."); + $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); + $this->image = $imageResized; + $this->width = $this->newWidth; + $this->height = $this->newHeight; + + } 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); + $this->image = $imageResized; + $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); - $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."); + } 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; diff --git a/README.md b/README.md index f0964c4..55ec709 100644 --- a/README.md +++ b/README.md @@ -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 `fill-to-fit` to get the smaller image centered on a larger canvas. | @@ -280,6 +281,7 @@ Revision history v0.6.x (latest) +* 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. diff --git a/webroot/img.php b/webroot/img.php index 4b5706f..3d7a942 100644 --- a/webroot/img.php +++ b/webroot/img.php @@ -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 */ @@ -565,6 +574,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, diff --git a/webroot/test/test_option-no-upscale.php b/webroot/test/test_option-no-upscale.php new file mode 100644 index 0000000..96f8970 --- /dev/null +++ b/webroot/test/test_option-no-upscale.php @@ -0,0 +1,50 @@ +