Mugo Web main content.

Custom filters for "scalemin" image variations in Ibexa 4.x

By: Maxim Strukov | April 12, 2023 | Imagine and ibexadxp

Having quality images on your site is important, and creating all the different versions of images for the various ways they will be used on your site is time-consuming and potentially frustrating. Letting browsers auto-render images to different sizes and shapes will cause pixelation and stretched images that don’t reflect well on your brand. The good news is that there are several tactics to automatically generate high-quality and specific versions of images to cover all the use cases.

Suppose we have a 100x100px area on a page and want to use an existing image to fill it.  A straightforward way to do this would be using the CSS property object-fit: cover. While this would technically work on its own, it's not sufficient. It doesn’t give us the quality of image we want. And if the image is much larger, we are needlessly increasing the page load time.

Instead, we are going to use the Ibexa image variations mechanism to create a specific version of the uploaded image to match the size of this area.

For example, if the uploaded image has a 300x200 resolution, it should be resized by the variation filter to 150x100, not 100x50. In other words, we need to resize the image based on the smallest fitting dimension.

You can find examples of images resized using this technique in one of our previous blog posts. 

Unfortunately, the existing variation filters don’t support that specific way of resizing. If we take the geometry/scaledownonly filter it will resize the image based on the biggest dimension, which doesn’t fit our requirement.

box_image:
    reference: ~
    filters:
        - { name: geometry/scaledownonly, params: [ 100,100 ] }

Since the existing filters don’t suit our purposes, we have to create our own custom filter. Let's call our new filter geometry/scalemin (named after the ImageMagick filter). Here’s how to set it up:

1. Create the loader service which will do the resizing calculations:

namespace App\Services\Imagine\Filter\Loader;

use Ibexa\Bundle\Core\Imagine\Filter\Loader\FilterLoaderWrapped;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Image\ImageInterface;

/**
 * Filter loader for geometry/scalemin filter. Based on geometry/scale filter.
 * It resizes the image based on the smallest fitting dimension.
 * Proxy to RelativeResizeFilterLoader.
 */
class GeometryScaleMinLoader extends FilterLoaderWrapped
{
    public function load(ImageInterface $image, array $options = array())
    {
        if (count($options) < 2) {
            throw new InvalidArgumentException('Missing width and/or height options');
        }

        list($width, $height) = $options;
        $size = $image->getSize();
        $ratioWidth = $width / $size->getWidth();
        $ratioHeight = $height / $size->getHeight();

        // We shall use the side which has the highest ratio with target value
        // - so using the opposite method comparing to ScaleFilterLoader
        if ($ratioWidth <= $ratioHeight) {
            $method = 'heighten';
            $value = $width;
        } else {
            $method = 'widen';
            $value = $height;
        }

        return $this->innerLoader->load($image, [$method => $value]);
    }
}

2. Add this service to the YAML configuration:

App\Services\Imagine\Filter\Loader\GeometryScaleMinLoader:
    parent: ibexa.image_alias.imagine.filter.loader.relative_scale
    tags:
        - { name: liip_imagine.filter.loader, loader: "geometry/scalemin" }

The filter identifier that will be specified in the image variation is geometry/scalemin.

3. Add our custom image variation:

ibexa:
    system:
        site:
            image_variations:
                box_image:
                    reference: ~
                    filters:
                        - { name: geometry/scalemin, params: [ 100,100 ] }

If the image variation with the ID box_image already existed, we need to clear its cache to get the images of the new version generated:

php bin/console liip:imagine:cache:remove --filter=box_image

Now we can see how that looks on the front end. The original image uploaded to the Ibexa content object has 1024x682 dimensions.

Image file properties

Our custom variation filter then resizes it to 150x100 (you can see it in the image properties on the left screenshot below) to fit the 100x100 area on the page (the right screenshot below).

The image properties when you hover your mouse over the image in the Elements tab of the browser DevTools.

properly sized image of a nursing facility room

This is how the 100x100 box on the page looks when it's filled with the 150x100 image. To get the exact crop, we can either use the object-fit: cover CSS property or add some more code to do the crop as part of the image variation.

 

You can use similar steps to create all kinds of variations and custom filters for images on your site. Setting this up will save you time in the long run and maintain the quality of images on your site to keep it looking and performing its best.