I tried to use the allow_upscale option on a thumbnail filter but the file did not upscale. So i debugged and found this in https://github.com/avalanche123/Imagine/blob/v0.6.1/lib/Imagine/Image/AbstractImage.php:
// if target width is larger than image width
// AND target height is longer than image height
if ($size->contains($imageSize)) {
return $thumbnail;
}
I do not see any short workaround on that as far as this is code form imagine.
I am not sure I get all the details, but as far as I can see the problem comes from imagine library hence it has to be fixed there.
I also noticed, that allow_upscale: true does not actually work, because the Imagine library does not actually allow images to be upscaled. In order for Imagine to always allow upscaling (or any scaling for that matter) the following change would have to be made:
In lib/Imagine/Image/AbstractImage.php change lines 48 - 67 from
// if target width is larger than image width
// AND target height is longer than image height
if ($size->contains($imageSize)) {
return $thumbnail;
}
if ($mode === ImageInterface::THUMBNAIL_INSET) {
$ratio = min($ratios);
} else {
$ratio = max($ratios);
}
if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) {
if (!$imageSize->contains($size)) {
$size = new Box(
min($imageSize->getWidth(), $size->getWidth()),
min($imageSize->getHeight(), $size->getHeight())
);
} else {
$imageSize = $thumbnail->getSize()->scale($ratio);
$thumbnail->resize($imageSize, $filter);
}
$thumbnail->crop(new Point(
max(0, round(($imageSize->getWidth() - $size->getWidth()) / 2)),
max(0, round(($imageSize->getHeight() - $size->getHeight()) / 2))
), $size);
} else {
if (!$imageSize->contains($size)) {
$imageSize = $imageSize->scale($ratio);
$thumbnail->resize($imageSize, $filter);
} else {
$imageSize = $thumbnail->getSize()->scale($ratio);
$thumbnail->resize($imageSize, $filter);
}
}
to
if ($mode === ImageInterface::THUMBNAIL_INSET) {
$ratio = min($ratios);
} else {
$ratio = max($ratios);
}
if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) {
$imageSize = $thumbnail->getSize()->scale($ratio);
$thumbnail->resize($imageSize, $filter);
$thumbnail->crop(new Point(
max(0, round(($imageSize->getWidth() - $size->getWidth()) / 2)),
max(0, round(($imageSize->getHeight() - $size->getHeight()) / 2))
), $size);
} else {
if (!$imageSize->contains($size)) {
$imageSize = $imageSize->scale($ratio);
$thumbnail->resize($imageSize, $filter);
} else {
$imageSize = $thumbnail->getSize()->scale($ratio);
$thumbnail->resize($imageSize, $filter);
}
}
@fritzmg could you do a pull request please?
Sure, however I don't think this should be the final solution. I just consider this as a quick fix for anyone who needs image upscaling through imagine/liip-imagine. All I do here is simply removing all the checks that would otherwise prevent image upscaling. The final solution should probably add an "upscale" parameter to the imagine bundle somewhere, though I don't know where it would fit.
Having the same issue.
My temporary solution is this:
card_image:
filters:
upscale: { min: [400, 200] }
thumbnail: { size: [400, 200], mode: outbound }
skarnl, your workaround works fine, thank you!
Any news about it ?
@JulienItard I think in the end @skarnl 's workaround is the way to go
The upscale filter is not quite sufficient. Consider the following:
An image of 200x100 needs to be thumbnailed to 300x300.
In this case I would suppose this config would do the trick:
thumbnail:
quality: 70
filters:
strip: ~
upscale:
min: [300, 300]
thumbnail:
size: [300, 300]
mode: outbound
But what it actually does is:
The upscale method works, but when you are dealing with user uploaded images you can't guarantee the images to always have the same aspect ratio. So in this case this does not solve the issue.
You know what, I made a custom filter, enjoy:
EDIT: I noticed this did not "center crop", so I added an origin to the crop filter.
<?php
namespace Bazookas\MediaBundle\Imagine\Filter\Loader;
use Imagine\Filter\Basic\Crop;
use Imagine\Filter\Basic\Resize;
use Imagine\Image\Box;
use Imagine\Image\BoxInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Point;
use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ThumbnailFilter implements LoaderInterface
{
/**
* @param ImageInterface $image
* @param array $options
*
* @return ImageInterface
*/
public function load(ImageInterface $image, array $options = array())
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setRequired(['width', 'height']);
$options = $optionsResolver->resolve($options);
// get the original image size and create a crop box
$size = $image->getSize();
$box = new Box($options['width'], $options['height']);
// determine scale
$size = $this->fillBox($size, $box);
// define filters
$resize = new Resize($size);
$origin = new Point(
floor(($size->getWidth() - $box->getWidth()) / 2),
floor(($size->getHeight() - $box->getHeight()) / 2)
);
$crop = new Crop($origin, $box);
// apply filters to image
$image = $resize->apply($image);
$image = $crop->apply($image);
return $image;
}
/**
* @param BoxInterface $sourceBox
* @param BoxInterface $targetBox
* @return BoxInterface
*/
private function fillBox(BoxInterface $sourceBox, BoxInterface $targetBox) {
$sourceAspect = $sourceBox->getWidth() / $sourceBox->getHeight();
$targetAspect = $targetBox->getWidth() / $targetBox->getHeight();
if ($sourceAspect > $targetAspect) {
return $sourceBox->heighten($targetBox->getHeight());
}
return $sourceBox->widen($targetBox->getWidth());
}
}
NOTE: only "outbound" cropping is supported here. Inbound cropping can be done with the above.
@robbeman A custom filter is the best solution, thank you for sharing!
For those looking to implement their own filters, possibly different from the above example, the steps required to do so are well documented:
http://symfony.com/doc/current/bundles/LiipImagineBundle/filters.html#custom-filters
See #1015.
Most helpful comment
Having the same issue.
My temporary solution is this: