diff --git a/Neos.Media/Classes/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingy.php b/Neos.Media/Classes/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingy.php new file mode 100644 index 00000000000..50f84153fd5 --- /dev/null +++ b/Neos.Media/Classes/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingy.php @@ -0,0 +1,188 @@ +getWidth() > $maximumWidth) { + $newDimensions = $newDimensions->widen($maximumWidth); + } + + if ($maximumHeight !== null && $newDimensions->getHeight() > $maximumHeight) { + $newDimensions = $newDimensions->heighten($maximumHeight); + } + + return $newDimensions; + } + + /** + * @param BoxInterface $originalDimensions + * @param integer $requestedWidth + * @param integer $requestedHeight + * @param bool $allowUpScaling + * @param string $ratioMode + * @return BoxInterface + */ + protected static function calculateWithFixedDimensions(BoxInterface $originalDimensions, int $requestedWidth, int $requestedHeight, bool $allowUpScaling = false, string $ratioMode = ImageInterface::RATIOMODE_INSET): BoxInterface + { + if ($ratioMode === ImageInterface::RATIOMODE_OUTBOUND) { + return self::calculateOutboundBox($originalDimensions, $requestedWidth, $requestedHeight, $allowUpScaling); + } + + $newDimensions = clone $originalDimensions; + + $ratios = [ + $requestedWidth / $originalDimensions->getWidth(), + $requestedHeight / $originalDimensions->getHeight() + ]; + + $ratio = min($ratios); + $newDimensions = $newDimensions->scale($ratio); + + if ($allowUpScaling === false && $originalDimensions->contains($newDimensions) === false) { + return clone $originalDimensions; + } + + return $newDimensions; + } + + /** + * Calculate the final dimensions for an outbound box. usually exactly the requested width and height unless that + * would require upscaling and it is not allowed. + * + * @param BoxInterface $originalDimensions + * @param integer $requestedWidth + * @param integer $requestedHeight + * @param bool $allowUpScaling + * @return BoxInterface + */ + protected static function calculateOutboundBox(BoxInterface $originalDimensions, int $requestedWidth, int $requestedHeight, bool $allowUpScaling): BoxInterface + { + $newDimensions = new Box($requestedWidth, $requestedHeight); + + if ($allowUpScaling === true || $originalDimensions->contains($newDimensions) === true) { + return $newDimensions; + } + + // We need to make sure that the new dimensions are such that no upscaling is needed. + $ratios = [ + $originalDimensions->getWidth() / $requestedWidth, + $originalDimensions->getHeight() / $requestedHeight + ]; + + $ratio = min($ratios); + $newDimensions = $newDimensions->scale($ratio); + + return $newDimensions; + } + + /** + * Calculates new dimensions with a requested width applied. Takes upscaling into consideration. + * + * @param BoxInterface $originalDimensions + * @param integer $requestedWidth + * @param bool $allowUpScaling + * @return BoxInterface + */ + protected static function calculateScalingToWidth(BoxInterface $originalDimensions, int $requestedWidth, bool $allowUpScaling): BoxInterface + { + if ($allowUpScaling === false && $requestedWidth >= $originalDimensions->getWidth()) { + return $originalDimensions; + } + + $newDimensions = clone $originalDimensions; + $newDimensions = $newDimensions->widen($requestedWidth); + + return $newDimensions; + } + + /** + * Calculates new dimensions with a requested height applied. Takes upscaling into consideration. + * + * @param BoxInterface $originalDimensions + * @param integer $requestedHeight + * @param bool $allowUpScaling + * @return BoxInterface + */ + protected static function calculateScalingToHeight(BoxInterface $originalDimensions, int $requestedHeight, bool $allowUpScaling): BoxInterface + { + if ($allowUpScaling === false && $requestedHeight >= $originalDimensions->getHeight()) { + return $originalDimensions; + } + + $newDimensions = clone $originalDimensions; + $newDimensions = $newDimensions->heighten($requestedHeight); + + return $newDimensions; + } + + /** + * Calculates a resize dimension box that allows for outbound resize. + * The scaled image will be bigger than the requested dimensions in one dimension and then cropped. + * + * @param BoxInterface $imageSize + * @param BoxInterface $requestedDimensions + * @return BoxInterface + */ + public static function calculateFinalDimensions(BoxInterface $imageSize, BoxInterface $requestedDimensions, string $ratioMode = ImageInterface::RATIOMODE_INSET): BoxInterface + { + if ($ratioMode === ImageInterface::RATIOMODE_OUTBOUND) { + $ratios = [ + $requestedDimensions->getWidth() / $imageSize->getWidth(), + $requestedDimensions->getHeight() / $imageSize->getHeight() + ]; + + return $imageSize->scale(max($ratios)); + } + return $requestedDimensions; + } +} diff --git a/Neos.Media/Classes/Domain/Model/Adjustment/ResizeImageAdjustment.php b/Neos.Media/Classes/Domain/Model/Adjustment/ResizeImageAdjustment.php index c94d5c43e89..3c07bf5868a 100644 --- a/Neos.Media/Classes/Domain/Model/Adjustment/ResizeImageAdjustment.php +++ b/Neos.Media/Classes/Domain/Model/Adjustment/ResizeImageAdjustment.php @@ -288,7 +288,15 @@ public function setAllowUpScaling(bool $allowUpScaling): void */ public function canBeApplied(ImagineImageInterface $image) { - $expectedDimensions = $this->calculateDimensions($image->getSize()); + $expectedDimensions = ImageDimensionCalculationHelperThingy::calculateRequestedDimensions( + $image->getSize(), + $this->width, + $this->height, + $this->maximumWidth, + $this->maximumHeight, + $this->allowUpScaling ?? false, + $this->ratioMode ?? ImageInterface::RATIOMODE_INSET + ); return ((string)$expectedDimensions !== (string)$image->getSize()); } @@ -311,132 +319,19 @@ public function applyToImage(ImagineImageInterface $image) * * @param BoxInterface $originalDimensions Dimensions of the unadjusted image * @return BoxInterface + * @deprecated use ImageDimensionCalculationHelperThingy::calculateRequestedDimensions instead */ protected function calculateDimensions(BoxInterface $originalDimensions): BoxInterface { - $newDimensions = clone $originalDimensions; - - switch (true) { - // height and width are set explicitly: - case ($this->width !== null && $this->height !== null): - $newDimensions = $this->calculateWithFixedDimensions($originalDimensions, $this->width, $this->height); - break; - // only width is set explicitly: - case ($this->width !== null): - $newDimensions = $this->calculateScalingToWidth($originalDimensions, $this->width); - break; - // only height is set explicitly: - case ($this->height !== null): - $newDimensions = $this->calculateScalingToHeight($originalDimensions, $this->height); - break; - } - - // We apply maximum dimensions and scale the new dimensions proportionally down to fit into the maximum. - if ($this->maximumWidth !== null && $newDimensions->getWidth() > $this->maximumWidth) { - $newDimensions = $newDimensions->widen($this->maximumWidth); - } - - if ($this->maximumHeight !== null && $newDimensions->getHeight() > $this->maximumHeight) { - $newDimensions = $newDimensions->heighten($this->maximumHeight); - } - - return $newDimensions; - } - - /** - * @param BoxInterface $originalDimensions - * @param integer $requestedWidth - * @param integer $requestedHeight - * @return BoxInterface - */ - protected function calculateWithFixedDimensions(BoxInterface $originalDimensions, int $requestedWidth, int $requestedHeight): BoxInterface - { - if ($this->ratioMode === ImageInterface::RATIOMODE_OUTBOUND) { - return $this->calculateOutboundBox($originalDimensions, $requestedWidth, $requestedHeight); - } - - $newDimensions = clone $originalDimensions; - - $ratios = [ - $requestedWidth / $originalDimensions->getWidth(), - $requestedHeight / $originalDimensions->getHeight() - ]; - - $ratio = min($ratios); - $newDimensions = $newDimensions->scale($ratio); - - if ($this->getAllowUpScaling() === false && $originalDimensions->contains($newDimensions) === false) { - return clone $originalDimensions; - } - - return $newDimensions; - } - - /** - * Calculate the final dimensions for an outbound box. usually exactly the requested width and height unless that - * would require upscaling and it is not allowed. - * - * @param BoxInterface $originalDimensions - * @param integer $requestedWidth - * @param integer $requestedHeight - * @return BoxInterface - */ - protected function calculateOutboundBox(BoxInterface $originalDimensions, int $requestedWidth, int $requestedHeight): BoxInterface - { - $newDimensions = new Box($requestedWidth, $requestedHeight); - - if ($this->getAllowUpScaling() === true || $originalDimensions->contains($newDimensions) === true) { - return $newDimensions; - } - - // We need to make sure that the new dimensions are such that no upscaling is needed. - $ratios = [ - $originalDimensions->getWidth() / $requestedWidth, - $originalDimensions->getHeight() / $requestedHeight - ]; - - $ratio = min($ratios); - $newDimensions = $newDimensions->scale($ratio); - - return $newDimensions; - } - - /** - * Calculates new dimensions with a requested width applied. Takes upscaling into consideration. - * - * @param BoxInterface $originalDimensions - * @param integer $requestedWidth - * @return BoxInterface - */ - protected function calculateScalingToWidth(BoxInterface $originalDimensions, int $requestedWidth): BoxInterface - { - if ($this->getAllowUpScaling() === false && $requestedWidth >= $originalDimensions->getWidth()) { - return $originalDimensions; - } - - $newDimensions = clone $originalDimensions; - $newDimensions = $newDimensions->widen($requestedWidth); - - return $newDimensions; - } - - /** - * Calculates new dimensions with a requested height applied. Takes upscaling into consideration. - * - * @param BoxInterface $originalDimensions - * @param integer $requestedHeight - * @return BoxInterface - */ - protected function calculateScalingToHeight(BoxInterface $originalDimensions, int $requestedHeight): BoxInterface - { - if ($this->getAllowUpScaling() === false && $requestedHeight >= $originalDimensions->getHeight()) { - return $originalDimensions; - } - - $newDimensions = clone $originalDimensions; - $newDimensions = $newDimensions->heighten($requestedHeight); - - return $newDimensions; + return ImageDimensionCalculationHelperThingy::calculateRequestedDimensions( + $originalDimensions, + $this->width, + $this->height, + $this->maximumWidth, + $this->maximumHeight, + $this->allowUpScaling ?? false, + $this->ratioMode ?? ImageInterface::RATIOMODE_INSET + ); } /** @@ -456,22 +351,31 @@ protected function resize(ImagineImageInterface $image, string $mode = ImageInte throw new \InvalidArgumentException('Invalid mode specified', 1574686891); } - $imageSize = $image->getSize(); - $requestedDimensions = $this->calculateDimensions($imageSize); + $originalDimensions = $image->getSize(); - $image->strip(); + $requestedDimensions = ImageDimensionCalculationHelperThingy::calculateRequestedDimensions( + $originalDimensions, + $this->width, + $this->height, + $this->maximumWidth, + $this->maximumHeight, + $this->allowUpScaling ?? false, + $this->ratioMode ?? ImageInterface::RATIOMODE_INSET + ); - $resizeDimensions = $requestedDimensions; - if ($mode === ImageInterface::RATIOMODE_OUTBOUND) { - $resizeDimensions = $this->calculateOutboundScalingDimensions($imageSize, $requestedDimensions); - } + $finalDimensions = ImageDimensionCalculationHelperThingy::calculateFinalDimensions( + $originalDimensions, + $requestedDimensions, + $this->ratioMode + ); - $image->resize($resizeDimensions, $filter); + $image->strip(); + $image->resize($finalDimensions, $filter); if ($mode === ImageInterface::RATIOMODE_OUTBOUND) { $image->crop(new Point( - max(0, round(($resizeDimensions->getWidth() - $requestedDimensions->getWidth()) / 2)), - max(0, round(($resizeDimensions->getHeight() - $requestedDimensions->getHeight()) / 2)) + max(0, round(($finalDimensions->getWidth() - $requestedDimensions->getWidth()) / 2)), + max(0, round(($finalDimensions->getHeight() - $requestedDimensions->getHeight()) / 2)) ), $requestedDimensions); } @@ -485,14 +389,14 @@ protected function resize(ImagineImageInterface $image, string $mode = ImageInte * @param BoxInterface $imageSize * @param BoxInterface $requestedDimensions * @return BoxInterface + * @deprecated use ImageDimensionCalculationHelperThingy::calculateFinalDimensions instead */ protected function calculateOutboundScalingDimensions(BoxInterface $imageSize, BoxInterface $requestedDimensions): BoxInterface { - $ratios = [ - $requestedDimensions->getWidth() / $imageSize->getWidth(), - $requestedDimensions->getHeight() / $imageSize->getHeight() - ]; - - return $imageSize->scale(max($ratios)); + return ImageDimensionCalculationHelperThingy::calculateFinalDimensions( + $imageSize, + $requestedDimensions, + $this->ratioMode + ); } } diff --git a/Neos.Media/Classes/Domain/Model/ThumbnailGenerator/ImageThumbnailGenerator.php b/Neos.Media/Classes/Domain/Model/ThumbnailGenerator/ImageThumbnailGenerator.php index 1710a6f5bff..692646a9be6 100644 --- a/Neos.Media/Classes/Domain/Model/ThumbnailGenerator/ImageThumbnailGenerator.php +++ b/Neos.Media/Classes/Domain/Model/ThumbnailGenerator/ImageThumbnailGenerator.php @@ -58,7 +58,7 @@ public function refresh(Thumbnail $thumbnail) { try { /** - * @todo ... add additional crop to ensure that the focal point is + * @todo ... add additional crop to ensure that the focal point is in view * in view after resizing ... needs common understanding wit * the thumbnail service here: Packages/Neos/Neos.Media/Classes/Domain/Service/ThumbnailService.php:151 */ diff --git a/Neos.Media/Tests/Unit/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingyTest.php b/Neos.Media/Tests/Unit/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingyTest.php new file mode 100644 index 00000000000..afbfcbe0d4a --- /dev/null +++ b/Neos.Media/Tests/Unit/Domain/Model/Adjustment/ImageDimensionCalculationHelperThingyTest.php @@ -0,0 +1,177 @@ +