vendor/sulu/sulu/src/Sulu/Bundle/MediaBundle/Media/ImageConverter/ImagineImageConverter.php line 271
<?php/** This file is part of Sulu.** (c) Sulu GmbH** This source file is subject to the MIT license that is bundled* with this source code in the file LICENSE.*/namespace Sulu\Bundle\MediaBundle\Media\ImageConverter;use Imagine\Exception\RuntimeException;use Imagine\Filter\Basic\Autorotate;use Imagine\Image\ImageInterface;use Imagine\Image\ImagineInterface;use Imagine\Image\Palette\RGB;use Sulu\Bundle\MediaBundle\Entity\FileVersion;use Sulu\Bundle\MediaBundle\Entity\FormatOptions;use Sulu\Bundle\MediaBundle\Media\Exception\ImageProxyInvalidFormatOptionsException;use Sulu\Bundle\MediaBundle\Media\Exception\ImageProxyInvalidImageFormat;use Sulu\Bundle\MediaBundle\Media\Exception\InvalidFileTypeException;use Sulu\Bundle\MediaBundle\Media\ImageConverter\Cropper\CropperInterface;use Sulu\Bundle\MediaBundle\Media\ImageConverter\Focus\FocusInterface;use Sulu\Bundle\MediaBundle\Media\ImageConverter\Scaler\ScalerInterface;use Sulu\Bundle\MediaBundle\Media\Storage\StorageInterface;/*** Sulu imagine converter for media.*/class ImagineImageConverter implements ImageConverterInterface{/*** @var ImagineInterface*/private $imagine;/*** @var ImagineInterface*/private $svgImagine;/*** @var StorageInterface*/private $storage;/*** @var MediaImageExtractorInterface*/private $mediaImageExtractor;/*** @var TransformationPoolInterface*/private $transformationPool;/*** @var FocusInterface*/private $focus;/*** @var ScalerInterface*/private $scaler;/*** @var CropperInterface*/private $cropper;/*** @var array*/private $formats;/*** @var array*/private $supportedMimeTypes;public function __construct(ImagineInterface $imagine,StorageInterface $storage,MediaImageExtractorInterface $mediaImageExtractor,TransformationPoolInterface $transformationPool,FocusInterface $focus,ScalerInterface $scaler,CropperInterface $cropper,array $formats,array $supportedMimeTypes,?ImagineInterface $svgImagine = null) {$this->imagine = $imagine;$this->storage = $storage;$this->mediaImageExtractor = $mediaImageExtractor;$this->transformationPool = $transformationPool;$this->focus = $focus;$this->scaler = $scaler;$this->cropper = $cropper;$this->formats = $formats;$this->supportedMimeTypes = $supportedMimeTypes;$this->svgImagine = $svgImagine;}public function getSupportedOutputImageFormats(?string $mimeType): array{if (!$mimeType) {return [];}foreach ($this->supportedMimeTypes as $supportedMimeType) {if (\fnmatch($supportedMimeType, $mimeType)) {$preferredExtension = 'jpg';switch ($mimeType) {case 'image/png':$preferredExtension = 'png';break;case 'image/svg+xml':case 'image/svg':$preferredExtension = 'png';if ($this->svgImagine) {$preferredExtension = 'svg';}break;case 'image/webp':$preferredExtension = 'webp';break;case 'image/gif':$preferredExtension = 'gif';break;}return \array_unique([$preferredExtension,'jpg','gif','png','webp',]);}}return [];}public function convert(FileVersion $fileVersion, $formatKey, $imageFormat){$imageResource = $this->mediaImageExtractor->extract($this->storage->load($fileVersion->getStorageOptions()),$fileVersion->getMimeType());$imagine = $this->imagine;if ('svg' === $imageFormat && $this->svgImagine) {$imagine = $this->svgImagine;}try {$image = $imagine->read($imageResource);} catch (RuntimeException $e) {throw new InvalidFileTypeException($e->getMessage(), $e);}$image = $this->toRGB($image);$image = $this->autorotate($image);$format = $this->getFormat($formatKey);$cropParameters = $this->getCropParameters($image,$fileVersion->getFormatOptions()->get($formatKey),$this->formats[$formatKey]);if (isset($cropParameters)) {$image = $this->applyFormatCrop($image, $cropParameters);} elseif (isset($format['scale']) && ImageInterface::THUMBNAIL_INSET !== $format['scale']['mode']) {$image = $this->applyFocus($image, $fileVersion, $format['scale']);}if (isset($format['scale'])) {$image = $this->applyScale($image, $format['scale']);}if (isset($format['transformations'])) {$image = $this->applyTransformations($image, $format['transformations']);}$image->strip();try {// Set Interlacing to plane for smaller image size.if (1 == \count($image->layers())) {$image->interlace(ImageInterface::INTERLACE_PLANE);}} catch (RuntimeException $exception) {// ignore exceptions here (some imagine adapter does not implement this)}$imagineOptions = $format['options'];return $image->get($imageFormat,$this->getOptionsFromImage($image, $imageFormat, $imagineOptions));}/*** Applies an array of transformations on a passed image.** @param array $tansformations** @return ImageInterface The modified image** @throws ImageProxyInvalidFormatOptionsException*/private function applyTransformations(ImageInterface $image, $tansformations){foreach ($tansformations as $transformation) {if (!isset($transformation['effect'])) {throw new ImageProxyInvalidFormatOptionsException('Effect not found');}$image = $this->modifyAllLayers($image,function(ImageInterface $layer) use ($transformation) {return $this->transformationPool->get($transformation['effect'])->execute($layer,$transformation['parameters']);});}return $image;}/*** Crops a given image according to given parameters.** @param ImageInterface $image The image to crop* @param array $cropParameters The parameters which define the area to crop** @return ImageInterface The cropped image*/private function applyFormatCrop(ImageInterface $image, array $cropParameters){return $this->modifyAllLayers($image,function(ImageInterface $layer) use ($cropParameters) {return $this->cropper->crop($layer,$cropParameters['x'],$cropParameters['y'],$cropParameters['width'],$cropParameters['height']);});}/*** Crops the given image according to the focus point defined in the file version.** @return ImageInterface*/private function applyFocus(ImageInterface $image, FileVersion $fileVersion, array $scale){return $this->modifyAllLayers($image,function(ImageInterface $layer) use ($fileVersion, $scale) {return $this->focus->focus($layer,$fileVersion->getFocusPointX(),$fileVersion->getFocusPointY(),$scale['x'],$scale['y']);});}/*** Scales a given image according to the information passed as the second argument.** @param array $scale** @return ImageInterface*/private function applyScale(ImageInterface $image, $scale){return $this->modifyAllLayers($image,function(ImageInterface $layer) use ($scale) {return $this->scaler->scale($layer,$scale['x'],$scale['y'],$scale['mode'],$scale['forceRatio'],$scale['retina']);});}/*** Ensures that the color mode of the passed image is RGB.** @return ImageInterface $image The modified image*/private function toRGB(ImageInterface $image){if ('cmyk' == $image->palette()->name()) {$image->usePalette(new RGB());}return $image;}/*** Autorotate based on metadata of an image.** @return ImageInterface*/private function autorotate(ImageInterface $image){$autorotateFilter = new Autorotate();return $autorotateFilter->apply($image);}/*** Constructs the parameters for the cropper. Returns null when* the image should not be cropped.** @param FormatOptions|null $formatOptions** @return ?array*/private function getCropParameters(ImageInterface $image, $formatOptions, array $format){if (isset($formatOptions)) {$parameters = ['x' => $formatOptions->getCropX(),'y' => $formatOptions->getCropY(),'width' => $formatOptions->getCropWidth(),'height' => $formatOptions->getCropHeight(),];if ($this->cropper->isValid($image,$parameters['x'],$parameters['y'],$parameters['width'],$parameters['height'],$format)) {return $parameters;}}return null;}/*** Applies a callback to every layer of an image and returns the resulting image.** @param callable $modifier The callable to apply to all layers** @return ImageInterface*/private function modifyAllLayers(ImageInterface $image, callable $modifier){try {$layers = $image->layers();} catch (RuntimeException $exception) {$layers = [];}if (\count($layers) > 1) {$countLayer = 0;$image->layers()->coalesce();/** @var ImageInterface $temporaryImage */$temporaryImage = null;foreach ($image->layers() as $layer) {++$countLayer;$layer = \call_user_func($modifier, $layer);if (1 === $countLayer) {$temporaryImage = clone $layer; // use first layer as main image} else {$temporaryImage->layers()->add($layer);}}$image = $temporaryImage;} else {$image = \call_user_func($modifier, $image);}return $image;}/*** Return the options for the given format.** @param string $formatKey** @return array** @throws ImageProxyInvalidImageFormat*/private function getFormat($formatKey){if (!isset($this->formats[$formatKey])) {throw new ImageProxyInvalidImageFormat('Format was not found');}return $this->formats[$formatKey];}/*** @param string $imageExtension* @param array $imagineOptions** @return array*/private function getOptionsFromImage(ImageInterface $image, $imageExtension, $imagineOptions){$options = [];if ('gif' == $imageExtension && \count($image->layers()) > 1) {$options['animated'] = true;$options['optimize'] = true;}return \array_merge($options, $imagineOptions);}}