diff --git a/Classes/Domain/Model/ImageSource.php b/Classes/Domain/Model/ImageSource.php index a3ff756..f1d95d3 100644 --- a/Classes/Domain/Model/ImageSource.php +++ b/Classes/Domain/Model/ImageSource.php @@ -6,18 +6,20 @@ use Sitegeist\MediaComponents\Domain\Model\CropArea; use Sitegeist\MediaComponents\Domain\Model\SourceSet; use Sitegeist\MediaComponents\Interfaces\ConstructibleFromImage; -use SMS\FluidComponents\Domain\Model\FalImage; +use SMS\FluidComponents\Domain\Model\FalFile; use SMS\FluidComponents\Domain\Model\Image; use SMS\FluidComponents\Interfaces\ConstructibleFromArray; use SMS\FluidComponents\Interfaces\ConstructibleFromExtbaseFile; use SMS\FluidComponents\Interfaces\ConstructibleFromFileInterface; use SMS\FluidComponents\Interfaces\ConstructibleFromInteger; use SMS\FluidComponents\Interfaces\ConstructibleFromString; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Interfaces\ProcessableImage; use SMS\FluidComponents\Utility\ComponentArgumentConverter; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Domain\Model\FileReference; -use TYPO3\CMS\Extbase\Service\ImageService; class ImageSource implements ConstructibleFromArray, @@ -32,8 +34,6 @@ class ImageSource implements */ protected ?Image $image = null; - protected ?ImageService $imageService = null; - protected float $scale = 1.0; protected ?CropArea $crop = null; @@ -49,7 +49,6 @@ class ImageSource implements public function __construct( protected ?Image $originalImage = null ) { - $this->imageService = GeneralUtility::makeInstance(ImageService::class); $this->setCrop(new CropArea); } @@ -58,7 +57,7 @@ public static function fromArray(array $value): ImageSource $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); try { - $image = $argumentConverter->convertValueToType($value['originalImage'], Image::class); + $image = $argumentConverter->convertValueToType($value['originalImage'] ?? $value, Image::class); } catch (\SMS\FluidComponents\Exception\InvalidArgumentException) { // TODO better error handling here: // Image is not required, but invalid combination of parameters should @@ -76,20 +75,23 @@ public static function fromArray(array $value): ImageSource if (isset($value['media'])) { $imageSource->setMedia((string) $value['media']); } - if (isset($value['scale'])) { + if (isset($value['sizes'])) { $imageSource->setSizes((string) $value['sizes']); } - if (isset($value['crop']) || isset($value['srcset'])) { - $argumentConverter = GeneralUtility::makeInstance(ComponentArgumentConverter::class); - - if (isset($value['crop'])) { - $imageSource->setCrop($argumentConverter->convertValueToType($value['crop'], CropArea::class)); - } + if (isset($value['crop'])) { + $imageSource->setCrop( + $argumentConverter->convertValueToType($value['crop'], CropArea::class) + ); + } elseif ($image instanceof ImageWithCropVariants) { + $cropVariant = (isset($value['cropVariant'])) + ? $image->getCropVariant($value['cropVariant']) + : $image->getDefaultCrop(); + $imageSource->setCrop(new CropArea($cropVariant)); + } - if (isset($value['srcset'])) { - $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); - } + if (isset($value['srcset'])) { + $imageSource->setSrcset($argumentConverter->convertValueToType($value['srcset'], SourceSet::class)); } return $imageSource; @@ -164,6 +166,40 @@ public function setCrop(CropArea $crop): self return $this; } + public function getCroppedWidth(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getWidth(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getWidth()); + } + + return (int) round($this->crop->getArea()->getWidth() * $this->originalImage->getWidth()); + } + + public function getCroppedHeight(): ?int + { + if (!($this->originalImage instanceof ImageWithDimensions)) { + return null; + } + + if ($this->crop->getArea()->isEmpty()) { + $this->originalImage->getHeight(); + } + + if ($this->getOriginalImage() instanceof FalFile) { + return (int) round($this->crop->getArea()->makeAbsoluteBasedOnFile($this->getOriginalImage()->getFile())->getHeight()); + } + + return (int) round($this->crop->getArea()->getHeight() * $this->originalImage->getHeight()); + } + public function getImage(): Image { // TODO throw exception if image is not defined @@ -193,12 +229,14 @@ public function getDescription(): ?string public function getHeight(): ?int { - return $this->getImage()->getHeight(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getHeight() : null; } public function getWidth(): ?int { - return $this->getImage()->getWidth(); + $image = $this->getImage(); + return ($image instanceof ImageWithDimensions) ? $image->getWidth() : null; } public function getMedia(): ?string @@ -244,25 +282,17 @@ public function __toString(): string protected function processImage(): void { - $originalImage = $this->getOriginalImage(); - - if ($originalImage) { - $crop = null; - if ($this->getCrop()) { - $cropArea = $this->getCrop()->getArea(); - if (!$cropArea->isEmpty()) { - $crop = $cropArea->makeAbsoluteBasedOnFile($originalImage->getFile()); - } - } - - $processingInstructions = [ - 'width' => round($originalImage->getWidth() * $this->getScale()), - 'height' => round($originalImage->getHeight() * $this->getScale()), - 'fileExtension' => $this->getFormat(), - 'crop' => $crop - ]; - - $this->image = new FalImage($this->imageService->applyProcessingInstructions($originalImage->getFile(), $processingInstructions)); + // TODO implement runtime cache + $this->image = null; + + $original = $this->getOriginalImage(); + if ($original instanceof ProcessableImage && $original instanceof ImageWithDimensions) { + $this->image = $original->process( + (int) round($this->getCroppedWidth() * $this->getScale()), + (int) round($this->getCroppedHeight() * $this->getScale()), + $this->getFormat(), + $this->getCrop()->getArea(), + ); } } } diff --git a/Classes/ViewHelpers/Image/CropVariantViewHelper.php b/Classes/ViewHelpers/Image/CropVariantViewHelper.php index e058579..583ba50 100644 --- a/Classes/ViewHelpers/Image/CropVariantViewHelper.php +++ b/Classes/ViewHelpers/Image/CropVariantViewHelper.php @@ -3,23 +3,31 @@ namespace Sitegeist\MediaComponents\ViewHelpers\Image; -use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; -use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; -use TYPO3\CMS\Core\Resource\FileInterface; +use Sitegeist\MediaComponents\Domain\Model\CropArea; +use Sitegeist\MediaComponents\Domain\Model\ImageSource; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class CropVariantViewHelper extends AbstractViewHelper { public function initializeArguments(): void { - $this->registerArgument('image', FileInterface::class, 'FAL file'); + $this->registerArgument('image', ImageSource::class, 'Image object'); $this->registerArgument('name', 'string', 'name of the crop variant that should be used', false, 'default'); } - public function render(): Area + public function render(): CropArea { $this->arguments['image'] ??= $this->renderChildren(); - $cropVariantCollection = CropVariantCollection::create((string)$this->arguments['image']->getProperty('crop')); - return $cropVariantCollection->getCropArea($this->arguments['name']); + if (!($this->arguments['image']->getOriginalImage() instanceof ImageWithDimensions)) { + return $this->arguments['image']; + } + + if ($this->arguments['image']->getOriginalImage() instanceof ImageWithCropVariants) { + return new CropArea($this->arguments['image']->getOriginalImage()->getCropVariant($this->arguments['name'])); + } else { + return new CropArea; + } } } diff --git a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php index d3bd581..7e309c4 100644 --- a/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php +++ b/Classes/ViewHelpers/Image/Modify/ScaleViewHelper.php @@ -24,8 +24,8 @@ public function render(): ImageSource return $imageSource; } if ($this->arguments['height'] || $this->arguments['width']) { - $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getOriginalImage()->getHeight() : 1; - $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getOriginalImage()->getWidth() : 1; + $heightFactor = $this->arguments['height'] ? $this->arguments['height'] / $imageSource->getCroppedWidth() : 1; + $widthFactor = $this->arguments['width'] ? $this->arguments['width'] / $imageSource->getCroppedHeight() : 1; $scaleFactor = ($this->arguments['maxDimensions']) ? min($heightFactor, $widthFactor) : max($heightFactor, $widthFactor); diff --git a/Classes/ViewHelpers/Image/SrcsetViewHelper.php b/Classes/ViewHelpers/Image/SrcsetViewHelper.php index e340ec4..18d5064 100644 --- a/Classes/ViewHelpers/Image/SrcsetViewHelper.php +++ b/Classes/ViewHelpers/Image/SrcsetViewHelper.php @@ -5,8 +5,7 @@ use Sitegeist\MediaComponents\Domain\Model\ImageSource; use Sitegeist\MediaComponents\Domain\Model\SourceSet; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Service\ImageService; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class SrcsetViewHelper extends AbstractViewHelper @@ -24,6 +23,11 @@ public function render(): string return ''; } $this->arguments['imageSource'] ??= $this->renderChildren(); + + if (!($this->arguments['imageSource']->getOriginalImage() instanceof ImageWithDimensions)) { + return ''; + } + return self::generateSrcsetString($this->arguments['imageSource'], $this->arguments['srcset'], $this->arguments['base']); } @@ -32,12 +36,11 @@ public static function generateSrcsetString(ImageSource $imageSource, SourceSet $output = []; $base ??= $imageSource; $widths = $srcset->getSrcsetAndWidths($base->getWidth()); - $imageService = GeneralUtility::makeInstance(ImageService::class); $localImageSource = clone $imageSource; foreach ($widths as $widthDescriptor => $width) { - $localImageSource->setScale($width / $imageSource->getOriginalImage()->getWidth()); - $output[] = $imageService->getImageUri($localImageSource->getImage()->getFile()) . ' ' . $widthDescriptor; + $localImageSource->setScale($width / $localImageSource->getCroppedWidth()); + $output[] = (string) $localImageSource . ' ' . $widthDescriptor; } return implode(', ', $output); diff --git a/Resources/Private/Components/Image/Image.html b/Resources/Private/Components/Image/Image.html index 2d9dc95..933713e 100644 --- a/Resources/Private/Components/Image/Image.html +++ b/Resources/Private/Components/Image/Image.html @@ -12,6 +12,7 @@ + @@ -27,6 +28,9 @@ + + + + @@ -22,6 +23,9 @@ + + +