From 5493d5fea68673678b33b2190b75f9c6c7918fd0 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Tue, 13 Feb 2018 06:02:30 -0500 Subject: [PATCH] Add @horst-n support for focus zoom setting in the ImageSizerEngines --- wire/core/ImageSizerEngine.php | 149 +++++++++++++++++- wire/core/ImageSizerEngineGD.php | 7 + wire/core/Pageimage.php | 1 - .../ImageSizerEngineAnimatedGif.module | 7 + .../ImageSizerEngineIMagick.module | 7 + 5 files changed, 164 insertions(+), 7 deletions(-) diff --git a/wire/core/ImageSizerEngine.php b/wire/core/ImageSizerEngine.php index 9887d33a..9d593e25 100755 --- a/wire/core/ImageSizerEngine.php +++ b/wire/core/ImageSizerEngine.php @@ -745,10 +745,29 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable $cropping = strtolower($cropping); if(strpos($cropping, ',')) { $cropping = explode(',', $cropping); - if(strpos($cropping[0], '%') !== false) $cropping[0] = round(min(100, max(0, $cropping[0]))) . '%'; - else $cropping[0] = (int) $cropping[0]; - if(strpos($cropping[1], '%') !== false) $cropping[1] = round(min(100, max(0, $cropping[1]))) . '%'; - else $cropping[1] = (int) $cropping[1]; + } else if(strpos($cropping, 'x') && preg_match('/^([pd])(\d+)x(\d+)(z\d+)?/', $cropping, $matches)) { + $cropping = array(0 => $matches[1], 1 => $matches[2]); + if(isset($matches[3])) $cropping[2] = (int) $matches[3]; + if($matches[1] == 'p') { + $cropping[0] .= '%'; + $cropping[0] .= '%'; + } + } + } + if(is_array($cropping)) { + if(strpos($cropping[0], '%') !== false) { + $cropping[0] = round(min(100, max(0, $cropping[0]))) . '%'; + } else { + $cropping[0] = (int) $cropping[0]; + } + if(strpos($cropping[1], '%') !== false) { + $cropping[1] = round(min(100, max(0, $cropping[1]))) . '%'; + } else { + $cropping[1] = (int) $cropping[1]; + } + if(isset($cropping[2])) { // zoom + $cropping[2] = (int) $cropping[2]; + if($cropping[2] < 2 || $cropping[2] > 99) unset($cropping[2]); } } @@ -778,8 +797,12 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable // crop name if custom center point is specified if(is_array($cropping)) { - // p = percent, d = pixel dimension - $cropping = (strpos($cropping[0], '%') !== false ? 'p' : 'd') . ((int) $cropping[0]) . 'x' . ((int) $cropping[1]); + // p = percent, d = pixel dimension, z = zoom + $zoom = isset($cropping[2]) ? (int) $cropping[2] : 0; + $cropping = + (strpos($cropping[0], '%') !== false ? 'p' : 'd') . + ((int) $cropping[0]) . 'x' . ((int) $cropping[1]); + if($zoom > 1 && $zoom < 100) $cropping .= "z$zoom"; } // if crop is TRUE or FALSE, we don't reflect that in the filename, so make it blank @@ -1673,6 +1696,120 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable return 4; } + /** + * Helper function to perform a cropExtra / cropBefore cropping + * + * Intended for use by the getFocusZoomCropDimensions() method + * + * @param string $focus (focus point in percent, like: 54.7%) + * @param int $sourceDimension (source image width or height) + * @param int $cropDimension (target crop-image width or height) + * + * @return int $position (crop position x or y in pixel) + * + */ + protected function getFocusZoomPosition($focus, $sourceDimension, $cropDimension) { + $focus = intval($focus); // string with float value and percent char, (needs to be converted to integer) + $source = 100; // the source-dimensions percent-value (100) + $target = ($cropDimension / $sourceDimension * 100); // the crop-dimensions percent-value + $rest = $source - $target; // the unused dimension-part-value in percent + $tmp = $focus + ($target / 2); // temp value + + // calculate the position in pixel ! + if($tmp >= 100) { + $position = $sourceDimension - $cropDimension; + } else if($tmp <= floor($rest / 2)) { + $position = 0; + } else { + $position = ceil(($sourceDimension - $cropDimension) / 100 * $focus); + } + + return $position; + } + + /** + * Get an array of the 4 dimensions necessary to perform a cropExtra / cropBefore cropping + * + * Intended for use by the resize() method + * + * @param int $zoom + * @param int $fullWidth + * @param int $fullHeight + * @param int $finalWidth + * @param int $finalHeight + * @return array + * + */ + protected function getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight) { + // validate & calculate / prepare params + $zoom = $zoom <= 70 ? $zoom : 70; // validate / correct the zoom value, it needs to be between 2 and 70 + $zoom = $zoom >= 2 ? $zoom : 2; + + // calculate the max crop dimensions + $ratioFinal = $finalWidth / $finalHeight; // get the ratio of the requested crop + $percentW = $finalWidth / $fullWidth * 100; // calculate percentage of the crop width in regard of the original width + $percentH = $finalHeight / $fullHeight * 100; // calculate percentage of the crop height in regard of the original height + if($percentW >= $percentH) { // check wich one is greater + $maxW = $fullWidth; // if percentW is greater, maxW becomes the original Width + $maxH = $fullWidth / $ratioFinal; // ... and maxH gets calculated via the ratio + + } else { + $maxH = $fullHeight; // if percentH is greater, maxH becomes the original Height + $maxW = $fullHeight * $ratioFinal; // ... and maxW gets calculated via the ratio + } + + // calculate the zoomed dimensions + $cropW = $maxW - ($maxW * $zoom / 100); // to get the final crop Width and Height, the amount for zoom-in + $cropH = $maxH - ($maxH * $zoom / 100); // needs to get stripped out + + // validate against the minimal dimensions + if(!$this->upscaling) { // if upscaling isn't allowed, we decrease the zoom, so that we get a crop with the min-Dimensions + if($cropW < $finalWidth) { + $cropW = $finalWidth; + $cropH = $finalWidth / $ratioFinal; + } + if($cropH < $finalHeight) { + $cropH = $finalHeight; + $cropW = $finalHeight * $ratioFinal; + } + } + + // calculate the crop positions + $tmpX = $this->getFocusZoomPosition($this->cropping[0], $fullWidth, $cropW); // calculate the x-position + $tmpY = $this->getFocusZoomPosition($this->cropping[1], $fullHeight, $cropH); // calculate the y-position + + return array( + 0 => (int) $tmpX, + 1 => (int) $tmpY, + 2 => (int) $cropW, + 3 => (int) $cropH + ); + } + + /** + * Get current zoom percentage setting or 0 if not set + * + * Value is determined from the $this->cropping array index 2 and is used only if index 0 and + * index 1 are percentages (and indicated as such with a percent sign). + * + * @return int + * + */ + protected function getFocusZoomPercent() { + // check if we have to proceed a zoomed focal point cropping, + // therefore we need index 0 and 1 to be strings with '%' sign included + // and index 2 to be an integer between 2 and 70 + $a = $this->cropping; + if(is_array($a) && isset($a[2]) && strpos($a[0], '%') !== false && strpos($a[1], '%') !== false) { + $zoom = (int) $a[2]; + if($zoom < 2) $zoom = 0; + if($zoom > 70) $zoom = 70; + } else { + $zoom = 0; + } + return $zoom; + } + /** * Module info: not-autoload * diff --git a/wire/core/ImageSizerEngineGD.php b/wire/core/ImageSizerEngineGD.php index 4325a918..9ed645f0 100755 --- a/wire/core/ImageSizerEngineGD.php +++ b/wire/core/ImageSizerEngineGD.php @@ -162,6 +162,13 @@ class ImageSizerEngineGD extends ImageSizerEngine { $isModified = true; } } + + $zoom = $this->getFocusZoomPercent(); + if($zoom > 1) { + // we need to configure a cropExtra call to respect the zoom factor + $this->cropExtra = $this->getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight); + $this->cropping = false; + } // if there is requested to crop _before_ resize, we do it here @horst if(is_array($this->cropExtra)) { diff --git a/wire/core/Pageimage.php b/wire/core/Pageimage.php index 465e7525..adef3009 100644 --- a/wire/core/Pageimage.php +++ b/wire/core/Pageimage.php @@ -594,7 +594,6 @@ class Pageimage extends Pagefile { if($options['cropping'] === true && empty($options['cropExtra']) && $options['focus'] && $this->hasFocus && $width && $height) { // crop to focus area $focus = $this->focus(); - $focus['zoom'] = 0; // not yet supported $options['cropping'] = array("$focus[left]%", "$focus[top]%", "$focus[zoom]"); $crop = ''; // do not add suffix diff --git a/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module b/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module index e052bcaa..7143f3da 100755 --- a/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module +++ b/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module @@ -127,6 +127,13 @@ class ImageSizerEngineAnimatedGif extends ImageSizerEngine { $this->setTimeLimit(120); + $zoom = $this->getFocusZoomPercent(); + if($zoom > 1) { + // we need to configure a cropExtra call to respect the zoom factor + $this->cropExtra = $this->getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight); + $this->cropping = false; + } + // if extra crop manipulation is requested, it is processed first if(is_array($this->cropExtra) && 4 == count($this->cropExtra)) { // crop before resize list($cropX, $cropY, $cropWidth, $cropHeight) = $this->cropExtra; diff --git a/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module b/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module index 904b91ea..421b0687 100755 --- a/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module +++ b/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module @@ -289,6 +289,13 @@ class ImageSizerEngineIMagick extends ImageSizerEngine { } } + $zoom = $this->getFocusZoomPercent(); + if($zoom > 1) { + // we need to configure a cropExtra call to respect the zoom factor + $this->cropExtra = $this->getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight); + $this->cropping = false; + } + if(is_array($this->cropExtra) && 4 == count($this->cropExtra)) { // crop before resize list($cropX, $cropY, $cropWidth, $cropHeight) = $this->cropExtra; #list($x, $y, $w, $h) = $this->cropExtra;