Skip to content

Commit

Permalink
Add CropResizer
Browse files Browse the repository at this point in the history
  • Loading branch information
core23 committed Mar 18, 2021
1 parent 54de03d commit 54e67bd
Show file tree
Hide file tree
Showing 4 changed files with 414 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/DependencyInjection/SonataMediaExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,16 @@ private function configureAdapters(ContainerBuilder $container, array $config):

private function configureResizers(ContainerBuilder $container, array $config): void
{
if ($container->hasParameter('sonata.media.resizer.crop.class')) {
$class = $container->getParameter('sonata.media.resizer.crop.class');
$definition = new Definition($class, [
new Reference('sonata.media.adapter.image.default'),
new Reference('sonata.media.metadata.proxy'),
]);
$definition->addTag('sonata.media.resizer');
$container->setDefinition('sonata.media.resizer.crop', $definition);
}

if ($container->hasParameter('sonata.media.resizer.simple.class')) {
$class = $container->getParameter('sonata.media.resizer.simple.class');
$definition = new Definition($class, [
Expand Down
166 changes: 166 additions & 0 deletions src/Resizer/CropResizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\MediaBundle\Resizer;

use Gaufrette\File;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Point;
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
use Sonata\MediaBundle\Model\MediaInterface;

/**
* @author Christian Gripp <[email protected]>
*/
final class CropResizer implements ResizerInterface
{
/**
* @var ImagineInterface
*/
private $adapter;

/**
* @var MetadataBuilderInterface
*/
private $metadata;

public function __construct(ImagineInterface $adapter, MetadataBuilderInterface $metadata)
{
$this->adapter = $adapter;
$this->metadata = $metadata;
}

public function resize(MediaInterface $media, File $in, File $out, $format, array $settings): void
{
if (!isset($settings['width'])) {
throw new \RuntimeException(sprintf(
'Width parameter is missing in context "%s" for provider "%s"',
$media->getContext(),
$media->getProviderName()
));
}

if (!isset($settings['height'])) {
throw new \RuntimeException(sprintf(
'Height parameter is missing in context "%s" for provider "%s"',
$media->getContext(),
$media->getProviderName()
));
}

$image = $this->adapter->load($in->getContent());

$sourceSize = $media->getBox();
$targetSize = $this->createTargetBox($settings);

if ($this->shouldModify($sourceSize, $targetSize)) {
$image = $this->cropImage($image, $sourceSize, $targetSize);
}

// Always change format and quality
$content = $image->get($format, [
'quality' => $settings['quality'],
]);

$out->setContent($content, $this->metadata->get($media, $out->getName()));
}

public function getBox(MediaInterface $media, array $settings)
{
$sourceSize = $media->getBox();
$targetSize = $this->createTargetBox($settings);

return new Box(
min($sourceSize->getWidth(), $targetSize->getWidth()),
min($sourceSize->getHeight(), $targetSize->getHeight())
);
}

/**
* @param array<string, mixed> $settings
*/
private function createTargetBox(array $settings): Box
{
return new Box($settings['width'], $settings['height']);
}

private function shouldModify(Box $sourceSize, Box $targetSize): bool
{
return !($sourceSize->getWidth() <= $targetSize->getWidth() && $sourceSize->getHeight() <= $targetSize->getHeight());
}

private function shouldResize(Box $sourceSize, Box $targetSize): bool
{
if ($sourceSize->getWidth() <= $targetSize->getWidth()) {
return false;
}

return $sourceSize->getHeight() > $targetSize->getHeight();
}

private function shouldCrop(Box $sourceSize, Box $targetSize): bool
{
return $sourceSize->getWidth() > $targetSize->getWidth() || $sourceSize->getHeight() > $targetSize->getHeight();
}

private function cropImage(ImageInterface $image, Box $sourceSize, Box $targetSize): ImageInterface
{
if ($this->shouldResize($sourceSize, $targetSize)) {
$scaleSize = $this->createBox($sourceSize, $targetSize, false);

$image = $image->thumbnail($scaleSize, 'outbound');

$sourceSize = $scaleSize;
}

if ($this->shouldCrop($sourceSize, $targetSize)) {
$cropSize = new Box(
min($sourceSize->getWidth(), $targetSize->getWidth()),
min($sourceSize->getHeight(), $targetSize->getHeight())
);

$point = new Point(
(int) (($sourceSize->getWidth() - $cropSize->getWidth()) / 2),
(int) (($sourceSize->getHeight() - $cropSize->getHeight()) / 2)
);

$image = $image->crop($point, $cropSize);
}

return $image;
}

private function createBox(Box $sourceSize, Box $targetSize, bool $smallest = true): Box
{
$widthRatio = (float) ($targetSize->getWidth() / $sourceSize->getWidth());
$heightRatio = (float) ($targetSize->getHeight() / $sourceSize->getHeight());

if (0.0 !== $widthRatio - $heightRatio) {
return $sourceSize->scale(
$smallest ? min($widthRatio, $heightRatio) : max($widthRatio, $heightRatio)
);
}

if ($targetSize->getHeight() >= $sourceSize->getHeight()) {
return $sourceSize;
}

if ($targetSize->getWidth() >= $sourceSize->getWidth()) {
return $sourceSize;
}

return $targetSize;
}
}
1 change: 1 addition & 0 deletions src/Resources/config/media.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sonata.media.resizer.crop.class">Sonata\MediaBundle\Resizer\CropResizer</parameter>
<parameter key="sonata.media.resizer.simple.class">Sonata\MediaBundle\Resizer\SimpleResizer</parameter>
<parameter key="sonata.media.resizer.square.class">Sonata\MediaBundle\Resizer\SquareResizer</parameter>
<parameter key="sonata.media.adapter.image.gd.class">Imagine\Gd\Imagine</parameter>
Expand Down
Loading

0 comments on commit 54e67bd

Please sign in to comment.